Auto deploy Shiny app changes to the DigitalOcean App Platform

I introduced the DigitalOcean App Platform in a previous post and described how to deploy Shiny apps from existing Docker images using the control panel or the command line.

This post continues the App Platform discussion by looking at how to integrate the App Platform with GitHub and set up continuous integration and deployment (CICD).

Prerequisites

If you have not done so before, go to the App Platform landing page and sign up for a DigitalOcean account. Log into your account and go to your dashboard. In the left drawer, click on 'Apps'.

For the analythium/app-platform-shiny repository because you will have to give permissions to access the repo.

GitHub - analythium/app-platform-shiny: DigitalOcean App Platform Example for Shiny
DigitalOcean App Platform Example for Shiny. Contribute to analythium/app-platform-shiny development by creating an account on GitHub.

Here is what's inside the repo:

.
├── .do
│   ├── app.yaml
│   └── deploy.template.yaml
├── app
│   ├── global.R
│   ├── server.R
│   └── ui.R
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
└── renv.lock

The .do folder contains a template file that is needed if you want to deploy the Shiny app template from the button in the README file. The app specification (app.yaml) that was introduced before, but it is largely simplified:

name: app-platform-shiny
services:
- dockerfile_path: Dockerfile
  github:
    branch: main
    deploy_on_push: true
    repo: analythium/app-platform-shiny
  name: app-platform-shiny

The app folder contains multiple files for the Shiny app. The Dockerfile uses the renv package to handle the dependencies specified in the renv.lock file.

The app itself, as you will see, is a relatively simple example with some range slider, a dependency that does not require building from source. It also has a file upload button – I use the upload functionality to test when a large file upload is required. Large file upload is often restricted, Shiny itself has a 5 Mb file size limit.

GitHub integration

Let's set up the GitHub integration. Go to the DigitalOcean control panel In your control panel. Click on 'Launch New App' under the 'App' item in the left side menu:

When you are asked to choose a source, click on the GitHub icon:

Follow the prompts and install the GitHub app for your personal account or the organization of your choice. Select 'All repositories' or 'Only select repositories' as appropriate. Finally, click 'Save':

Deploy from GitHub repository

Now go back to the Apps control panel. Select the repository from the dropdown menu, specify the branch you wish to deploy. If you want to deploy both a development and a production version of the same repository, you can create multiple apps with the same repo but using different branches.

Leave 'autodeploy' checked if you want to trigger new deployments when the code changes, then click 'Next':

Review the app settings inferred from the Dockerfile, i.e. the HTTP port, etc.:

Type in the name of the service, select the data region, then click 'Next:

The final step lets you define the performance of the app and the corresponding pricing. Here I use the smallest size under the 'Basic' plan for $5 US per month:

After a few minutes, you should see the green checks besides the build and deployment log entries. Building the image might take some time, depending on the number of dependencies in your app:

Follow the app URL from the control panel to see the app online served over HTTPS:

Add a custom domain

Adding a custom domain is done via the control panel. Click 'Add Domain':

Follow the prompts and copy the app URL:

Add a CNAME record in your DNS settings on your DNS providers page (Google Domains shown here), make it point to a domain or subdomain of yours:

You will see the custom domain listed in the app settings:

Click on your new custom domain link and check. You might have to wait a bit if your record has not propagated through the name servers:

Auto deploy updates

Next, we will make some changes to the Shiny app and see the app magically update via git. Do it in your forged repo. The small change I made was to edit the app title. Here is the change:

Once you commit the changes and push to GitHub, it triggers the web-flow pipeline. The new deployment is then listed in the deployment history of your app:

Visit the custom URL and see the new version deployed:

Don't forget to delete the app and the CNAME record if you don't need them anymore to avoid accruing hosting charges. Dynamic app hosting is not free on DigitalOcean.

Testing before deployment

One potential issue with this GitHub integration is it does not depend on passing tests before deployment. If you want to test your changes before deployment, here are the steps to follow:

  1. push changes to the development branch,
  2. run automated tests for the development branch, e.g. using GitHub actions,
  3. set up the production branch as a protected branch, so that merging pull requests require passing tests.

Conclusions

When we deployed an exiting Docker image, the image was pulled to the App Platform from Docker Hub. In this case, an immutable image was pushed and pulled.

The GitHub integration described here takes a different approach. It builds the Docker image after changes are pushed to a pre-defined branch in the GitHub repository. The image is built and stored in the DigitalOcean Container Registry.

With this approach, you get a new deployment on every git push event. You can debate how much this automated deployment (CD) can be considered continuous integration and deployment (CICD) without actually having the CI part.

Testing the app before deployment requires additional steps as part of your git workflow. To achieve full CICD experience, you can use the doctl command-line utility in GitHub Actions. As it turns out, this route is a bit more involved and there are several gotchas to address. Thus, I will explain it in a follow-up post.

Further reading