Repository navigation
CI: a tag release ends with a GitHub Release built from the version's milestone - #254
Merged
Merged
Conversation
… milestone (#253) workflow_run on both release workflows, not a last job in each: the two do not wait for each other and either can need a publish-only re-run, so each firing checks both for the tag commit and the second to finish creates the release. A PyPI check for both versions is the last gate. Notes come from scripts/release_notes.py (merged PRs of the milestone named after the base, in the tag and not in the previous tag of the same base, grouped by label), devN is a pre-release, a final release closes its milestone. Only contents/issues write plus two read scopes, no secret. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…en items to the next milestone (#253) The final release closes the base milestone, so a postN cannot depend on it: its notes now come from the PRs whose merge commit lies between the previous tag of the same base and the tag, whatever milestone they carry. Closing a milestone with open items would strand them, so a final release first moves them to the open milestone due next; with none it creates one named after the next upstream engine version (due 30 days later), and when that cannot be read it moves and closes nothing and fails with a message, because guessing a name (A.B.C.post1) would be wrong under the no-milestone rule for post releases. Every move is logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #253
A tag release now ends with a GitHub Release whose notes come from the version's milestone, and a final release closes the milestone.
Design.
release-notes.ymlis a separate workflow onworkflow_runofRelease native (arcadedb)andRelease JVM (arcadedb-jvm)(typescompleted), plusworkflow_dispatchwith a tag for a version that is already published. I chose this over a last job in each release workflow because the two workflows do not wait for each other, take different times, and either can need a publish-only re-run: a job inside one of them cannot know the other finished.workflow_runfires when each one completes, including after a re-run. Each firing looks up both workflows for the tag commit (head_sha) and continues only when both concludedsuccess, so the first to finish exits quietly with a notice and the second creates the release. It is not a PyPI poll: a poll needs a deadline and a sleep loop, and a workflow already only concludessuccessafter its own upload (and for the JVM package after the install from PyPI on every platform). PyPI is still asked once for both versions as the last gate, and a mismatch fails the run. If both firings run at once, one creates and the other edits the release (and a concurrency group serialises them), so the result is the same.What each piece does.
release-notes.yml: gate (tag regex, both release runssuccess, both versions on PyPI), notes,gh release create --verify-tag(orgh release editwhen it exists), then the milestone.A.B.C.devNis a pre-release,A.B.CandA.B.C.postNa normal release marked latest. Only a finalA.B.Ccloses the milestoneA.B.C, and before that it moves what is still open in it (issues and PRs) to the next milestone, logging every move. The next milestone is the open one with the earliest due date after this one. When there is none, it creates one named after the next upstream engine version (read from the upstreammainpom, the command already inrelease.adoc; today26.12.1), due 30 days after the closed one. When that version cannot be read, or equals the base, nothing is moved or closed and the run fails with a message telling the maintainer to create the next milestone with a due date and re-run the workflow.A.B.C.post1is not used as a name because post releases have no milestone. CI: resemble ArcadeData/arcadedb automation (labels, milestones, in-progress, PR template, Dependabot merge) #252's auto-assignment on merge then picks the next milestone, since the closed one is no longer open. Permissions:contents: readat the top, the job hascontents: write(release),issues: write(milestone),pull-requests: read(merge commits) andactions: read(the two runs). No new secret,GITHUB_TOKENonly. The checkout and the tag go throughenv, never into a script body. Actions are pinned like the other workflows (actions/checkoutby full SHA,# v7.0.1).scripts/release_notes.py TAG [--dry-run] [--output FILE]: standard library plusgh api, GET only. The milestone is named after the base (26.11.1for26.11.1,26.11.1.dev2,26.11.1.post1). It lists merged PRs of the milestone, grouped by first matching label (bugBug fixes,performancePerformance,enhancementFeatures,documentationDocumentation, else Other), each linetitle (#N), then install lines, the engine and the PyPI pages. A PR is in a tag when its merge commit is an ancestor of the tag (compare API), so a PR merged while the release runs is not listed. A final lists the whole milestone;.devNlists only PRs not indev(N-1), so a preview does not repeat the last one..postNdoes not use a milestone, because the final release closes it: it lists every PR whose merge commit lies between the previous tag of the same base (the final orpost(N-1)) and the tag (compare API for the commits,commits/{sha}/pullsfor the PR of each), whatever milestone the PR has or none. A tag that does not exist yet is read as the tip ofmain, so--dry-runworks before the tag. A missing milestone gives notes without a change list and a warning, not a failure.release.adochas a new section "GitHub Release and milestone" (what runs, which tags get what, how to preview with--dry-run, the move of open items and the failure case) and a--dry-runline in the commands;.github/ISSUE_TEMPLATE/release.mdpreviews the notes at the go step and checks the release and the milestone at the end;ci.adocandCLAUDE.mdname the workflow. The README does not describe the release flow, so it is unchanged.How it was checked.
uvx --from actionlint-py actionlint .github/workflows/release-notes.yml(the repo's.github/actionlint.yamlapplies): no findings.uvx pre-commit run --files <changed>run twice: all hooks pass (the YAML formatter skips.github/, shfmt had no files).yaml.safe_load),compile()andpyflakeson the script are clean.python3 -I scripts/release_notes.py 26.11.1 --dry-runagainst the live repository lists the six merged PRs of the milestone (below);26.11.1.dev2(an existing tag) reads from the tag and lists nothing sincedev1, andv26.11.1exits 1.run:block was extracted and run read-only withINPUT_TAG=26.11.1.dev2: both release workflowssuccessfor662851c, both versions on PyPI,ready=true.jqlookup of the milestone was run against the live repository (1 open 18). The move-and-close step was run read-only with a stubghthat executes GETs for real and only prints the PATCH and POST calls: with no next milestone it would create26.12.1(due2026-12-10) and move the open items one by one; with an unreadable upstream pom it exits 1 with the message and writes nothing; for a base without a milestone it warns and exits 0.26.11.1.post1was dry-run as a hypothetical tag: it lists the 73 PRs merged since26.11.1.dev2(78 commits), including ones with no milestone. No release, tag or milestone was created or changed. No pytest, docker or java was run, and I added no unit tests for the script.Dry run for
26.11.1today (the tag does not exist yet; #194 now has its corrected title):What the first real run will show.
workflow_runworkflows run the file on the default branch, so nothing happens until this is merged. The first real run is the next tag: the first firing logs a notice and creates nothing, the second creates the release. To try it earlier, runGitHub Releaseby hand (Actions,workflow_dispatch) with26.11.1.dev2: that creates a real pre-release for an existing tag. Still to confirm on a real run: thathead_branchis the tag name for a tag push (the docs say so), thatGITHUB_TOKENmay edit the milestone withissues: write, and that the repository setting for Actions allows the workflow to write releases. After the final26.11.1the 18 items open today move to the next open milestone, or to a new26.12.1created by the run; the milestone step was exercised only with a stubghthat logs writes (see below), never against the live milestone.🤖 Generated with Claude Code