From 70bdb88e4c3807dae6ed4786d2cc19ff287841ed Mon Sep 17 00:00:00 2001 From: Meng Meng <227010654+MengMengCode@users.noreply.github.com> Date: Sat, 15 Aug 2026 22:29:45 +0800 Subject: [PATCH] Refactor PR size limit workflow for clarity and efficiency --- .github/workflows/pr-size-limit.yml | 229 +++++++++++++++++++++------- 1 file changed, 178 insertions(+), 51 deletions(-) diff --git a/.github/workflows/pr-size-limit.yml b/.github/workflows/pr-size-limit.yml index 063c5b8..6d54641 100644 --- a/.github/workflows/pr-size-limit.yml +++ b/.github/workflows/pr-size-limit.yml @@ -2,7 +2,13 @@ name: Pull request size limit on: pull_request_target: - types: [opened, synchronize, reopened, ready_for_review] + branches: + - master + types: + - opened + - synchronize + - reopened + - ready_for_review permissions: contents: read @@ -15,6 +21,7 @@ concurrency: jobs: enforce-size-limit: + # 保持这个名字不变,这样你 Ruleset 里的 Required Check 不需要修改 name: Enforce 5,000-line limit runs-on: ubuntu-latest timeout-minutes: 5 @@ -32,7 +39,7 @@ jobs: fetch-depth: 0 persist-credentials: false - - name: Calculate real pull request size + - name: Check conflicts and pull request size shell: bash run: | set -euo pipefail @@ -40,40 +47,164 @@ jobs: echo "Checking PR #${PR_NUMBER}" echo "Base branch: ${BASE_REF}" - # Fetch the latest target branch and the PR head. - # The PR code is fetched only for diff inspection; it is never executed. + ############################################################ + # Helper: comment on and close rejected PR + ############################################################ + + reject_pr() { + local message="$1" + + echo "::error::${message}" + + COMMENT_PAYLOAD="$( + jq -nc \ + --arg body "${message}" \ + '{body: $body}' + )" + + echo "Posting rejection comment..." + + curl \ + --fail-with-body \ + --silent \ + --show-error \ + --request POST \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer ${GH_TOKEN}" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \ + --data "${COMMENT_PAYLOAD}" \ + >/dev/null + + echo "Closing PR #${PR_NUMBER}..." + + curl \ + --fail-with-body \ + --silent \ + --show-error \ + --request PATCH \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer ${GH_TOKEN}" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \ + --data '{"state":"closed"}' \ + >/dev/null + + exit 1 + } + + ############################################################ + # Fetch target branch and PR HEAD + ############################################################ + + echo "Fetching base branch and PR head..." + git fetch --no-tags --force origin \ - "+refs/heads/${BASE_REF}:refs/remotes/origin/base-size-check" \ + "+refs/heads/${BASE_REF}:refs/remotes/origin/base-pr-check" \ "+refs/pull/${PR_NUMBER}/head:refs/remotes/origin/pr-${PR_NUMBER}" - BASE_COMMIT="$(git rev-parse refs/remotes/origin/base-size-check)" - PR_COMMIT="$(git rev-parse refs/remotes/origin/pr-${PR_NUMBER})" + BASE_COMMIT="$( + git rev-parse refs/remotes/origin/base-pr-check + )" + + PR_COMMIT="$( + git rev-parse refs/remotes/origin/pr-${PR_NUMBER} + )" echo "Base commit: ${BASE_COMMIT}" echo "PR commit: ${PR_COMMIT}" - if ! MERGE_BASE="$(git merge-base "${BASE_COMMIT}" "${PR_COMMIT}")"; then - echo "::error::Unable to determine merge base." + ############################################################ + # STEP 1: Reject PRs with merge conflicts + ############################################################ + + echo + echo "Checking for merge conflicts..." + + set +e + + git merge-tree \ + --write-tree \ + --quiet \ + "${BASE_COMMIT}" \ + "${PR_COMMIT}" + + MERGE_STATUS=$? + + set -e + + if [[ "${MERGE_STATUS}" -eq 1 ]]; then + + { + echo "### Pull request policy" + echo + echo "- Merge conflicts: ❌ Detected" + echo "- Result: Rejected" + } >> "${GITHUB_STEP_SUMMARY}" + + reject_pr "This pull request has merge conflicts with the current master branch and cannot be accepted. Please update your branch with the latest master, resolve all merge conflicts locally, and submit a conflict-free pull request." + + elif [[ "${MERGE_STATUS}" -ne 0 ]]; then + + echo "::error::Unable to determine whether the pull request can be merged." + echo "git merge-tree returned status ${MERGE_STATUS}." + + { + echo "### Pull request policy" + echo + echo "- Merge conflict check: ⚠️ Error" + echo "- Result: Check failed" + } >> "${GITHUB_STEP_SUMMARY}" + exit 1 + fi - echo "Merge base: ${MERGE_BASE}" + echo "No merge conflicts detected." + + ############################################################ + # STEP 2: Determine merge base + ############################################################ + + if ! MERGE_BASE="$( + git merge-base "${BASE_COMMIT}" "${PR_COMMIT}" + )"; then + + echo "::error::Unable to determine merge base." + + { + echo "### Pull request policy" + echo + echo "- Merge conflicts: ✅ None" + echo "- Diff calculation: ⚠️ Failed" + } >> "${GITHUB_STEP_SUMMARY}" + + exit 1 + + fi + + echo "Merge base: ${MERGE_BASE}" + + ############################################################ + # STEP 3: Calculate actual PR changed lines + ############################################################ NUMSTAT_FILE="$(mktemp)" - # Do not allow external diff/textconv helpers. git diff \ --no-ext-diff \ --no-textconv \ --numstat \ "${MERGE_BASE}" \ - "${PR_COMMIT}" > "${NUMSTAT_FILE}" + "${PR_COMMIT}" \ + > "${NUMSTAT_FILE}" ADDITIONS="$( awk ' $1 ~ /^[0-9]+$/ { total += $1 } + END { print total + 0 } @@ -85,6 +216,7 @@ jobs: $2 ~ /^[0-9]+$/ { total += $2 } + END { print total + 0 } @@ -92,64 +224,59 @@ jobs: )" CHANGED_FILES="$( - awk 'END { print NR + 0 }' "${NUMSTAT_FILE}" + awk ' + END { + print NR + 0 + } + ' "${NUMSTAT_FILE}" )" CHANGED_LINES=$((ADDITIONS + DELETIONS)) + ############################################################ + # Action summary + ############################################################ + { - echo "### Pull request size" + echo "### Pull request policy" echo + echo "- Merge conflicts: ✅ None" echo "- Changed files: ${CHANGED_FILES}" echo "- Additions: ${ADDITIONS}" echo "- Deletions: ${DELETIONS}" echo "- Total changed lines: ${CHANGED_LINES}" - echo "- Limit: ${MAX_CHANGED_LINES}" + echo "- Maximum allowed: ${MAX_CHANGED_LINES}" } >> "${GITHUB_STEP_SUMMARY}" + echo echo "Changed files: ${CHANGED_FILES}" echo "Additions: ${ADDITIONS}" echo "Deletions: ${DELETIONS}" echo "Total changed lines: ${CHANGED_LINES}" echo "Limit: ${MAX_CHANGED_LINES}" - if (( CHANGED_LINES <= MAX_CHANGED_LINES )); then - echo "Pull request is within the ${MAX_CHANGED_LINES}-line limit." - exit 0 + ############################################################ + # STEP 4: Reject oversized PRs + ############################################################ + + if (( CHANGED_LINES > MAX_CHANGED_LINES )); then + + reject_pr "This pull request changes ${CHANGED_LINES} lines (${ADDITIONS} additions + ${DELETIONS} deletions) across ${CHANGED_FILES} files, exceeding the repository limit of ${MAX_CHANGED_LINES} changed lines. It has been closed automatically. Please split the changes into smaller pull requests." + fi - echo "::error::Pull request changes ${CHANGED_LINES} lines; maximum allowed is ${MAX_CHANGED_LINES}." + ############################################################ + # PASS + ############################################################ - MESSAGE="This pull request changes ${CHANGED_LINES} lines (${ADDITIONS} additions + ${DELETIONS} deletions) across ${CHANGED_FILES} files, exceeding the repository limit of ${MAX_CHANGED_LINES} changed lines. It has been closed automatically. Please split the changes into smaller pull requests." + echo + echo "Pull request passed all policy checks." + echo "No merge conflicts." + echo "Changed lines: ${CHANGED_LINES}/${MAX_CHANGED_LINES}." - COMMENT_PAYLOAD="$( - jq -nc \ - --arg body "${MESSAGE}" \ - '{body: $body}' - )" - - # Leave a comment explaining why the PR was rejected. - curl \ - --fail-with-body \ - --silent \ - --show-error \ - --request POST \ - --header "Accept: application/vnd.github+json" \ - --header "Authorization: Bearer ${GH_TOKEN}" \ - --header "X-GitHub-Api-Version: 2022-11-28" \ - "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \ - --data "${COMMENT_PAYLOAD}" >/dev/null - - # Close the oversized PR. - curl \ - --fail-with-body \ - --silent \ - --show-error \ - --request PATCH \ - --header "Accept: application/vnd.github+json" \ - --header "Authorization: Bearer ${GH_TOKEN}" \ - --header "X-GitHub-Api-Version: 2022-11-28" \ - "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \ - --data '{"state":"closed"}' >/dev/null - - exit 1 + { + echo + echo "### Result" + echo + echo "✅ Pull request passed." + } >> "${GITHUB_STEP_SUMMARY}"