GitHub Actions
每个应用三个工作流,每个工作流只有一个 Job。模板中的 .github/workflows/{pr,main,release}.yaml 是唯一事实来源;其带注释的副本位于 references/workflows.md。
拉取请求 ──► pr.yaml 仅检查,不发布任何内容
合并到 main ─► main.yaml 构建一次镜像 → ghcr.io/nuvraxis/<app>:sha-<short>, :main
打标签 vX.Y.Z ──► release.yaml 复用同一镜像,重新打标签为 :vX.Y.Z, :latest · 图表 X.Y.Z → OCI · GitHub Release
每个工作流的规则
-
在顶部设置
permissions: contents: read;仅在需要时按 Job 扩大权限。 推送镜像到 GHCR 的 Job 需要packages: write,仅创建 Release 的 Job 需要contents: write。不使用id-token: write或attestations: write:Provenance 和 SBOM 来自 BuildKit,而非actions/attest*(后者需要私有仓库的 GitHub Enterprise)。 -
仅使用
GITHUB_TOKEN。 GHCR 登录使用${{ github.actor }}/${{ secrets.GITHUB_TOKEN }};gh使用GH_TOKEN: ${{ github.token }}。CI 中不使用个人访问令牌。(从集群拉取私有包需要不同的凭据;参见helm-nextjs。) -
锁定 Action 主版本(
@v7)并保持 SHA 表更新。 模板使用主版本标签以提高可读性。如果组织要求锁定 SHA,请将每个@vN替换为 references/action-pins.md 中的 SHA,并保留# vN.M.P注释。当前主版本:checkout v7, setup-node v7, pnpm/action-setup v6, setup-qemu v4, setup-buildx v4, login v4, metadata v6, build-push v7, azure/setup-helm v5。 -
工具链:
pnpm/action-setup在actions/setup-node之前。 action-setup 从packageManager读取 pnpm 版本;setup-node 读取.node-version并缓存 pnpm store(cache: pnpm需要 pnpm 先在 PATH 上)。然后执行pnpm install --frozen-lockfile。绝不使用npm ci。 -
每个工作流和 ref 设置
concurrency;仅在 PR 上取消进行中的运行。 对 PR 的新推送会取消旧运行。在 main 和标签上,运行始终完成:release.yaml 需要该确切提交的镜像。 -
永远不要在
run:中回显密钥或插入不受信任的输入。 通过env:传递值并引用它们("${APP}")。${{ github.event.pull_request.title }}等绝不放入脚本中。当 Job 不会推送到 git 时,在 checkout 上设置persist-credentials: false。 -
每个 Job 都设置
timeout-minutes。 否则挂起的 Docker 构建会消耗六个小时。 -
名称使用小写。 GHCR 拒绝大写仓库。
IMAGE: ghcr.io/nuvraxis/<app>在env中写成小写;脚手架会将应用名称小写。
pr.yaml
-
on: pull_request带types: [opened, synchronize, reopened, ready_for_review],且没有branches过滤器,这样也会检查针对功能分支的堆叠式 PR。每个 Job 都有if: ${{ !github.event.pull_request.draft }};标记为“准备就绪”会触发运行。 -
四个并行 Job:
quality(biome ci --reporter=github, stylelint,turbo run typecheck test --affected,pnpm audit --audit-level high),build(turbo run build --affected),docker(构建一个平台并load: true,以只读模式运行并删除 capabilities,curl/api/health),helm(helm lint --strict并对两个 values 文件执行 kubeconform)。--affected需要fetch-depth: 0。 -
PR 不会推送任何内容。
push: false,只读缓存(仅cache-from),不登录注册表。
main.yaml
-
on: push: branches: [main]构建一次镜像,适用于两种架构,并推送。 由docker/metadata-action生成的标签:type=sha,format=short,prefix=sha-和type=raw,value=main,flavor: latest=false。latest属于 Release。 -
provenance: mode=max,sbom: true,cache-from/to: type=gha,scope=<app>,mode=max。 Provenance 和 SBOM 附加到镜像索引;release.yaml 在重新打标签时会携带它们。NEXT_DEPLOYMENT_ID=${{ github.sha }}是用于偏差保护的构建参数。 -
main.yaml 不发布 chart,也不重新运行 PR 检查。 分支保护要求 pr.yaml;main 仅生成制品。
release.yaml
-
on: push: tags: ['v*.*.*'],然后按顺序执行:- 除非标签恰好是
vMAJOR.MINOR.PATCH且其提交是origin/main的祖先,否则失败。 - 不要重新构建。 如果
ghcr.io/nuvraxis/<app>:sha-<short>不存在,则失败并显示“让 main.yaml 完成,然后重新运行”。不进行回退构建。 docker buildx imagetools create --tag :vX.Y.Z --tag :latest <sha-image>:一个 manifest 级别的复制,以相同摘要保留两种架构、provenance 和 SBOM。helm package --version X.Y.Z --app-version vX.Y.Z,helm push到oci://ghcr.io/nuvraxis/charts。gh release create vX.Y.Z --verify-tag --generate-notes,并附加 chart.tgz、image-digest.txt和sbom.spdx.json。
- 除非标签恰好是
-
发布仪式: 合并到 main → 等待 main.yaml 变绿 →
git tag vX.Y.Z && git push origin vX.Y.Z。在 main.yaml 完成之前打标签会导致步骤 2 故意失败;一旦镜像存在,重新运行发布 Job。错误的发布通过新的补丁标签修复,绝不通过移动标签。
加速(仅在需要时)
- QEMU 下的 arm64 是 main.yaml 的缓慢部分。将其替换为在
ubuntu-24.04-arm+ubuntu-latest上按摘要推送的矩阵,然后使用imagetools create进行合并 Job。保留标签和单个索引。 - pr.yaml 的 Turborepo 远程缓存:参见
pnpm-turborepo。
验证
docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:latest # lint 所有工作流
gh workflow list && gh run list --limit 5
gh run view <run-id> --log-failed
发布后:docker buildx imagetools inspect ghcr.io/nuvraxis/<app>:vX.Y.Z 显示两个平台,helm show chart oci://ghcr.io/nuvraxis/charts/<app> --version X.Y.Z 显示 appVersion: vX.Y.Z。
来源
- https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions
- https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions
- https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token
- https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry
- https://github.com/pnpm/action-setup
- https://github.com/actions/setup-node
- https://github.com/docker/metadata-action
- https://github.com/docker/build-push-action
- https://docs.docker.com/build/ci/github-actions/cache/#github-cache
- https://docs.docker.com/build/metadata/attestations/
- https://docs.docker.com/reference/cli/docker/buildx/imagetools/create/
- https://helm.sh/docs/topics/registries/
- https://cli.github.com/manual/gh_release_create
- https://turborepo.dev/docs/reference/run#--affected