Auto deploy Shiny app changes to the DigitalOcean App Platform
Tags: shiny, docker, cicd, rstats
Published: 2021-10-24 18:06:13

*By: [Peter Solymos](https://hosting.analythium.io/author/peter/)*

**Integrate the DigitalOcean App Platform with GitHub for automated deployment of your Shiny app.**

I introduced the DigitalOcean App Platform in a previous post and described how to [deploy Shiny apps from existing Docker images](https://hosting.analythium.io/how-to-host-shiny-apps-on-the-digitalocean-app-platform/) 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](https://www.digitalocean.com/products/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](https://github.com/analythium/app-platform-shiny) repository because you will have to give permissions to access the repo.


Here is what's inside the repo:

``` bash
.
├── .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](https://hosting.analythium.io/how-to-host-shiny-apps-on-the-digitalocean-app-platform/) (`app.yaml`) that was introduced before, but it is largely simplified:

``` yaml
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](https://hosting.analythium.io/run-shiny-apps-locally/#multiple-files). The `Dockerfile` uses the [renv package to handle the dependencies](https://hosting.analythium.io/dockerized-shiny-apps-with-dependencies/#use-the-renv-r-package) 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](https://mastering-shiny.org/action-transfer.html).

## 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:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM-1.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM-1.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM-1.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM-1.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM-1.png 2400w" width="2000" height="1301" />
</figure>

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

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.41.27-PM-2.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.41.27-PM-2.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.41.27-PM-2.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.41.27-PM-2.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.41.27-PM-2.png 2400w" width="2000" height="1301" />
</figure>

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':

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-8.31.03-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-8.31.03-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-8.31.03-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-8.31.03-PM.png 1600w, https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-8.31.03-PM.png 2020w" width="2000" height="1194" />
</figure>

## 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':

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-8.33.35-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-8.33.35-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-8.33.35-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-8.33.35-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-8.33.35-PM.png 2400w" width="2000" height="1293" />
</figure>

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

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-8.37.49-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-8.37.49-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-8.37.49-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-8.37.49-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-8.37.49-PM.png 2400w" width="2000" height="1293" />
</figure>

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

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-8.38.09-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-8.38.09-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-8.38.09-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-8.38.09-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-8.38.09-PM.png 2400w" width="2000" height="1293" />
</figure>

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:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-8.38.32-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-8.38.32-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-8.38.32-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-8.38.32-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-8.38.32-PM.png 2400w" width="2000" height="1293" />
</figure>

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:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-9.48.00-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-9.48.00-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-9.48.00-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-9.48.00-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-9.48.00-PM.png 2400w" width="2000" height="1293" />
</figure>

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

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-9.50.51-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-9.50.51-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-9.50.51-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-9.50.51-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-9.50.51-PM.png 2400w" width="2000" height="1293" />
</figure>

## Add a custom domain

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

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-9.55.41-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-9.55.41-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-9.55.41-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-9.55.41-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-9.55.41-PM.png 2400w" width="2000" height="1293" />
</figure>

Follow the prompts and copy the app URL:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-9.57.59-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-9.57.59-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-9.57.59-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-9.57.59-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-9.57.59-PM.png 2400w" width="2000" height="1293" />
</figure>

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:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-9.57.03-PM.png" class="kg-image" />
</figure>

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

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-10.01.47-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-10.01.47-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-10.01.47-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-10.01.47-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-10.01.47-PM.png 2400w" width="2000" height="1293" />
</figure>

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:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-10.01.17-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-10.01.17-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-10.01.17-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-10.01.17-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-10.01.17-PM.png 2400w" width="2000" height="1293" />
</figure>

## 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:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-10.05.00-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-10.05.00-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-10.05.00-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-10.05.00-PM.png 1600w, https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-10.05.00-PM.png 1616w" width="1616" height="1176" />
</figure>

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:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-10.09.35-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-10.09.35-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-10.09.35-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-10.09.35-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-10.09.35-PM.png 2400w" width="2000" height="1293" />
</figure>

Visit the custom URL and see the new version deployed:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-10.10.00-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-10.10.00-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-10.10.00-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-10.10.00-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-10.10.00-PM.png 2400w" width="2000" height="1293" />
</figure>

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](https://www.digitalocean.com/community/questions/app-platform-wait-for-tests-to-pass-before-deploying). 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](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests).

## 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](https://www.digitalocean.com/products/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](https://hosting.analythium.io/how-to-host-shiny-apps-on-the-digitalocean-app-platform/#programmatic-image-deployment) 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

-   [App Platform documentation](https://docs.digitalocean.com/products/app-platform/)
-   [Deploy app from a monorepo](https://docs.digitalocean.com/products/app-platform/how-to/deploy-from-monorepo/)
-   [Troubleshoot your app](https://docs.digitalocean.com/products/app-platform/how-to/troubleshoot-app/)