feat: auto-merge release pull requests once a day - #50
Merged
Conversation
Auto-merge now finds the release pull requests by their `autorelease: pending` label instead of the action's `prs` output, which carries only what a run wrote. That output is unset on a run that finds the release pull request already correct, which is the ordinary case for a caller proposing on every push and merging on a schedule. This repository's own caller takes that shape: it proposes on push and passes `auto-merge` only on a new 09:00 UTC schedule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 27, 2026
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.
Summary
Batches releases to one a day instead of one per merge, by keeping
Release Pleaseproposing on every push while only a new 09:00 UTC scheduled run turns auto-merge on. Before this change, every merge tomainended up cutting a version: the push run opened or updated the release pull request and enabled auto-merge on it, so it merged as soon as its checks passed. The release pull request now stays open through the day, current withmain, and merges once in the morning.Getting there required changing how the shared workflow finds those pull requests — see Solution, because the obvious version of this merges nothing at all.
Problem
Releases are involuntary.
_release-please.yamlturns auto-merge on at the end of every run, and a push tomainstarts a run, so any merged pull request cuts a version a few minutes later. A day of five merges is five releases, each carrying one change.That is noise for consumers, who pin exact tags and get a Renovate bump per release. It also produces the race
release-please.yamlalready documents: five auto-merges landing inside a minute started five overlapping runs, and one died onLabel does not exist.Solution
The push trigger stays, because it is what keeps the release pull request's changelog and version current and pending work visible all day. A new
schedule:entry at 09:00 UTC — 06:00America/Sao_Paulo, the timezone Renovate runs on — is the only trigger that passesauto-merge: true.flowchart TD A[Push to main] --> B[Propose releases] B --> C([Release pull request open and current, not merging]) D[Schedule, 09:00 UTC] --> E[Propose releases] E --> F{Open pull request labelled<br/>autorelease: pending?} F -- no --> G([Nothing to merge, run green]) F -- yes --> H[gh pr merge --auto --squash] H --> I([Merges once checks pass; that push cuts the tag]) J[workflow_dispatch] --> K[Propose releases] K --> L([Release pull request re-proposed, not merging])Auto-merge now finds the pull requests by label, not by the action's
prsoutput. That is the part worth reviewing, because gating the scheduled run onprswould have merged nothing, ever, with no failing run to say so.prscarries only what a run created or updated.remained the sameand drops that pull request.prsoutput at all, rather than an empty array.The
autorelease: pendinglabel is on the pull request regardless of which run last touched it.--state openis load-bearing rather than tidy — a merged release pull request keeps that label until a later run tags it, so filtering by label alone would turn up one with nothing left to merge.Two consequences to weigh:
auto-mergeinput's meaning widened, from the release pull requests a run opens or updates to the open ones however they got there. Not breaking under this repository's definition, since no consumer must edit anything: a caller left on the push-only shape keeps auto-merging its release pull request, and now also does so on runs where release-please rewrote nothing.!inputs.dry-runbecame load-bearing. It used to be backed up byprsbeing empty whenever the pull request was skipped. A label lookup finds the standing release pull request either way, so that condition is now the only thing keeping a dry run from merging a real release.This repository's own caller takes the new shape in the same commit, because it calls
_release-please.yamlby path rather than by tag.kanso-ui,unplugin-style-dictionaryandhome-assistant-applicationspin a tag and follow in their own pull requests once this releases as v3.2.0, each bumping its pin and adding the schedule in one commit.Review Guide
Focus areas
.github/workflows/_release-please.yaml:140-173— the rewritten auto-merge step. Theif:shed two clauses and the step gained a lookup that reaches GitHub rather than reading a step output, so it is the one place a mistake merges something it should not.Design Decisions
github.event_namein the caller, not inside the shared workflowscheduleitself. That would impose one release policy on all four consumers at once and makeauto-mergemean something other than its name. Keeping the expression in the caller leaves the input mechanical and the event unambiguous.workflow_dispatchproposes without mergingmaincarrying merged work with nothing proposed for it between runs. Proposing on push costs nothing and keeps the pending release visible.Blast Radius
Not behind a feature flag. Merging this changes how this repository releases itself immediately, since its caller runs
_release-please.yamlstraight frommain.The three tag-pinning consumers are unaffected until they bump to v3.2.0, and unaffected in behaviour even then until they add the schedule — their default
auto-merge: truekeeps working.Watch for the first 09:00 UTC run in Actions, and for a release pull request sitting open longer than a day, which is what a schedule that never fired would look like.
Rollback Plan
Revert this pull request via GitHub's Revert button. Nothing is migrated and no tag is moved. If a release pull request is left standing at the time, enable auto-merge on it by hand or merge it directly.
Test plan
Verifiable by agent before merging
npm ci,npm run formatandnpm run lintclean under the Node in.tool-versions(24.20.0)actionlint1.7.12 clean over all workflowsThe label lookup returns cleanly against all four repositories, and handles the empty case
The label lifecycle confirms
--state openis required — a merged release pull request carriesautorelease: tagged, and both labels exist on the consumersCanary the gate through a real caller: point a branch in a consumer at
_release-please.yaml@ci/daily-release-automergewith the schedule change, dispatch it, and confirm the run proposes the release pull request and skipsEnable auto-merge on the open release pull requests.Dry run release-pleaseintest.yamlcannot cover this — it passes no secrets, soCLIENT_ID == ''and the step is skipped for the wrong reason.Cannot be verified before merge
schedule:trigger cannot be dispatched, and GitHub only registers one once the workflow is on the default branch.mainafter merge.