Implementing Deploy

So far, we have a slash-commands GitHub Action that translates /deploy and /teardown ChatOps commands into repository_dispatch events.

To handle the /deploy slash command, we’ll need to handle a repository_dispatch event of type deploy-command. In your GitHub repository, open up the Actions tab and choose “New workflow” and then “Set up a workflow yourself”. Name the file deploy-command.yml and paste this in:

# Inputs:
#  client_payload.pull_request.number - PR number
#  client_payload.pull_request.head.sha - PR SHA

name: Create PR Staging Environment

on:
  repository_dispatch:
    types: [deploy-command]

# Set environment variables available to all action steps.
env:
  DOMAIN: ${{ format('{0}-{1}-pr{2}.surge.sh', github.event.repository.owner.login, github.event.repository.name, github.event.client_payload.pull_request.number) }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          ref: ${{ github.event.client_payload.pull_request.head.sha }}

      - name: Install dependencies
        run: npm ci

      - name: Build static site
        run: npx gatsby build

      - name: Publish to surge.sh
        run: npx surge ./public ${{ env.DOMAIN }}
        env:
          SURGE_LOGIN: ${{ secrets.SURGE_LOGIN }}
          SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}

      - name: Add comment to PR
        uses: peter-evans/create-or-update-comment@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          issue-number: ${{ github.event.client_payload.pull_request.number }}
          body: |
            ${{ format('Published to [staging environment](https://{0})', env.DOMAIN) }}

            To teardown, comment with the `/teardown` command.

This one’s a bit longer, so let’s walk through the steps.

The first step checks out the repository. Note that it specifically checks out the SHA of the pull request (client_payload.pull_request is provided by slash-command-dispatch). So we’re checking out the code for that PR.

      - name: Checkout
        uses: actions/checkout@v2
        with:
          ref: ${{ github.event.client_payload.pull_request.head.sha }}

The next couple steps build the site by running npm ci and npx gatsby build. Just like building locally, the output is placed in the public folder.

The publish step runs npx surge ./public ${{ env.DOMAIN }}. This time we’re running surge and giving it the name of the domain we want to publish to. The DOMAIN environment variable was defined earlier in the file:

env:
  DOMAIN: ${{ format('{0}-{1}-pr{2}.surge.sh', github.event.repository.owner.login, github.event.repository.name, github.event.client_payload.pull_request.number) }}

What’s really nice about this setup is that every pull request gets a different domain - and thus a different staging environment.

SURGE_LOGIN and SURGE_TOKEN are additional environment variables used by the surge command line so it authenticates under your account while deploying.

The last step adds a comment to the pull request with a clickable link for the deployed staging environment:

      - name: Add comment to PR
        uses: peter-evans/create-or-update-comment@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          issue-number: ${{ github.event.client_payload.pull_request.number }}
          body: |
            ${{ format('Published to [staging environment](https://{0})', env.DOMAIN) }}

            To teardown, comment with the `/teardown` command.

This ends up looking like this:

Set Up Surge Secrets

There are a couple of new secrets used by the deploy action: SURGE_LOGIN and SURGE_TOKEN. These can be added as repository secrets just like last time.

Set SURGE_LOGIN to the email address you used to sign up with Surge.

To get SURGE_TOKEN, run surge token from your own computer. This will give you a token that you can save in the SURGE_TOKEN secret, so your deployments are associated with your Surge account.

Try It Out!

At this point, you should be able to create a pull request and then add a /deploy comment on it. Check out the Actions tab of the repository to watch your actions run or see the logs for old action runs.

Implementing Teardown

The next step is to implement teardown. In your GitHub repository, open up the Actions tab and choose “New workflow” and then “Set up a workflow yourself”. Name the file teardown-command.yml and paste this in:

# Inputs:
#  client_payload.pull_request.number - PR number

name: Delete PR Staging Environment

on:
  repository_dispatch:
    types: [teardown-command]

env:
  DOMAIN: ${{ format('{0}-{1}-pr{2}.surge.sh', github.event.repository.owner.login, github.event.repository.name, github.event.client_payload.pull_request.number) }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Teardown surge.sh
        run: npx surge teardown ${{ env.DOMAIN }}
        env:
          SURGE_LOGIN: ${{ secrets.SURGE_LOGIN }}
          SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}

      - name: Add comment to PR
        uses: peter-evans/create-or-update-comment@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          issue-number: ${{ github.event.client_payload.pull_request.number }}
          body: ${{ format('Tore down {0}', env.DOMAIN) }}

This one is pretty simple; we use a similar pattern to /deploy but there are fewer steps since there’s no build (or even a checkout). We use the same pattern for defining DOMAIN and the Surge secrets, and then we run npx surge teardown ${{ env.DOMAIN }} to tear down the environment for this pull request. The last step adds a comment to the PR indicating that its staging environment has been torn down.

Who Can Issue Commands?

By default, only developers with write access to your repository can issue slash commands. This is the default behavior of slash-command-dispatch. So if this is just your project, then only you can create or tear down staging environments. If you have an open-source project - and if creating staging environments is cheap for you - you can edit the slash-commands.yml file and add a permission argument to slash-command-dispatch with the value none. That way, anyone would be able to create and tear down staging environments.

Next Time

ChatOps are cool. But can we automate this further?