@@ -56,11 +56,18 @@ concurrency:
5656 group : lint-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
5757 cancel-in-progress : ${{ github.event_name == 'pull_request' }}
5858
59+ # #2030: one Discussions category holds every report with no pull request to
60+ # comment on, one thread per job.
61+ env :
62+ REPORT_CATEGORY : CI reports
63+
5964jobs :
6065 lint :
6166 name : Lint
6267 runs-on : ubuntu-latest
6368 timeout-minutes : 30
69+ outputs :
70+ status : ${{ steps.run-report.outputs.status }}
6471
6572 steps :
6673 - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -97,9 +104,11 @@ jobs:
97104 # run` prefix, so this and a developer's `make pylint` cannot drift apart.
98105 # `continue-on-error: true` lets every tool run whatever another found;
99106 # `set +e` and `status=` keep the summary lines running under the default
100- # `bash -e`.
107+ # `bash -e`. Each block goes to lint-summary.md too, for the run report
108+ # (#2030).
101109
102110 - name : bandit
111+ id : bandit
103112 continue-on-error : true
104113 run : |
105114 set +e
@@ -112,11 +121,12 @@ jobs:
112121 echo '```'
113122 sed -n '/^Run metrics:/,$p' bandit.log || echo 'no run metrics in output'
114123 echo '```'
115- } >> "$GITHUB_STEP_SUMMARY"
124+ } | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"
116125
117126 exit "$status"
118127
119128 - name : mypy
129+ id : mypy
120130 continue-on-error : true
121131 run : |
122132 set +e
@@ -129,7 +139,7 @@ jobs:
129139 echo '```'
130140 tail -n 1 mypy.log
131141 echo '```'
132- } >> "$GITHUB_STEP_SUMMARY"
142+ } | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"
133143
134144 exit "$status"
135145
@@ -141,6 +151,7 @@ jobs:
141151 # code's real floor, 3.11 (`enum.StrEnum`, `enum.show_flag_values`).
142152 # Raising either belongs in its own change.
143153 - name : vermin
154+ id : vermin
144155 continue-on-error : true
145156 run : |
146157 set +e
@@ -154,13 +165,14 @@ jobs:
154165 grep -E '^(Minimum required versions|Incompatible versions|Target versions not met):' vermin.log \
155166 || echo 'no verdict in output'
156167 echo '```'
157- } >> "$GITHUB_STEP_SUMMARY"
168+ } | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"
158169
159170 exit "$status"
160171
161172 # Last of the advisory steps because it is by far the slowest, so the
162173 # quick verdicts reach the summary first.
163174 - name : pylint
175+ id : pylint
164176 continue-on-error : true
165177 run : |
166178 set +e
@@ -174,7 +186,7 @@ jobs:
174186 printf 'messages by class (C/E/R/W):\n'
175187 grep -oE ': [A-Z][0-9]{4}:' pylint.log | grep -oE '[A-Z]' | sort | uniq -c || echo 'none'
176188 echo '```'
177- } >> "$GITHUB_STEP_SUMMARY"
189+ } | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"
178190
179191 exit "$status"
180192
@@ -183,8 +195,8 @@ jobs:
183195 # is safe because #2023, #2037 and #2038 fixed every finding it reported.
184196 # It runs last, though it needs no Python, so that a finding skips none of
185197 # the advisory steps above, and on `!cancelled()`, so that a failed Python
186- # setup does not skip it. Lint is not among ruleset 23497679's required
187- # checks, so a red Lint does not hold a merge.
198+ # setup does not skip it. ` Lint` is one of ruleset 23497679's required
199+ # checks (#1967) , so a red Lint holds a merge.
188200 #
189201 # The image's shellcheck is not pinned: a runner image that adds a check
190202 # can flag unchanged code and turn Lint red on an unrelated pull request.
@@ -199,6 +211,7 @@ jobs:
199211 # yet (rhysd/actionlint#657, fixed by rhysd/actionlint#654). Drop the
200212 # pattern once a release with that fix is pinned.
201213 - name : actionlint
214+ id : actionlint
202215 if : ${{ !cancelled() }}
203216 timeout-minutes : 5
204217 env :
@@ -228,6 +241,100 @@ jobs:
228241 printf 'findings by rule:\n%s\n' "${rules:-none}"
229242 printf 'shellcheck findings by code:\n%s\n' "${shellcheck:-none}"
230243 echo '```'
231- } >> "$GITHUB_STEP_SUMMARY"
244+ } | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"
232245
233246 exit "$status"
247+
248+ # #2030: the report the `discussion` job posts, on the schedule and on
249+ # dispatch, never on a pull request, whose run has checks of its own:
250+ # each tool's outcome, then the blocks the steps above wrote to the
251+ # summary. `status=warning` where an advisory step failed. Both steps
252+ # are `continue-on-error`, so a report never fails the run.
253+ - name : Write the run report
254+ id : run-report
255+ if : ${{ !cancelled() && github.event_name != 'pull_request' }}
256+ continue-on-error : true
257+ env :
258+ BANDIT : ${{ steps.bandit.outcome }}
259+ MYPY : ${{ steps.mypy.outcome }}
260+ VERMIN : ${{ steps.vermin.outcome }}
261+ PYLINT : ${{ steps.pylint.outcome }}
262+ ACTIONLINT : ${{ steps.actionlint.outcome }}
263+ run : |
264+ status=success
265+ for outcome in "$BANDIT" "$MYPY" "$VERMIN" "$PYLINT"; do
266+ if [ "$outcome" = failure ]; then
267+ status=warning
268+ fi
269+ done
270+ report="$RUNNER_TEMP/lint-report.md"
271+ {
272+ echo '### Lint'
273+ echo
274+ echo "- bandit, advisory: ${BANDIT:-not run}"
275+ echo "- mypy, advisory: ${MYPY:-not run}"
276+ echo "- vermin, advisory: ${VERMIN:-not run}"
277+ echo "- pylint, advisory: ${PYLINT:-not run}"
278+ echo "- actionlint, blocking: ${ACTIONLINT:-not run}"
279+ if [ -s "$RUNNER_TEMP/lint-summary.md" ]; then
280+ echo
281+ cat "$RUNNER_TEMP/lint-summary.md"
282+ fi
283+ } > "$report"
284+ echo "status=$status" >> "$GITHUB_OUTPUT"
285+ cat "$report"
286+
287+ - name : Upload the run report
288+ if : ${{ !cancelled() && github.event_name != 'pull_request' }}
289+ continue-on-error : true
290+ uses : actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
291+ with :
292+ name : lint-report
293+ path : ${{ runner.temp }}/lint-report.md
294+ retention-days : 14
295+
296+ # #2030: posts the report `lint` uploaded to the thread `lint: weekly lint
297+ # report`, every run, as a scheduled report: Lint has no push trigger, and a
298+ # dispatch is a deliberate report, as in ci-timing.yml. Never on a pull
299+ # request. A green job whose advisory steps failed reports `warning`. The job
300+ # and its steps are `continue-on-error`; util/discussion_report.py writes a
301+ # warning rather than failing.
302+ discussion :
303+ name : Post the lint report
304+ needs : [ lint ]
305+ if : ${{ always() && github.event_name != 'pull_request' && needs.lint.result != 'skipped' }}
306+ runs-on : ubuntu-latest
307+ timeout-minutes : 10
308+ continue-on-error : true
309+ permissions :
310+ contents : read
311+ discussions : write
312+
313+ steps :
314+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
315+ with :
316+ persist-credentials : false
317+
318+ # A run that never wrote a report uploaded none; the script then warns.
319+ - name : Download the report
320+ continue-on-error : true
321+ uses : actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
322+ with :
323+ name : lint-report
324+ path : report
325+
326+ - name : Post the report to Discussions
327+ continue-on-error : true
328+ env :
329+ GH_TOKEN : ${{ github.token }}
330+ LINT_RESULT : ${{ needs.lint.result }}
331+ LINT_STATUS : ${{ needs.lint.outputs.status }}
332+ run : |
333+ if [ "$LINT_RESULT" = success ]; then
334+ status="${LINT_STATUS:-success}"
335+ else
336+ status="$LINT_RESULT"
337+ fi
338+ python3 util/discussion_report.py --category "$REPORT_CATEGORY" \
339+ --title 'lint: weekly lint report' --report report/lint-report.md \
340+ --mode scheduled --status "$status"
0 commit comments