Skip to content

ci(release): degrade the back-merge instead of failing a shipped release - #636

Open
0x-copilot-dev wants to merge 2 commits into
devfrom
fix/release-back-merge-ruleset
Open

0x-copilot-dev wants to merge 2 commits into
devfrom
fix/release-back-merge-ruleset

Conversation

@0x-copilot-dev

Copy link
Copy Markdown
Owner

release-cli's final step, Back-merge the release commit into dev, fails on every release. Seen most recently on run 32044497852 publishing @0x-copilot/cli 0.3.0:

remote: error: GH013: Repository rule violations found for refs/heads/dev.
remote: - Changes must be made through a pull request.
remote: - 4 of 4 required status checks are expected.
 ! [remote rejected]   dev -> dev (push declined due to repository rule violations)

Why it cannot just be fixed with a permission

GITHUB_TOKEN is not a ruleset bypass actor, and this repository is owned by a user, not an org — so actor_type: Integration bypass entries cannot be granted at all (root CLAUDE.md #6). There is no permission to add that makes the direct push work.

Why the failure was worse than it looked

The publish is irreversible several steps earlier. So the red run described a release that had shipped — 0.3.0 was live on npm, tagged, and released — and the obvious response, re-running it, dies on "already published" without repairing the drift it was named for.

Meanwhile main sits one commit ahead of dev, which is exactly what promote-to-main refuses to promote over. The previous release's uncleaned drift blocks the next one.

The change

The step now degrades rather than fails:

  • Direct push when REPO_ADMIN_TOKEN is configured — a fine-grained PAT for an account that is a bypass actor (0x-copilot-dev) makes this fully automatic. Unset today, so the fallback is what runs.
  • Otherwise a PR: parks the merged state on release/back-merge-<version> and opens it into dev. Rulesets cover only refs/heads/{dev,main}, so that side-branch push is allowed. The repair becomes one click instead of a git incantation someone has to remember at the end of a release.
  • Says which happened in the job summary, and warns loudly when dev did not get the commit, including the exact manual fast-forward.
  • Never fails the release. continue-on-error is the backstop for unexpected paths; the script handles the expected ones and exits 0.

A conflict is still reported and never forced, as before. Per CLAUDE.md #3 no ${{ }} is interpolated into the script — token, repo and version arrive through env:.

Verification

The script was extracted and run against stubbed git/gh across all six paths:

path exit outcome
merge conflicts 0 warning + manual resolve instructions
push succeeds 0 success summary
push succeeds with admin token 0 success summary
push rejected → PR opened 0 warning naming the PR url
rejected, gh fails 0 warning + manual fast-forward
rejected, side-branch push fails 0 warning + manual fast-forward

Path 4 is what runs today. Takes effect on the next release; nothing changes until then.

🤖 Generated with Claude Code

`release-cli`'s last step pushes `dev` directly, and dev's ruleset rejects it:

    GH013: Repository rule violations found for refs/heads/dev.
    - Changes must be made through a pull request.
    - 4 of 4 required status checks are expected.

`GITHUB_TOKEN` is not a bypass actor, and because this repository is owned by a
USER rather than an org, `actor_type: Integration` bypass entries cannot be
granted at all (root CLAUDE.md #6) -- so there is no permission to add that
would make the push work. It has failed on every release, not just the last one.

That produced the worst outcome available at that point in the job. The publish
is irreversible several steps earlier, so the red run described a release that
had in fact shipped (0.3.0 was live on npm, tagged, and released), and the
obvious response -- re-run it -- dies on "already published" without repairing
the drift it was named for. Meanwhile main sits one commit ahead of dev, which
is exactly what `promote-to-main` refuses to promote over, so the NEXT release
is blocked by the previous one's cleanup.

The step now degrades rather than fails:

  * pushes directly when `REPO_ADMIN_TOKEN` is configured (a PAT for an account
    that IS a bypass actor makes this fully automatic; unset today),
  * otherwise parks the merged state on `release/back-merge-<version>` and opens
    a PR into dev -- rulesets cover only refs/heads/{dev,main}, so that push is
    allowed -- turning the repair into one click instead of a git incantation
    someone has to remember at the end of a release,
  * writes which of those happened to the job summary, and warns loudly when
    dev did not get the commit,
  * never fails the release. `continue-on-error` is the backstop for the
    unexpected paths; the script itself handles the expected ones and exits 0.

A conflict is still reported and never forced, as before.

Verified by running the extracted script against stubbed `git`/`gh` over all six
paths -- conflict, push-ok, push-ok-with-token, rejected-then-PR, rejected-then-
gh-fails, rejected-then-side-push-fails. Every one exits 0 with the right
annotation and summary line.

Per CLAUDE.md #3 no `${{ }}` is interpolated into the script; the token, repo
and version arrive through `env:`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GitHub does not start workflow runs for events raised by GITHUB_TOKEN, so the
four required checks on the back-merge PR never report -- not pending-then-green,
never. Merging it therefore needs `--admin` for two independent reasons, and a
PR wedged on checks that will never run is a confusing thing to find at the end
of a release. Say so in the body the workflow writes, and in the step comment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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