From 45551ac429d5db5bfb9f8031068bd1060e3618a3 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sun, 21 Jun 2026 04:59:10 -0500 Subject: [PATCH 1/5] ci: fix merge-check to validate PR head against its own base 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/dash#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/workflows/merge-check.yml | 36 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml index 59daf7faf791..05733522a6a4 100644 --- a/.github/workflows/merge-check.yml +++ b/.github/workflows/merge-check.yml @@ -2,12 +2,17 @@ name: Check Merge Fast-Forward Only permissions: pull-requests: write + issues: write on: push: + branches: + - master pull_request_target: - pull_request_review: - types: [submitted] + +concurrency: + group: merge-check-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: check_merge: @@ -24,33 +29,30 @@ jobs: git config user.email "noreply@example.com" - name: Check merge --ff-only + env: + BASE_REF: ${{ github.event.pull_request.base.ref }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | - if [[ "${{ github.ref_name }}" == "master" ]]; then - echo "Already on master, no need to check --ff-only" + 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" + git checkout base_branch + git merge --ff-only pr_head else - git fetch --no-tags origin master:master - if [[ "${{ github.event_name }}" == "pull_request"* ]]; then - git fetch --no-tags origin ${{ github.event.pull_request.base.ref }}:base_branch - git checkout base_branch - git pull --rebase=false origin pull/${{ github.event.pull_request.number }}/head - git checkout master - git merge --ff-only base_branch - else - git checkout master - git merge --ff-only ${{ github.sha }} - fi + echo "Push event on '${{ github.ref_name }}'; no fast-forward check required." fi - name: add labels + if: failure() && github.event_name == 'pull_request_target' uses: actions-ecosystem/action-add-labels@v1 - if: failure() with: labels: | needs rebase - name: comment + if: failure() && github.event_name == 'pull_request_target' uses: mshick/add-pr-comment@v3 - if: failure() with: message: | This pull request has conflicts, please rebase. From 9bd03c008468cbbb8515fbe9e6728e0c90122e1f Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sun, 21 Jun 2026 07:50:13 -0500 Subject: [PATCH 2/5] ci: replace third-party label action with gh CLI in merge-check 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) --- .github/workflows/merge-check.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml index 05733522a6a4..dcae433b8712 100644 --- a/.github/workflows/merge-check.yml +++ b/.github/workflows/merge-check.yml @@ -45,10 +45,12 @@ jobs: - name: add labels if: failure() && github.event_name == 'pull_request_target' - uses: actions-ecosystem/action-add-labels@v1 - with: - labels: | - needs rebase + env: + GITHUB_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + gh pr edit "${PR_NUMBER}" --add-label "needs rebase" - name: comment if: failure() && github.event_name == 'pull_request_target' From 41a3cbdbe43d6cc5d3db08df7c5aa8c19a868bb8 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sun, 21 Jun 2026 09:34:53 -0500 Subject: [PATCH 3/5] ci(merge-check): harden pull_request_target workflow - 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) --- .github/workflows/merge-check.yml | 39 +++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml index dcae433b8712..552fcf5234eb 100644 --- a/.github/workflows/merge-check.yml +++ b/.github/workflows/merge-check.yml @@ -9,6 +9,12 @@ on: branches: - master pull_request_target: + types: + - opened + - synchronize + - reopened + - edited + - ready_for_review concurrency: group: merge-check-${{ github.event.pull_request.number || github.ref }} @@ -23,12 +29,7 @@ jobs: with: fetch-depth: 0 - - name: Set up Git - run: | - git config user.name "GitHub Action" - git config user.email "noreply@example.com" - - - name: Check merge --ff-only + - name: Check fast-forward env: BASE_REF: ${{ github.event.pull_request.base.ref }} PR_NUMBER: ${{ github.event.pull_request.number }} @@ -37,8 +38,11 @@ jobs: 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" - git checkout base_branch - git merge --ff-only pr_head + if ! git merge-base --is-ancestor base_branch pr_head; then + echo "PR head does not contain the tip of '${BASE_REF}'; rebase required." + exit 1 + fi + echo "PR head fast-forwards from '${BASE_REF}'." else echo "Push event on '${{ github.ref_name }}'; no fast-forward check required." fi @@ -54,7 +58,18 @@ jobs: - name: comment if: failure() && github.event_name == 'pull_request_target' - uses: mshick/add-pr-comment@v3 - with: - message: | - This pull request has conflicts, please rebase. + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + MARKER='' + BODY=$(printf '%s\n%s\n' "${MARKER}" "This pull request has conflicts, please rebase.") + EXISTING_ID=$(gh api "repos/${GH_REPO}/issues/${PR_NUMBER}/comments" --paginate \ + --jq "[.[] | select(.body | contains(\"${MARKER}\"))][0].id // empty") + if [[ -n "${EXISTING_ID}" ]]; then + gh api -X PATCH "repos/${GH_REPO}/issues/comments/${EXISTING_ID}" -f body="${BODY}" + else + gh pr comment "${PR_NUMBER}" --body "${BODY}" + fi From cd160fded7962cacf6830261374347f58da9c23a Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sun, 21 Jun 2026 10:07:46 -0500 Subject: [PATCH 4/5] ci(merge-check): gate rebase label on explicit needs_rebase output 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) --- .github/workflows/merge-check.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml index 552fcf5234eb..87b0973183b2 100644 --- a/.github/workflows/merge-check.yml +++ b/.github/workflows/merge-check.yml @@ -30,16 +30,18 @@ jobs: fetch-depth: 0 - 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 + git fetch --no-tags origin "+${BASE_REF}:refs/tmp/base_branch" + git fetch --no-tags origin "+pull/${PR_NUMBER}/head:refs/tmp/pr_head" + if ! git merge-base --is-ancestor refs/tmp/base_branch refs/tmp/pr_head; then echo "PR head does not contain the tip of '${BASE_REF}'; rebase required." + echo "needs_rebase=true" >> "$GITHUB_OUTPUT" exit 1 fi echo "PR head fast-forwards from '${BASE_REF}'." @@ -48,7 +50,7 @@ jobs: fi - name: add labels - if: failure() && github.event_name == 'pull_request_target' + if: always() && steps.ff_check.outputs.needs_rebase == 'true' && github.event_name == 'pull_request_target' env: GITHUB_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} @@ -57,7 +59,7 @@ jobs: gh pr edit "${PR_NUMBER}" --add-label "needs rebase" - name: comment - if: failure() && github.event_name == 'pull_request_target' + if: always() && steps.ff_check.outputs.needs_rebase == 'true' && github.event_name == 'pull_request_target' env: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} From 9ef5dc5ab3a61c41bc72cdba1763753877378954 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sun, 21 Jun 2026 10:30:02 -0500 Subject: [PATCH 5/5] ci(merge-check): qualify base branch fetch ref --- .github/workflows/merge-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml index 87b0973183b2..166859a8b67d 100644 --- a/.github/workflows/merge-check.yml +++ b/.github/workflows/merge-check.yml @@ -37,7 +37,7 @@ jobs: run: | set -euo pipefail if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then - git fetch --no-tags origin "+${BASE_REF}:refs/tmp/base_branch" + git fetch --no-tags origin "+refs/heads/${BASE_REF}:refs/tmp/base_branch" git fetch --no-tags origin "+pull/${PR_NUMBER}/head:refs/tmp/pr_head" if ! git merge-base --is-ancestor refs/tmp/base_branch refs/tmp/pr_head; then echo "PR head does not contain the tip of '${BASE_REF}'; rebase required."