GitHub Actions workflow for Node.js (lint, tests and build)

A continuous integration workflow for a Node.js project: it installs with npm ci and runs lint, tests and build on several Node versions, with npm caching. This is the exact YAML the application generates.

When to use it

  • Your project has a package.json and a package-lock.json.
  • You want to run lint, tests and build on several Node versions.
  • You want a safe starting point to build on (deployment, publishing…).

The complete workflow

Save it as .github/workflows/node-ci.yml in your repository
name: Node CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
  workflow_dispatch:
permissions:
  contents: read
jobs:
  build:
    name: Node ${{ matrix.node-version }}
    runs-on: ubuntu-latest
    timeout-minutes: 15
    strategy:
      matrix:
        node-version:
          - 20
          - 22
          - 24
      fail-fast: false
    steps:
      - name: Check out the code (actions/checkout v7.0.1)
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
      - name: Set up Node (actions/setup-node v7.1.0)
        uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Lint
        run: npm run lint --if-present
      - name: Tests
        run: npm test --if-present
      - name: Build
        run: npm run build --if-present

It is a starting point: you can open it in the editor, change events, jobs and steps, and the application validates it again before generating the file.

Before you use it

  • Needs a package-lock.json (it uses `npm ci`).
  • The `lint`, `test` and `build` scripts only run if they exist in your package.json (`--if-present`).
  • Change the matrix versions to the ones your project supports.

What each part does

  • EventsThe workflow runs on pushes to main, on pull requests targeting main, and manually from the Actions tab (workflow_dispatch).
  • Version matrixA matrix with three Node versions creates one job per version; with fail-fast disabled, a failure in one does not cancel the others.
  • Optional scriptsThe lint, test and build scripts only run if they exist in your package.json (--if-present), so the template does not fail for lacking them.
  • npm cacheThe action that installs Node stores the npm cache between runs.
  • Permissions and securityMinimal permissions: the token can only read the repository contents (contents: read). Third-party actions are pinned to a specific commit SHA (with the readable version in the step name), not to a movable tag.
  • Maximum timeEvery job has a maximum run time (timeout-minutes) so a hang cannot burn minutes forever.

How to use it in your repository

  1. Save the file in the .github/workflows/ folder of your repository.
  2. Push it (commit and push). GitHub detects the workflow and runs it according to its events.
  3. Check the result in the Actions tab of the repository.

Frequently asked questions

Why npm ci and not npm install?

npm ci installs exactly what package-lock.json says and fails if it does not match package.json, which makes runs reproducible.

What if I use pnpm or yarn?

Change the install-dependencies step and the Node action cache to your manager's. The rest of the template works the same.

How do I change the Node versions?

Edit the matrix list (node-version) to the versions your project supports.

Other templates

  • GitHub Actions workflow for Python (pytest)Installs dependencies and runs pytest on several Python versions with pip caching.
  • GitHub Actions workflow to build and publish a Docker image to GHCRBuilds the repository image and publishes it to GitHub Container Registry (ghcr.io) when you push to main or create a v* tag.

Try it for free

The editor is available with a free account, with usage limits. Nothing runs in your repository or your installation: you decide which file to use.

Create a free accountLog in