# Cicd Pipeline

> Set up GitHub Actions for Next.js with parallel type-check / lint / test / build, dependency + Next.js caching, concurrency cancellation, Vercel Preview, branch protection, and least-privilege CI security. Use at project start, when manual deploys cause errors, or when the team grows. Coordinates with test-strategy for what gets gated and developer-experience for local pre-commit gating.

- Skill: `jaykim88/cicd-pipeline-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add jaykim88/cicd-pipeline-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jaykim88/cicd-pipeline-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: JayKim88 (https://skillmd.com/u/jaykim88)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/jaykim88/cicd-pipeline-2

---


# CI/CD Pipeline

## Purpose
Every PR runs type-check, lint, test, and build automatically. Failures block merge. Pipeline completes in under 10 minutes (ideally under 3) so developers don't abandon discipline.

**Universal concept, platform-bound Procedure** — the pipeline *structure* (parallel type-check / lint / test / build, dependency + framework-output caching, branch protection, preview deploys) applies to any stack. But the Procedure below is written concretely for **GitHub Actions + Vercel**; the job-DAG and caching concepts port to GitLab CI / CircleCI / others (see Other stacks), where the YAML and platform UI differ.

## Procedure

1. **Define the workflow stages (parallel where possible)**
   - Triggers: `on: pull_request` + `push` to `main`; add `paths-ignore` for docs-only changes so a README edit doesn't run the full build
   - **Concurrency**: `concurrency: { group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true }` — a new push to a PR cancels the stale in-progress run (saves minutes + CI cost)
   - Pin the Node version to what you deploy on (`.nvmrc` / `engines`)
   - **Job 1**: dependency install (cached)
   - **Job 2** (parallel): `tsc --noEmit`
   - **Job 3** (parallel): `eslint .`
   - **Job 4** (parallel): `vitest run`
   - **Job 5** (depends on 2-4): `next build`
   - Stages 2-4 in parallel ⇒ pipeline = max(typecheck, lint, test) + build, not sum

2. **Configure caching**
   - npm cache: `actions/setup-node@v4` with `cache: 'npm'`
   - Next.js build cache: `actions/cache@v4` for `~/.npm` and `.next/cache`
   - Use the canonical cache key from Next.js docs (the `restore-keys` fallback is what delivers the warm-cache speedup):
     ```yaml
     - uses: actions/cache@v4
       with:
         path: |
           ~/.npm
           ${{ github.workspace }}/.next/cache
         key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
         restore-keys: |
           ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
     ```
   - Result: expect 2-3x speedup on warm cache (exact numbers depend on project size)

3. **Vercel Preview integration**
   - Connect repo in Vercel dashboard
   - Every PR gets a preview URL
   - Comment the URL on the PR for stakeholder review
   - Production: promote from a verified preview; run a post-deploy smoke check; keep instant rollback available (Vercel retains prior deployments)

4. **Branch protection on `main`**
   - GitHub → Settings → Branches → Add rule
   - Require status checks: type-check, lint, test, build
   - Require PR review (≥ 1 approval)
   - Block force push, block direct push to main

5. **Environment variable management**
   - Separate Vercel envs: Development / Preview / Production
   - GitHub Secrets for CI-only values
   - Validate via Zod schema (see `developer-experience` skill)

5b. **Harden the CI itself (it runs with your secrets)**
   - **Least privilege**: set `permissions: contents: read` at the workflow top, widen per-job only as needed — the default `GITHUB_TOKEN` is over-permissioned
   - **Pin third-party actions to a full commit SHA**, not a moving tag (`@v4` can be re-pointed to malicious code); let Dependabot bump them
   - **Never expose secrets to untrusted fork PRs** — `pull_request` (the safe default) has no secret access; reach for `pull_request_target` only with extreme care (it runs with secrets against the PR's code)
   - **Use OIDC** for cloud deploys instead of long-lived access keys stored in secrets
   - (coordinate with `security-audit`)

6. **Optional integrations**
   - Lighthouse CI for performance regression detection
   - axe-core for a11y regression (coordinate with `accessibility-audit`)
   - Sentry release tagging (coordinate with `observability-setup`)
   - Upload artifacts on failure (Playwright traces/screenshots, coverage) so red runs are debuggable
   - Wire a new gate as non-blocking first (`continue-on-error`), then flip it to required once it's stable

7. **Verify pipeline performance (validation loop)**
   - Trigger a real PR; measure total runtime
   - If > 10 min: identify the long pole job (usually `next build` or `npm install`), expand cache scope or parallelize further, and re-trigger; loop until ≤ 10 min
   - If warm-cache runs don't show ≥ 2x speedup over cold: cache key probably too narrow — broaden the `restore-keys` fallback and re-measure

## Completion Criteria
- [ ] Pipeline runs on every PR open + push
- [ ] Failure blocks merge (branch protection enforced)
- [ ] Type-check + lint + test run in parallel
- [ ] Concurrency cancels stale in-progress runs (`cancel-in-progress`)
- [ ] Cold build < 10 min, warm cache delivers ≥ 2x speedup
- [ ] Vercel Preview URL on every PR
- [ ] Environment variables split by env (dev / preview / production)
- [ ] Least-privilege `permissions:`; third-party actions pinned to SHA; no secrets exposed to fork PRs

## Output
- **CI workflow file**: `.github/workflows/ci.yml` with parallel jobs (type-check / lint / test / build) + caching block
- **Branch protection**: rule on `main` requiring all CI status checks to pass + PR review ≥ 1
- **Preview deploys**: Vercel/Netlify/Cloudflare Pages project linked to repo, commenting URL per PR
- **Pipeline metrics** (paste into PR description): cold build time / warm cache time / cache hit rate
- **Commit format**: `chore(ci): <change>` for workflow updates; `chore(infra): <change>` for branch protection / env config

## Implementation

### React + Next.js (default — GitHub Actions + Vercel)
- Workflow: `actions/checkout` + `actions/setup-node` + `actions/cache`, each pinned to a commit SHA (Dependabot `github-actions` ecosystem bumps them)
- Concurrency: `concurrency: { group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true }`
- Permissions: `permissions: contents: read` at the top; widen per-job as needed
- Build cache key: `${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.{js,jsx,ts,tsx}') }}` + restore-keys fallback
- Preview: Vercel project auto-deploys every PR, comments URL on PR
- Deploy auth: Vercel/cloud OIDC over static tokens; instant rollback via prior deployments
- Branch protection: require type-check + lint + test + build status checks

### Other stacks
- **Vue / Nuxt**: cache `.nuxt/cache` and `node_modules/.cache/nuxt`; deploy preview via Vercel/Netlify (both auto-detect Nuxt)
- **SvelteKit**: cache `.svelte-kit/output`; deploy preview via Vercel/Netlify/Cloudflare Pages (built-in adapters)
- **Angular**: cache `.angular/cache`; deploy preview via Firebase Hosting or Vercel
- **Other CI platforms**: GitLab CI, CircleCI, Buildkite — same parallel structure (jobs DAG), different YAML syntax
- **Other deploy platforms**: Netlify deploy previews, Cloudflare Pages, Render preview environments — all support PR-triggered builds with unique URLs
- **Universal**: parallel job DAG > sequential steps; caching keyed on lockfile + source hash is the universal pattern; branch protection rules are platform-specific but conceptually identical

## Related skills
- `test-strategy` — defines what gets gated in CI (and keeps required checks non-flaky)
- `accessibility-audit` — axe-core wired here as a gate
- `security-audit` — least-privilege tokens, SHA-pinned actions, `npm audit` as a CI gate
- `observability-setup` — Sentry release tagging coordinated with CI
- `developer-experience` — husky + lint-staged complement CI by gating locally

## Reference
- **Key insight encoded**: Cache `~/.npm` + `.next/cache` keyed on `package-lock.json` + source hashes — this is the single biggest pipeline speedup. Run type-check / lint / test as parallel jobs (not sequential steps) to hit sub-3-minute warm pipelines. The `next build` step is usually the long pole; its caching benefit compounds across PRs. Two senior must-haves often missed: `concurrency: cancel-in-progress` (kill stale runs on a new push) and treating the workflow as a security boundary — least-privilege `permissions:`, SHA-pinned actions, and no secrets for fork PRs.

