fix(ci): comprehensive overhaul of release-drafter setup#15623
fix(ci): comprehensive overhaul of release-drafter setup#15623jamesfredley wants to merge 1 commit into7.0.xfrom
Conversation
Fixes a long-standing set of issues with the Release - Drafter workflow that
collectively prevented per-branch draft releases from working correctly
across 7.0.x, 7.1.x, 7.2.x, and 8.0.x. Symptoms included drafter runs queued
for hours behind the release pipeline, the 8.0.x draft never being created,
and release notes silently bumping from stale baselines.
Root causes addressed:
1. Concurrency lock with release.yml: release-notes.yml shared the
"release-pipeline-${branch}" concurrency group with release.yml, whose
manual approval gates routinely keep release runs in the "waiting" state
for days. Drafter runs queued behind those gates routinely lasted
1400-2000+ minutes before being cancelled, leaving drafts stale. Fix:
switch to "release-drafter-${branch}" group with cancel-in-progress so
the latest push wins. Drafter and release.yml never touch the same
release object: drafter targets the next-version draft, release.yml the
currently-published tag, so splitting the groups is safe.
2. Prereleases excluded from "last release" detection: every Apache Grails
release (v7.0.11, v7.1.1, v8.0.0-M1, ...) is published with
prerelease=true on GitHub during the ASF vote process. With
release-drafter's default include-pre-releases=false, those releases
were filtered out when looking for the "last release", so 7.0.x bumped
from v7.0.10 instead of v7.0.11 and 8.0.x reported "No last release
found" (because v8.0.0-M1 was the only release on that branch and was
excluded), falling back to walking the entire 265-release commit
history and exhausting the GitHub API rate limit. Fix: set
include-pre-releases: true.
3. Unbounded git history walk on rate-limit exhaustion: when no last
release was found, release-drafter walked unbounded parent commit
history calling the GraphQL API per commit. Fix: set
initial-commits-since to bound the walk to a recent date floor for
branches with no prior matching release (e.g. 7.2.x today).
4. Release-drafter v7.2.0 bug: initial-commits-since was silently ignored
when set only in release-drafter.yml (not also as a workflow input).
Fix: pin to v7.2.1 (commit 563bf132657a13ded0b01fcb723c5a58cdd824e2)
which ships the fix from upstream PR #1593.
5. Floating action tag on 7.0.x: 7.0.x used release-drafter@v7 while
8.0.x was pinned to a SHA. Fix: pin all branches to the v7.2.1 commit
SHA per ASF security policy (PR #15523).
6. continue-on-error swallowed all failures silently: a transient or
permanent drafter failure looked identical to a successful run, which
is why the broken state went unnoticed for so long. Fix: keep
continue-on-error so PR checks stay green for transient API blips, but
add an explicit verification step that loudly logs a workflow-summary
warning and a GitHub Actions annotation when no draft id was produced.
7. Workflow ran on PRs targeting any branch, including feature-to-feature
PRs that can never affect a release. Fix: restrict the pull_request
trigger to PRs whose base ref is a release branch.
8. issues trigger ran on every issue close/reopen even though release
drafting has nothing to do with issue lifecycle. Fix: removed.
9. Cross-branch leakage prevention: combine filter-by-commitish: true,
filter-by-range: ~MAJOR.MINOR.0 (derived dynamically from the branch
name) and tag-prefix: v so release-drafter can never match a release
from a different release line when looking for either the "last
release" to bump from or an existing draft to update.
The fix is being landed on 7.0.x and will be merged forward into 7.1.x,
7.2.x, and 8.0.x. The 8.0.x branch's existing release-drafter@5de93583
pin (v7.2.0) will be superseded by the new v7.2.1 pin during merge.
Assisted-by: claude-code:claude-4.6-opus
✅ All tests passed ✅🏷️ Commit: d4608a4 Learn more about TestLens at testlens.app. |
| concurrency: | ||
| group: release-pipeline-${{ github.event.pull_request.base.ref || github.ref_name }} | ||
| cancel-in-progress: false | ||
| group: release-drafter-${{ github.event.pull_request.base.ref || github.ref_name }} |
There was a problem hiding this comment.
I dont' think this is correct. We want the concurrency to be shared with the release, otherwise release drafter will modify a release that is being released. We do not want that to ever happen.
|
The change separates the concurrency group from 'release-pipeline-${branch}' to 'release-drafter-${branch}' to prevent drafter runs from queuing behind long-waiting release pipeline jobs with manual approvals. The comment explains drafter updates a draft for the next version (e.g., v7.0.12), while release.yml handles the current published tag (e.g., v7.0.11), so they don't conflict. Sharing concurrency isn't needed since they target different releases. .github/workflows/release-notes.yml |
This is incorrect. The creation of the tag itself was enough to historically trigger the release drafter workflow and thus a release being performed would overlap the release drafter run too. |
Summary
Comprehensive overhaul of the Release - Drafter setup that fixes a long-standing set of compounding issues across
7.0.x,7.1.x,7.2.x, and8.0.x. Lands first on7.0.xand is intended to be merged forward into the higher branches in the usual cascade.The drafter has been quietly broken for months. Symptoms users have hit:
8.0.xdraft was never created (no draft for any branch currently exists).v7.0.10instead ofv7.0.11) because the latest releases were excluded from the "last release" lookup.continue-on-error: truemade all of the above invisible: every run reported "success" even when it produced nothing.This PR addresses all nine root causes in a single change so the next merge cascade gives every release branch a working drafter at once.
Root causes and fixes
1. Concurrency lock with
release.yml(the hour-long delays)release-notes.ymlandrelease.ymlboth usedrelease-pipeline-${branch}.release.ymlhas manual approval gates (environment: release,environment: docs,environment: sdkman) which routinely keep a release run inwaitingstate for days until a maintainer approves the next stage. Every push to a release branch during that window queued behind the waiting release run.Evidence from the workflow history:
Fix: switch to
release-drafter-${branch}withcancel-in-progress: true. The drafter andrelease.ymlnever touch the same release object - the drafter targets the next-version draft (e.g.v7.0.12),release.ymlthe currently-published tag (e.g.v7.0.11) - so splitting the groups is safe.2. Prereleases excluded from "last release" detection (the missing 8.0.x draft)
This is the single biggest fix and the reason no draft exists for
8.0.x.Every Apache Grails release -
v7.0.11,v7.1.1,v8.0.0-M1, ... - is published on GitHub withprerelease=trueduring the ASF vote process. release-drafter's defaultinclude-pre-releases=falsefilters those out when finding the "last release", which means:7.0.xbumped fromv7.0.10(last non-prerelease) instead ofv7.0.11.7.1.xbumped fromv7.1.0instead ofv7.1.1.8.0.xhad no last release at all (onlyv8.0.0-M1exists, excluded as a prerelease) - so the action fell back to walking the entire 265-release commit history and exhausted the GitHub API rate limit (visible verbatim in the workflow logs):Simulation of the new filter pipeline against today's release set:
lastRelease7.0.xv7.0.10(wrong)v7.0.117.1.xv7.1.0(wrong)v7.1.17.2.xinitial-commits-since)8.0.xv8.0.0-M1Fix:
include-pre-releases: truein.github/release-drafter.yml.3. Unbounded git history walk on rate-limit exhaustion
When no last release is found, release-drafter walks parent commit history through GraphQL, paging until the entire history is consumed.
Fix:
initial-commits-since: '2026-04-29T00:00:00Z'(just before the most recent releases). This is consulted only when no last release matches the filters - so it does not affect7.0.x/7.1.x/8.0.x(which now find their last release thanks to fix #2), and it bounds7.2.x's walk to days instead of years.4. release-drafter
v7.2.0buginitial-commits-sincewas silently ignored when set only inrelease-drafter.yml(not also as a workflow input) - exactly the path we use. Fixed upstream in release-drafter#1593, shipped inv7.2.1.Fix: pin to
release-drafter@563bf132657a13ded0b01fcb723c5a58cdd824e2(v7.2.1).5. Floating action tag on 7.0.x
7.0.xusedrelease-drafter@v7while8.0.xwas already pinned to a SHA. This violates the ASF security policy enforced in #15523.Fix: SHA pin on every branch via the merge cascade.
6.
continue-on-errorswallowed all failures silentlyA transient or permanent drafter failure looked identical to a successful run; that is precisely why this broken state went unnoticed for months.
Fix: keep
continue-on-error(so transient API blips do not turn every PR check red) but add an explicit verification step that:::warning::annotation and a workflow-summary warning when the action produced no draft id (the failure mode that made this invisible).7. Workflow ran on every PR
pull_request: types: [opened, reopened, synchronize, labeled]matched PRs targeting any branch, including feature-to-feature PRs that can never affect a release.Fix:
branches: ['[0-9]+.[0-9]+.x']on the trigger.8.
issuestriggerDrafter has nothing to do with issue lifecycle; the trigger fired on every issue close/reopen.
Fix: removed.
9. Cross-branch leakage prevention (defense in depth)
Combine three filters so release-drafter cannot match a release from a different release line either when looking for the "last release" to bump from or for an existing draft to update:
filter-by-commitish: true(release'starget_commitishmust equal the branch).filter-by-range: ~MAJOR.MINOR.0(computed dynamically from the branch name in the workflow step).tag-prefix: v(release's tag must start withv).Files changed
.github/release-drafter.ymltag-prefix: v,include-pre-releases: true,initial-commits-since.filter-by-commitish: true, autolabeler, categories, version-resolver, template) unchanged..github/workflows/release-notes.ymlissues:trigger.pull_requesttrigger to release branches.release-drafter-${branch}withcancel-in-progress: true.release-draftertov7.2.1by commit SHA.id: drafterand a verification step that surfaces missing-draft failures via workflow summary and annotations.How to verify after merge
7.0.xtrigger the drafter and complete in seconds (no longer queued behindrelease.yml).v7.0.12(or whatever the version-resolver picks) appears at https://github.com/apache/grails-core/releases withtarget_commitish=7.0.x.8.0.x, confirm av8.0.0-M2(orv8.0.0) draft appears withtarget_commitish=8.0.x- this is the missing draft from the original report.Out of scope
v7.0.xreleases whosetarget_commitishisrefs/heads/7.0.xinstead of7.0.xare benign:find-previous-releases.tsstripsrefs/heads/before comparing, so they still matchfilter-by-commitish: true. No cleanup needed.target_commitishcannot match any current branch filter and are correctly excluded.Assisted-by: claude-code:claude-4.6-opus