| name | ci-github-actions |
| description | GitHub Actions CI pipeline conventions — job shape, caching, permissions, and action pinning. Use when asked to "set up CI", "add a GitHub Actions workflow", "fix the pipeline", or when creating/reviewing files under .github/workflows/ for Go or Node/TS projects. |
CI with GitHub Actions
Complete copy-paste workflows for Go and Node/TS live in references/workflows.md —
read it when writing an actual workflow file. This file is the rules.
Pipeline shape
Default pipeline: lint → typecheck → test → build, as parallel jobs (they don't
depend on each other's outputs) with build optionally needs: the rest if you want
fail-fast economics. Trigger:
on:
push:
branches: [main]
pull_request:
Don't run push on every branch and pull_request — that double-builds PR branches.
Concurrency — cancel superseded runs
Every CI workflow gets this; without it, ten pushes to a PR queue ten full runs:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
For deploy workflows, keep the group but set cancel-in-progress: false (queue, don't kill mid-deploy).
Permissions — least privilege
Top of every workflow:
permissions:
contents: read
Grant more only per-job, only what that job proves it needs (pull-requests: write for a
comment bot, id-token: write for OIDC cloud auth, packages: write for pushing images).
Never leave the org/repo default write-all doing the work implicitly.
Pin third-party actions to SHA
-
Official actions/* and other heavily-trusted orgs: major tag is acceptable (actions/checkout@v4).
-
Everything else: pin to a full commit SHA with a version comment:
uses: some-org/some-action@3d1a2b...
Tags are mutable; a compromised action re-tagged at v2 runs in your CI with your
secrets. Dependabot/Renovate can bump SHAs for you — enable it.
Caching
- Go:
actions/setup-go@v5 with cache: true (default when a go.sum exists) — caches module and build cache. Don't hand-roll actions/cache for Go.
- Node/npm or yarn:
actions/setup-node@v4 with cache: npm — caches the package cache (correct; don't cache node_modules).
- pnpm: setup-node's
cache: pnpm caches the pnpm store. Install pnpm first (pnpm/action-setup, SHA-pinned), then setup-node with cache: pnpm, then pnpm install --frozen-lockfile.
- Cache keys must include the lockfile hash — built-in caching does this for you.
- Never cache build outputs between CI runs as a correctness shortcut; only as a measured optimization (Next.js
.next/cache is the common legitimate case).
Matrix — only when genuinely needed
A matrix multiplies CI cost. Use one when you actually support multiple targets
(a library testing Node 20/22, or linux+windows CLIs). An app deployed to one runtime
tests on that one runtime version — pin it to the version production runs.
Secrets
- Secrets come from GitHub Environments (
environment: production on the job), which gate them behind protection rules and reviewers — not from repo-wide secrets sprayed into every job.
- CI jobs (lint/test/build) should need zero secrets. If a test needs a secret, question the test.
- Never
echo secrets, never pass them as CLI args (visible in process lists/logs). Prefer OIDC (id-token: write + cloud role assumption) over long-lived cloud keys.
pull_request_target + checkout of PR head = classic secret-exfiltration hole. Don't use pull_request_target unless you know exactly why.
Monorepo path filters
Scope workflows to what changed:
on:
pull_request:
paths:
- "services/api/**"
- ".github/workflows/api.yml"
Include the workflow file itself in its own paths. If a filtered check is a required
status check, add a no-op fallback workflow with the inverse paths-ignore so PRs that
don't touch the service aren't blocked forever.
Artifacts
Upload build outputs when a later job or a human needs them:
- uses: actions/upload-artifact@v4
with:
name: server-dist
path: dist/
retention-days: 7
Set retention-days deliberately (default 90 wastes storage). Pass files between jobs
via artifacts, not by rebuilding. Test reports/coverage: upload with
if: always() so failures still produce the report.
Checklist for a new workflow