Skip to content

fix(rollback): guard the first environment against rollback - #418

Merged
joshua-temple merged 1 commit into
mainfrom
fix/rollback-first-env-guard
Jun 30, 2026
Merged

joshua-temple merged 1 commit into
mainfrom
fix/rollback-first-env-guard

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

What

Closes #385 (reframed). cascade rollback on the trunk-tracking first environment now fails fast with an actionable error instead of silently resolving a wrong target from its structurally-empty deploy-history ring.

Why

The first env (IsFirstEnvironment, the build/deploy-on-merge target) tracks trunk and is never promoted into, so its ring (advanced only by promote/hotfix/rollback finalize) is always empty. A no-target rollback there silently mis-resolves. The trunk-native undo for the first env is a revert merge, not a ring rollback. (The original 'advance the ring on deploy-on-merge' framing was wrong: it would bolt ring-rollback onto the one env that should not use it.)

Changes

  • Runtime guard: Rollbacker.firstEnvErr called from Plan (covers preflight, finalize, and repository_dispatch, which all route through Plan) and Apply (defense in depth). Inert when no config is parsed.
  • Generator: rollback.Enabled() now requires >= 2 environments and the dispatch dropdown enumerates Environments[1:], mirroring the HotfixGenerator precedent. A single-env project (its only env tracks trunk) now generates no rollback workflow. The runtime guard stays authoritative because repository_dispatch carries a free-string environment the choice list cannot constrain.
  • Docs: a one-line note in workflows.md (and corrected the now-stale 'at least one environment' to 'at least two').

Tests

  • Unit: guard fires on Plan (no-target, with-target, deployable-scoped) and Apply for the first env; promoted env unaffected; inert with no config.
  • Generator: Enabled false-with-one-env / true-with-two; dropdown excludes the first env, keeps promoted envs.
  • e2e (rollback/rollback-first-env-guard.yaml): first-env rollback fails fast asserting the guard message (new ExpectLog field); promoted-env rollback succeeds back to the prior version. Docker-green (43.8s).

Verification

go build/vet/test (2079 pass), golangci-lint, actionlint (incl. the rollback dispatch generator test) all clean.

The first environment tracks trunk and is never promoted into, so its
deploy-history ring is structurally always empty. A no-target rollback there
resolved its target from an empty or stale ring, producing a silent wrong
target. Refuse a rollback aimed at the first environment in the preflight and
apply paths with an actionable error that points at the trunk revert path.

Exclude the first environment from the generated rollback workflow's choice
dropdown and gate emission on at least two environments, mirroring the hotfix
generator, so the manual path never offers an environment the guard refuses.

Add unit coverage for the guard on the preflight, apply, no-target, and
deployable-scoped paths, and an e2e scenario proving a first-environment
rollback fails fast while a promoted environment still rolls back.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.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.

rollback: guard against rollback on the trunk-tracking first env (revert-merge instead)

1 participant