Repository navigation
ci: check the pull request title, which a squash merge turns into a commit - #50
Merged
Merged
Conversation
…ommit A squash merge discards every subject on the branch and uses the pull request title instead, composed by GitHub after all local hooks have run. So the `commit-msg` hook cannot see the subject that actually lands on `main`, and `release-please` parses exactly that subject. #45 is what this costs. Squash-merged as "Make the station selection mean what the config says: radius, and one instrument per station (#45)", the release job recorded: commit could not be parsed: 2af57e8 Make the station selection mean ... error message: Error: unexpected token ' ' at 1:5, valid tokens [(, !, :] Considering: 0 commits No commits for path: ., skipping A radius fix, a WGS84 cut and two new configuration fields went out with no version bump and no changelog entry; both had to be repaired by hand in the release pull request afterwards. Written as a script rather than a third-party action so the accepted types are the ones `release-please-config.json` declares — a test asserts the two lists are identical, since a type this accepts but the release configuration does not know lands in no changelog section, which is the same silent failure reached from the other side. The failure message names one thing to change rather than printing the grammar. The title is attacker-chosen text, so it reaches the script through the environment, never interpolated into a shell command, and the workflow runs on `pull_request` with no permissions rather than `pull_request_target`. Merging with a merge commit remains the safer default and keeps the individual subjects; this makes a squash safe when one is used anyway. Co-Authored-By: Claude Opus 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 the hole that cost v0.5.0 its changelog entry. Its own title is the first thing the check runs against.
Why a hook cannot do this
A squash merge discards every subject on the branch and uses the PR title instead, composed by GitHub server-side, after all local hooks have run. The
commit-msghook never sees the subject that lands onmain— and that subject is exactly what release-please parses.#45 is the cost:
A radius fix, a WGS84 cut and two new config fields went out with no bump and no changelog entry. Both had to be repaired by hand in #47.
What this adds
.github/workflows/pr-title.ymlrunstools/check_pr_title.pyonopened,editedandreopened.Written as a script rather than pulling in
amannn/action-semantic-pull-request, for one reason worth the extra file: the accepted types are the onesrelease-please-config.jsondeclares, and a test asserts the two lists are identical. A generic action would happily accept a type the release config does not know — which lands the work in no changelog section, the same silent failure reached from the other side.The failure message names one thing to change:
Not "does not match
^(feat|fix|…)(\(.*\))?!?: .+$".Security
The title is text anyone who can open a PR chooses. It reaches the script through
env:, never interpolated into a shell command, and the workflow ispull_requestwithpermissions: {}— notpull_request_target, which would hand a write token to a fork's title.Tests
29 cases in
tests/test_pr_title.py, seeded from what actually happened rather than from the grammar: #45's real title, #46's originalRoadmap: …, a capitalised type, a missing space, an empty scope, every known type, and the(#45)suffix a squash appends.Verification
All 14 checks green on
3b4db69, includingpr-titlepassing on this PR's own title.Two things this does not do
The estimator default change is not in here — measuring it turned up 21 failing tests and a prerequisite (
REFACTOR_PLAN§6.6's absent config pin), so it wants its own PR rather than riding along with a CI fix.🤖 Generated with Claude Code