Skip to content

ci: fix merge-check base branch handling#7374

Closed
thepastaclaw wants to merge 5 commits into
dashpay:developfrom
thepastaclaw:ci/fix-merge-check-base
Closed

ci: fix merge-check base branch handling#7374
thepastaclaw wants to merge 5 commits into
dashpay:developfrom
thepastaclaw:ci/fix-merge-check-base

Conversation

@thepastaclaw

Copy link
Copy Markdown

Fix merge-check workflow base handling

Issue being fixed or feature implemented

Fixes the Check Merge Fast-Forward Only workflow so PRs are validated
against their actual base branch instead of always checking whether the result
can fast-forward master.

This was causing false needs rebase failures for PRs targeting develop:
the workflow could successfully fast-forward the PR head onto develop, then
fail because it tried to fast-forward master to that develop result.

What was done

  • Removed the pull_request_review trigger from merge-check.yml; review
    submissions do not change whether a PR branch is fast-forwardable and were
    causing duplicate same-name required checks.
  • Added workflow concurrency so stale runs for the same PR/ref are cancelled.
  • Changed PR validation to fetch the PR's actual base branch and PR head into
    local refs, then run git merge --ff-only pr_head from base_branch.
  • Scoped push runs to master and made the push path a no-op, since the branch
    is already the protected target being checked.
  • Added issues: write permission because the failure path applies a PR label
    or comment through issue APIs.
  • Limited the label/comment failure steps to pull_request_target events.

How Has This Been Tested

  • Parsed .github/workflows/merge-check.yml with Python YAML loading.
  • Extracted all embedded run: | shell blocks from the workflow and validated
    them with bash -n.
  • Ran a dry-run of the new PR fetch/merge command sequence for
    BASE_REF=develop and PR_NUMBER=7339 to verify it uses
    develop:base_branch and pull/7339/head:pr_head, then merges pr_head into
    base_branch.
  • Ran the pre-PR code review gate against
    upstream/develop...ci/fix-merge-check-base; result: ship.

Breaking Changes

None.

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone

