Skip to content

Publishing to RStudio Connect using Github Actions#

Demo GitHub Repository: https://github.com/sol-eng/gha-deployment-connect-private-repo

RStudio Connect hosts a variety of data artifacts with different development life cycles. Whenever you want to publish one of these data artifacts to RStudio Connect, there are three paths you can follow:

  • Push-button deployment process within the RStudio IDE
  • Git-backed deployment within RStudio Connect
  • Programmatic deployment

The first deployment path is the push button deployment process; within the IDE you press publish at the top right corner of your source code pane, establish your credentials, and voila! Your content is available in RStudio Connect.

The second deployment path you can follow is a Git-backed deployment from within the RStudio Connect interface. To do this, you log in into Connect, click the Publish button, select the Import from Git option and follow the process to connect the Git repository to RStudio Connect.

However, this process has some limitations. If you want to publish content from a private Git repository, you need to add a service account 1 to the repositories, and add the credentials of this service account to the RStudio Connect configuration file, an action which requires the server administrator. One feature that is not part of RStudio Connect, given its focus on being a publication platform, is the fact that you can’t execute tests before publishing your artifact because it will assume that the version that it is fetching is ready for production use. This is where GitHub Actions comes into play.

GitHub Actions is a tool from GitHub that allows you to integrate tests into your deployment pipeline, publish to RStudio Connect from private repositories without a service account, automate your builds of documentation and webpages, and download data into R packages.

To illustrate this, the following section examines the deployment of a Shiny app to RStudio Connect using GitHub Actions.

The first thing to consider is how to manage the R packages as dependencies within the Action environment. One solution is to do a one-by-one installation of every package that is used in the Shiny app, however this gets cumbersome as the app grows bigger. To simplify package management of the environment, you can use the renv package. The package is useful in terms of reproducibility because it saves the package version and the repository from where you installed the packages used by the app so that it can restore the environment in other computers. You use renv in this deployment process to maintain consistency between the development environment and the Action environment.

Without renv:

install.packages(c(shiny,ggplot2,dplyr,leaflet,shinyjs))

With renv:

renv::snapshot() #Development Environment
renv::restore() #GitHub Actions

To learn more about renv, visit the package documentation website. Below you can see how the files are organized in the GitHub repository.

.
├── README.md
├── app
│   ├── app.R
│   ├── rsconnect
│   │   ├── colorado_server
│   │   └── rsc_server
│   └── tests
│       ├── shinytest
│       └── shinytest.R
├── gha-deployment-connect-private-repo.Rproj
├── renv
│   ├── activate.R
│   └── settings.dcf
└── renv.lock

7 directories, 7 files

The Shiny application files and corresponding tests are within the app/ folder. The main file of interest, .github/workflows/deploy_to_connect.yml specifies the workflow that Actions will execute.

on:
  push:
    branches:
      - main
      - master

name: deploy-to-connect

jobs:
  deploy-to-connect:
    runs-on: macOS-latest
    env:
      APP_NAME: "GHA_SHINYTEST_RSC"
      APP_DIR: "app"
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
      RSCONNECT_URL: ${{ secrets.RSCONNECT_URL }}
      RSCONNECT_APIKEY: ${{ secrets.RSCONNECT_APIKEY }}
    steps:
      - uses: actions/checkout@v2

      - uses: r-lib/actions/setup-r@master

      - name: Install renv
        run: |
          install.packages("renv")
        shell: Rscript {0}

      - name: Restore environment
        run: |
          renv::consent(TRUE)
          renv::restore()
        shell: Rscript {0}

      - name: Install shinytest dependencies
        run: |
          shinytest::installDependencies()
        shell: Rscript {0}

      - name: Run shinytest tests
        run: |
          shinytest::testApp("${{ env.APP_DIR }}")
        shell: Rscript {0}

      - name: Generate manifest.json
        run: |
          rsconnect::writeManifest(appDir = "${{ env.APP_DIR }}")
        shell: Rscript {0}

      - name: Deploy to RStudio Connect
        uses: rstudio/actions/connect-publish@main
        with:
          url: https://${{ env.RSCONNECT_APIKEY }}@${{ env.RSCONNECT_URL }}
          access-type: logged_in
          show-logs: true
          force: true
          dir: ${{ env.APP_DIR }}
          require-vanity-path: true

