ci-github-actions
GitHub提供GitHub Actions CI流水线规范,涵盖Go/Node项目的工作流结构、并行作业设计、并发控制、最小权限管理及第三方Action SHA固定策略。指导CI配置、审查及修复,确保构建安全高效。
触发场景
安装
npx skills add gnomeria/usbtree --skill ci-github-actions -g -y
SKILL.md
Frontmatter
{
"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@v5). -
Everything else: pin to a full commit SHA with a version comment:
uses: some-org/some-action@3d1a2b... # v2.1.0Tags are mutable; a compromised action re-tagged at
v2runs in your CI with your secrets. Dependabot/Renovate can bump SHAs for you — enable it.
Caching
- Go:
actions/setup-go@v6withcache: true(default when ago.sumexists) — caches module and build cache. Don't hand-rollactions/cachefor Go. - Node/npm or yarn:
actions/setup-node@v5withcache: npm— caches the package cache (correct; don't cachenode_modules). - pnpm: setup-node's
cache: pnpmcaches the pnpm store. Install pnpm first (pnpm/action-setup, SHA-pinned), then setup-node withcache: pnpm, thenpnpm install --frozen-lockfile. - bun:
oven-sh/setup-bun(SHA-pinned) has no built-in caching — pair withactions/cacheon~/.bun/install/cache, keybun-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/bun.lock') }}, thenbun 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/cacheis 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: productionon 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
echosecrets, 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 usepull_request_targetunless 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@v5
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
- Triggers:
pushto main +pull_request, no double-trigger -
concurrencywithcancel-in-progress: true -
permissions: contents: readat top; extras per-job only - Third-party actions SHA-pinned with version comment
- setup-go / setup-node built-in caching, keyed off lockfile
- Installs use lockfile-strict mode (
npm ci,pnpm install --frozen-lockfile,bun install --frozen-lockfile) - No matrix unless multiple targets are truly supported
- Secrets via environments; none in lint/test/build jobs
- Monorepo:
pathsfilters incl. the workflow file - Artifacts have
retention-days; reports uploadedif: always() - Runtime versions read from the repo (
go.mod,.nvmrc/package.json engines), not hardcoded twice
版本历史
- 171c884 当前 2026-07-19 10:05


