Automated CI with Cypress and Github Actions

At the day job, we've introduced a development pipeline that consists of creating feature branches that deploy to their own environments, and using Cypress tests to validate that the build is good before merging. I've known enough about it to talk about it with my team, but not do it in practice. So I've followed some guidelines using some free tools that help me get there.

It consists of using Github with Github Actions, Cypress with Cypress Dashboard, and Vercel to handle deployments. Here's how to set this up from scratch.

Set up a local repo

Instructions below are true as of the last-updated date, and at least on my machine.

  1. In a terminal, type yarn create next-app
  2. Name the app. Let's pretend it's twitter-killer
  3. cd twitter-killer
  4. Open in code, and add a pages/index.js file with the following contents, and save:
export default function Home() {
  return (
    <div>
      <h1>Hello</h1>
      <p>How are you</p>
    </div>
  )
}
  1. Back in the terminal at project root, add cypress: yarn add cypress --dev

Set up a Cypress test locally

  1. Still in the terminal at project root: yarn run cypress open
  2. Within the Cypress app, select e2e testing.
  3. Accept defaults and continue, choosing Chrome for the browser, and continue.
  4. Select the Create new empty spec option
  5. Accept default path of cypress/e2e/spec.cy.js and click "Create spec".
  6. Then "Ok, run the spec"
  7. It works! Now close cypress.
  8. Open cypress.config.js, and as a sibling to setupNodeEvents, add baseUrl: 'http://localhost:3000', so it ends up looking like this:
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
    baseUrl: 'http://localhost:3000'
  },
  1. Open spec.cy.js and change cy.visit line to cy.visit('/')
  2. Then add an additional test: cy.get('h1').should('contain.text', 'Welcome to').should('be.visible')
  3. Validate this works by running the app (yarn dev) and also running cypress (yarn run cypress open)
  4. Do a git add / commit to capture this.

Add to GitHub

  1. Visit github.com and create a new repo
  2. Follow the directions to push an existing repository from local.
  3. There should now be a 'main' branch in GitHub.

Configure Vercel

  1. Log into vercel.
  2. Create a new project, and enable the new git repo to be added.
  3. Proceed with the deployment

Cypress Dashboard projectId and record key

  1. In Cypress Dashboard's UI, create a new project, adding 'GitHub Actions' as the CI.
  2. You'll be given a projectId to add to your cypress.config.js file. Add it, and follow the instructions to run the npx command with the record key.
  3. Copy the record key. (If already navigated away, go to Cypress Dashboard for that project, and go to Project Settings. The record key should be listed.
  4. Within the GitHub UI for the project, navigate to Settings -> Secrets -> Actions -> New Repository Secret.
  5. Name it CYPRESS_RECORD_KEY, and paste in your record key

Now add a GitHub workflow

  1. Within the GitHub project -> Actions -> Set up a workflow yourself
  2. Name it deploy.yml
  3. Paste in the yaml
name: Cypress Tests

on:
  pull_request:
  push:
    branches: main

jobs:
  runTestsAgainstPreview:
    name: Run Tests Against Preview URL
    runs-on: "ubuntu-latest"
    steps:
      - name: Capture Vercel preview URL
        id: waitFor200
        uses: patrickedqvist/wait-for-vercel-preview@v1.2.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          max_timeout: 60
      - name: Checkout
        uses: actions/checkout@v2
      - name: Run Cypress
        uses: cypress-io/github-action@v4
        id: runCypress
        with:
          record: true
          parallel: true
          ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
          config: baseUrl=${{steps.waitFor200.outputs.url}}
        env:
          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Add Label
        uses: actions-ecosystem/action-add-labels@v1
        with:
          labels: automerge
        if: ${{ steps.runCypress.outcome == 'success' }}

  automerge:
    runs-on: ubuntu-latest
    needs: runTestsAgainstPreview
    steps:
      - id: automerge
        name: automerge
        uses: "pascalgn/automerge-action@v0.15.3"
        env:
          GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
          MERGE_RETRIES: 10
          MERGE_RETRY_SLEEP: 10000
  1. By going through with that commit in the GitHub UI, it should now trigger that deploy.yml file, so switch over to 'Actions' tab and check it out. It will fail, as we haven't pushed up the projectId edit that we made locally earlier.
  2. So locally, first, add cypress/videos/ to the gitignore.
  3. Things may still fail, as I got HttpError: Empty value for parameter 'issue_number': undefined. I should try this as a pull request instead.
  4. git checkout -b feature-copy-edit
  5. Make change and push up a new branch, and intitiate the pull request within GitHub. Actions should now run, and automerge
  6. Should be live!

Lock down main branch in GitHub to enforce good habits

  1. Github project -> Settings -> Branches
  2. Add rule, that matches branch name main
  3. Check "Require a pull request before merging"
  4. Check "Require status checks to pass before merging"
  5. In the 'Search for status checks...' field, enter 'Run Tests Against Preview URL', which is defined in the yaml
  6. Check "Do not allow bypassing the above settings"

In summary, the new workflow

Going forward, to make changes to this repo, proceed as follows:

  1. Create a branch
  2. Make your changes
  3. Commit and push up feature branch. (Vercel will deploy)
  4. Initiate PR. (Cypress will run against the deployment)

If Cypress fails...

  1. The pull request doesn't merge automatically.
  2. Continue pushing up new commits to the pull request until this is fixed.

If Cypress passes...

  1. The pull request is given a label of automerge.
  2. Upon merging into 'main' branch, vercel deploys to production url.

Missing parts of my notes

  1. I've done the create next-app 3 times and not getting consistent results with the creation of a gitignore file.
  2. How does this work with Netlify?
  3. Don't we need some automated way for older preview environments to close down?
August 17, 2022🏷cypresscivercelgithub actions