diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..e737dcb8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# Workflow and CI configuration changes require maintainer review, +# since these files run with repo secrets (incl. the renovate bot's +# GitHub App private key). +.github/** @l50 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 3449a1ca..3ee20e8a 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -34,9 +34,14 @@ jobs: pre-commit: name: Update pre-commit hooks and run pre-commit runs-on: ubuntu-latest + outputs: + has-fixes: ${{ steps.capture.outputs.has-fixes }} steps: - name: Checkout git repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.event.pull_request.head.ref || github.ref }} + persist-credentials: false - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 @@ -118,5 +123,74 @@ jobs: install-only: true - name: Run pre-commit + id: precommit run: | pre-commit run --show-diff-on-failure --color=always --all-files + + - name: Capture autofix patch + id: capture + if: ${{ failure() && steps.precommit.outcome == 'failure' }} + run: | + if git diff --quiet HEAD; then + echo "pre-commit failed without modifying tracked files; no autofix possible" + echo "has-fixes=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + git diff --binary HEAD > autofix.patch + echo "has-fixes=true" >> "$GITHUB_OUTPUT" + + - name: Upload autofix patch + if: ${{ steps.capture.outputs.has-fixes == 'true' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: autofix-patch + path: autofix.patch + retention-days: 1 + if-no-files-found: error + + autocommit: + name: Apply pre-commit autofixes to bot PR + needs: pre-commit + if: >- + failure() && + needs.pre-commit.outputs.has-fixes == 'true' && + github.event_name == 'pull_request' && + github.event.pull_request.user.login == 'dreadnode-renovate-bot[bot]' && + !github.event.pull_request.head.repo.fork + runs-on: ubuntu-latest + environment: pre-commit-autofix + permissions: + contents: read + steps: + - name: Generate app token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + id: app-token + with: + app-id: "${{ secrets.BOT_APP_ID }}" + private-key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" + + - name: Checkout PR head + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.event.pull_request.head.ref }} + persist-credentials: false + + - name: Download autofix patch + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: autofix-patch + + - name: Apply patch and push + env: + HEAD_REF: ${{ github.event.pull_request.head.ref }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} + GIT_AUTHOR_NAME: "${{ secrets.BOT_USERNAME }}[bot]" + GIT_AUTHOR_EMAIL: "${{ secrets.BOT_USER_ID }}+${{ secrets.BOT_USERNAME }}[bot]@users.noreply.github.com" + GIT_COMMITTER_NAME: "${{ secrets.BOT_USERNAME }}[bot]" + GIT_COMMITTER_EMAIL: "${{ secrets.BOT_USER_ID }}+${{ secrets.BOT_USERNAME }}[bot]@users.noreply.github.com" + run: | + git apply --index autofix.patch + git commit -m "chore: apply pre-commit autofixes" + AUTH_HEADER="AUTHORIZATION: basic $(printf 'x-access-token:%s' "${APP_TOKEN}" | base64 -w0)" + git -c http.https://github.com/.extraheader="${AUTH_HEADER}" \ + push "https://github.com/${GITHUB_REPOSITORY}.git" "HEAD:${HEAD_REF}"