feat: merging a release-labeled PR is the release #112

Merged
dan-claude-bot merged 9 commits from feat/release-on-merge into main 2026-07-19 17:12:19 +00:00
2 changed files with 14 additions and 11 deletions
Showing only changes of commit d4409c3f42 - Show all commits

View file

@ -20,21 +20,22 @@ name: release
# and tsc first. So the build happens ONCE, here, and the asset is the # and tsc first. So the build happens ONCE, here, and the asset is the
# runnable tree: bin/, dist/, production node_modules/, package.json. # runnable tree: bin/, dist/, production node_modules/, package.json.
on: on:
# ONE push key, both filters — YAML maps are last-key-wins, so a second
# sibling `push:` would silently REPLACE the first and kill a door
# (grok's round-2 catch: the tag fallback had stopped triggering).
push: push:
# Every tag, not a shape filter (box's and rig's precedent): a tag that # Every tag, not a shape filter (box's and rig's precedent): a tag that
# mismatches package.json — a habitual v0.1.0, a typo — must fail the # mismatches package.json — a habitual v0.1.0, a typo — must fail the
# assert LOUDLY below, not be silently skipped by a pattern that didn't # assert LOUDLY below, not be silently skipped by a pattern that didn't
# match. # match.
tags: ["**"] tags: ["**"]
push:
# The merge-is-the-release path (#111) rides pushes to MAIN, not # The merge-is-the-release path (#111) rides pushes to MAIN, not
# pull_request events, for one load-bearing reason the first review # pull_request events: a pull_request run from a public FORK gets a
# round caught: a workflow run triggered by a pull_request from a # READ-ONLY GITHUB_TOKEN — `permissions:` cannot raise that ceiling —
# public FORK gets a READ-ONLY GITHUB_TOKEN — `permissions:` cannot # and every ceremony PR this org merges is cross-repo from the bot
# raise that ceiling — and every ceremony PR this org has ever merged # fork; the tag create would 403 after green asserts. A push to main
# is cross-repo from the bot fork. The asserts would pass and the tag # is an in-repo event with the full write token, whoever authored the
# create would 403, red on main, every release. A push to main is an # PR. The steps split on the pushed ref.
# in-repo event with the full write token, whoever authored the PR.
branches: [main] branches: [main]
permissions: permissions:

View file

@ -185,12 +185,14 @@ describe("release.yml", () => {
// intent — is read via the API off the merge commit's PR, and a // intent — is read via the API off the merge commit's PR, and a
// transition with no labeled PR behind it refuses. // transition with no labeled PR behind it refuses.
expect(RY).toContain("branches: [main]"); expect(RY).toContain("branches: [main]");
// YAML maps are last-key-wins: a second sibling push: key silently
// replaces the first and kills a door (grok's round-2 catch — the tag
// fallback had stopped triggering). Exactly ONE push key may exist.
expect(RY.match(/^ push:$/gm)).toHaveLength(1);
expect(RY).toContain("startsWith(github.ref, 'refs/tags/')"); expect(RY).toContain("startsWith(github.ref, 'refs/tags/')");
expect(RY).toContain("github.ref == 'refs/heads/main'"); expect(RY).toContain("github.ref == 'refs/heads/main'");
expect(RY).toContain("commits/$GITHUB_SHA/pulls"); expect(RY).toContain("commits/$GITHUB_SHA/pulls");
expect(RY).toContain( expect(RY).toContain("no merged, release-labeled PR is behind this commit");
"no merged, release-labeled PR is behind this commit",
);
expect(RY).not.toContain("pull_request:"); expect(RY).not.toContain("pull_request:");
}); });