Skip to content

fix(ci): auto-promote :latest on image build too, not only E2E - #2238

Merged
HongmingWang-Rabbit merged 2 commits into
stagingfrom
fix/auto-promote-latest-on-publish-too
Apr 29, 2026
Merged

fix(ci): auto-promote :latest on image build too, not only E2E#2238
HongmingWang-Rabbit merged 2 commits into
stagingfrom
fix/auto-promote-latest-on-publish-too

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

Closes the structural gap that left :latest ~7h / 9 PRs behind main as of 2026-04-28: this workflow had run zero times since merge despite eight main pushes, because its only trigger (E2E Staging SaaS) is paths-filtered to runtime handlers, and recent PRs touched canvas/CI infra instead.

The mismatch

Workflow Path filter
publish-workspace-server-image workspace-server/**, canvas/**, manifest.json
e2e-staging-saas workspace-server/internal/handlers/{registry,workspace_provision,a2a_proxy}.go, middleware/**, provisioner/**, tests/e2e/test_staging_full_saas.sh

E2E paths are a strict subset of publish paths. Result: a canvas-only, cmd-only, or sweep-only PR rebuilds the platform image but never advances :latest.

Fix

Add publish-workspace-server-image as a second workflow_run trigger. Inside the job, gate on E2E Staging SaaS state for the same SHA:

  • E2E green → promote (existing path)
  • E2E red → abort (kill-switch preserved; :latest stays put)
  • E2E didn't fire → promote (paths-filtered out; pre-merge gates on staging already validated this SHA)

Pre-merge gates that auto-promote-staging requires: CI + E2E Canvas + E2E API + CodeQL — all green before main moves. So canvas-only or cmd-only changes are still vetted before they reach :latest.

Idempotency

When a SHA matches both path sets, both workflows fire and both trigger this. Concurrency group serializes per-SHA; crane tag is idempotent — second run is a no-op.

Test plan

Previously this workflow only triggered on E2E Staging SaaS completion,
which is itself paths-filtered to runtime handlers
(workspace-server/internal/handlers/{registry,workspace_provision,
a2a_proxy}.go, middleware/**, provisioner/**). publish-workspace-server
-image fires on a STRICTLY BROADER path set (workspace-server/**,
canvas/**, manifest.json) — so canvas-only or cmd-only or sweep-only
PRs rebuilt the platform image without ever advancing :latest.

Result observed 2026-04-28: zero runs of this workflow since merge
despite eight main pushes. :latest sat ~7 hours / 9 PRs behind main.

Fix: add publish-workspace-server-image as a second trigger. Add an
explicit gate inside the job that aborts when E2E Staging SaaS for the
same SHA ended red. When E2E didn't fire (paths-filtered), proceed —
auto-promote-staging's pre-merge gates (CI + E2E Canvas + E2E API +
CodeQL on staging) already validated this SHA before main moved.

Concurrency group serializes promotes per-SHA so the publish+E2E both-
fired race lands cleanly. Idempotent crane tag makes it safe regardless.
Self-review caught a real correctness bug: scenario where publish-
workspace-server-image completes BEFORE E2E Staging SaaS for a runtime-
touching SHA. Publish typically takes ~5-10min; E2E ~10-15min, so this
ordering is the common case for runtime-path PRs.

Previous gate logic:
  - completed/success: proceed
  - completed/failure: abort
  - everything else (including in_progress): proceed   ← BUG

If publish-trigger fires while E2E is still running, the gate returned
"in_progress/none" and fell through the catch-all "proceed" branch.
Result: :latest retagged on the publish signal alone. Then E2E ends
red — but :latest was already wrongly advanced; the E2E-completion
trigger's job-level if=conclusion==success filter just skips, never
rolls back.

Fix: explicit case for in_progress|queued|requested|waiting|pending
that DEFERS — sets gate.proceed=false, writes a "deferred" summary,
exits 0 (workflow run shows success, retag steps skipped). The E2E
completion trigger then fires later and either promotes (green) or
aborts (red), giving us correct ordering regardless of who finishes
first.

Subsequent steps now guarded by `if: steps.gate.outputs.proceed ==
'true'` instead of relying on `exit 1` for skip semantics.

Also added an explicit catch-all `*)` branch that aborts on unknown
states (forward-compat: GitHub adds a new status, we surface it
instead of silently promoting through it).
@github-actions

Copy link
Copy Markdown
Contributor

🔒 Auto-merge disabled — new commit (475a51a) pushed after auto-merge was enabled. The merge queue locks SHAs at entry, so subsequent pushes can race. Verify the new commit and re-enable with gh pr merge --auto.

@HongmingWang-Rabbit
HongmingWang-Rabbit added this pull request to the merge queue Apr 29, 2026
Merged via the queue into staging with commit 128c1ee Apr 29, 2026
22 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the fix/auto-promote-latest-on-publish-too branch April 29, 2026 00:26
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
Serialized merge by gitea-merge-queue after current-main, genuine approvals, and required CI checks were green.
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