Skip to content

ci(promote): make promotion idempotent and immutable-safe - #3

Merged
GeekTrainer merged 2 commits into
mainfrom
geektrainer/congenial-train
Aug 26, 2026
Merged

ci(promote): make promotion idempotent and immutable-safe#3
GeekTrainer merged 2 commits into
mainfrom
geektrainer/congenial-train

Conversation

@GeekTrainer

Copy link
Copy Markdown
Contributor

Description

The Promote learner branches workflow has never succeeded. It aborted the whole job whenever an immutable acc-<version>/start-of-module-N tag already existed, and those monthly tags are pre-seeded for the current month. So every run (including the initial commit and the PR #2 merge) died on start-of-module-02 regardless of content, and the mutable start-of-module-N learner branches never advanced. The "refuse to overwrite an immutable tag" intent is correct; failing the entire job instead of skipping an already-present tag was the bug.

Related Issue

N/A

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)
  • 📚 Documentation update
  • 🧪 Test update
  • 🔧 Refactor (no functional changes)

Affected Services

  • web (Astro SSR + React)
  • assets-svc (.NET 10)
  • workforce-svc (Java 21 / Spring Boot 3)
  • reporting-svc (Python FastAPI)
  • notifications-svc (Python FastAPI)
  • audit-svc (legacy Java 11 / Spring Boot 2.7)
  • auth-svc (legacy Java 11 / Spring Boot 2.7)
  • devcontainer / tooling / docs

Changes Made

Reworked the refspec assembly in promote-branches.yml to be idempotent and immutability-safe:

  • Immutable version tag (acc-<version>/start-of-module-N): created only if absent. If it already exists, it is left untouched (never overwritten) and skipped, instead of aborting the job.
  • Mutable alias (start-of-module-N): moved only when the freshly built tree differs from what the branch already points at, so unchanged public start branches are not gratuitously rewritten to new commit shas on every promotion.
  • No-op is success: when nothing changed, the push step is skipped and the job succeeds rather than erroring.

Testing

  • End-to-end (web): npm run test:e2e (Playwright) passes
  • .NET: dotnet test in services/assets-svc passes
  • Python: pytest in services/reporting-svc and/or services/notifications-svc passes
  • Modern Java: mvn test in services/workforce-svc passes
  • Currency-lagging Java: mvn test in services/audit-svc and/or services/auth-svc passes
  • Manually verified the app runs and the affected flow works

Simulated the exact refspec-assembly loop locally against the current repo state (built the staging refs with build-branches.mjs, compared staged trees to the live origin/start-of-module-* trees and existing tags). Result: only start-of-module-06 and -07 (the QR-selector fix) are selected to move; 02-05 stay in place and no immutable tag is re-cut.

Checklist

  • My change targets main (I have not hand-edited generated learner branches or promoted refs — see CONTRIBUTING.md)
  • My code follows the project's coding standards for the affected language/stack
  • My changes are focused on a single concern
  • I have added or updated tests where it makes sense
  • I have updated documentation (README, per-service READMEs, exercises.md) if needed
  • I have written clear commit messages explaining what and why

Additional Notes

Merging this only fixes the workflow logic; it does not itself run a promotion (this PR does not touch course-build/**, so the push-triggered promotion won't fire). A promotion still needs to be dispatched to actually advance start-of-module-06/07 to the QR fix.

Immutability note: by design this preserves existing acc-2026-08/start-of-module-06 and -07 tags, which currently point at the pre-QR-fix content. After a promotion, the mutable start branches (what learners clone) carry the fix, while those two archival month tags stay frozen at the original snapshot. If the archival tags should also reflect the fix, that requires a deliberate re-cut under a new version — happy to do that as a follow-up.

The Promote learner branches workflow has never succeeded: it aborted the
whole job whenever an immutable acc-<version>/start-of-module-N tag already
existed. Those monthly tags are pre-seeded for the current month, so every
run died on start-of-module-02 regardless of content, and the mutable
start-of-module-N aliases never advanced.

Rework the refspec assembly to be idempotent and immutability-safe:
- Immutable version tag: create only if absent; if it already exists, leave
  it untouched (never overwrite) and skip it instead of failing.
- Mutable alias: move it only when the freshly built tree differs from what
  the branch already points at, so unchanged public start-of-module-* branches
  are not gratuitously rewritten to new commit shas every promotion.
- Treat "nothing changed" as success (skip the push) rather than an error.

Verified locally against the current repo state: only start-of-module-06 and
-07 (the QR-selector fix) are selected to move; 02-05 stay put and no tags are
re-cut.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3750cefd-d322-4f02-9184-c8ac3eba2fff
Copilot AI lite review requested due to automatic review settings August 26, 2026 20:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the Promote learner branches GitHub Actions workflow so promotions are idempotent and safe around immutable archival tags (e.g., acc-YYYY-MM/start-of-module-N). It ensures existing immutable tags are never overwritten while allowing mutable start-of-module-* branches to advance only when the generated content actually changes.

Changes:

  • Skip (do not fail) when an immutable acc-<version>/start-of-module-* tag already exists, preserving immutability while allowing re-runs.
  • Only move mutable start-of-module-* branches when the staged tree differs from the current remote branch tree.
  • Treat a “no changes” promotion as success and skip the push step.
Show a summary per file
File Description
.github/workflows/promote-branches.yml Makes refspec assembly/push idempotent, avoids overwriting immutable tags, and skips no-op promotions.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread .github/workflows/promote-branches.yml Outdated
Address review: `git fetch ... || true` would hide a real failure
(network/auth/remote down), leaving cur_tree empty for every branch and
force-pushing ALL start-of-module-* branches -- exactly the gratuitous rewrite
the tree comparison is meant to avoid. A wildcard refspec that matches nothing
(e.g. the first-ever promotion, before any start branch exists) already exits
0, so no `|| true` is needed for the benign case. Drop it so genuine fetch
failures abort the job under set -e.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3750cefd-d322-4f02-9184-c8ac3eba2fff
@GeekTrainer
GeekTrainer merged commit 4f9d1eb into main Aug 26, 2026
6 checks passed
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.

2 participants