How to build a CI/CD pipeline for Shiny apps with Gitlab-CI and RStudio (Posit) Connect
RStudio Connect makes publishing Shiny Apps incredibly simple with only a few easy-to-follow steps. You can find step-by-step instructions in RStudio’s guide for publishing a Shiny app. Despite that ease of use,
Speed up production with Appsilon’s open source Shiny Dashboard Templates, available now!
- Requirements
- Continuous integration with Gitlab CI
- Continuous deployment using Git-backed content
- Benefits of building a CI/CD pipeline
Note: At the time of writing this article, Posit PBC was RStudio PBC. We use RStudio and Posit interchangeably in this text (e.g. RStudio Connect == Posit Connect).
Requirements to build a CI/CD pipeline for Shiny apps
Before we can enable the CI/CD pipeline we have to prepare our repository with a Shiny app for automatic testing and deployments.
First, initialize renv.lock by using renv package to restore the packages inside your CI/CD job. The package will be used to restore packages inside your CI/CD job. Next, generate manifest.json and push it to your repository. The manifest.json file tells RStudio Connect how to deploy and host your content. Lastly, ensure that you have GitLab runner available and enabled for your project and verify that there is network connectivity between RStudio Connect and GitLab-CI.
Initializing renv.lock
To familiarize yourself with the renv package, check out RStudio’s comprehensive renv documentation. But for now, all you have to do is to call renv::init()
to initialize a new project-local environment with a private R library.
Generating manifest.json
The manifest can be created by calling the R function rsconnect::writeManifest()
from within your project directory. Pushing it and storing it in the repo will allow you to review the manifests before they are deployed. This additional manual step will allow you to fully control which packages, and from which sources, will be installed on RStudio Connect.
Repository file structure
What you want to achieve is the file structure shown in the image below. After initializing renv.lock and generating manifest.json file, one last thing to do is to create gitlab-ci.yml file. It will include the definition of the CI pipeline we want to implement. I intentionally said CI instead of CI/CD because in our case CD part will be configured outside of GitLab-CI platform.
Continuous integration with GitLab-CI
In general, you should run tests with every commit to ensure that new code is properly tested. This will help prevent new errors from being introduced. To do this we first need to create a CI/CD pipeline definition and name it properly – .gitlab-ci.yml. Note: the name must be .gitlab-ci.yml when using the GitLab CI platform.
Running tests inside the CI/CD pipeline
Before we run our tests, our environment needs to meet requirements. Inside the environment, you should be able to run an R job and restore necessary packages before running a testthat command. It’s very easy to achieve, you only have to pick a base docker image that has R installed and then execute renv::restore()
command before running the main script.
image: rstudio/r-base:4.1.0-bionic before_script: - R -e 'renv::restore(prompt=FALSE)'
High quality, reproducible code takes skill. Learn to write production-ready R code with Marcin Dubel
Now the core part, running tests. I will use testthat::test_dir
command with a reporter parameter to save the result of the test job as a file with the proper format.
script: - R -e 'testthat::test_dir("tests/testthat", reporter = testthat::JunitReporter$new(file = "../../junit_result.xml"))'
Displaying test results in GitLab-CI platform
This code will run all tests and save the results as junit_result.xml file by using testthat::JunitReporter$new
function so that results can be displayed in the GitLab-CI platform in a user-friendly format by invoking the Unit test reports GitLab feature.
To achieve such results you will have to add a code block responsible for publishing reports in your pipeline:
artifacts: reports: junit: junit_result.xml
To read more about the above configuration please refer to official GitLab docs for unit test reports.
Final CI/CD Pipeline for Shiny Apps
stages: - tests variables: RENV_PATHS_CACHE: ${CI_PROJECT_DIR}/cache RENV_PATHS_LIBRARY: ${CI_PROJECT_DIR}/renv/library cache: key: ${CI_COMMIT_REF_SLU} paths: - ${RENV_PATHS_CACHE} - ${RENV_PATHS_LIBRARY} tests: stage: tests image: rstudio/r-base:4.1.0-bionic before_script: - apt-get update && apt-get install libxml2-dev - R -e 'renv::restore(prompt=FALSE)' script: - R -e 'testthat::test_dir("tests/testthat", reporter = testthat::JunitReporter$new(file = "../../junit_result.xml"))' artifacts: when: always reports: junit: junit_result.xml
Continuous deployment using Git-backed content
You can deploy content directly from a remote Git repository using the Git-backed content feature available in RStudio Connect. This means that content will automatically fetch from the associated remote Git repository and re-deployed with changes. This part will be configured inside RStudio Connect and is very straightforward. You can check out how to configure this by watching the gif below. Additionally, you can go to official RStudio documentation to read more and use their prepared examples from GitHub to test it yourself.
There is no need to change the RStudio Connect configuration file unless you want to pull code from the private repository. In this case, you will have to specify git credentials for RStudio Connect to use. You can read more about this from the official RStudio documentation on private repos.
Does your Shiny app need a check-up? Discover how to pull Shiny usage data from RStudio Connect
Benefits of building a CI/CD pipeline for Shiny apps
Setting up CI/CD for your shiny apps with RStudio Connect is simple and straightforward. A CI/CD pipeline can save you the headache of sending a good build to your production environment and ensure its readiness for production release. Consider setting up such a pipeline in your projects and introduce best practices for testing, reviewing, and deploying! Reduce your costs, minimize errors, and free up your developer resources so your team can focus on more important matters. It’s a small investment that will help you develop better Shiny applications and save you a lot of time wasted for manual deployments in the future.
Another way forward
Automated pipelines can significantly reduce dev resources and speed up time to a production-ready launch. But every project is unique and requires some forethought for the right tool. As experts in R/Shiny, we know how to find the right tools for the right project.
Appsilon is a proud RStudio Full Service Certified Partner with a well-balanced team devoted to providing industry-leading solutions to our clients. We can help guide you to find the best solution for a high-quality, enterprise application.