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.
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
-
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. ↩