GitHub Actions can be used to implement continuous integration (CI) for code that is maintained in GitHub repositories.

  • CI is the practice of using automation to build and test software every time a developer commits changes to version control.
  • CI helps teams discover issues early in the development process and fix them quickly.

What is CI?

  • CI can help you stick to your team’s quality standards by running tests and reporting the results on GitHub.
  • CI tools run builds and tests, triggered by commits.
  • The results post back to GitHub in the pull request.
  • This reduces context switching for developers, and improves consistency for testing.
  • The goal is fewer bugs in production and faster feedback while developing.

What are artifacts?

When a workflow produces something other than a log entry, it’s called an artifact.

Storing an artifact helps to preserve it between jobs. Each job uses a fresh instance of a VM, so you can’t reuse the artifact by saving it on the VM. If you need your artifact in a different job, you can upload the artifact to storage in one job, and download it for the other job.

Artifact storage

Artifacts are stored in storage space on GitHub. The space is free for public repositories and some amount is free for private repositories, depending on the account. GitHub stores your artifact for 90 days.

I took GitHub Learning lab: GitHub Actions: Continuous Integration, which builds this repository.

Use a templated workflow

Templated workflows are available in the Actions tab. It will help you add a workflow file, for example, .github/workflows/node.js.yml

Examining the workflow file

The .github/workflows/node.js.yml defines the CI workflow.

on

The on: field is what tells GitHub Actions when to run. In this case, we’re running the workflow anytime there’s a push.

1
2
3
4
5
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs

The jobs: block defines the core component of an Actions workflow. Workflows are made of jobs, and our template workflow defines a single job with the identifier build.

Every job also needs a specific host machine on which to run, the runs-on: field is how we specify it. The template workflow is running the build job in the latest version of Ubuntu, a Linux-based operating system.

The template workflow is running the build: job in the latest version of Ubuntu, a Linux-based operating system.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [10.x, 12.x, 14.x]

steps:
- uses: actions/checkout@v2
# actions/checkout@v2 is used to ensure our virtual machine has a copy of our codebase. The checked out code will be used to run tests against.
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
# actions/setup-node@v1 is used to set up proper versions of Node.js since we'll be performing testing against multiple versions.
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
# 'run:' In addition to running pre-built actions, the workflow can also execute commands, just as you would if you had direct access to the virtual machine. In this portion of the template workflow, we run some common commands relevant to Node.js projects, like npm install to install dependencies and npm test to run the chosen testing framework.
- run: npm run build --if-present
- run: npm test

Accessing artifacts

With this version of CI workflow, You may notice build succeeded, but each of the test jobs failed. That’s because the build artifacts created in build aren’t available to the test job. Each job executes in a fresh instance of the virtual environment. This is due to the design of the virtual environments themselves.

To solve the problem, first add the step to upload artifact.
Then the test job needs to start after build finishes (jobs run in parallel unless specified):

1
2
3
test:
needs: build
runs-on: ubuntu-latest

The test job will download the artifact:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@master
with:
name: webpack artifacts
path: public
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: npm install, and test
run: |
npm install
npm test
env:
CI: true

Adding an approval workflow

There can be multiple workflows for a repo. .github/workflows/approval-workflow.yml uses a community-created action, pullreminders/label-when-approved-action@master to take care of labelling a pull request as "approved" when there have been 2 reviews.

Use branch protections

In repo settings, you can create branch protection rules for branches that match a certain name pattern.
You can reinforce reviews before merge, require status check and prevent branch deletion etc.