Creating Per-Pull Request Environments on Digital Ocean's App Platform With Github Actions


Below is a quick template I use when deploying new static site apps to Digital Ocean App Platform. This quickly sets up auto-deploying staging environments for each PR opened to the main branch.

Some limitations:

  • The create_review_app step triggers the initial build on App Platform, but returns while the app is still building. An improvement would be to poll DO to see when the app is finished building. Ideally, we would then add a comment to the open PR with build url.
  • As of today, creating build notification alerts via the doctl command line is incomplete. You can turn alerts on or off, but you cannot specify the type (email, slack) or configure it further (select a specific slack chat or email).
name: Deploy Review App to DO App Platform

on:
  # only run this workflow on pull request events
  pull_request:
    # Since DO Apps auto build on new push once an app is created, the synchronize type is not needed
    types: [opened, reopened, closed, labeled]
    branches:
      - main

env:
  APP_NAME: my-app-name-pr-${{github.event.pull_request.number}}

jobs:
  create_review_app:
    runs-on: ubuntu-latest
    # only run when a pull request label of build is added
    if: github.event.action == 'labeled' && github.event.label.name == 'build'
    # Alternatively, you can run this action when a pull request is opened
    # if: github.event_name == 'pull_request' && github.event.action != 'closed'
    steps:
      - name: Cloning repo
        uses: actions/checkout@v2

      - name: Install doctl
        uses: digitalocean/action-doctl@v2
        with:
          token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

      - name: create-json
        id: create-json
        uses: jsdaniell/create-json@1.1.2
        with:
          name: "app_spec.json"
          json: >
            {
              "name": "${{env.APP_NAME}}",
              "region": "nyc",
              "envs": [
                {
                  "key": "REACT_APP_API_URL",
                  "scope": "RUN_AND_BUILD_TIME",
                  "value": "my build secret"
                }
              ],
              "static_sites": [
                {
                  "build_command": "npm install && npm run build",
                  "catchall_document": "index.html",
                  "environment_slug": "node-js",
                  "github": {
                    "branch": "${{ github.event.pull_request.head.ref }}",
                    "deploy_on_push": true,
                    "repo": "cfu288/my-app-name"
                  },
                  "name": "${{env.APP_NAME}}",
                  "routes": [
                    {
                      "path": "/"
                    }
                  ],
                  "source_dir": "/"
                }
              ]
            }

      - name: Create review app
        id: create-review-app
        run: >
          doctl apps create --spec app_spec.json

      - name: Comment on PR
        uses: unsplash/comment-on-pr@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          msg: Review app ${{ env.APP_NAME }} is currently being created 🚀. Check out https://cloud.digitalocean.com/apps to see if it has been deployed.
          check_for_duplicate_msg: false # OPTIONAL

  destroy_review_app:
    runs-on: ubuntu-latest
    # only run when a pull request is closed
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    steps:
      - name: Install doctl
        uses: digitalocean/action-doctl@v2
        with:
          token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

      - name: Get app id
        id: get-app-id
        run: |
          echo "::set-output name=appid::$(doctl apps list -o json | jq '.[] | select(.spec.name=="${{env.APP_NAME}}")' | jq '.id')"

      - name: Destroy review app
        run: >
          doctl apps delete --force ${{steps.get-app-id.outputs.appid}}

      - name: Comment on PR
        uses: unsplash/comment-on-pr@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          msg: Review app ${{ env.APP_NAME }} has been destoyed 🗑️.
          check_for_duplicate_msg: false # OPTIONAL