Skip to content
Move custom links under /@handle
Sniptide

GitHub Actions Node CI

This workflow runs your lint, type check, tests and build on every pull request and every push to main, with npm's cache so repeat runs install fast. Save it as .github/workflows/ci.yml, make sure the four npm scripts exist in package.json, and push.

Updated

Files

.github/workflows/ci.ymlRaw
name: CI

on:
  push:
    branches: [main]
  pull_request:

# A new push to the same branch or pull request cancels the run still in progress.
concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-node@v5
        with:
          node-version-file: .nvmrc
          cache: npm

      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test
      - run: npm run build

Save with your agent

Connect the Sniptide MCP server once, then ask your agent to keep this template in your library. It reads the files from the JSON version of this page and saves them as a private snippet you can edit and file into a collection.

Terminal
claude mcp add --transport http sniptide https://app.sniptide.com/api/mcp
Prompt
Read https://sniptide.com/templates/github-actions-node-ci.json and save its files as a private Sniptide snippet titled "GitHub Actions Node CI".

What each part does

Part
What it does
When to change it
on
Runs on pull requests and on pushes to main. Pushes to other branches run through their pull request, so nothing runs twice.
Add workflow_dispatch: to start runs by hand.
concurrency
Cancels a run that is still going when a newer push arrives on the same branch.
Rarely.
permissions
Gives the job read access to the code and nothing else.
Only when a step needs more, such as writing a comment.
timeout-minutes
Stops a stuck job after 15 minutes instead of the default 6 hours.
When your build legitimately takes longer.
setup-node
Installs the Node version named in .nvmrc and caches npm's download cache by the lockfile.
Use node-version: 24 if you don't keep an .nvmrc.
npm ci
Installs exactly what the lockfile says, and fails if the lockfile is out of date.
Use the variant below for pnpm.
Checks
One step per script, so the log shows which one failed.
Remove the scripts your project doesn't have.

To block merging when a check fails, open the repository's settings, add a branch ruleset for main and require the test job.

Variant: pnpm

pnpm has to be installed before setup-node can cache it. pnpm/action-setup reads the version from packageManager in package.json.

.github/workflows/ci.yml
    steps:
      - uses: actions/checkout@v5
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v5
        with:
          node-version-file: .nvmrc
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm typecheck
      - run: pnpm test
      - run: pnpm build

Variant: several Node versions

A library that supports more than one Node version can test them all with a matrix. Each version runs as its own job.

.github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        node: [20, 22, 24]
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: ${{ matrix.node }}
          cache: npm
      - run: npm ci
      - run: npm test

fail-fast: false keeps the other versions running when one fails, so you see every failure at once.

Variant: tests that need Postgres

A service container starts Postgres next to the job. The health check makes the steps wait until it accepts connections.

.github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: app
          POSTGRES_PASSWORD: app
          POSTGRES_DB: app_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U app"
          --health-interval 5s
          --health-timeout 5s
          --health-retries 10
    env:
      DATABASE_URL: postgres://app:app@localhost:5432/app_test
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm test

The same database runs on your machine with the Docker Compose Postgres template.

All templates
  • Docker and deployNext.js Dockerfile

    A multi-stage Next.js Dockerfile that builds the standalone output into a small image running as a non-root user, with .dockerignore and variants.

  • Docker and deployDocker Compose Postgres

    A Docker Compose file for Postgres with a persistent volume, a health check and init scripts, plus variants with an app service and pgAdmin.

  • .env
    Project setup.env.example template

    A .env.example template with grouped variables, safe defaults and empty secrets, plus how to keep it in sync with your code and share the real values.

Pick where your library lives.

Use sniptide.com

Save your first pattern in the app, then connect Claude Code, Cursor or Codex with one sign-in in the browser.

Run it on your own server

One image and a database give you the app, the API and the MCP endpoint. Sniptide is open source under AGPL-3.0.

$docker compose up -d