diff --git a/.github/workflows/deploy_dev.yaml b/.github/workflows/deploy_dev.yaml new file mode 100644 index 00000000..9f1e317d --- /dev/null +++ b/.github/workflows/deploy_dev.yaml @@ -0,0 +1,165 @@ +--- +name: Deploy Dev Apps 🚀 + +on: + push: + branches: + - dev + workflow_dispatch: + schedule: + - cron: "12 3 * * *" + +concurrency: + group: publish-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + SHINYAPPSIO_ACCOUNT: genentech + APP_PREFIX: NEST + APP_SUFFIX: dev + GITHUB_PAT: ${{ secrets.REPO_GITHUB_TOKEN }} + BRANCH: dev + +jobs: + deploy: + defaults: + run: + shell: bash + name: Publish 🗞 + runs-on: ubuntu-latest + container: + image: ghcr.io/insightsengineering/rstudio_4.3.1_bioc_3.17:latest + if: > + !contains(github.event.commits[0].message, '[skip deploy]') + strategy: + fail-fast: false + matrix: + directory: + [ + "RNA-seq", + "basic-teal", + "efficacy", + "exploratory", + "longitudinal", + "early-dev", + "patient-profile", + "python", + "safety", + ] + steps: + - name: Checkout repo 🛎 + uses: actions/checkout@v3 + + - name: Check if cypress test exists + id: find-cypress + run: | + if [ -d ${{ matrix.directory }}/tests/cypress ]; then + echo "has-cypress-tests=true" >> $GITHUB_OUTPUT + else + echo "Skipping frontend tests because cypress directory does not exist." + echo "has-cypress-tests=false" >> $GITHUB_OUTPUT + fi + + - name: Setup system dependencies for cypress and python app + run: > + apt-get update && apt-get install --yes + libgtk2.0-0 libgbm-dev libnotify-dev libgconf-2-4 xvfb python3.10-venv + + - name: Setup Node + uses: actions/setup-node@v3 + if: steps.find-cypress.outputs.has-cypress-tests == 'true' + with: + node-version: 16 + + - name: Restore renv from cache + uses: actions/cache@v3 + env: + CACHE_KEY: renv-${{ runner.arch }}-${{ runner.os }}-${{ matrix.directory }}-${{ env.APP_SUFFIX }} + with: + path: ${{ matrix.directory }}/renv/library + key: ${{ env.CACHE_KEY }}-${{ hashFiles(format('{0}/renv.lock', matrix.directory)) }} + restore-keys: ${{ env.CACHE_KEY }}- + + - name: Update renv.lock file with updated GitHub packages + shell: Rscript {0} + run: | + setwd("${{ matrix.directory }}") + lockfile <- renv::lockfile_read() + for (package in lockfile$Packages) { + if (package$Source == "GitHub") { + renv::record(paste0(package$RemoteUsername, "/", package$Package)) + } + } + + - name: Install R packages using renv and update the renv snapshot + shell: Rscript {0} + working-directory: ${{ matrix.directory }} + run: | + options(renv.config.cache.symlinks = FALSE) + lockfile_pkgs <- renv::lockfile_read()$Package + github_pkgs <- names(lockfile_pkgs)[sapply(lockfile_pkgs, function(x) x$Source == "GitHub")] + renv::restore(clean = TRUE) + renv::update(exclude = github_pkgs) + renv::snapshot() + + - name: Print the new renv.lock file for ${{ matrix.directory }} + working-directory: ${{ matrix.directory }} + run: cat renv.lock + + - name: Front end test to check if the app works fine + if: steps.find-cypress.outputs.has-cypress-tests == 'true' + continue-on-error: true + uses: cypress-io/github-action@v6 + with: + build: npm install cypress --save-dev + working-directory: ${{ matrix.directory }}/tests + start: npm run run-app + wait-on: "http://localhost:3333" + wait-on-timeout: 500 + + - name: Install deploy R package dependencies + shell: Rscript {0} + working-directory: ${{ matrix.directory }} + run: | + install.packages(c("BiocManager", "rsconnect")) + + - name: Deploy 🖨 ${{ matrix.directory }} 🎨 + shell: Rscript {0} + working-directory: ${{ matrix.directory }} + run: | + rsconnect::setAccountInfo( + name = "${{ env.SHINYAPPSIO_ACCOUNT }}", + token = "${{ secrets.SHINYAPPSIO_TOKEN }}", + secret = "${{ secrets.SHINYAPPSIO_SECRET }}", + server = "shinyapps.io" + ) + rsconnect::deployApp( + appFiles = c("app.R"), + appName = rsconnect::generateAppName("${{ env.APP_PREFIX }}_${{ matrix.directory }}_${{ env.APP_SUFFIX }}"), + appTitle = "${{ env.APP_PREFIX }}_${{ matrix.directory }}_${{ env.APP_SUFFIX }}", + account = "${{ env.SHINYAPPSIO_ACCOUNT }}", + upload = TRUE, + logLevel = "normal", + lint = FALSE, + forceUpdate = TRUE + ) + + - name: Commit updated renv.lock file + continue-on-error: true + run: | + git config --global user.email "action@github.com" + git config --global user.name "GitHub Action" + git config --global --add safe.directory /__w/teal.gallery/teal.gallery + git fetch + git stash + git checkout ${{ env.BRANCH_NAME }} + git pull + git stash apply + git add ${{ matrix.directory }}/renv.lock + git add ${{ matrix.directory }}/renv/activate.R + if [ -n "$(git diff --staged)" ]; then + git commit -m "[skip deploy] Update renv.lock file for ${{ matrix.directory }} app" + git push origin ${{ env.BRANCH_NAME }} + else + echo "renv.lock was not modified. Nothing to commit."; + fi diff --git a/.github/workflows/deploy_stable.yaml b/.github/workflows/deploy_stable.yaml new file mode 100644 index 00000000..d0cbb85f --- /dev/null +++ b/.github/workflows/deploy_stable.yaml @@ -0,0 +1,165 @@ +--- +name: Deploy Stable Apps 🚀 + +on: + push: + branches: + - main + workflow_dispatch: + schedule: + - cron: "12 3 * * *" + +concurrency: + group: publish-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + SHINYAPPSIO_ACCOUNT: genentech + APP_PREFIX: NEST + APP_SUFFIX: stable + GITHUB_PAT: ${{ secrets.REPO_GITHUB_TOKEN }} + BRANCH: main + +jobs: + deploy: + defaults: + run: + shell: bash + name: Publish 🗞 + runs-on: ubuntu-latest + container: + image: ghcr.io/insightsengineering/rstudio_4.3.1_bioc_3.17:latest + if: > + !contains(github.event.commits[0].message, '[skip deploy]') + strategy: + fail-fast: false + matrix: + directory: + [ + "RNA-seq", + "basic-teal", + "efficacy", + "exploratory", + "longitudinal", + "early-dev", + "patient-profile", + "python", + "safety", + ] + steps: + - name: Checkout repo 🛎 + uses: actions/checkout@v3 + + - name: Check if cypress test exists + id: find-cypress + run: | + if [ -d ${{ matrix.directory }}/tests/cypress ]; then + echo "has-cypress-tests=true" >> $GITHUB_OUTPUT + else + echo "Skipping frontend tests because cypress directory does not exist." + echo "has-cypress-tests=false" >> $GITHUB_OUTPUT + fi + + - name: Setup system dependencies for cypress and python app + run: > + apt-get update && apt-get install --yes + libgtk2.0-0 libgbm-dev libnotify-dev libgconf-2-4 xvfb python3.10-venv + + - name: Setup Node + uses: actions/setup-node@v3 + if: steps.find-cypress.outputs.has-cypress-tests == 'true' + with: + node-version: 16 + + - name: Restore renv from cache + uses: actions/cache@v3 + env: + CACHE_KEY: renv-${{ runner.arch }}-${{ runner.os }}-${{ matrix.directory }}-${{ env.APP_SUFFIX }} + with: + path: ${{ matrix.directory }}/renv/library + key: ${{ env.CACHE_KEY }}-${{ hashFiles(format('{0}/renv.lock', matrix.directory)) }} + restore-keys: ${{ env.CACHE_KEY }}- + + - name: Update renv.lock file with updated GitHub packages + shell: Rscript {0} + run: | + setwd("${{ matrix.directory }}") + lockfile <- renv::lockfile_read() + for (package in lockfile$Packages) { + if (package$Source == "GitHub") { + renv::record(paste0(package$RemoteUsername, "/", package$Package, "@*release")) + } + } + + - name: Install R packages using renv and update the renv snapshot + shell: Rscript {0} + working-directory: ${{ matrix.directory }} + run: | + options(renv.config.cache.symlinks = FALSE) + lockfile_pkgs <- renv::lockfile_read()$Package + github_pkgs <- names(lockfile_pkgs)[sapply(lockfile_pkgs, function(x) x$Source == "GitHub")] + renv::restore(clean = TRUE) + renv::update(exclude = github_pkgs) + renv::snapshot() + + - name: Print the new renv.lock file for ${{ matrix.directory }} + working-directory: ${{ matrix.directory }} + run: cat renv.lock + + - name: Front end test to check if the app works fine + if: steps.find-cypress.outputs.has-cypress-tests == 'true' + continue-on-error: true + uses: cypress-io/github-action@v6 + with: + build: npm install cypress --save-dev + working-directory: ${{ matrix.directory }}/tests + start: npm run run-app + wait-on: "http://localhost:3333" + wait-on-timeout: 500 + + - name: Install deploy R package dependencies + shell: Rscript {0} + working-directory: ${{ matrix.directory }} + run: | + install.packages(c("BiocManager", "rsconnect")) + + - name: Deploy 🖨 ${{ matrix.directory }} 🎨 + shell: Rscript {0} + working-directory: ${{ matrix.directory }} + run: | + rsconnect::setAccountInfo( + name = "${{ env.SHINYAPPSIO_ACCOUNT }}", + token = "${{ secrets.SHINYAPPSIO_TOKEN }}", + secret = "${{ secrets.SHINYAPPSIO_SECRET }}", + server = "shinyapps.io" + ) + rsconnect::deployApp( + appFiles = c("app.R"), + appName = rsconnect::generateAppName("${{ env.APP_PREFIX }}_${{ matrix.directory }}_${{ env.APP_SUFFIX }}"), + appTitle = "${{ env.APP_PREFIX }}_${{ matrix.directory }}_${{ env.APP_SUFFIX }}", + account = "${{ env.SHINYAPPSIO_ACCOUNT }}", + upload = TRUE, + logLevel = "normal", + lint = FALSE, + forceUpdate = TRUE + ) + + - name: Commit updated renv.lock file + continue-on-error: true + run: | + git config --global user.email "action@github.com" + git config --global user.name "GitHub Action" + git config --global --add safe.directory /__w/teal.gallery/teal.gallery + git fetch + git stash + git checkout ${{ env.BRANCH_NAME }} + git pull + git stash apply + git add ${{ matrix.directory }}/renv.lock + git add ${{ matrix.directory }}/renv/activate.R + if [ -n "$(git diff --staged)" ]; then + git commit -m "[skip deploy] Update renv.lock file for ${{ matrix.directory }} app" + git push origin ${{ env.BRANCH_NAME }} + else + echo "renv.lock was not modified. Nothing to commit."; + fi diff --git a/README.md b/README.md index 08e020e6..996dc6c6 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ - - A gallery of sample apps based on the [`teal`](https://github.com/insightsengineering/teal) framework. ## Links to apps on `shinyapps.io` @@ -48,7 +46,16 @@ restore_and_run("basic-teal") ## Development -All `teal` sample apps are wrapped into this package for the sake of portability. All development standards and practices that we currently use for teal app development also apply to this repository. +All `teal` sample apps are wrapped into this repository into it's own sub-directory. All development standards and practices that we currently use for teal app development also apply to this repository. + +### Auto-deployment + +All the sample apps are automatically deployed every day using the CI in two channels: + +- `stable` channel: The code for the Teal apps is taken from the `main` branch of `teal.gallery`, and the NEST packages are installed from the `main` branch of GitHub. This is done using the `deploy_stable.yaml`. +- `dev` channel: The code for the Teal apps is taken from the `dev` branch of `teal.gallery` and the NEST packages are installed from the last `release tag` of GitHub. This is done using the `deploy_dev.yaml`. + +_IMPORTANT_: Although we can now test the unreleased features of the NEST packages in deployments, currently, the divergent `dev` and `main` branches must be managed and merged manually as needed to ensure that the apps work fine in both deployment channels, i.e., making sure to merge the app changes from `dev` to `main` by creating a PR. ### Adding a sample app to `teal.gallery`