Migrating from CircleCI to GitHub Actions

Since the introduction of GitHub Actions there's often no need any more to use an external CI/CD service. After I moved my blog repository from BitBucket to GitHub it was time to move my continuous deployment configuration from CircleCI to GitHub Actions as well.

Despite extensive documentation it took me a while to get started. However, once I figured out the basics, tranforming most of the CircleCI configuration to GitHub Actions format was pretty straightforward.

I started by following the workflow configuration instructions pretty closely. I created a build.yml configuration file in the .github/workflows folder of my repository:

name: Build
on: [push]

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v1
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: 10.x

This part is just the intialization and doesn't map directly to the CircleCI configuration. It specifies the following:

The process of installing NPM modules and caching them didn't directly align with the CircleCI approach although it was conceptually identical. I based my configuration on the example in the documentation:

- name: Cache NPM modules
  uses: actions/cache@v1
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm

The cache key depends on the runner and the package-lock.json file. If the latter changes, the prefix with runner information only is used as a fallback to find the best match.

From here on, the steps mostly differ from the ones in CircleCI only because of the differences in YAML syntax:

- name: Install dependencies
  run: npm install
- name: Generate site
  run: npm start
- name: Run tests
  run: npm test
- name: Create ZIP archive
  run: bash ./.scripts/archive.sh
- name: Deploy
  env:
    DEPLOY_USERNAME: ${{ secrets.DEPLOY_USERNAME }}
    DEPLOY_PASSWORD: ${{ secrets.DEPLOY_PASSWORD }}
    DEPLOY_APPNAME: ${{ secrets.DEPLOY_APPNAME }}
  run: |
    if [ "${GITHUB_REF}" == "refs/heads/master" ]; then
      curl --fail -X POST --data-binary @out.zip https://${DEPLOY_USERNAME}:${DEPLOY_PASSWORD}@${DEPLOY_APPNAME}.scm.azurewebsites.net/api/zipdeploy
    fi

The only exception is the final step:

To get everything working, there was one last change I had to make in the Bash script invoked from the Create ZIP archive step. Since I couldn't find a way to control the working directory for the job in GitHub Actions, I couldn't rely on it in the script any more and had to make all paths relative. This change resulted in a more generic script any way:

#!/bin/bash
cd ./out
zip -r ../out.zip ./*

After I committed the changes to the repository (and entered the secret values in the repository settings), the workflow was automatically triggered on every commit as specified in the configuration file. It was time to remove the project from CircleCI to avoid duplicate deployments.

Get notified when a new blog post is published (usually every Friday):

If you're looking for online one-on-one mentorship on a related topic, you can find me on Codementor.
If you need a team of experienced software engineers to help you with a project, contact us at Razum.
Copyright
Creative Commons License