GitHub Actions
Drei Workflows pro App, jeder mit einem Job seiner Art. Die Vorlage .github/workflows/{pr,main,release}.yaml ist die Quelle der Wahrheit; sie werden mit Anmerkungen in references/workflows.md reproduziert.
pull request ──► pr.yaml Prüfungen nur, nichts veröffentlicht
merge to main ─► main.yaml Image einmal gebaut → ghcr.io/nuvraxis/<app>:sha-<short>, :main
tag vX.Y.Z ────► release.yaml gleiches Image retagged :vX.Y.Z, :latest · Chart X.Y.Z → OCI · GitHub Release
Regeln für jeden Workflow
-
permissions: contents: readoben; pro Job nur nach Bedarf erweitern.packages: writefür Jobs, die in GHCR pushen,contents: writenur für den Job, der das Release erstellt. Keinid-token: writeoderattestations: write: Provenance und SBOM kommen von BuildKit, nicht vonactions/attest*(benötigt GitHub Enterprise für private Repos). -
Nur
GITHUB_TOKEN. GHCR-Login verwendet${{ github.actor }}/${{ secrets.GITHUB_TOKEN }};ghverwendetGH_TOKEN: ${{ github.token }}. Keine persönlichen Access-Tokens in CI. (Das Ziehen privater Pakete aus dem Cluster ist ein anderes Zugangsdaten; siehehelm-nextjs.) -
Action-Majors pinnen (
@v7) und die SHA-Tabelle aktuell halten. Die Vorlage verwendet Major-Tags zur Lesbarkeit. Falls die Organisation SHA-Pinning verlangt, ersetze jedes@vNdurch den SHA aus references/action-pins.md und behalte den# vN.M.P-Kommentar bei. Aktuelle Majors: 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. -
Toolchain:
pnpm/action-setupvoractions/setup-node. action-setup liest die pnpm-Version auspackageManager; setup-node liest.node-versionund cacht den pnpm-Store (cache: pnpmbenötigt pnpm zuerst im PATH). Dannpnpm install --frozen-lockfile. Niemalsnpm ci. -
concurrencypro Workflow und Ref; nur bei PRs laufende abbrechen. Ein neuer Push auf einen PR bricht den alten Lauf ab. Auf main und Tags laufen immer vollständig: release.yaml benötigt das Image des exakten Commits. -
Nie Secrets ausgeben oder unvertraute Eingaben in
run:interpolieren. Werte überenv:übergeben und in Anführungszeichen setzen ("${APP}").${{ github.event.pull_request.title }}und ähnliches niemals in ein Skript einfügen.persist-credentials: falsebei Checkout, wenn der Job nicht in Git pusht. -
Jeder Job hat
timeout-minutes. Ein hängender Docker-Build würde sonst sechs Stunden verbrauchen. -
Namen in Kleinbuchstaben. GHCR lehnt Großbuchstaben-Repositories ab.
IMAGE: ghcr.io/nuvraxis/<app>wird in Kleinbuchstaben inenvgeschrieben; das Scaffold wandelt den App-Namen in Kleinbuchstaben um.
pr.yaml
-
on: pull_requestmittypes: [opened, synchronize, reopened, ready_for_review]und ohnebranches-Filter, damit auch gestapelte PRs gegen Feature-Branches geprüft werden. Jeder Job hatif: ${{ !github.event.pull_request.draft }}; als bereit markieren löst den Lauf aus. -
Vier parallele Jobs:
quality(biome ci --reporter=github, stylelint,turbo run typecheck test --affected,pnpm audit --audit-level high),build(turbo run build --affected),docker(eine Plattform mitload: truebauen, schreibgeschützt mit reduzierten Capabilities ausführen, curl/api/health),helm(helm lint --strictund kubeconform auf beiden values-Dateien).--affectedbenötigtfetch-depth: 0. -
Von einem PR wird nichts gepusht.
push: false, Cache schreibgeschützt (nurcache-from), kein Registry-Login.
main.yaml
-
on: push: branches: [main]baut das Image einmal für beide Architekturen und pusht es. Tags vondocker/metadata-action:type=sha,format=short,prefix=sha-undtype=raw,value=main,flavor: latest=false.latestgehört zu Releases. -
provenance: mode=max,sbom: true,cache-from/to: type=gha,scope=<app>,mode=max. Provenance und SBOM werden an den Image-Index angehängt; release.yaml trägt sie beim Retaggen mit.NEXT_DEPLOYMENT_ID=${{ github.sha }}ist das Build-Argument für Skew-Schutz. -
main.yaml veröffentlicht weder das Chart noch wiederholt es PR-Prüfungen. Branch-Schutz erfordert pr.yaml; main produziert nur das Artefakt.
release.yaml
-
on: push: tags: ['v*.*.*'], dann in dieser Reihenfolge:- Fehlschlagen, es sei denn, der Tag ist exakt
vMAJOR.MINOR.PATCHund sein Commit ist ein Vorfahre vonorigin/main. - Nicht neu bauen. Wenn
ghcr.io/nuvraxis/<app>:sha-<short>nicht existiert, fehlschlagen mit „main.yaml beenden lassen, dann erneut ausführen“. Kein Fallback-Build. docker buildx imagetools create --tag :vX.Y.Z --tag :latest <sha-image>: Eine Manifest-Ebene-Kopie, die beide Architekturen, Provenance und SBOM bei demselben Digest behält.helm package --version X.Y.Z --app-version vX.Y.Z,helm pushanoci://ghcr.io/nuvraxis/charts.gh release create vX.Y.Z --verify-tag --generate-notesmit dem Chart.tgz,image-digest.txtundsbom.spdx.jsonals Anhänge.
- Fehlschlagen, es sei denn, der Tag ist exakt
-
Das Release-Ritual: Merge auf main → warten, bis main.yaml grün ist →
git tag vX.Y.Z && git push origin vX.Y.Z. Das Taggen vor Abschluss von main.yaml führt Schritt 2 absichtlich zum Fehlschlag; führe den Release-Job erneut aus, sobald das Image existiert. Ein fehlerhaftes Release wird mit einem neuen Patch-Tag behoben, niemals durch Verschieben eines Tags.
Beschleunigung (nur bei Bedarf)
- arm64 unter QEMU ist der langsame Teil von main.yaml. Ersetze durch eine Matrix auf
ubuntu-24.04-arm+ubuntu-latest, die per Digest pusht, dann einen Merge-Job mitimagetools create. Behalte die Tags und den einzelnen Index. - Turborepo-Remote-Cache für pr.yaml: siehe
pnpm-turborepo.
Überprüfen
docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:latest # alle Workflows linten
gh workflow list && gh run list --limit 5
gh run view <run-id> --log-failed
Nach einem Release: docker buildx imagetools inspect ghcr.io/nuvraxis/<app>:vX.Y.Z zeigt beide Plattformen, und helm show chart oci://ghcr.io/nuvraxis/charts/<app> --version X.Y.Z zeigt appVersion: vX.Y.Z.
Quellen
- 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