Skip to content

Fire the GA promote on the master merge instead of a manual dispatch - #166

Draft
jsokol wants to merge 2 commits into
masterfrom
FEATURE-promote-on-ga-merge
Draft

Fire the GA promote on the master merge instead of a manual dispatch#166
jsokol wants to merge 2 commits into
masterfrom
FEATURE-promote-on-ga-merge

Conversation

@jsokol

@jsokol jsokol commented Aug 21, 2026

Copy link
Copy Markdown
Member

Description

Makes GA promotion automatic. Today promote-latest.yml is workflow_dispatch-only, which means publishing the release images is the one manual step in the release — and nothing fails if it is skipped: :latest silently stays on the previous release while every monitored stage reports green.

Fires on a master push instead, path-filtered to simplerisk-minimal/Dockerfile — the file carrying the ENV version= this job promotes, so it changes on exactly the pushes that matter. workflow_dispatch stays for heals.

Paired with code-development's side, which opens the docker testing → master PR when the release merges to code-development's master. That merge lands here, and this fires.

Why auto-firing is safe now

It's safe precisely because nothing is rebuilt. The 20260820-001 failure came from rebuilding from the prod S3 bundle on a master push and racing its upload; a retag touches no bundle at all.

Note

This deliberately reverses one line of the approved design (2026-07-10-release-image-promotion-design.md), which specified "Triggered by manual workflow_dispatch (deliberate release gate; no accidental promotion from a branch merge)."

The gate isn't removed — it moves to where the release decision is actually made: the code-development testing → master merge, which is restricted to release owners. A second dispatch is a second gate for the same decision, and it's the one nobody is prompted to perform.

Idempotence guard

A master push can touch that Dockerfile without being a release (a CVE regeneration at an unchanged version), and re-promoting would re-mirror to GHCR, mint fresh cosign signatures, and rewrite SSM for nothing. New Decide whether anything needs promoting step: if :latest already resolves to the digest we'd promote for every image, all five mutating steps skip. A workflow_dispatch always runs in full.

An absent RC tag is treated as an error, not as "nothing to do." That condition means the release has no images to promote — the loudest thing this workflow can say — so it must not surface as a green no-op run.

Release Notes

None — internal change. The released container images will now publish automatically at GA rather than depending on a manual step.

Manual Validation Steps

  1. Merge this. Nothing should fire — it changes no Dockerfile.
  2. Dispatch promote-latest manually against the current release. It should run in full (dispatch bypasses the guard) and be a no-op in effect, since :latest already points at 20260820-001's digests.
  3. At the next GA, confirm the run fires automatically off the testing → master merge and that :latest/:testing digests match for both images afterwards.
  4. Negative: push a Dockerfile change at an unchanged version → the run should fire, report nothing to promote in the step summary, and skip all mutating steps.

Type of Change

  • Build / CI configuration

Testing Results

  • actionlint clean; shellcheck -S warning clean on every embedded run block.
  • Guard logic exercised offline across 8 scenarios with stubbed digests — already-promoted, new release, each image differing alone, minimal RC missing, full RC missing, and both skip_full_image paths. Correct verdict and exit status in every case.
  • Specifically verified no set -e trap in the && chains: bash exempts non-final commands in an && list, confirmed by execution rather than assumed.
  • Verified all five mutating steps carry the guard condition (Promote ×2, Mirror, Configure AWS credentials, SSM).
  • Not exercised: an actual automatic promote. It needs a real GA merge; step 3 above is the first. It is reversible — retagging :latest back is one imagetools create.

CIA Impact Check

MAY negatively impact Confidentiality, Integrity and/or Availability.

Justification for the change

  • GA image publishing is currently a manual step that fails silently when forgotten, with no job going red and no monitored stage reporting it.
  • The 20260820-001 release shipped with :latest on the previous version for several hours for exactly this class of reason.

Potential impact on Confidentiality, Integrity and/or Availability

Availability (the main change). Production tier=latest services roll when SSM changes, and that now happens without a human pressing anything. The trigger is a merge to master, which only release owners can perform, and the promoted artifact is the digest already validated in testing. The idempotence guard prevents an unrelated Dockerfile change from re-rolling the fleet.

Integrity. Unchanged — the promoted bytes are the same RC digest, and the currency guard against :testing still refuses a stale version. The new guard only decides whether to act, never what to promote.

Confidentiality. No change. Same permissions, same environment: release, no new secrets.

Additional Implementation Steps

Requires the code-development counterpart to open the testing → master PR at GA; until that lands, this changes nothing in practice because no automation pushes to master.

jsokol and others added 2 commits August 21, 2026 15:50
Publishing the release images was the one manual step in the release, and
nothing failed if it was skipped -- :latest simply stayed on the previous
release while every monitored stage reported green.

Fire on a master push instead, path-filtered to simplerisk-minimal/Dockerfile
(the file carrying the `ENV version=` this job promotes). workflow_dispatch
stays for heals.

Auto-firing is safe precisely BECAUSE nothing is rebuilt. The 20260820-001
failure came from rebuilding on a master push and racing the GA bundle upload;
a retag touches no bundle. The deliberate release gate moves to where the
release decision is actually made -- the code-development testing -> master
merge, restricted to release owners -- rather than a second dispatch nobody is
prompted to run. This reverses the "manual workflow_dispatch" line in the
2026-07-10 design on purpose.

Adds an idempotence guard so the automatic path is safe to re-enter: if
:latest already resolves to the digest we would promote for every image, all
five mutating steps skip. A dispatch always runs in full.

The guard fails loudly when the minimal RC tag is ABSENT rather than treating
it as "nothing to do" -- that condition means the release has no images to
promote, which must not surface as a green no-op run.

Guard logic exercised offline across 8 scenarios (already-promoted, new
release, each image differing alone, minimal RC missing, full RC missing,
both skip_full paths): correct verdict and exit status in every case, no
set -e traps in the && chains.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant