GitHub Actions CI Workflow Builder (Node.js)

Generate a Node.js CI workflow with test, lint, and build steps

Create a GitHub Actions CI workflow YAML for Node.js projects with a multi-version test matrix, npm, yarn, or pnpm dependency caching, and lint, test, and build jobs. Copy a ready-to-commit .github/workflows file built in your browser. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

Where does this file go?

Save it as .github/workflows/ci.yml in the root of your repository. GitHub automatically detects any YAML file under .github/workflows and runs it on the events you configure, here push and pull_request to your chosen branch.

A GitHub Actions CI workflow builder for Node.js that generates a valid .github/workflows/ci.yml. Pick the Node versions to test, your package manager, and which jobs to run, and it produces a workflow with a version matrix, dependency caching, and lint, test, and build steps.

How the generated workflow is structured

The workflow triggers on push and pull_request to the branch you choose. It defines a single build job on ubuntu-latest with a strategy.matrix.node-version listing your versions, so the job runs once per version in parallel. fail-fast: false keeps the other versions running even if one fails, so you see every failure at once rather than stopping at the first.

Each matrix run executes these steps in order:

  1. actions/checkout@v4 — clones the repository
  2. actions/setup-node@v4 with cache: npm|yarn|pnpm — installs Node and sets up dependency caching keyed on the lockfile
  3. Installnpm ci, yarn install --frozen-lockfile, or pnpm install --frozen-lockfile (for pnpm, a preceding pnpm/action-setup step is added automatically)
  4. Lint (if enabled) — runs npm run lint
  5. Test — runs npm test
  6. Build (if enabled) — runs npm run build

A typical generated workflow

name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        node-version: [18, 20, 22]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

Why npm ci instead of npm install?

npm ci installs exactly what is locked in package-lock.json. It fails fast if the lockfile and package.json disagree — catching mismatches before they cause mysterious test failures. It is also typically faster in CI because it skips resolution. The yarn and pnpm equivalents use --frozen-lockfile for the same guarantee.

Practical tips

  • Choose your matrix wisely. Testing on the oldest and newest Node versions you support (for example 18 and 22) catches most compatibility issues without using extra minutes. If you maintain a library supporting many versions, add the LTS releases in between.
  • Script names must exist. The lint and build steps run your package.json scripts by those exact names. Untick any step you do not have a script for.
  • Add a deploy job downstream. To publish or deploy after the matrix passes, add a second job with needs: build and the condition if: github.ref == 'refs/heads/main' so it only runs on the main branch, not on every PR.
  • Pin major action versions. actions/checkout@v4 and actions/setup-node@v4 are pinned to major versions. Dependabot can keep these updated automatically — enable it under your repository’s Code Security settings.

Common issues and how to fix them

“Missing script: lint” failure — the lint step runs npm run lint, which requires a lint script in package.json. If your script is named differently (e.g. eslint), update the step’s run command. Same applies to build.

Cache misses on every run — the setup-node cache key is derived from the lockfile. If you commit package.json changes without updating the lockfile, or if you do not have a lockfile at all, the cache never hits. Always commit package-lock.json, yarn.lock, or pnpm-lock.yaml.

pnpm workflow fails at the install step — pnpm requires a pnpm/action-setup step before setup-node. The generated workflow includes it automatically, but if you add pnpm to an existing workflow manually, this step is commonly forgotten.

The matrix takes too long — if you are testing on many Node versions and the workflow is slow, consider using the --if-present flag (npm run lint --if-present) to skip steps gracefully, or reduce the matrix to just the LTS versions most relevant to your users.