Skip to content

ci: land release bumps through a PR instead of pushing to master - #19

Merged
GSTJ merged 1 commit into
masterfrom
gstj/release-via-pr
Jul 27, 2026
Merged

ci: land release bumps through a PR instead of pushing to master#19
GSTJ merged 1 commit into
masterfrom
gstj/release-via-pr

Conversation

@GSTJ

@GSTJ GSTJ commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Release bumps now reach master through an auto-merging PR instead of a direct push, so required status checks can go on master without bricking releases.

Push restrictions

Rulesets apply to pushes as well as PR merges, and a commit created seconds ago has no checks against it, so release.yml pushing the version bump would fail at git push. magic-modal is already stuck there and works around it with git.push: false and master left out of sync.

A bypass for github-actions[bot] only exists on org-owned repos. On a user-owned one the API answers "Actor GitHub Actions integration must be part of the ruleset source or owner organization" and accepts nothing but the admin repository role, which the bot doesn't hold.

Release flow

release-it runs with git.push: false. It bumps, writes the changelog, commits and tags locally, then publishes to npm. The workflow pushes that commit to chore/release-vX.Y.Z, opens a PR and enables auto-merge. A release stays one workflow_dispatch.

Tags live in refs/tags/* and a ruleset targeting the default branch covers refs/heads/master, so the annotated tag still goes straight to the remote.

Hook placement

The tag has to reach the remote before the GitHub release is created, or GitHub invents a lightweight tag at master's head and the release points at the wrong commit with no changelog in it. after:git:release never fires under this config: Git.release() returns the result of its push step, release-it skips after: hooks on a falsy return, and push: false makes that return false every time. The hook sits on before:github:release, which runs unconditionally.

Merge strategy

Auto-merge uses a merge commit. The tag points at the bump commit, and a squash rewrites it into a new SHA, dropping the tag out of master's history. git describe --tags can't find it after that, and the next release computes its changelog from the wrong boundary.

Config location

The config moved from package.json to .release-it.cjs, since a commitFilter function is now needed to keep chore(release) commits out of later changelogs. Every release leaves two of them, the bump and the merge commit.

Verified with --dry-run on this branch: 1.0.9 to 1.0.10, git push origin refs/tags/v1.0.10 sitting between the tag and repos.createRelease, and nothing actually pushed, because release-it treats hooks as writes and skips them in a dry run.

Proof

Required status checks are active on master (Lint, test and build and Analyze (javascript), admin-role bypass only), and v1.0.11 went out with them on. npm, master, the tag and the changelog all agree, and the tag is an ancestor of master so git describe still resolves it:

Ruleset config plus npm, master, tag and changelog all at 1.0.11

The release run itself, including the step that opens the bump PR:

Release workflow run, every step green

And the PR it opened, auto-merged as a merge commit after three checks passed:

PR 22 opened by github-actions, auto-merge enabled, merged into master

Two rough edges showed up on the way, both fixed in follow-ups. v1.0.10 published and tagged and then failed to open its PR, because "Allow GitHub Actions to create and approve pull requests" was off; #20 landed it by hand and #21 turned the error into instructions. v1.0.11's checks were held as action_required and needed approving before auto-merge fired; #23 documents that and the fork-PR approval policy is loosened.

master is about to require status checks, and a commit that was just created
has none, so the release workflow can no longer push the version bump straight
to master. That's the failure magic-modal is stuck on, and
`github-actions[bot]` can't be given a ruleset bypass on a user-owned repo:
the API only accepts the admin repository role, which the bot doesn't have.

release-it now runs with `git.push: false`. It commits and tags locally,
publishes to npm, and the workflow pushes the branch to
`chore/release-vX.Y.Z`, opens a PR and turns on auto-merge. Tags live outside
`refs/heads/*` so the ruleset doesn't cover them, and a hook pushes the
annotated tag before the GitHub release is created.

Merge commit, not squash. The tag points at the bump commit, and a squash
rewrites it, which would leave the tag outside master's history and break
`git describe` for the next release's changelog range.

Moved the config to `.release-it.cjs` because it now needs a `commitFilter`
function to keep `chore(release)` commits, both the bump and the merge, out of
later changelogs.
@GSTJ
GSTJ merged commit a7016d2 into master Jul 27, 2026
3 checks passed
@GSTJ
GSTJ deleted the gstj/release-via-pr branch July 27, 2026 00:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant