From 6e259214f73382bb1b55281ca55292764a860e8f Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Tue, 21 Apr 2026 14:37:41 -0700 Subject: [PATCH 1/3] Add weekly PyTorch pin bump workflow with Claude CI fix loop Adds two new workflows: - weekly-pytorch-pin-bump.yml: Weekly cron that bumps the PyTorch nightly pin, syncs c10 headers, and creates a PR - pin-bump-ci-handler.yml: Watches trunk CI on pin bump PRs and posts @claude comments to fix failures (up to 3 attempts), then escalates to a human Authored with Claude. --- .github/workflows/pin-bump-ci-handler.yml | 116 ++++++++++++++++++ .github/workflows/weekly-pytorch-pin-bump.yml | 90 ++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 .github/workflows/pin-bump-ci-handler.yml create mode 100644 .github/workflows/weekly-pytorch-pin-bump.yml diff --git a/.github/workflows/pin-bump-ci-handler.yml b/.github/workflows/pin-bump-ci-handler.yml new file mode 100644 index 00000000000..c4acdee6846 --- /dev/null +++ b/.github/workflows/pin-bump-ci-handler.yml @@ -0,0 +1,116 @@ +name: Pin Bump CI Handler + +on: + workflow_run: + workflows: ["trunk"] + types: [completed] + +jobs: + handle-ci-result: + if: github.repository_owner == 'pytorch' + runs-on: ubuntu-latest + environment: update-commit-hash + permissions: + pull-requests: write + issues: write + steps: + - uses: actions/github-script@v7 + with: + github-token: ${{ secrets.UPDATEBOT_TOKEN }} + script: | + const { owner, repo } = context.repo; + const workflowRun = context.payload.workflow_run; + const conclusion = workflowRun.conclusion; + const runUrl = workflowRun.html_url; + + const prs = workflowRun.pull_requests; + if (!prs || prs.length === 0) { + console.log('No PRs associated with this workflow run. Skipping.'); + return; + } + + const prNumber = prs[0].number; + const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }); + + const isPinBump = pr.data.labels.some(l => l.name === 'ci/pytorch-pin-bump'); + if (!isPinBump) { + console.log(`PR #${prNumber} is not a pin bump PR. Skipping.`); + return; + } + + if (pr.data.user.login !== 'pytorchbot') { + console.log(`PR #${prNumber} was created by ${pr.data.user.login}, not pytorchbot. Skipping.`); + return; + } + + console.log(`Pin bump PR #${prNumber}, trunk concluded: ${conclusion}`); + + const comments = await github.rest.issues.listComments({ + owner, repo, issue_number: prNumber, per_page: 100 + }); + const fixAttempts = comments.data.filter( + c => c.body && c.body.includes('[ci-fix-attempt') + ).length; + + if (conclusion === 'success') { + const note = fixAttempts > 0 + ? `Claude fixed CI failures in ${fixAttempts} attempt(s).` + : 'CI passed on the first try.'; + + await github.rest.issues.createComment({ + owner, repo, issue_number: prNumber, + body: `## CI Passed\n\nAll trunk CI checks have passed on this pin bump PR. ${note}\n\n**This PR is ready for human review and merge.**\n\ncc @jakeszwe` + }); + return; + } + + if (conclusion !== 'failure') { + console.log(`Trunk concluded with "${conclusion}" (not failure). Skipping.`); + return; + } + + if (fixAttempts >= 3) { + await github.rest.issues.createComment({ + owner, repo, issue_number: prNumber, + body: [ + '## Automated Fix Attempts Exhausted', + '', + `CI is still failing after ${fixAttempts} automated fix attempt(s).`, + `Failed trunk run: ${runUrl}`, + '', + 'This pin bump likely requires human intervention. Common causes:', + '- BC-breaking API changes in PyTorch that need design discussion', + '- New dependencies or build system changes', + '- Test infrastructure issues unrelated to the pin bump', + '', + 'cc @jakeszwe' + ].join('\n') + }); + return; + } + + const attemptNum = fixAttempts + 1; + await github.rest.issues.createComment({ + owner, repo, issue_number: prNumber, + body: [ + `@claude [ci-fix-attempt ${attemptNum}/3]`, + '', + `The \`trunk\` CI workflow has failed on this automated PyTorch pin bump PR.`, + `Failed run: ${runUrl}`, + '', + 'Please:', + '1. Read the Dr. CI comment on this PR for a summary of which jobs failed and whether they are flaky. Ignore failures marked as FLAKY.', + '2. Use your CI tools to download the failure logs for the non-flaky failing jobs', + '3. Identify the root cause of the failure', + '4. If this is a build or test failure caused by PyTorch API changes, fix the ExecuTorch code to be compatible with the new PyTorch version', + '5. If this is a c10 header sync issue, the headers have already been synced by the pin bump script — the issue is likely in ExecuTorch code that uses those headers', + '6. Run `lintrunner -a` on any files you change', + '7. Push your fix as a new commit to this PR branch', + '', + 'Important constraints:', + '- Do NOT modify torch_pin.py or .ci/docker/ci_commit_pins/pytorch.txt — the pin itself is correct', + '- Do NOT modify files under runtime/core/portable_type/c10/ unless the sync introduced a new API that ExecuTorch code needs to adapt to', + '- Focus on fixing ExecuTorch code to be compatible with the new PyTorch APIs', + '- If this is a major BC-breaking change that requires architectural discussion, say so clearly and stop — do not attempt a fix' + ].join('\n') + }); diff --git a/.github/workflows/weekly-pytorch-pin-bump.yml b/.github/workflows/weekly-pytorch-pin-bump.yml new file mode 100644 index 00000000000..d428e15282e --- /dev/null +++ b/.github/workflows/weekly-pytorch-pin-bump.yml @@ -0,0 +1,90 @@ +name: Weekly PyTorch Pin Bump + +on: + schedule: + - cron: '0 9 * * 1' # Monday 9:00 UTC + workflow_dispatch: + +jobs: + create-pin-bump-pr: + if: github.repository_owner == 'pytorch' + runs-on: ubuntu-latest + environment: update-commit-hash + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.UPDATEBOT_TOKEN }} + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Determine nightly version + id: nightly + run: | + NIGHTLY_DATE=$(date -u -d 'yesterday' '+%Y%m%d') + NIGHTLY_VERSION="dev${NIGHTLY_DATE}" + echo "version=${NIGHTLY_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Read current TORCH_VERSION + id: torch + run: | + TORCH_VERSION=$(python -c "exec(open('torch_pin.py').read()); print(TORCH_VERSION)") + echo "version=${TORCH_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Update torch_pin.py with new NIGHTLY_VERSION + run: | + printf 'TORCH_VERSION = "%s"\nNIGHTLY_VERSION = "%s"\n' \ + "${{ steps.torch.outputs.version }}" \ + "${{ steps.nightly.outputs.version }}" > torch_pin.py + + - name: Run pin bump script + run: python .github/scripts/update_pytorch_pin.py + + - name: Create branch and PR + env: + GH_TOKEN: ${{ secrets.UPDATEBOT_TOKEN }} + run: | + BRANCH="automated/pytorch-pin-bump-${{ steps.nightly.outputs.version }}" + + git config user.name "pytorchbot" + git config user.email "pytorchbot@users.noreply.github.com" + git checkout -b "${BRANCH}" + git add torch_pin.py + git add .ci/docker/ci_commit_pins/pytorch.txt + git add runtime/core/portable_type/c10/ + + if git diff --cached --quiet; then + echo "No changes to commit. Pin is already up to date." + exit 0 + fi + + git commit -m "Bump PyTorch pin to nightly ${{ steps.nightly.outputs.version }}" + git push -u origin "${BRANCH}" + + EXISTING=$(gh pr list --label "ci/pytorch-pin-bump" --state open --json number --jq '.[0].number') + if [ -n "${EXISTING}" ]; then + echo "Closing existing pin bump PR #${EXISTING} in favor of new one" + gh pr close "${EXISTING}" --comment "Superseded by newer pin bump." + fi + + NIGHTLY="${{ steps.nightly.outputs.version }}" + PR_BODY="## Summary + + Automated weekly PyTorch pin bump. + + - Updates \`NIGHTLY_VERSION\` in \`torch_pin.py\` to \`${NIGHTLY}\` + - Updates \`.ci/docker/ci_commit_pins/pytorch.txt\` to the corresponding nightly commit hash + - Syncs c10 headers from PyTorch into \`runtime/core/portable_type/c10/\` + + This PR was created automatically. If CI fails, Claude will attempt to fix issues (up to 3 attempts). If CI still fails, human review will be requested. + + cc @jakeszwe" + + gh pr create \ + --title "Bump PyTorch pin to nightly ${NIGHTLY}" \ + --body "${PR_BODY}" \ + --label "ci/pytorch-pin-bump" From 6334c31452a787fc80df8012abcb6ea3cfcd2829 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Tue, 21 Apr 2026 15:17:50 -0700 Subject: [PATCH 2/3] increase authors --- .github/workflows/pin-bump-ci-handler.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pin-bump-ci-handler.yml b/.github/workflows/pin-bump-ci-handler.yml index c4acdee6846..d15de85c5ad 100644 --- a/.github/workflows/pin-bump-ci-handler.yml +++ b/.github/workflows/pin-bump-ci-handler.yml @@ -38,8 +38,9 @@ jobs: return; } - if (pr.data.user.login !== 'pytorchbot') { - console.log(`PR #${prNumber} was created by ${pr.data.user.login}, not pytorchbot. Skipping.`); + const allowedAuthors = new Set(['pytorchbot', 'pytorchupdatebot', 'facebook-github-bot']); + if (!allowedAuthors.has(pr.data.user.login)) { + console.log(`PR #${prNumber} was created by ${pr.data.user.login}, not an allowed automation account. Skipping.`); return; } From e650c7f88dc1b8b2539a784f46497ddd97e7f651 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Tue, 21 Apr 2026 15:32:48 -0700 Subject: [PATCH 3/3] small fixes --- .github/workflows/pin-bump-ci-handler.yml | 2 +- .github/workflows/weekly-pytorch-pin-bump.yml | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pin-bump-ci-handler.yml b/.github/workflows/pin-bump-ci-handler.yml index d15de85c5ad..b07ac8307df 100644 --- a/.github/workflows/pin-bump-ci-handler.yml +++ b/.github/workflows/pin-bump-ci-handler.yml @@ -50,7 +50,7 @@ jobs: owner, repo, issue_number: prNumber, per_page: 100 }); const fixAttempts = comments.data.filter( - c => c.body && c.body.includes('[ci-fix-attempt') + c => c.body && c.body.startsWith('@claude [ci-fix-attempt') ).length; if (conclusion === 'success') { diff --git a/.github/workflows/weekly-pytorch-pin-bump.yml b/.github/workflows/weekly-pytorch-pin-bump.yml index d428e15282e..30579c77701 100644 --- a/.github/workflows/weekly-pytorch-pin-bump.yml +++ b/.github/workflows/weekly-pytorch-pin-bump.yml @@ -72,7 +72,8 @@ jobs: fi NIGHTLY="${{ steps.nightly.outputs.version }}" - PR_BODY="## Summary + read -r -d '' PR_BODY <