Setting up continuous integration pipeline for JavaScript project

Table of contents

In this post we will look at detailed instructions how to setup continuous integration (CI) for JavaScript project hosted on GitHub and GitHub Actions.

It is important to run static analysis and unit tests on every new portion of code changes as often and as early as possible. So in this example we will look at how to lint and test your Create React App (CRA)-based project both on pull request stage and on main branch.

Initial preparations

Let's create new CRA project:

npx create-react-app app-with-ci

CRA comes with predefined and test npm script but it will not run eslint on your code. We need to add lint script and make it run together with tests.

In package.json add 2 new lines in "scripts" fields:

"scripts": {
  ...
  "pretest": "npm run lint",
  "lint": "eslint \"./src/**/*.{js,jsx}\" --quiet"
},

lint script will run eslint on your js and jsx files, when pretest script will add lint script to run before npm t command automatically.

Create GitHub Action workflow file

In order to run out linter and tests on CI - we need to tell GitHub when and how to this operation. Let's create a new file by next path: PROJECT_ROOT/.github/workflows/test.yml and put there next content:

name: Test
on:
  pull_request:
  push:
    branches:
      - main
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18.x]
    steps:
      - uses: actions/checkout@v1
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: npm install
        run: npm ci
      - name: npm test
        run: npm t

This workflow will prevent PR from merging in case linter or tests failed which will prevent from merging broken code to stable branches. In the same time workflow will also run after PR merge to double check that merged code is correct together with other code from main branch.

Run tests on multiple versions of Node.js

In example above we've put only one version Node.js to setup on pipeline. In case you are developing an open source package - you definitely want to run tests on multiple versions of Node.js, so you will see your tests are 'green' on all Node.js versions your package supports.

This task can be achieved by adding mode Node.js versions to node-version array:

node-version: [16.x, 18.x, 20.x]

But in the same time this will require your to spend time and periodically update this workflow to reflect all new Node.js versions through time. Definitely possible on low amount of packages but it will quickly become very time consuming manual task that can be avoided.

Let me introduce you pkgjs/action - a workflow to automatically add new versions of Node.js to your test matrix.

With pkgjs/action your workflow may look like this:

name: Test
on:
  pull_request:
  push:
    branches:
      - main
jobs:
  test:
    uses: pkgjs/action/.github/workflows/node-test.yaml@v0.1
    with:
      runs-on: ubuntu-latest
      upgrade-policy: lts
      test-command: npm test

It will automatically run test command on LTS versions defined in engines field of your package.json.