The merge-check workflow incorrectly checked whether master could
fast-forward to a PR's merged head, which always failed for PRs
targeting develop and falsely applied the "needs rebase" label
(e.g. dashpay#7339).

Replace the master-centric logic with a direct fast-forward check
of the PR head against `github.event.pull_request.base.ref`, so a
PR is only flagged when it cannot fast-forward into its own base.

Additional cleanup:
- Drop the `pull_request_review` trigger so review submissions do
  not rerun the merge-base check and produce duplicate same-name
  check runs.
- Restrict the `push` trigger to `master` so feature/develop branch
  pushes do not trigger spurious checks.
- Add a concurrency group keyed by PR number / ref to cancel stale
  runs.
- Add `issues: write` so the labeler step can actually apply the
  `needs rebase` label, and gate label/comment steps on
  `pull_request_target` to avoid running them on push failures.
- Pass `base.ref` / PR number via `env:` and quote shell expansions
  to keep the embedded script injection-safe.
@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw

Copy link
Copy Markdown
Author

The failing Check Merge Fast-Forward Only / check_merge result on this PR is the workflow bug this PR fixes, not an actual rebase conflict.

Because this workflow runs on pull_request_target, GitHub evaluates the workflow file from the current develop base branch, not from this PR branch. The failed run therefore used the old logic:

git fetch --no-tags origin master:master
git fetch --no-tags origin develop:base_branch
git checkout base_branch
git pull --rebase=false origin pull/7374/head
git checkout master
git merge --ff-only base_branch
fatal: Not possible to fast-forward, aborting.

This PR changes that PR path to validate pr_head directly against the PR's actual base branch (base_branch) and removes the redundant pull_request_review trigger that created duplicate same-name jobs.

Since the required check is produced by the broken base-branch workflow, this fix may need maintainer bypass or a temporary branch-protection exception for the check_merge status on this PR.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1a210d28-0366-4c49-8489-bf0304d87db5

📥 Commits

Reviewing files that changed from the base of the PR and between cd160fd and 9ef5dc5.

📒 Files selected for processing (1)
  • .github/workflows/merge-check.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/merge-check.yml

Walkthrough

The merge-check.yml workflow is updated in three areas. The trigger configuration changes from pull_request_review to push (restricted to master) and pull_request_target with a fixed list of activity types, and issues: write permission is added alongside a new concurrency group to cancel in-progress runs per PR number or ref. The fast-forward merge check step is rewritten to set BASE_REF and PR_NUMBER, then conditionally fetch the base branch and PR head into named local refs and verify via git merge-base --is-ancestor only when the event is pull_request_target; other events skip the check and log a message. The "add labels" and "comment" failure handlers are similarly restricted to pull_request_target events and reimplemented using gh CLI commands (gh pr edit --add-label and gh pr comment with comment-lookup logic) instead of external actions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly relates to the main change—fixing base branch handling in the merge-check workflow.
Description check ✅ Passed The description thoroughly explains the issue, changes, testing, and context directly related to the workflow fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/merge-check.yml (1)

46-51: ⚡ Quick win

Consider replacing third-party action with built-in gh CLI or pinning to SHA.

actions-ecosystem/action-add-labels@v1 uses deprecated Node.js 16 and has open migration issues. The March 2025 tj-actions/changed-files compromise demonstrated how tag-based pins can be exploited to leak secrets.

Two options:

  1. Replace with gh CLI (recommended): The runner includes gh natively, eliminating third-party dependency risk.
  2. Pin to SHA: If keeping the action, pin to the commit hash for the v1.1.0 tag.
♻️ Option 1: Replace with gh CLI (recommended)
       - name: add labels
         if: failure() && github.event_name == 'pull_request_target'
-        uses: actions-ecosystem/action-add-labels@v1
-        with:
-          labels: |
-            needs rebase
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: gh pr edit ${{ github.event.pull_request.number }} --add-label "needs rebase"
♻️ Option 2: Pin action to SHA
       - name: add labels
         if: failure() && github.event_name == 'pull_request_target'
-        uses: actions-ecosystem/action-add-labels@v1
+        uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac219571df80a3f708ed3e0 # v1.1.0
         with:
           labels: |
             needs rebase

Note: Verify the SHA by running:

git ls-remote https://github.com/actions-ecosystem/action-add-labels refs/tags/v1.1.0
🤖 Prompt for 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.

In @.github/workflows/merge-check.yml around lines 46 - 51, The "add labels"
step uses the deprecated actions-ecosystem/action-add-labels@v1 action which
runs on unsupported Node.js 16 and poses security risks from tag-based pinning.
Replace this action with the native gh CLI command instead: use run with gh pr
edit to add the labels directly using the built-in GitHub CLI that's already
available in the runner environment. This eliminates the third-party dependency
while achieving the same result of adding the "needs rebase" label to the pull
request.

Source: Linters/SAST tools

🤖 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.

Nitpick comments:
In @.github/workflows/merge-check.yml:
- Around line 46-51: The "add labels" step uses the deprecated
actions-ecosystem/action-add-labels@v1 action which runs on unsupported Node.js
16 and poses security risks from tag-based pinning. Replace this action with the
native gh CLI command instead: use run with gh pr edit to add the labels
directly using the built-in GitHub CLI that's already available in the runner
environment. This eliminates the third-party dependency while achieving the same
result of adding the "needs rebase" label to the pull request.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 41de5d72-d159-433a-aa15-dbcee821feb8

📥 Commits

Reviewing files that changed from the base of the PR and between a59ad9e and 45551ac.

📒 Files selected for processing (1)
  • .github/workflows/merge-check.yml

Use the built-in GitHub CLI (gh pr edit --add-label) to apply the
"needs rebase" label on PR merge-check failures, removing the
actions-ecosystem/action-add-labels@v1 third-party dependency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw

thepastaclaw commented Jun 21, 2026

Copy link
Copy Markdown
Author

✅ Review complete (commit 9ef5dc5)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The workflow correctly fixes the master-centric fast-forward check and tightens trigger/permission handling for pull_request_target. Two convergent observations stand out: the trigger does not include edited, so retargeting a PR leaves a stale required-check result against the old base, and the failure path still invokes a mutable third-party comment action from a privileged context.

🟡 3 suggestion(s) | 💬 1 nitpick(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `.github/workflows/merge-check.yml`:
- [SUGGESTION] .github/workflows/merge-check.yml:11: Retargeted PRs are not re-checked against the new base
  The fast-forward check now depends on `github.event.pull_request.base.ref`, but `pull_request_target` defaults to the `opened`, `synchronize`, and `reopened` activities only. Changing a PR's base branch is an `edited` activity and does not change the head SHA, so a PR can pass this required check while targeting `develop`, then be retargeted to `master`, and the previous successful `check_merge` result remains attached to the unchanged head SHA without re-running `git merge --ff-only` from `master`. That defeats the invariant this PR is introducing — that each required-check result corresponds to the PR's current base branch. Add `edited` to the activity types (and ideally re-run on `ready_for_review` for draft promotion) so a base change forces re-evaluation.
- [SUGGESTION] .github/workflows/merge-check.yml:55-60: Replace or pin the third-party comment action in the privileged failure path
  This job runs under `pull_request_target` with `pull-requests: write` and `issues: write`, and on failure it invokes `mshick/add-pr-comment@v3` via a mutable major-version tag. The earlier commit in this PR already removed the other third-party action (`actions-ecosystem/action-add-labels`) in favor of `gh`. Doing the same for the comment step (e.g. `gh pr comment "$PR_NUMBER" --body ...`) — or, failing that, pinning to a full commit SHA — closes the remaining supply-chain surface in this privileged context.
- [SUGGESTION] .github/workflows/merge-check.yml:38-41: Avoid checking out the untrusted PR head into the privileged workspace
  Under `pull_request_target` the job runs with the repo's elevated token. The current step does `git checkout base_branch` and then `git merge --ff-only pr_head`, which on a successful fast-forward materializes the PR's untrusted tree into the workspace. No later step uses that tree today, but this makes the workflow brittle — any future step appended after the merge would silently execute against attacker-controlled files. Use an ancestor-based check that never mutates the worktree, e.g. `git merge-base --is-ancestor base_branch pr_head` (or `git merge-tree`-based dry-run), so the privileged checkout stays at the trusted base.

Comment thread .github/workflows/merge-check.yml
Comment thread .github/workflows/merge-check.yml Outdated
Comment thread .github/workflows/merge-check.yml Outdated
Comment thread .github/workflows/merge-check.yml Outdated
- Add edited (base retarget) and ready_for_review activity types so the
  required check reruns on base changes and draft promotion.
- Replace third-party mshick/add-pr-comment@v3 with built-in gh CLI,
  preserving the marker-based update-or-create comment semantics and
  body text. Uses GH_TOKEN: github.token.
- Avoid materialising the untrusted PR head in the privileged workspace:
  fetch base_branch and pr_head, then verify with
  git merge-base --is-ancestor base_branch pr_head instead of
  checking out base and running git merge --ff-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
.github/workflows/merge-check.yml (1)

51-60: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate failure handlers on an explicit rebase result, not generic step failure.

At Line 51 and Line 60, failure() catches all upstream failures (for example transient fetch/auth/network issues from Line 39-40), which can incorrectly apply needs rebase and post a misleading comment.

Suggested change
-      - name: Check fast-forward
+      - name: Check fast-forward
+        id: ff_check
         env:
           BASE_REF: ${{ github.event.pull_request.base.ref }}
           PR_NUMBER: ${{ github.event.pull_request.number }}
         run: |
           set -euo pipefail
           if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then
             git fetch --no-tags origin "${BASE_REF}:base_branch"
             git fetch --no-tags origin "pull/${PR_NUMBER}/head:pr_head"
             if ! git merge-base --is-ancestor base_branch pr_head; then
+              echo "needs_rebase=true" >> "$GITHUB_OUTPUT"
               echo "PR head does not contain the tip of '${BASE_REF}'; rebase required."
               exit 1
             fi
+            echo "needs_rebase=false" >> "$GITHUB_OUTPUT"
             echo "PR head fast-forwards from '${BASE_REF}'."
           else
             echo "Push event on '${{ github.ref_name }}'; no fast-forward check required."
           fi

-      - name: add labels
-        if: failure() && github.event_name == 'pull_request_target'
+      - name: add labels
+        if: failure() && github.event_name == 'pull_request_target' && steps.ff_check.outputs.needs_rebase == 'true'
...
-      - name: comment
-        if: failure() && github.event_name == 'pull_request_target'
+      - name: comment
+        if: failure() && github.event_name == 'pull_request_target' && steps.ff_check.outputs.needs_rebase == 'true'
🤖 Prompt for 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.

In @.github/workflows/merge-check.yml around lines 51 - 60, The failure()
conditions at lines 51 and 60 are too broad and catch all upstream failures
including transient network/auth issues, causing incorrect application of the
"needs rebase" label and misleading comments. Identify the specific step that
checks for rebase requirements (the step around lines 39-40), assign it an
explicit step ID, and then replace the generic failure() condition in both the
label-adding step and the comment step with a reference to that specific step's
result (using failure() on that step's ID only) instead of the global failure()
condition.
🤖 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.

Duplicate comments:
In @.github/workflows/merge-check.yml:
- Around line 51-60: The failure() conditions at lines 51 and 60 are too broad
and catch all upstream failures including transient network/auth issues, causing
incorrect application of the "needs rebase" label and misleading comments.
Identify the specific step that checks for rebase requirements (the step around
lines 39-40), assign it an explicit step ID, and then replace the generic
failure() condition in both the label-adding step and the comment step with a
reference to that specific step's result (using failure() on that step's ID
only) instead of the global failure() condition.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4f33e2d5-4257-47ff-aab9-f5ece8b88bdc

📥 Commits

Reviewing files that changed from the base of the PR and between 9bd03c0 and 41a3cbd.

📒 Files selected for processing (1)
  • .github/workflows/merge-check.yml

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The PR successfully resolves three of four prior findings: it adds edited/ready_for_review triggers, replaces the third-party mshick/add-pr-comment action with gh api, and switches to a worktree-free git merge-base --is-ancestor check that no longer materializes attacker-controlled files in the privileged workspace. Two suggestions remain: the failure handlers still treat any git fetch error as needs rebase, and the unqualified ${BASE_REF}:base_branch refspec could in principle resolve to a same-named tag.

💬 1 nitpick(s)

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Comment thread .github/workflows/merge-check.yml Outdated
Narrow the failure handlers so transient fetch/auth/network failures no
longer mislabel PRs as needing rebase. The fast-forward step now sets a
needs_rebase=true output only when git merge-base --is-ancestor proves
the PR head does not contain the base tip, and the label/comment steps
gate on that output instead of the generic failure() condition.

Also fetch into explicit refs/tmp/... refs so the local ref names cannot
collide with tags or branches in the workspace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The latest commit successfully resolves the prior failure-gating concern: handlers now trigger only on an explicit needs_rebase=true output, so transient fetch errors no longer mislabel PRs. One convergent suggestion remains — the unqualified source side of the base-ref fetch refspec can in principle resolve to a same-named tag instead of the branch.

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The tip commit completes the hardening series by qualifying the base-ref fetch source as refs/heads/${BASE_REF}, addressing the prior tag-shadow suggestion. Both general agents confirm no remaining in-scope defects in the workflow. The only flagged item is a commit-history squash preference, which falls outside the verifier's scope (consistent with the prior-run precedent dropping the same class of finding).

Note: posted as a COMMENT review because GitHub does not allow approving my own PR.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw thepastaclaw marked this pull request as ready for review June 21, 2026 16:43
@PastaPastaPasta

Copy link
Copy Markdown
Member

This is intentional behavior. The goal is that CI breaks if develop / pr doesn't ff-only merge to master.

The problem is we haven't merged master back into develop. This is a proper fail imo.

@thepastaclaw

Copy link
Copy Markdown
Author

Got it — I had misread the invariant. If the intended behavior is that develop/PR must fast-forward to master and the current failure is correctly exposing develop not having master merged back in, this PR is the wrong fix. Closing.

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.

2 participants