diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml index 59daf7faf791..166859a8b67d 100644 --- a/.github/workflows/merge-check.yml +++ b/.github/workflows/merge-check.yml @@ -2,12 +2,23 @@ name: Check Merge Fast-Forward Only permissions: pull-requests: write + issues: write on: push: + branches: + - master pull_request_target: - pull_request_review: - types: [submitted] + types: + - opened + - synchronize + - reopened + - edited + - ready_for_review + +concurrency: + group: merge-check-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: check_merge: @@ -18,39 +29,49 @@ 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 + id: ff_check + 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" - 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 }} + set -euo pipefail + if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then + 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." + echo "needs_rebase=true" >> "$GITHUB_OUTPUT" + 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 - name: add labels - uses: actions-ecosystem/action-add-labels@v1 - if: failure() - with: - labels: | - needs rebase + if: always() && steps.ff_check.outputs.needs_rebase == 'true' && github.event_name == 'pull_request_target' + 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 - uses: mshick/add-pr-comment@v3 - if: failure() - with: - message: | - This pull request has conflicts, please rebase. + if: always() && steps.ff_check.outputs.needs_rebase == 'true' && github.event_name == 'pull_request_target' + 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