npm ci. It wipes node_modules and installs the package-lock.json exactly, so every run is clean and identical. It's faster than npm install and never changes the lockfile.Guide
The final step: let a server run your npm commands automatically — on every push, with no clicks.
CI/CD means a service automatically runs your project's commands whenever you push
code — installing packages, building, testing, and often deploying. Instead of running
npm run build on your laptop and uploading by hand, a robot does the exact same steps,
the same way, every time. GitHub Actions, GitLab CI and Cloudflare Pages are common examples.
In automation you want a clean, identical install every run. That's exactly what
npm ci does (from the versioning chapter): it wipes
node_modules and installs the package-lock.json precisely, never changing
it. It's faster than npm install and can't drift.
The standard CI sequence
$ npm ci # exact, clean install from the lockfile
$ npm run build # build the project
$ npm test # run the tests (if any) npm ci needs a committed package-lock.json — which is why you always commit itIf npm ci errors that the lockfile is out of sync, run npm install locally, commit the updated lockfile, and push.
CI should build with the same Node version your project expects (this one wants
>=22.12.0). You tell the CI which Node to use — and turn on npm caching so installs
are quick:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm // caches ~/.npm between runs for speed
A .nvmrc file (containing just a version like 22) is another common way to
record the intended Node version so both humans and CI agree.
Here's a GitHub Actions workflow that installs, builds and deploys a static site like this one to
Cloudflare Pages — mirroring this project's own deploy script
(astro build && wrangler pages deploy dist). It lives at
.github/workflows/deploy.yml:
name: Deploy
on:
push:
branches: [master]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- run: npx wrangler pages deploy dist --project-name=siazly
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Read it top to bottom: on every push to master, a fresh Ubuntu machine checks out the
code, sets up Node 22 with npm caching, runs npm ci, builds, then deploys — using a
secret token kept in the repo's settings, never in the code.
Secrets, not hard-coded keys: the ${{ secrets.CLOUDFLARE_API_TOKEN }} reads a value you store in GitHub → Settings → Secrets. The same golden rule as the .npmrc chapter: tokens live in secrets or environment variables, never committed.
A couple of npm commands earn their place in pipelines:
npm audit --audit-level=high — fail the build if a high-severity vulnerability appears, so security issues can't sneak in unnoticed.npm publish --provenance — when publishing a package from CI, this attaches a verifiable record of where and how it was built, which users can trust.Good news: many hosts (including Cloudflare Pages and Netlify) have CI built in — you just tell them your build command (npm run build) and they run npm ci + build for you on every push, no workflow file required.
Up next: the cheat sheet — every command from this guide on one page.
FAQ
npm ci. It wipes node_modules and installs the package-lock.json exactly, so every run is clean and identical. It's faster than npm install and never changes the lockfile.package.json, npm ci errors out — which is itself a useful warning that something is out of sync.actions/setup-node with cache: 'npm' does it. Also pin the Node version so installs are consistent.npm run build) and they run npm ci and build on every push for you.