Skip to content

Add a GitHub Actions workflow publishing prebuilt desktop app bundles (ShinyApps store) - #124

Open
fentouxungui wants to merge 1 commit into
LieberInstitute:develfrom
fentouxungui:devel
Open

fentouxungui wants to merge 1 commit into
LieberInstitute:develfrom
fentouxungui:devel

Conversation

@fentouxungui

Copy link
Copy Markdown

PR title

Add a GitHub Actions workflow publishing prebuilt desktop app bundles (ShinyApps store)

PR body

Summary

This PR adds one GitHub Actions workflow — .github/workflows/spatiallibd-build-app-bundle.yml — that packages spatialLIBD into a prebuilt, self-contained desktop app bundle for Windows and macOS and attaches it to a GitHub Release. Those bundles let people run the spatialLIBD Shiny app as a desktop application with no R/Bioconductor installation and no install.packages(), through an open-source desktop app store called ShinyApps.

It is a CI-only addition: no package code, DESCRIPTION, NAMESPACE, R/, man/, data/, inst/, tests, or vignettes are changed. There is no effect on the Bioconductor build, R CMD check, or BiocCheck.

What is ShinyApps?

ShinyApps is an open-source desktop app store for R/Shiny apps (built on shinyelectron, AGPL-3.0). A lightweight Electron shell ships once with a pinned R 4.6.1 runtime; users then install, run, update, and uninstall individual Shiny apps on demand from an online catalog. Users get a real desktop app without any R/Bioconductor setup, and app authors distribute updates without reshipping the shell.

What this workflow does

  • Triggers: release: published, or manual workflow_dispatch (with an optional tag).
  • Matrix: windows-latest → win-x64, macos-14 → mac-arm64, on R 4.6.1 (matching the store's runtime).
  • Build: runs R CMD build --no-build-vignettes --no-manual, then resolves the dependency closure from DESCRIPTION (Depends/Imports/LinkingTo) against CRAN + Bioconductor (BiocManager::repositories()) and installs the binaries into the bundle.
  • Assemble: writes the entry app/app.R and a manifest.json, then zips and computes a sha256.
  • Publish: attaches <id>-<version>-<platform>.zip to the Release.

Artifact produced (per platform):

spatiallibd-<version>-<platform>.zip
├─ app/app.R          # library(spatialLIBD); options(...); spatialLIBD::run_app()
├─ rlib/              # prebuilt binary dependency closure (pinned to R 4.6.1)
└─ manifest.json      # id, version, platform, entry, r_version, package list

Contract compliance

spatialLIBD already satisfies the store's entry contract:

  • run_app() returns a shiny.appobj and does not call shiny::runApp(). It ends with golem::with_golem_options(app = shinyApp(app_ui, app_server), golem_opts = ...), and the man page documents @return A shiny.appobj that contains the input data — so the shell can host it directly. (Same pattern as the iSEE and UCSCXenaShiny apps already in the store.)
  • It builds as a standard R source package (NeedsCompilation: no).
  • run_app()'s default docs_path = system.file("app", "www", package = "spatialLIBD") resolves inside the installed bundle (inst/app/www/ ships documentation_spe.md, documentation_sce_layer.md, favicon.ico, footer.html, README.md), so the app's own documentation renders.

Why the closure is resolved explicitly (spatialLIBD-specific)

Most dependencies are declared in DESCRIPTION and resolve cleanly from CRAN + Bioconductor. Scanning R/*.R for runtime pkg:: references surfaced three packages the app uses but does not declare, so the workflow installs them explicitly (all CRAN) to make the bundle self-contained:

  • Polychrome — the palette backend behind paletteer::paletteer_d("Polychrome::palette36", ...) (app_ui.R, app_server.R, get_colors.R);
  • RColorBrewer — default colour scales in layer_stat_cor_plot() / gene_set_enrichment_plot() (it is in Suggests, not Imports);
  • markdown — rendering the in-app docs via shiny::includeMarkdown().

Reference to HDF5Array, lobstr, scran, and visiumStitched appear only inside roxygen examples, so they are not runtime dependencies. The workflow excludes only R base-priority packages and keeps recommended ones, so the bundle launches offline.

Testing

I integrated a fork into ShinyApps and built the bundles end-to-end:

One honest note on first run: exactly like the package locally, the default run_app() downloads the LIBD Visium objects (spe, sce_layer, modeling_results) from ExperimentHub (Dropbox fallback) via BiocFileCache before the server starts listening (~GBs, one time, cached afterwards). The desktop shell ships a configurable startup timeout for this; after the first successful download, subsequent launches are fast.

How maintainers can test it

  1. Download the ShinyApps installer from https://github.com/fentouxungui/ShinyApps/releases (Windows .exe / macOS .dmg).
  2. Launch ShinyApps, find the spatialLIBD card, click Install → Run.
  3. Explore exactly as at http://spatial.libd.org/spatialLIBD/ (the shell only replaces the browser host).

The store catalog is public: https://fentouxungui.github.io/ShinyApps/catalog.json.

Impact and maintenance

  • CI cost: one job per platform (2 total) on release or manual dispatch only — never on push/PR.
  • No side effects on the package: only files under .github/ are added, and .github is already listed in .Rbuildignore, so the Bioconductor source tarball, R CMD check, and BiocCheck are unchanged.
  • Flexible: if you prefer, I can switch the trigger to workflow_dispatch only, or upload the bundles as workflow artifacts instead of Release assets, so nothing is ever attached to your Releases automatically.
  • Actions are currently on major-version tags; I am happy to pin them to commit SHAs or add a concurrency guard to match your policy.
  • The bundling contract (R version, entry point) is documented in the ShinyApps manual: https://github.com/fentouxungui/ShinyApps/wiki/ManualEN.

Licensing

  • This PR changes nothing about spatialLIBD's license (Artistic-2.0): the workflow only builds and publishes an artifact.
  • The bundle redistributes spatialLIBD and its dependency binaries. Redistribution respects each package's license; ShinyApps keeps a third-party notices file, and the Electron shell is AGPL-3.0. spatialLIBD itself stays Artistic-2.0.

If this PR is accepted

As the ShinyApps maintainer, I will update the store catalog entry to point at the official LieberInstitute/spatialLIBD releases, so every release of the package becomes installable in the store — an additional, zero-maintenance distribution channel that lowers the setup barrier for wet-lab and clinical users who want to browse the DLPFC Visium data without managing an R/Bioconductor environment.

Beyond convenience, this is aligned with the spatialLIBD project's own goal of letting people interactively explore spatially-resolved transcriptomics data: the same run_app() you already ship, runnable from a one-click desktop installer, on Windows and macOS.

Thanks for considering this — I am glad to adjust the workflow (naming, gating, SHA-pinning, or dropping the Release-asset behavior) to fit your preferences.

Checklist

  • CI-only change; no package source touched
  • .github/ already ignored by .Rbuildignore
  • Built end-to-end on a fork (Windows + macOS bundles published)
  • Entry point returns a shinyApp object and does not call runApp()
  • Undeclared runtime dependencies (Polychrome, RColorBrewer, markdown) handled explicitly
  • (optional, maintainer's call) README/wiki note about the desktop distribution

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant