Skip to content

ci: give each pull request its own release drafter run - #16213

Open
codeconsole wants to merge 3 commits into
apache:8.0.xfrom
codeconsole:ci/release-drafter-pr-concurrency
Open

ci: give each pull request its own release drafter run#16213
codeconsole wants to merge 3 commits into
apache:8.0.xfrom
codeconsole:ci/release-drafter-pr-concurrency

Conversation

@codeconsole

Copy link
Copy Markdown
Contributor

Every pull request against a release branch computes the same release-drafter concurrency group, because the group is keyed on the branch it targets:

group: release-drafter-${{ github.event.pull_request.base.ref || github.ref_name }}
cancel-in-progress: true

Two pull requests pushed within seconds of each other therefore share one slot, and the later one cancels the earlier:

05:30:24  feat/hiddenmethod-handler-mapping        cancelled   (#16183)
05:30:36  perf/urlmapping-name-resolution-8.0.x    success

The evicted pull request shows a cancelled check with nothing wrong with it, and never receives the labels the run exists to apply. Anything that raises several pull requests at once — rebasing a stack, merging a branch into each of them — makes it routine.

Queueing instead of cancelling would not fix it: GitHub keeps one pending run per group and cancels the one pending before it, so a sweep of ten pull requests would still show eight cancellations, of queued runs rather than running ones.

The group is the pull request

group: release-drafter-${{ github.event.pull_request.number || github.ref_name }}

Pull requests no longer meet. cancel-in-progress still does the right thing within one subject: pushing twice to the same pull request supersedes its own earlier run, and pushes to a release branch still share a group by branch name.

A pull request run labels; it does not draft

disable-releaser: ${{ github.event_name == 'pull_request' }}

They shared a slot for a reason — two runs creating the same draft at once is how the duplicate drafts described in that file's header appear — so this removes the reason rather than working around it.

Release notes are built from merged pull requests. An open one contributes nothing to the draft, so the releaser was rewriting the draft it had already written. Checked against the live v8.0.0 draft: it lists merged pull requests only, which also corrects the claim in the workflow comment that the draft "picks up new PRs as soon as they are opened".

Labels still apply on every pull request event. The draft is written by pushes to the release branch, which is when a merge can actually change it.

./gradlew validateActions passes.

Every pull request against a release branch computed the same concurrency group -
the group is keyed on the branch it targets - so two of them pushed within seconds
of each other shared one slot and cancel-in-progress had the later kill the
earlier. The evicted pull request shows a cancelled check with nothing wrong with
it, and never gets the labels the run exists to apply. Raising several at once,
which is what rebasing a stack or merging a branch into each of them does, made it
routine: 16183 was cancelled 12 seconds in by an unrelated PR.

Queueing rather than cancelling would not have fixed it. GitHub holds one pending
run per group and cancels the one that was pending before it, so a sweep of ten
pull requests would still have shown eight cancellations, of queued runs.

The group is now the pull request, so they no longer meet. Pushing twice to one
pull request still supersedes its own earlier run, and pushes to a release branch
still share a group by branch.

They shared a slot for a reason - two runs creating the same draft at once is how
duplicate drafts appear - so a pull request run no longer writes one. Release notes
are built from merged pull requests: an open one contributes nothing, and the
releaser was rewriting the draft it had already written. Confirmed against the live
v8.0.0 draft, which lists merged pull requests only. Labels still apply on every
pull request event; the draft is written by the pushes that can change it.
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.1237%. Comparing base (e40cb27) to head (02a2338).
⚠️ Report is 7 commits behind head on 8.0.x.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##                8.0.x     #16213        +/-   ##
==================================================
- Coverage     54.1238%   54.1237%   -0.0001%     
+ Complexity      20307      20306         -1     
==================================================
  Files            2107       2107                
  Lines          101144     101146         +2     
  Branches        17921      17922         +1     
==================================================
+ Hits            54743      54744         +1     
- Misses          38595      38596         +1     
  Partials         7806       7806                

see 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Corrects the commit before this one, which passed `disable-releaser` to an action that has no
such input. The workflow's own run said so - "Unexpected input(s) 'disable-releaser'" - and then
tried to create a release and failed. The releaser therefore still ran on pull requests, so the
concurrency isolation added there did not prevent the concurrent writes it claimed to.

The pull request trigger was there so the autolabeler could label pull requests, and the action
pinned here cannot do that. release-drafter 7 split labelling into a second entrypoint,
release-drafter/release-drafter/autolabeler, and the entrypoint used here has no labelling in it
at all - its source mentions labels zero times, against twenty three in the autolabeler's.

So a pull request run applied no labels. On a pull request from a fork it could not write a draft
either: the token is read only, and the run failed with "Resource not accessible by integration",
hidden by continue-on-error. Runs that could only fail also cancelled each other, because every
pull request against a release branch computed the same concurrency group - which is how apache#16183
lost its check twelve seconds in, to an unrelated pull request that pushed after it.

Nothing is lost by removing the trigger and the collision goes with it. Drafts are still written
on every push to a release branch, which is when a merge can change them, and the concurrency
group is a branch again because a branch is the only subject left.

Restoring labels needs the autolabeler entrypoint, which is a separate action and not on the ASF
approved list - approved_patterns.yml carries only release-drafter/release-drafter@* - so it
wants an INFRA request first, and its own change.
The job held pull-requests: write for the autolabeler, and there is no autolabeler here any more.
What remains reads merged pull requests to build the draft, which the action does through
find-recent-merged-pull-requests, so the scope stays at read rather than going altogether: a
permissions block is restrictive, and an unnamed scope is denied.
@testlens-app

testlens-app Bot commented Aug 25, 2026

Copy link
Copy Markdown

🚨 TestLens detected 1 failed test 🚨

Here is what you can do:

  1. Inspect the test failures carefully.
  2. If you are convinced that some of the tests are flaky, you can mute them below.
  3. Finally, trigger a rerun by checking the rerun checkbox.

Test Summary

CI - Groovy Joint Validation Build / Build Grails with Groovy snapshot (shard 2) > :grails-test-examples-scaffolding:integrationTest

Test Runs Flakiness
UserControllerSpec > User list 1% 🟡

🏷️ Commit: 02a2338
▶️ Tests: 69136 executed
🟡 Checks: 82/84 completed

Test Failures

UserControllerSpec > User list (:grails-test-examples-scaffolding:integrationTest in CI - Groovy Joint Validation Build / Build Grails with Groovy snapshot (shard 2))
geb.waiting.WaitTimeoutException: condition did not pass in 30 seconds (failed with exception)
	at geb.waiting.Wait.waitFor(Wait.groovy:128)
	at geb.waiting.DefaultWaitingSupport.doWaitFor(DefaultWaitingSupport.groovy:55)
	at geb.waiting.DefaultWaitingSupport.waitFor(DefaultWaitingSupport.groovy:41)
	at geb.Page.waitFor(Page.groovy:120)
	at com.example.pages.LoginPage.login(LoginPage.groovy:39)
	at com.example.UserControllerSpec.User list(UserControllerSpec.groovy:48)
Caused by: Assertion failed: 

title != pageTitle && $('input', name: 'username').empty
|     |  |         |
|     |  |         false
|     |  'Please sign in'
|     false
'Please sign in'

	at com.example.pages.LoginPage.login_closure1(LoginPage.groovy:39)
	at com.example.pages.LoginPage.login_closure1(LoginPage.groovy)
	at geb.waiting.Wait.waitFor(Wait.groovy:117)
	... 5 more

Rerun Controls

Note

Checks are currently running using the configuration below.

Select tests to mute in this pull request:

🔲 UserControllerSpec > User list

Reuse successful test results:

🔲 ♻️ Only rerun the tests that failed or were muted before

Click the checkbox to trigger a rerun:

🔲 Rerun jobs


Learn more about TestLens at testlens.app/docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant