Skip to content

Commit bba5db7

Browse files
committed
ci: resolve the version pull request before acting on it
Both release steps found the pull request by head branch alone. That can select a fork's branch of the same name, and `|| true` turned an API or auth failure into "no version PR" — so a lookup that broke looked exactly like a release with nothing to do. Resolve it once instead: take the number the changesets action reports, and fall back to a listing scoped to this repository's own head branch, keeping a failed lookup distinct from an empty result. Auto-merge and the review request both consume that number. Also state in CONTRIBUTING that a changeset landing while the release pull request waits on its checks joins that release, and correct the claim that a major breaks pinned installs — it breaks consumers who upgrade.
1 parent 0884f5e commit bba5db7

2 files changed

Lines changed: 72 additions & 17 deletions

File tree

.github/workflows/release.yml

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,34 +195,87 @@ jobs:
195195
# Never fail the release over this: a version PR that stays open costs a
196196
# manual merge, while a failure here would block npm, the Marketplace and
197197
# the CDN behind it.
198-
- name: Enable auto-merge on the version PR
199-
if: steps.changesets.outputs.published != 'true' && vars.AUTO_MERGE_RELEASE_PR == 'true'
200-
continue-on-error: true
198+
# Resolve the version PR once, for every step that acts on it.
199+
#
200+
# The changesets action reports the pull request it just created or
201+
# updated, which is the only unambiguous identifier. The fallback covers
202+
# the run where changesets had nothing to change, and it is scoped to a
203+
# head branch in THIS repository: a head-name match on its own can select
204+
# a fork's branch of the same name, which would point the steps below at
205+
# somebody else's pull request.
206+
- name: Resolve the version PR
207+
id: version-pr
208+
if: steps.changesets.outputs.published != 'true'
201209
env:
202210
GH_TOKEN: ${{ steps.release-bot.outputs.token }}
211+
CHANGESETS_PR: ${{ steps.changesets.outputs.pullRequestNumber }}
203212
run: |
204213
set -uo pipefail
205-
pr=$(gh pr list --head changeset-release/main --state open --json number --jq '.[0].number' || true)
206-
if [ -z "$pr" ] || [ "$pr" = "null" ]; then
207-
echo "::notice::No open version PR; nothing to auto-merge."
214+
if [ -n "${CHANGESETS_PR}" ] && [ "${CHANGESETS_PR}" != "null" ]; then
215+
echo "number=${CHANGESETS_PR}" >> "${GITHUB_OUTPUT}"
216+
echo "Version PR #${CHANGESETS_PR}, as reported by the changesets action."
208217
exit 0
209218
fi
210-
if gh pr merge "$pr" --squash --auto; then
211-
echo "Auto-merge armed on #${pr}; it lands when its required checks pass."
212-
else
213-
echo "::warning::Could not arm auto-merge on #${pr}. Merge it by hand to cut the release."
219+
if ! open_prs=$(gh pr list \
220+
--repo "${GITHUB_REPOSITORY}" \
221+
--head changeset-release/main \
222+
--base main \
223+
--state open \
224+
--json number,headRepositoryOwner); then
225+
echo "::warning::Could not look up the version PR. Nothing downstream will run; check it by hand."
226+
exit 0
227+
fi
228+
number=$(printf '%s' "${open_prs}" \
229+
| jq -r --arg owner "${GITHUB_REPOSITORY_OWNER}" \
230+
'[.[] | select(.headRepositoryOwner.login == $owner)][0].number // ""')
231+
if [ -z "${number}" ]; then
232+
echo "::notice::No open version PR in this repository."
233+
exit 0
214234
fi
235+
echo "number=${number}" >> "${GITHUB_OUTPUT}"
236+
echo "Version PR #${number}."
215237
216-
- name: Request CodeRabbit review on version PR
217-
if: steps.changesets.outputs.published != 'true'
238+
# Release cadence switch. With this on, the version PR merges itself once
239+
# every required check passes, so one merge to main becomes one release
240+
# and the version tracks each change instead of collapsing a backlog of
241+
# changesets into a single jump. Turn it off to go back to releasing by
242+
# hand: `gh variable set AUTO_MERGE_RELEASE_PR --body false`.
243+
#
244+
# This is deliberately not a blanket auto-merge. It only ever targets the
245+
# changesets-authored branch in this repository, the repository requires
246+
# its status checks before any merge, and a major bump is gated separately
247+
# on the pull request that introduces the changeset — so an unattended
248+
# release can still never rename the major version on its own.
249+
#
250+
# A changeset that lands while this pull request is waiting on its checks
251+
# joins the same release rather than starting the next one. That window is
252+
# how changesets works, not something this step can close; keeping one
253+
# changeset per pull request keeps it small.
254+
#
255+
# Never fail the release over this: a version PR that stays open costs a
256+
# manual merge, while a failure here would block npm, the Marketplace and
257+
# the CDN behind it.
258+
- name: Enable auto-merge on the version PR
259+
if: steps.version-pr.outputs.number != '' && vars.AUTO_MERGE_RELEASE_PR == 'true'
260+
continue-on-error: true
218261
env:
219262
GH_TOKEN: ${{ steps.release-bot.outputs.token }}
263+
PR: ${{ steps.version-pr.outputs.number }}
220264
run: |
221-
pr=$(gh pr list --head changeset-release/main --state open --json number --jq '.[0].number' || true)
222-
if [ -n "$pr" ] && [ "$pr" != "null" ]; then
223-
gh pr comment "$pr" --body '@coderabbitai review'
265+
set -uo pipefail
266+
if gh pr merge "${PR}" --squash --auto; then
267+
echo "Auto-merge armed on #${PR}; it lands when its required checks pass."
268+
else
269+
echo "::warning::Could not arm auto-merge on #${PR}. Merge it by hand to cut the release."
224270
fi
225271
272+
- name: Request CodeRabbit review on version PR
273+
if: steps.version-pr.outputs.number != ''
274+
env:
275+
GH_TOKEN: ${{ steps.release-bot.outputs.token }}
276+
PR: ${{ steps.version-pr.outputs.number }}
277+
run: gh pr comment "${PR}" --body '@coderabbitai review'
278+
226279
- name: Resolve Pythinker Code native release
227280
if: steps.changesets.outputs.published == 'true'
228281
id: pythinker-release

CONTRIBUTING.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ This repo uses [changesets](https://github.com/changesets/changesets) to manage
8888
| `minor` | A capability a user could not reach before | `2.1.3``2.2.0` |
8989
| `major` | A break: something that worked stops working, or works differently | `2.2.0``3.0.0` |
9090

91-
Prefer one changeset per pull request. A pull request that needs several is usually several releases wearing one hat, and the changelog cannot attribute the changes afterwards.
91+
Prefer one changeset per pull request. A pull request that needs several is usually carrying several separate releases, and once they are versioned together the changelog can no longer say which change each entry came from.
9292

93-
A `major` needs a maintainer's sign-off: the `changeset-policy` workflow fails a pull request that adds one unless it carries the `breaking-change-approved` label. A major renames the release and breaks every pinned install, and an npm publish cannot be taken back — so it is a decision, never a side effect of a large branch.
93+
A `major` needs a maintainer's sign-off: the `changeset-policy` workflow fails a pull request that adds a major changeset, or edits an existing one up to `major`, unless it carries the `breaking-change-approved` label. A pinned install keeps working, but every consumer who upgrades has to deal with the break, and an npm publish cannot be taken back — so it is a decision, never a side effect of a large branch.
9494

9595
### Release cadence
9696

@@ -101,6 +101,8 @@ Changesets keeps a `ci: release packages` pull request open on `main` and rewrit
101101

102102
The repository variable `AUTO_MERGE_RELEASE_PR` chooses between the two. Set to `true`, the release pull request merges itself once its required checks pass, giving one release per change. Unset or `false`, a maintainer merges it when a release is wanted.
103103

104+
Either way there is a window: a changeset that lands while the release pull request is waiting on its checks joins that release instead of starting the next one. That is how changesets works, so a release can still carry more than one change — one changeset per pull request keeps the window small.
105+
104106
## Pull Requests
105107

106108
Every PR opens with the [PR template](.github/pull_request_template.md). PR titles must follow [Conventional Commits](#commit-convention); CI runs `pnpm lint`, `pnpm typecheck`, and `pnpm test` on every PR. Update user-facing docs in `docs/` when behavior changes — use the `gen-docs` skill when working with coding agents.

0 commit comments

Comments
 (0)