ci: reconcile dev after hotfix-staging releases#21
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR implements rebase-based branch management automation across release workflows. It introduces a new GitHub Actions workflow to auto-rebase pull requests against ChangesRelease Branch Management Automation
🎯 3 (Moderate) | ⏱️ ~20 minutes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/auto-rebase-prs.yaml:
- Around line 81-87: When handling a manual run with ONLY_PR, validate the
fetched PR (PR_JSON) before assigning PRS: after running gh pr view into
PR_JSON, parse and check that baseRefName equals "dev" and state equals "OPEN"
(and optionally isDraft is false) and only then set PRS to '[.]' for processing;
if the checks fail, exit with an error or set PRS to an empty list to avoid
rebasing the wrong PR. Ensure this validation is done immediately after the gh
pr view/PR_JSON step and references the ONLY_PR variable, PR_JSON and PRS
symbols so manual runs are guarded to open PRs targeting dev only.
In @.github/workflows/release-semantic.yaml:
- Around line 234-248: The workflow currently treats a failed rebase-reconcile
push (the if block guarding the git push --force-with-lease="dev:${CURRENT_DEV}"
... "HEAD:refs/heads/dev") as success by calling exit 0; change that final exit
0 to exit 1 so the job fails on lease-rejection, ensuring the
GITHUB_STEP_SUMMARY warning and the echo "::warning::Dev moved during release —
manual reconcile required." surface as a failed run and do not leave dev
unreconciled silently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 164fa980-950f-4cf7-984e-ab8164b018e0
📒 Files selected for processing (3)
.claude/CLAUDE.md.github/workflows/auto-rebase-prs.yaml.github/workflows/release-semantic.yaml
The release-semantic.yaml `ff_target` flow skipped its dev reconcile when the promotion source was a hotfix-staging branch, on the (wrong) assumption that patch-id-dedup would collapse the duplicates at the next dev→main promotion. The `chore(release):` commit and its tag have no patch-id equivalent on dev, so they never entered dev's ancestry. The lineage invariant "main is an ancestor of dev after reconcile" silently broke, and the next RC cut on dev (e.g. v1.6.0-rc.1) computed its version floor and changelog against the wrong prior tag — the 1.5.2 hotfix was invisible. Fix the reconcile by rebasing dev onto the new main after a hotfix release. `git rebase` automatically drops dev's originals of the cherry-picked fixes (patch-id match) and replays any unique dev work on top, keeping linear history. Push is `--force-with-lease`; lease failure surfaces a remediation block instead of clobbering concurrent dev pushes. Rebase conflicts fail the job with a recovery script. Add an `Auto-rebase open PRs` workflow that runs on every push to dev (including the rebase-forward push from this fix) and rebases open PRs in-place. Drafts and PRs labeled `no-auto-rebase` are skipped; forks without "Allow edits by maintainers" are listed in the summary; conflicting PRs get a `needs-rebase` label and a comment. Document both flows and the one sanctioned force-push in CLAUDE.md. The new workflow reuses the existing RELEASE_PAT secret rather than introducing a separate token. RELEASE_PAT needs `repo` + `workflow` scopes (classic PAT): `repo` for protected-branch push and PR-head push via maintainer-edit; `workflow` so rebased PRs that include .github/workflows/ changes can be force-pushed without GitHub's "refusing to allow a Personal Access Token to create or update workflow" rejection.
Switch the auto-rebase-PRs workflow from the hand-rolled bash loop to
peter-evans/rebase@v3. The original choice mixed up Option 1 (off-the-
shelf action) and Option 2 (custom workflow with conflict labeling and
fork-permission reporting) from the design discussion — Option 1 was
the intent. Drops:
- ~150 lines of custom shell that iterated PRs, ran rebase in a
worktree per PR, force-pushed with --force-with-lease, applied
`needs-rebase` labels, posted PR comments on conflict.
- The `no-auto-rebase` label exclusion is preserved via the action's
`exclude-labels` input.
- Drafts are still excluded (`exclude-drafts: true`).
- Single-PR mode is preserved by resolving `pr_number` to the
`<owner>:<branch>` head spec the action accepts.
Behavior trade-offs (intentional with the simpler path):
- Conflict-skipped PRs no longer get an auto-applied `needs-rebase`
label or an explanatory comment — the action just skips them. The
workflow summary documents this bucket so reviewers know what's
happening.
- Fork PRs without "Allow edits by maintainers" are silently skipped
(same as before, just not enumerated in the summary).
If conflict-handling visibility becomes important, layer it on as a
follow-up step that compares `rebased-count` vs open-PR count, or
re-introduce the custom loop. CLAUDE.md updated to reflect the new
(simpler) behavior.
Token remains RELEASE_PAT for the same scope reasons — GITHUB_TOKEN
can't push to fork PR head branches and can't force-push commits that
touch .github/workflows/ without the `workflow` scope.
b39a044 to
9c01a4b
Compare
Two valid findings from CodeRabbit on PR #21: 1. auto-rebase-prs.yaml: validate manual `pr_number` is OPEN and targets `dev` before rebasing. Without the guard, a wrong number could rebase and force-push a PR that targets a hotfix-staging branch (or any other base), silently corrupting it. The validation now exits cleanly with a `::warning::` if state != OPEN or baseRefName != dev, and an `if:` guard on the rebase step prevents the empty-head value from falling through to the all-PRs default path when validation rejected the input. 2. release-semantic.yaml: rebase-reconcile push lease failure was returning `exit 0`, so the workflow appeared green while dev remained unreconciled. That weakens the lineage invariant and makes follow-up easy to miss (the next RC on dev would compute against the wrong floor — same class of bug that caused the v1.5.2 hotfix to be invisible). Changed to `exit 1` with an `::error::` so the run goes red and forces a human to read the remediation block. Applied the same `exit 1` treatment to the dev-source FF-reconcile path on line 181 (not CodeRabbit-flagged because it's pre-existing code, but the same logical issue and the same invariant break).
Apply the new concise-comments directive to the workflows added in this PR. Removes ~40 lines of block comments that paraphrased the adjacent code or repeated input/step descriptions. Behavior unchanged.
Apply the present-tense-comments directive. Reframe two comments that
named past state ("pr_number was given but validation rejected it",
"v1.5.2-style miss") to describe the present invariant the code
protects. Behavior unchanged.
Empty commit to trigger CodeQL on this PR. The default-setup CodeQL workflow only fires on synchronize events, not reopened, so a push is required to gate the PR on the new code scan. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
release-semantic.yamlff_targetflow so a hotfix-staging promotion rebase-reconcilesdevinstead of skipping. The release commit and its tag now enterdev's ancestry, restoring the lineage invariant.Auto-rebase open PRsworkflow that runs on every push todev(including the rebase-forward push above) and rebases open PRs in place. Drafts andno-auto-rebase-labeled PRs are skipped; forks without "Allow edits by maintainers" are surfaced in the summary; conflicting PRs get aneeds-rebaselabel and an explanatory comment.CLAUDE.md.Background — why this matters
When v1.5.2 was released via the hotfix-staging path, dev never absorbed the release commit. The next RC cut on dev (
v1.6.0-rc.1) computed its version floor and changelog against the prior tag reachable from dev —v1.5.1— notv1.5.2. The hotfix was invisible to semantic-release.The old workflow's "Note dev reconcile skipped (hotfix-staging source)" step claimed patch-id-dedup would handle this at the next dev→main promotion. That assumption is wrong: the
chore(release):commit has no patch-id equivalent on dev, so it never lands there. The lineage invariant breaks and stays broken.How the fix works
git rebaseis used (not merge) to preserve linear history on dev.git rebaseautomatically drops commits whose patch-id matches an upstream commit, so dev's originals of the cherry-picked fixes are dedup'd against the cherries already on main.--force-with-lease-guarded; concurrent dev pushes surface a remediation block instead of being clobbered.Secrets / token requirements
RELEASE_PATis reused for the new workflow (no new secret introduced). Required scopes (classic PAT):repo— push to protected branches and to fork PR heads via maintainer-editworkflow— rebased PRs may include.github/workflows/changes; without this scope GitHub rejects the force-pushRELEASE_PATwas verified in the throwawaytest/verify-release-pat-scopesworkflow run on 2026-05-19:X-OAuth-Scopes: repo, workflow, all read/write probes passed (label create returns HTTP 201, delete returns 204).Test plan
RELEASE_PATincludesworkflowscope (verified — see throwaway run)Auto-rebase open PRsvia workflow_dispatch with nopr_numberagainst a test scenario to confirm summary table rendersrelease-semantic.yamljob summary contains theDev reconciled (rebase-forward)sectionauto-rebase-prs.yamlfires on the rebase-forward push and shows per-PR results🤖 Generated with Claude Code