Skip to content

Commit 856cbd8

Browse files
authored
ci: post the Lint and GitHub Pages reports to Discussions (#2030) (#2056)
- lint.yml: a `discussion` job posts the scheduled and dispatched runs to `lint: weekly lint report`. The tool steps get ids and tee their summary blocks to a file; `Write the run report` and `Upload the run report` close the job, skipping pull requests. Advisory failures report `warning`. - deploy-pages.yml: a `discussion` job posts the gate, build and publish results to `deploy-pages: documentation build report`, as `push` on the `workflow_run` and `scheduled` on the schedule, never on a pull request or a verdict for another commit. No artifact; it checks out the script alone. - Only the posting jobs hold `discussions: write`; the hardening, actionlint and loader pins allow for the new job, steps and `env`. - workflows.rst's CI Reports section documents both. Lint is a required check since #1967: the actionlint comment, its docs paragraph and the required contexts table now say so. tests/project/test_lint_pages_reports_2030.py pins both jobs; actionlint 1.7.12 is clean.
1 parent 3437c74 commit 856cbd8

7 files changed

Lines changed: 527 additions & 17 deletions

File tree

‎.github/workflows/deploy-pages.yml‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ concurrency:
3434
group: pages-${{ github.event.workflow_run.head_sha || github.ref }}
3535
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
3636

37+
# #2030: one Discussions category holds every report with no pull request to
38+
# comment on, one thread per job.
39+
env:
40+
REPORT_CATEGORY: CI reports
41+
3742
jobs:
3843
# #1967: the test gate on the schedule and the `verdict` on a `workflow_run`,
3944
# shared with Conda Update and Vendor Update; cron-head.yml says when each
@@ -185,3 +190,64 @@ jobs:
185190
clean: true # Automatically remove deleted files from the deploy branch
186191
# Read back by the step above; keep the two in step.
187192
commit-message: Publish the documentation of ${{ github.sha }}
193+
194+
# #2030: the run's report, the gate's, the build's and the publish's results,
195+
# posted to the thread `deploy-pages: documentation build report`. As
196+
# cron-vendor.yml's `discussion` job: a `workflow_run` follows a push to
197+
# main, so it posts as a push run, only when it failed and once more on the
198+
# next green run, and the schedule posts every run. It runs where
199+
# `deploy-pages` ran or `head`'s gate failed, so never on a pull request's
200+
# run or a verdict for another commit,
201+
# which skip `deploy-pages` deliberately. It reads no artifact, and checks
202+
# out the script alone. The job and its step are `continue-on-error`;
203+
# util/discussion_report.py writes a warning rather than failing.
204+
discussion:
205+
name: Post the Pages report
206+
needs: [ head, deploy-pages, publish-pages ]
207+
if: >-
208+
${{ always() && github.event_name != 'pull_request' && (needs.deploy-pages.result != 'skipped' ||
209+
needs.head.result == 'failure' || needs.head.result == 'cancelled') }}
210+
runs-on: ubuntu-latest
211+
timeout-minutes: 10
212+
continue-on-error: true
213+
permissions:
214+
contents: read
215+
discussions: write
216+
217+
steps:
218+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
219+
with:
220+
sparse-checkout: |
221+
util/discussion_report.py
222+
sparse-checkout-cone-mode: false
223+
persist-credentials: false
224+
225+
# A skipped `deploy-pages` reports the gate's result; a skipped
226+
# `publish-pages` after a green build, the skip.
227+
- name: Post the report to Discussions
228+
continue-on-error: true
229+
env:
230+
GH_TOKEN: ${{ github.token }}
231+
MODE: ${{ github.event_name == 'workflow_run' && 'push' || 'scheduled' }}
232+
HEAD_RESULT: ${{ needs.head.result }}
233+
BUILD_RESULT: ${{ needs.deploy-pages.result }}
234+
PUBLISH_RESULT: ${{ needs.publish-pages.result }}
235+
run: |
236+
if [ "$BUILD_RESULT" = skipped ]; then
237+
status="$HEAD_RESULT"
238+
elif [ "$BUILD_RESULT" != success ]; then
239+
status="$BUILD_RESULT"
240+
else
241+
status="$PUBLISH_RESULT"
242+
fi
243+
mkdir -p report
244+
{
245+
echo '### GitHub Pages'
246+
echo
247+
echo "- Gate: $HEAD_RESULT"
248+
echo "- Build: $BUILD_RESULT"
249+
echo "- Publish: $PUBLISH_RESULT"
250+
} > report/pages-report.md
251+
python3 util/discussion_report.py --category "$REPORT_CATEGORY" \
252+
--title 'deploy-pages: documentation build report' --report report/pages-report.md \
253+
--mode "$MODE" --status "$status"

‎.github/workflows/lint.yml‎

Lines changed: 115 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
5964
jobs:
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"

‎docs/source/contributing/workflows.rst‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,9 @@ grepping every workflow file for its name:
689689
* - Required context
690690
- Emitted by
691691
- Where
692+
* - ``Lint``
693+
- job ``lint``, required since `#1967 <https://github.com/JarryShaw/PyPCAPKit/issues/1967>`__
694+
- ``lint.yml``
692695
* - ``Required checks passed``
693696
- job ``required-checks``
694697
- ``unit-tests.yml``
@@ -1221,8 +1224,9 @@ does; each has tests under :file:`tests/project/` that run it.
12211224
``gate``, at ``error``.
12221225
* - :file:`util/discussion_report.py`
12231226
- ``Post the report to Discussions`` in the ``discussion`` job of
1224-
``ci-timing.yml``, ``cron-vendor.yml``, ``cron-conda.yml`` and
1225-
``cron-python-versions.yml`` (see `CI Reports`_).
1227+
``ci-timing.yml``, ``cron-vendor.yml``, ``cron-conda.yml``,
1228+
``cron-python-versions.yml``, ``lint.yml`` and ``deploy-pages.yml`` (see
1229+
`CI Reports`_).
12261230
* - :file:`util/apt_packages.py`
12271231
- The apt steps of ``unit-tests.yml``'s ``engine-tests`` and ``pypcap-parity``
12281232
(and so its KTLO twin): the plan, seed, install and collect steps, as
@@ -1306,6 +1310,20 @@ hand-edit note, for a failed audit of a changed tree, or for an unset
13061310
because ``pr`` waits for the owner's approval of ``github-release``, and the pull
13071311
request it opens is its own record.
13081312

1313+
``lint.yml`` has no push trigger, so it posts as ``scheduled`` to ``lint: weekly
1314+
lint report``, on ``schedule`` and ``workflow_dispatch``, never on a pull request,
1315+
whose run has checks of its own. Its report lists each tool step's outcome, then
1316+
the blocks those steps wrote to the job summary, which each also appends to a file
1317+
the report reads. It reports ``warning`` where an advisory step failed, as each
1318+
does while its findings stand, and ``failure`` where actionlint failed the job.
1319+
``deploy-pages.yml`` posts to ``deploy-pages: documentation build report`` by
1320+
``cron-vendor.yml``'s rules: ``push`` on its ``workflow_run``, ``scheduled`` on its
1321+
``schedule``, and never on a pull request or a verdict for another commit. Its
1322+
report is the results of ``head``, ``deploy-pages`` and ``publish-pages``, so it
1323+
uploads no artifact, and its ``discussion`` job checks out the script alone. The
1324+
``--status`` is the publish's result after a green build, the build's where the
1325+
build did not pass, and the gate's where the gate skipped the build.
1326+
13091327
The category is the workflow-level ``REPORT_CATEGORY``. The API cannot
13101328
create one, so it is made by hand, in Settings, Discussions.
13111329

@@ -1389,8 +1407,9 @@ actionlint runs the image's shellcheck over each ``run:`` block. Unlike Lint's
13891407
other steps, this one blocks: a finding fails the job, as the owner ruled on #1967
13901408
once #2023, #2037 and #2038 had fixed every finding it reported. It runs last and
13911409
on ``!cancelled()``, so a finding skips none of the advisory steps and a failed
1392-
Python setup does not skip it. Lint is not a required check of ruleset 23497679, so
1393-
a red Lint does not hold a merge. The image's shellcheck is not pinned, so a runner
1410+
Python setup does not skip it. ``Lint`` is a required check of ruleset 23497679
1411+
(`#1967 <https://github.com/JarryShaw/PyPCAPKit/issues/1967>`__), so a red Lint
1412+
holds a merge. The image's shellcheck is not pinned, so a runner
13941413
image that adds a check can turn Lint red on unchanged code; the fix is to the
13951414
script it flags. The step's run summary counts the findings by actionlint rule and
13961415
by shellcheck code.

‎tests/project/test_actionlint_in_lint_1967.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@
5656
),
5757
}
5858
#: The ``if:`` values under which a step still runs after an earlier step failed.
59-
RUNS_AFTER_A_FAILURE = frozenset({'${{ !cancelled() }}', '${{ always() }}', '!cancelled()', 'always()'})
59+
#: The last is the run report's (#2030), which a pull request skips whatever
60+
#: actionlint found.
61+
RUNS_AFTER_A_FAILURE = frozenset({'${{ !cancelled() }}', '${{ always() }}', '!cancelled()', 'always()',
62+
"${{ !cancelled() && github.event_name != 'pull_request' }}"})
6063
#: An ``-ignore`` argument in the step's script, quoted either way.
6164
IGNORE = re.compile(r'''-ignore\s+(?P<quote>['"])(?P<pattern>.*?)(?P=quote)''')
6265

0 commit comments

Comments
 (0)