From 7de5d64cd3bb42308b91c9776096e33cbe853f23 Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Sun, 15 Mar 2026 09:50:59 -0400 Subject: [PATCH] pre-commit: enable pushing of auto-fixes for forks --- .github/workflows/pre-commit-push-fixes.yml | 82 +++++++++++++++++++++ .github/workflows/pre-commit.yml | 56 +++++++------- 2 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/pre-commit-push-fixes.yml diff --git a/.github/workflows/pre-commit-push-fixes.yml b/.github/workflows/pre-commit-push-fixes.yml new file mode 100644 index 000000000000..c5cfe118d929 --- /dev/null +++ b/.github/workflows/pre-commit-push-fixes.yml @@ -0,0 +1,82 @@ +name: Push pre-commit auto-fixes +on: + workflow_run: + workflows: [ "Halide Presubmit Checks" ] + types: [ completed ] + +permissions: + contents: read + actions: read + +jobs: + push-fixes: + name: Push auto-fixes to PR branch + runs-on: ubuntu-slim + if: github.event.workflow_run.conclusion == 'failure' + steps: + - name: Download auto-fix artifacts + id: download + uses: actions/download-artifact@v4 + with: + name: pre-commit-fixes + path: /tmp/pre-commit-fixes + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + continue-on-error: true + + - name: Read PR metadata + if: steps.download.outcome == 'success' + id: pr + run: | + { + echo "number=$(jq -r '.number' /tmp/pre-commit-fixes/pr-metadata.json)" + echo "head-repo=$(jq -r '.head_repo' /tmp/pre-commit-fixes/pr-metadata.json)" + echo "head-ref=$(jq -r '.head_ref' /tmp/pre-commit-fixes/pr-metadata.json)" + echo "maintainer-can-modify=$(jq -r '.maintainer_can_modify' /tmp/pre-commit-fixes/pr-metadata.json)" + } >> "$GITHUB_OUTPUT" + + - name: Abort if maintainer edits not allowed + if: >- + steps.download.outcome == 'success' + && steps.pr.outputs.head-repo != github.repository + && steps.pr.outputs.maintainer-can-modify != 'true' + run: | + echo "::warning::PR #${{ steps.pr.outputs.number }} does not allow maintainer edits. Cannot push auto-fixes to fork." + echo "skip=true" >> "$GITHUB_ENV" + + - uses: actions/create-github-app-token@v2 + if: steps.download.outcome == 'success' && env.skip != 'true' + id: app-token + with: + app-id: ${{ secrets.LLVM_UPDATER_ID }} + private-key: ${{ secrets.LLVM_UPDATER_PRIVATE_KEY }} + + - name: Get GitHub App user ID + if: steps.download.outcome == 'success' && env.skip != 'true' + id: get-user-id + run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - uses: actions/checkout@v4 + if: steps.download.outcome == 'success' && env.skip != 'true' + with: + repository: ${{ steps.pr.outputs.head-repo }} + ref: ${{ steps.pr.outputs.head-ref }} + token: ${{ steps.app-token.outputs.token }} + + - name: Apply and push fixes + if: steps.download.outcome == 'success' && env.skip != 'true' + run: | + git config user.name "${{ steps.app-token.outputs.app-slug }}[bot]" + git config user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" + + if [ "$(git log -1 --format='%ae')" = "$(git config user.email)" ]; then + echo "::error::pre-commit auto-fixes were not idempotent. Please fix manually." + exit 1 + fi + + git apply /tmp/pre-commit-fixes/pre-commit-fixes.patch + git add -A + git commit -m "Apply pre-commit auto-fixes" + git push diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 938607da255f..60f569547fe7 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -17,28 +17,9 @@ jobs: name: Run pre-commit checks runs-on: ubuntu-slim steps: - - uses: actions/create-github-app-token@v2 - id: app-token - with: - app-id: ${{ secrets.LLVM_UPDATER_ID }} - private-key: ${{ secrets.LLVM_UPDATER_PRIVATE_KEY }} - - - name: Get GitHub App user ID - id: get-user-id - run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - - - name: Configure git and environment - run: | - echo "GH_TOKEN=${{ steps.app-token.outputs.token }}" >> "$GITHUB_ENV" - git config --global user.name "${{ steps.app-token.outputs.app-slug }}[bot]" - git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" - - uses: actions/checkout@v4 with: ref: ${{ github.head_ref }} - token: ${{ steps.app-token.outputs.token }} - uses: actions/setup-python@v5 - uses: astral-sh/setup-uv@v5 @@ -47,19 +28,42 @@ jobs: id: pre-commit uses: pre-commit/action@v3.0.1 - - name: Push auto-fixes + - name: Create auto-fix diff + id: diff if: ${{ !cancelled() && steps.pre-commit.outcome == 'failure' }} run: | if git diff --quiet; then - echo "::error::no auto-fixable changes; pre-commit failure requires manual attention." - elif [ "$(git log -1 --format='%ae')" = "$(git config user.email)" ]; then - echo "::error::pre-commit auto-fixes were not idempotent. Please fix manually." + echo "has-fixes=false" >> "$GITHUB_OUTPUT" else - git add -A - git commit -m "Apply pre-commit auto-fixes" - git push + git diff > /tmp/pre-commit-fixes.patch + echo "has-fixes=true" >> "$GITHUB_OUTPUT" fi + - name: Save PR metadata + if: ${{ !cancelled() && steps.diff.outputs.has-fixes == 'true' }} + run: | + jq -n \ + --argjson number "$PR_NUMBER" \ + --arg head_repo "$PR_HEAD_REPO" \ + --arg head_ref "$PR_HEAD_REF" \ + --argjson maintainer_can_modify "$PR_MAINTAINER_CAN_MODIFY" \ + '$ARGS.named' | tee /tmp/pr-metadata.json + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} + PR_MAINTAINER_CAN_MODIFY: ${{ github.event.pull_request.maintainer_can_modify }} + + - name: Upload auto-fix artifacts + if: ${{ !cancelled() && steps.diff.outputs.has-fixes == 'true' }} + uses: actions/upload-artifact@v4 + with: + name: pre-commit-fixes + path: | + /tmp/pre-commit-fixes.patch + /tmp/pr-metadata.json + retention-days: 1 + - name: Annotate codespell errors if: ${{ !cancelled() && steps.pre-commit.outcome == 'failure' }} run: |