C.W.K.
Stream
Lesson 02 of 13 · published

Node.js CI

~12 min · node, npm, pnpm

Level 0Apprentice
0 XP0/101 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

Pick your runtime, lock the version

Node has three common runtimes in active use: Node.js (Classic), Deno, Bun. For most production apps it's still Node.js. Package managers: npm (default), pnpm (fast, content-addressed), yarn (still around), bun (blazing fast, also a runtime).

The default Node CI shape:

  1. Checkout.
  2. actions/setup-node@v4 with a pinned version and cache for the package manager.
  3. npm ci (or pnpm install --frozen-lockfile, yarn install --immutable, bun install) — clean, reproducible install from the lockfile.
  4. Lint: eslint or biome.
  5. Type check: tsc --noEmit.
  6. Test: vitest run or jest or node --test.

Common pitfalls

  • npm install in CI is wrong — it can update the lockfile silently. Use npm ci.
  • Default cache key for actions/setup-node is the lockfile hash. If you have a monorepo with multiple lockfiles, set cache-dependency-path: explicitly.
  • Don't run a separate npm install per matrix cell — share the install across the matrix unless versions differ.

Code

Node CI with pnpm·yaml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with: { version: 9 }
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm tsc --noEmit
      - run: pnpm test --run
Node CI with bun (the speed benchmark)·yaml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
        with: { bun-version: latest }
      - run: bun install --frozen-lockfile
      - run: bun run lint
      - run: bun run test

External links

Exercise

Take a Node project and configure CI with two cache layers: actions/setup-node default cache (which keys on the lockfile) plus a build output cache for .next/ or dist/. Time the cold vs warm runs.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.