---
title: Switch your cross-platform CI to GitHub Actions in 5 minutes
url: https://photostructure.com/coding/switch-to-github-actions/
date: 2021-01-10
keywords: developer-workflow
---


If you maintain an open source project that's hosted on GitHub, and you're
frustrated by one or more of your current [continuous
integration](https://en.wikipedia.org/wiki/Continuous_integration) (CI)
providers, **good news**: [GitHub Actions](https://github.com/features/actions)
is a free CI service that supports running tests and other tasks on macOS,
Windows, and Linux.

Note that [many runtimes are supported by GitHub
Actions](https://docs.github.com/en/free-pro-team@latest/actions/guides). This
post includes some Node.JS-specific tips.

## ❓ What's wrong with GitHub's docs?

GitHub's
[quickstart](https://docs.github.com/en/free-pro-team@latest/actions/quickstart)
and [setup
docs](https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-nodejs)
are a good start for building and testing against the [currently supported
versions of Node.JS](https://nodejs.org/en/about/releases/), but if your code
has any OS-specific code, these docs don't tell you how to set up cross-platform
builds, and sometimes just knowing what to search for is the biggest stumbling
block.

Setting up cross-platform, cross-version builds is easy, though:

## ⚙️ Add a new workflow

Either click the GitHub Actions tab, and click the "New workflow" button:

{{< figure src="/img/2021/01/actions-tab.png" caption="GitHub Actions tab" >}}

Or, if you'd rather do it manually, edit a new file called
`.github/workflows/node.js.yml` from the base of your project repository.

Then replace the contents of the file with this:

```yml
name: Node.js CI
on:
  push:
    branches: [master]
  pull_request:
    branches: [master]
jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node-version: [10.x, 12.x, 14.x, 15.x]
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test
```

_Note that this assumes you're using_ `npm` _and have a_ `package-lock.json`. _If
you're a_ `yarn` _user, check out [Tip #2](#yarn)._

There's a bunch going on there, but the magick bits to
[grok](https://en.wikipedia.org/wiki/Grok) are the `strategy.matrix:`,
`runs-on:`, and `with:` keys.

As soon as you commit this and push to GitHub, you should see a build summary
show up in your Actions tab:

{{< figure src="/img/2021/01/actions-summary.png" caption="GitHub Actions build summary" >}}

You'll see that the build matrix will include every combination of `os` and
`node-version` specified in `jobs.build.strategy.matrix`, which in this case,
was 12 different builds.

## 🎯 Tip 1: Customizing the build matrix

GitHub's [`jobs.<job_id>.strategy.matrix`
reference](https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix)
describes how to include or exclude specific OSes and/or Node versions in your
build matrix.

<a id="yarn"></a>

## 🧵 Tip 2: How to use yarn instead of npm

GitHub's own runners actually install `yarn` by default, so (almost) all that
you need to do is switch out `npm` for `yarn` in your workflow.

If you're not aware, `npm ci` does the following:

1. Recursively deletes the `node_modules` directory
2. Runs `npm install`, but only uses the exact versions specified in the
   `package-lock.json`. If `package-lock.json` is missing, `npm ci` will fail.

If you're using `yarn` (and not using Windows), you can simulate this by adding
the following scripts to your `package.json`:

```json
{
  ...
  "scripts": {
    ...
    "preci": "rm -rf node_modules",
    "ci": "yarn install --frozen-lockfile"
    ...
  }
}
```

_Note that this_ `preci` _script won't work if you're building on Windows. For
cross-platform modules, I'm just cheating and skipping over the node_modules
cleanup step._

## 🏅 Tip 3: Add or replace your CI build badge

From the Actions tab, click a build summary, and in the upper right, click `…`,
and then click "Create status badge."

If you're building a badge for your README, you want to pick your release branch
(so, `main` or `master`).

**Unfortunately, the markdown that gets displayed is _just the image_**, so if you paste the
markdown into your `README.md`, and someone clicks the build badge, it navigates
to the badge image, not the build (!!).

It's up to you to add a proper link to the build badge: wrap the markdown from
the "Create status badge" tool with square brackets, and then add a reasonable
destination link, in parenthesis, right after the square brackets.

You'll end up with something like this:

```
[![CI build](https://github.com/.../badge.svg)](https://github.com/.../actions)
```

## 🚀 All together now

[Here's a commit](https://github.com/photostructure/exiftool-vendored.js/commit/952d34246f24d7586e669460bd34bb1d3aa90543) that

- switches to GitHub Actions,
- removes the other CI build configurations,
- updates the build badge in the `README.md`, and
- adds a `yarn ci` job (without the `preci` step)

My prior CI for this build matrix could take 30 minutes or more to complete, and
required using two different services.

GitHub Actions completes this same matrix in only 6 minutes. Kudos, GitHub!

