Skip to content

docs backend evidence cd CD 01

AlexandreZanata edited this page Nov 5, 2026 · 1 revision

CD-01 — Signed staging release artifacts from main

Status: INTEGRATED (PR #158, PR #159) / LOG_CLEAN_RUN_PENDING Validation: local PASS; run 40494565772 released and verified; landing setup changed to the preinstalled Node so runner cache paths do not appear in logs Scope: staging continuous-deploy plan, Phase 1 only. No VPS, k8s, nginx, production, release tag or Phase 2 work is included.

Delivered

  • .github/workflows/release-staging.yml builds and publishes api, worker and migrate from infra/docker/Dockerfile with REVISION=<sha> to ghcr.io/<owner>/abastevo-<role>:sha-<sha>. It also builds the landing for https://teste.abastevo.com.br and uploads landing-dist.tar.gz.
  • Each image gets an SPDX JSON SBOM and a build-provenance attestation bound to its digest. The workflow records image references and digests.
  • release-manifest.json (schema abastevo-release-manifest-v1) contains the commit SHA, creation time, one image@digest per role, landing bundle SHA-256, highest migration version and the triggering workflow-run URL. It is signed keyless with cosign and published as a workflow artifact and as the OCI artifact ghcr.io/<owner>/abastevo-release:sha-<sha> plus staging. schema_min_supported is deferred to Phase 3, where the runtime schema contract is introduced; publishing a placeholder would misstate compatibility.
  • The final workflow step prints the exact cosign verify-blob command with the workflow certificate identity at refs/heads/main and the GitHub OIDC issuer.
  • scripts/check-release-workflow.sh rejects unpinned actions, pull_request_target, pull_request, non-GITHUB_TOKEN secrets, set -x, environment dumps, echo of variables and latest tags. It runs from Quick.
  • .github/CODEOWNERS explicitly assigns .github/workflows/ and infra/deploy/ to the owner. The default owner entry remains.
  • backend.yml path selection includes the release workflow and the guard so the backend gate runs when this pipeline changes.

Release gate semantics

  • Automatic runs listen to completed quick, CI and backend runs on main. The gate requires a successful push run for all three workflows for the same head_sha before any build job starts.
  • Optional path-filtered workflows that did not run for a commit leave the release gate closed with no artifact; the gate does not fabricate success. A run that exists but is not successful closes the gate with an error.
  • Manual dispatch requires refs/heads/main and a full 40-hex SHA that is an ancestor of the checked-out main; it does not use an environment or a secret.
  • Minimal permissions are declared per job: read-only by default, with packages: write, id-token: write and attestations: write only where image publication/signing/attestation needs them. actions: read is limited to the gate query. No secret beyond the automatic GITHUB_TOKEN is used.

Local validation performed

  • actionlint v1.7.12 (downloaded from the pinned release asset and verified against the published SHA-256 checksum) passes for the release workflow and for all workflows.
  • The guard passes the delivered workflow and rejects synthetic fixtures for each forbidden pattern: unpinned action, pull_request_target, pull_request, non-token secret, set -x, variable echo, env/printenv dump and latest tag.
  • bash -n passes for the guard and quick-verify.sh; git diff --check passes.
  • A local manifest fixture generated with the workflow's jq expression and validation predicate produced the expected schema, all three digest-pinned image references, a valid landing SHA-256 and migrations_max 000132.
  • actionlint also passes release-staging.yml after shellcheck analysis of its embedded scripts.

Known constraints recorded before merge

  • Branch protection currently requires only Quick verification. CI and backend are path-selected workflow signals, not protection-required checks. The release gate requires all three when they run; a commit that does not trigger one of them does not produce a release.
  • CODEOWNERS is present, but the repository setting require_code_owner_reviews is false. Enforcing owner approval is a repository-settings decision outside this change.
  • GHCR package visibility is not changed by the workflow. Anonymous pulls and gh attestation verify for the published artifacts are verified after the first real main run; a package still private is reported, not worked around with a personal token.

Post-merge run 1

PR #158 merged as ef86cc93. quick, CI and backend succeeded on that main commit. The three release-staging runs created by their completion (40485613772, 40486612237, 40487056660) failed in the gate's oras-project/setup-oras step before any artifact was published: setup-oras v2.0.2 only bundles ORAS releases through 1.3.4, so the requested 1.3.6 was rejected. The fix pins the CLI to the bundled 1.3.4 release; the workflow logic and permissions are unchanged.

Post-merge run 2

PR #159 merged as 2cd8f0ac. quick, CI and backend succeeded on that main commit, and the automatic gate built and published the release in run 40494565772:

  • Manifest SHA 2cd8f0ac20da6f04e79885d9b5b03f8ceb79cb19; landing SHA-256 957d1a49a56aca588a5c83ff21dc9b016b5a0b37b2e5c002b7117017c1c3c213; migrations_max 000132.
  • Images: api sha256:044222cf..., worker sha256:e64e482e..., migrate sha256:074ccf44... (full references in the signed manifest).
  • cosign verify-blob with the workflow identity at refs/heads/main and the GitHub OIDC issuer: Verified OK.
  • gh attestation verify passed for the manifest and for all three image digests.
  • Anonymous docker manifest inspect passed for all three images, and an anonymous oras pull of abastevo-release:sha-<sha> returned the manifest and Sigstore bundle.

The log scan found only GitHub runner cache/tool metadata and Docker loopback network setup; no operator host, deployment address, secret or variable dump. The landing job's setup-node step was replaced by a preinstalled-Node version check so its runner cache paths no longer appear; that log-clean rerun is pending.

Post-merge verification checklist

Run against the real main release artifacts (results to be reported on the delivery PR, not inferred from local state):

cosign verify-blob \
  --bundle release-manifest.sigstore.json \
  --certificate-identity "https://github.com/AlexandreZanata/Abastevo/.github/workflows/release-staging.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  release-manifest.json
gh attestation verify release-manifest.json --repo AlexandreZanata/Abastevo
gh attestation verify oci://ghcr.io/alexandrezanata/abastevo-api@<digest> --repo AlexandreZanata/Abastevo
gh attestation verify oci://ghcr.io/alexandrezanata/abastevo-worker@<digest> --repo AlexandreZanata/Abastevo
gh attestation verify oci://ghcr.io/alexandrezanata/abastevo-migrate@<digest> --repo AlexandreZanata/Abastevo
docker pull ghcr.io/alexandrezanata/abastevo-api@<digest>

The run must also show no host, IP, operator path or variable dump in its logs. Phase 2 (deploy agent, VPS access, rollout) is not started.

Fuel prices wiki

Source: AlexandreZanata/brazil-fuel-prices @ 99f218303462287ad0d2122e065fa9d9759314ee

Clone this wiki locally