44name : Auto-fix formatting
55
66on :
7- pull_request :
8- types : [opened, synchronize, reopened]
97 workflow_run :
108 workflows : ["Linters and Tests"]
119 types : [completed]
1513 NIGHTLY_TOOLCHAIN : nightly-2026-02-05
1614
1715jobs :
18- check-fmt :
19- name : " Check formatting"
20- # For workflow_run events, only run if the triggering workflow was for a PR.
21- if : >-
22- github.event_name == 'pull_request'
23- || (github.event_name == 'workflow_run'
24- && github.event.workflow_run.event == 'pull_request'
25- && github.event.workflow_run.conclusion == 'completed')
16+ check-ci-fmt :
17+ name : " Check if CI fmt failed"
18+ # Only act on PR runs that completed (success or failure — the fmt step
19+ # uses continue-on-error so the workflow can "succeed" even when fmt fails).
20+ if : github.event.workflow_run.event == 'pull_request'
2621 runs-on : ubuntu-latest
27- timeout-minutes : 15
22+ timeout-minutes : 5
2823 outputs :
29- needs-fix : ${{ steps.check.outputs.needs-fix }}
30- pr- head-sha : ${{ steps.resolve .outputs.head-sha }}
31- pr- head-ref : ${{ steps.resolve .outputs.head-ref }}
24+ fmt-failed : ${{ steps.check.outputs.fmt-failed }}
25+ head-sha : ${{ steps.check .outputs.head-sha }}
26+ head-ref : ${{ steps.check .outputs.head-ref }}
3227 steps :
33- - name : Resolve PR ref
34- id : resolve
28+ - name : Check CI fmt step outcome
29+ id : check
3530 uses : actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
3631 with :
3732 script : |
38- let sha, ref;
39- if (context.eventName === 'pull_request') {
40- sha = context.payload.pull_request.head.sha;
41- ref = context.payload.pull_request.head.ref;
42- } else {
43- // workflow_run: look up the PR from the head branch
44- const wr = context.payload.workflow_run;
45- sha = wr.head_sha;
46- ref = wr.head_branch;
47- }
48- core.setOutput('head-sha', sha);
49- core.setOutput('head-ref', ref);
33+ const runId = context.payload.workflow_run.id;
34+ const headSha = context.payload.workflow_run.head_sha;
35+ const headBranch = context.payload.workflow_run.head_branch;
5036
51- - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
52- with :
53- ref : ${{ steps.resolve.outputs.head-sha }}
37+ // Find the "Rust (lint)" job in the CI run.
38+ const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({
39+ ...context.repo,
40+ run_id: runId,
41+ });
42+ const lintJob = jobs.find(j => j.name === 'Rust (lint)');
43+ if (!lintJob) {
44+ core.info('Rust (lint) job not found — skipping');
45+ core.setOutput('fmt-failed', 'false');
46+ return;
47+ }
5448
55- - name : Install nightly for fmt
56- run : rustup toolchain install $NIGHTLY_TOOLCHAIN --component rustfmt
49+ // Find the fmt step by its id ("fmt") or name.
50+ const fmtStep = lintJob.steps.find(
51+ s => s.name === 'Rust Lint - Format'
52+ );
53+ if (!fmtStep) {
54+ core.info('fmt step not found — skipping');
55+ core.setOutput('fmt-failed', 'false');
56+ return;
57+ }
5758
58- - name : Check formatting
59- id : check
60- run : |
61- if cargo +$NIGHTLY_TOOLCHAIN fmt --all --check; then
62- echo "needs-fix=false" >> "$GITHUB_OUTPUT"
63- else
64- echo "needs-fix=true" >> "$GITHUB_OUTPUT"
65- fi
59+ const failed = fmtStep.conclusion === 'failure';
60+ core.info(`fmt step conclusion: ${fmtStep.conclusion}`);
61+ core.setOutput('fmt-failed', String(failed));
62+ core.setOutput('head-sha', headSha);
63+ core.setOutput('head-ref', headBranch);
6664
6765 apply-fmt :
6866 name : " Apply formatting fix"
69- needs : check-fmt
70- if : needs.check-fmt.outputs.needs-fix == 'true'
67+ needs : check-ci- fmt
68+ if : needs.check-ci- fmt.outputs.fmt-failed == 'true'
7169 runs-on : ubuntu-latest
7270 timeout-minutes : 15
7371 environment : fmt-fix
7674 steps :
7775 - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
7876 with :
79- # Check out the exact commit that triggered this workflow , not the
80- # latest branch HEAD (which may have moved during approval wait).
81- ref : ${{ needs.check-fmt.outputs.pr- head-sha }}
77+ # Check out the exact commit that CI ran against , not the latest
78+ # branch HEAD (which may have moved during the approval wait).
79+ ref : ${{ needs.check-ci- fmt.outputs.head-sha }}
8280
8381 - name : Install nightly for fmt
8482 run : rustup toolchain install $NIGHTLY_TOOLCHAIN --component rustfmt
@@ -96,14 +94,14 @@ jobs:
9694
9795 Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
9896
99- - name : Push (fails if branch moved since trigger )
97+ - name : Push (fails if branch moved since CI ran )
10098 env :
101- EXPECTED_SHA : ${{ needs.check-fmt.outputs.pr- head-sha }}
102- BRANCH : ${{ needs.check-fmt.outputs.pr- head-ref }}
99+ EXPECTED_SHA : ${{ needs.check-ci- fmt.outputs.head-sha }}
100+ BRANCH : ${{ needs.check-ci- fmt.outputs.head-ref }}
103101 run : |
104102 # --force-with-lease atomically checks that the remote branch HEAD
105103 # is still EXPECTED_SHA before pushing. If someone else pushed a
106- # commit to the PR between trigger and approval, this fails safely.
104+ # commit to the PR between the CI run and approval, this fails safely.
107105 git push origin \
108106 "HEAD:refs/heads/$BRANCH" \
109107 "--force-with-lease=refs/heads/$BRANCH:$EXPECTED_SHA"
0 commit comments