GitHub Actions
アプリごとに 3 つのワークフローがあり、それぞれが 1 つのジョブを持ちます。テンプレートの .github/workflows/{pr,main,release}.yaml が信頼できる情報源であり、references/workflows.md に注釈付きで再掲しています。
pull request ──► pr.yaml チェックのみ、公開なし
merge to main ─► main.yaml イメージを 1 回ビルド → ghcr.io/nuvraxis/<app>:sha-<short>, :main
tag vX.Y.Z ────► release.yaml 同じイメージを再タグ :vX.Y.Z, :latest · チャート X.Y.Z → OCI · GitHub Release
全ワークフローのルール
-
最上部に
permissions: contents: readを設定し、必要なジョブでのみ権限を広げる。 GHCR にプッシュするジョブにはpackages: write、Release を作成するジョブにはcontents: write。id-token: writeやattestations: writeは不要: プロビナンスと SBOM は BuildKit から生成し、actions/attest*(プライベートリポジトリでは GitHub Enterprise が必要) は使用しません。 -
GITHUB_TOKENのみを使用。 GHCR へのログインは${{ github.actor }}/${{ secrets.GITHUB_TOKEN }}を使用し、ghはGH_TOKEN: ${{ github.token }}を使用します。CI で個人アクセストークンは使用しません。(クラスターからプライベートパッケージを取得するための認証情報は別途必要です。helm-nextjsを参照。) -
アクションのメジャーバージョンをピン留め (
@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 ストアをキャッシュします (cache: pnpmは PATH に pnpm が必要)。その後pnpm install --frozen-lockfileを実行。npm ciは使用しません。 -
ワークフローと参照 (
ref) ごとにconcurrencyを設定し、PR の進行中の実行はキャンセルする。 PR への新しいプッシュは古い実行をキャンセルします。main とタグの実行は常に完了させます: release.yaml はそのコミットの正確なイメージを必要とします。 -
シークレットをエコーしたり、信頼できない入力を
run:に展開したりしない。 値はenv:を介して渡し、引用符で囲みます ("${APP}")。${{ github.event.pull_request.title }}などはスクリプト内に記述しません。ジョブが git にプッシュしない場合は、checkout でpersist-credentials: falseを設定します。 -
全てのジョブに
timeout-minutesを設定する。 設定しないと、Docker ビルドがハングした場合に 6 時間も消費する可能性があります。 -
名前は小文字にする。 GHCR は大文字のリポジトリを拒否します。
IMAGE: ghcr.io/nuvraxis/<app>はenv内で小文字で記述します。スキャフォールドはアプリ名を小文字に変換します。
pr.yaml
-
on: pull_requestにtypes: [opened, synchronize, reopened, ready_for_review]を指定し、branchesフィルターは設定しない。 これにより、フィーチャーブランチに対する積み上げ PR もチェックされます。全てのジョブにif: ${{ !github.event.pull_request.draft }}を設定し、準備完了にマークすると実行がトリガーされます。 -
4 つの並列ジョブ:
quality(biome ci --reporter=github、stylelint、turbo run typecheck test --affected、pnpm audit --audit-level high)、build(turbo run build --affected)、docker(1 つのプラットフォームをload: trueでビルド、ケーパビリティを落として読み取り専用で実行、/api/healthを curl)、helm(helm lint --strictと両方の values ファイルに対する kubeconform)。--affectedを使用するにはfetch-depth: 0が必要です。 -
PR からは何もプッシュしない。
push: false、キャッシュは読み取り専用 (cache-fromのみ)、レジストリへのログインは行いません。
main.yaml
-
on: push: branches: [main]でイメージを 1 回ビルドし、両方のアーキテクチャに対応してプッシュする。docker/metadata-actionからのタグ:type=sha,format=short,prefix=sha-とtype=raw,value=main、flavor: latest=false。latestはリリース用です。 -
provenance: mode=max、sbom: true、cache-from/to: type=gha,scope=<app>,mode=max。 プロビナンスと SBOM はイメージインデックスに添付され、release.yaml で再タグする際に引き継がれます。NEXT_DEPLOYMENT_ID=${{ github.sha }}はスキュー防止用のビルド引数です。 -
main.yaml はチャートを公開せず、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>: マニフェストレベルのコピーで、両方のアーキテクチャ、プロビナンス、SBOM を同じダイジェストで保持します。helm package --version X.Y.Z --app-version vX.Y.Z、helm pushをoci://ghcr.io/nuvraxis/chartsに実行。- チャート
.tgz、image-digest.txt、sbom.spdx.jsonを添付してgh release create vX.Y.Z --verify-tag --generate-notesを実行。
- タグが正確に
-
リリースの儀式: main にマージ → main.yaml がグリーンになるのを待つ →
git tag vX.Y.Z && git push origin vX.Y.Z。main.yaml が完了する前にタグ付けすると、ステップ 2 が意図的に失敗します。イメージが存在するようになったらリリースジョブを再実行してください。不良リリースは新しいパッチタグで修正し、タグを移動することは決して行いません。
高速化 (必要な場合のみ)
- arm64 の QEMU は main.yaml の低速部分です。代わりに
ubuntu-24.04-arm+ubuntu-latestのマトリックスを使用してダイジェストでプッシュし、その後にマージジョブでimagetools createを実行します。タグと単一インデックスは維持します。 - 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