Skip to content

Prepare, Tag and Publish Releases on Release Branches - #129

Merged
heifner merged 4 commits into
masterfrom
feature/release-branch-support
Nov 9, 2026
Merged

heifner merged 4 commits into
masterfrom
feature/release-branch-support

Conversation

@heifner

@heifner heifner commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Lets a release/<major>.<minor> branch carry its own releases (1.1.0-rc1, 1.1.0, 1.1.1, …) while master moves on. Until now prepare-release.yaml and tag-release.yaml only worked on master, release.yaml refused any tag not on master, and PRs into any other branch ran no checks.

  • prepare-release.yaml and tag-release.yaml take a branch input, default master. A release branch takes only its own line's versions, and master refuses a version whose release/<major>.<minor> branch already exists.
  • Both keep master at the workspace root. Its cmake-version.py validates the inputs and reads or writes the target branch, which is checked out as refs/heads/<branch> into target/ (without credentials in tag-release.yaml). No code from the target branch runs.
  • tag-release.yaml refuses a branch whose rules have no pull_request rule. prepare-release.yaml refuses to force-push a prep branch whose open PR targets another branch.
  • release.yaml accepts a tag that sits on master or on its version's release/<major>.<minor> branch. The branch name is derived inline, because the job runs the tag's own copy of the parser.
  • release.yaml gives the latest label to the highest published stable version, and hands it back if a hand-published older release took it. Publishing and the label run in a publish-release job queued across every tag, so releases published together cannot leave the label on the lower version.
  • PRs into release/* run the same checks as PRs into master, and release/prep-* pushes build nothing.
  • A new release-tooling job, which All Required Tests Passed waits for, runs the unit tests for cmake-version.py in .github/scripts/test_cmake_version.py. It also fails a PR that changes its base branch's version, unless it is that version's release/prep-v<version> bump PR onto a branch that may carry it. Without that, merging release/1.1 forward into master can carry release/1.1's version with no conflict: master at 1.2.0-dev plus release/1.1 going 1.1.0-dev to 1.1.0 gives master 1.2.0.
  • cmake-version.py refuses a CMakeLists.txt with more than one set(VERSION_*) line, so unresolved conflict markers fail instead of reading as one side's version. It also names the bump branch for both prepare-release.yaml and the PR check, and has the highest-tag mode wire-sysio's tag build uses to pick its wire-cdt release.
  • docs/release-workflow.md documents release branches, how a PR into one is merged forward to master, and the post-bump after a stable release: master to its next -dev version, a release branch to its next patch's (1.1.0 to 1.1.1-dev).

Before the first release from release/1.1:

  • release/* needs the same branch ruleset as master, excluding release/prep-*. Until then tag-release.yaml refuses the branch.
  • Cut release/1.1 from master once this merges and master holds what 1.1 ships (the early cut was deleted), so it carries these workflow files.

wire-sysio gets the same change in its own PR.

…ches

prepare-release and tag-release take a `branch` input (default master). A release branch takes only its own line's versions, and master refuses a line that already has one. Master's cmake-version.py validates the inputs and reads the target branch from a separate refs/heads/ checkout, so no target-branch code runs. tag-release also refuses a branch without a pull_request rule.

release.yaml accepts a tag on master or its version's release branch, and gives the latest label to the highest published stable version. PRs into release/* run the master checks. Adds unit tests for cmake-version.py in a new release-tooling job.

Change-Id: If95aa740208950352ead33f612e6e01659f7693e
@heifner
heifner requested a review from jglanz November 3, 2026 21:36
A forward merge from a release branch can carry that branch's version into the target without a conflict when each side changed different VERSION_* lines: master at 1.2.0-dev merged with release/1.1 going 1.1.0-dev to 1.1.0 becomes 1.2.0. cmake-version.py check-pr compares a PR's merge result with its base and passes a changed version only from that version's release/prep-v<version> branch, onto a branch that may carry it. release-tooling runs it on every PR, and Prepare Release takes the prep branch name from the parser's new prep-branch mode.

read and write refuse more than one set(VERSION_*) line, so a tree with unresolved conflict markers fails instead of reporting one side's version.

docs/release-workflow.md documents forwarding a release branch's PRs to master, and cutting a release branch once master holds what the line ships.
@huangminghuang

Copy link
Copy Markdown
Contributor

Reviewed current head 59ec0031c3e79d4e541907507be1acca7440f95d. One actionable finding:

[P2 / Medium] Serialize latest updates across releases — .github/workflows/release.yaml:253–258.

Concurrency is scoped per tag, so different versions can publish simultaneously. If v1.1.1 lists releases before v1.2.0 publishes, it retains --latest and can overwrite the newer release’s label after uploading its assets. I reproduced this with the exact publish script and mocked GitHub calls: both releases were published, but latest ended at v1.1.1. Serialize the release-list, publication, and latest-label updates with a repository-wide publishing lock.

Validation: all 33 Python tests passed; actionlint passed for all five changed workflows.

After a stable release, the branch it shipped from is post-bumped: master to its next -dev version, a release branch to its next patch's (1.1.0 to 1.1.1-dev). The docs and the Prepare Release comments say so.

cmake-version.py gains highest-tag, which prints the highest release tag in a version's <major>.<minor> line or an older one, ordering versions numerically and skipping tags outside the grammar. wire-sysio's tag build picks its wire-cdt release with it, and the parser stays identical in both repos.
Publish Release's concurrency group is per tag, so two releases publishing together could each choose the latest label from a release list the other was about to change, leaving it on the lower version. The per-tag job, renamed attach-assets, verifies and uploads the assets, and on the hand-pushed-tag fallback creates the draft itself. Publishing and the label move to a publish-release job whose concurrency group is shared by every tag, with queue: max so each waiting release publishes in turn instead of replacing the one queued before it.
@heifner

heifner commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, fixed in 71fe87a. The per-tag job is now attach-assets (verify, checksums, upload, and on the hand-pushed-tag fallback it creates the draft). Publishing and the label moved to a publish-release job whose concurrency group, publish-releases, is shared by every tag. queue: max makes a third release waiting behind two others queue up instead of replacing the pending one. Run one after another against a stateful gh stand-in, in both orders and with the label previously taken by a hand-published release, the label always ended on the highest stable release.

actionlint, including its latest release 1.7.12, still rejects queue: that release predates the May 2026 feature. GitHub's workflow schema in actions/languageservices accepts queue: single|max on job-level concurrency. Neither repo runs actionlint in CI.

@huangminghuang huangminghuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed 71fe87a. No new actionable findings. The previously reported latest-label race is addressed by the shared publishing queue. All 41 parser tests and five mocked publication scenarios passed.

@heifner
heifner merged commit 71c3677 into master Nov 9, 2026
9 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