The rest of this article goes over the contents of the file so you understand what is going on.

The beginning of the file specifies the events that will trigger this Action. The Action example shown here will run whenever there is a push to the main or master branches of the repository: on:

  push:
    branches:
      - main
      - master

In the runs-on line below, you specify the OS on which you want to run the job. This deployment example uses the latest version of MacOS as it simplifies system dependency management. After the OS is indicated, you specify the environment variables. Note that within the env key, you specify variables in two manners. The first method is by explicitly specifying the variable value, which is what you use for APP_NAME and APP_DIR. The other method is using encrypted secrets, which enable you to manage credentials within the Action environment. To access the credentials, use the secrets keyword and the name of the variable you want to access.

jobs:
  deploy-to-connect:
    runs-on: macOS-latest
    env:
      APP_NAME: "GHA_SHINYTEST_RSC"
      APP_DIR: "app"
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
      RSCONNECT_URL: ${{ secrets.RSCONNECT_URL }}
      RSCONNECT_APIKEY: ${{ secrets.RSCONNECT_APIKEY }}

You specify the secrets in the settings panel of the repository.

secrets

Subsequently, enumerate the job steps under the steps key. Each entry is treated as a different step.

The first step checks out the files of the repository so that you have them available to:

  • Install the packages the app needs to run.
  • Deploy the app to RStudio Connect.

The second step sets up R within the Action environment.

These two steps are provided by GitHub and the r-lib organization respectively. Note that because they are provided, we specify them with the uses key.

    steps:
      - uses: actions/checkout@v2

      - uses: r-lib/actions/setup-r@master

Next, install renv and restore the renv environment. Observe that in the run key you specify what you want to run, while in the shell key you specify what program you want to execute the command with. For this Action all of the commands will be executed with R so you use the Rscript executable.

      - name: Install renv
        run: |
          install.packages("renv")
        shell: Rscript {0}

      - name: Restore environment
        run: |
          renv::restore()
        shell: Rscript {0}

To execute the tests, the shinytest package requires some system dependencies as part of the infrastructure. After installation, the tests run. If they are not successful, the job will fail and you will catch a bug before it goes into production!

      - name: Install shinytest dependencies
        run: |
          shinytest::installDependencies()
        shell: Rscript {0}

      - name: Run shinytest tests
        run: |
          shinytest::testApp("${{ env.APP_DIR }}")
        shell: Rscript {0}

RStudio Connect requires that a manifest.json file be created before uploading the content to the server if you are following a programatic deployment. This file looks at your environment and creates a record (manifest) of what R (and/or Python) version is available and what packages are needed for the deployment.

      - name: Generate manifest.json
        run: |
          rsconnect::writeManifest(appDir = "${{ env.APP_DIR }}")
        shell: Rscript {0}

Once the manifest has been generated by the writeManifest function, the rstudio/actions/connect-publish action is available to deploy the content to RStudio Connect. For the Action to deploy succesfully you have to provide the url of the RStudio Connect server with the API Key as part of it as included below. To read more about the documentation of this Action, visit the GitHub README.

      - name: Deploy to RStudio Connect
        uses: rstudio/actions/connect-publish@main
        with:
          url: https://${{ env.RSCONNECT_APIKEY }}@${{ env.RSCONNECT_URL }}
          access-type: logged_in
          show-logs: true
          force: true
          dir: ${{ env.APP_DIR }}
          require-vanity-path: true

  1. A service account is an account that is used to manage access credentials to a resource. In this case, a common account that would have access to the private Git repositories.