Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/deploy-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ concurrency:
group: pages-${{ github.event.workflow_run.head_sha || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# #2030: one Discussions category holds every report with no pull request to
# comment on, one thread per job.
env:
REPORT_CATEGORY: CI reports

jobs:
# #1967: the test gate on the schedule and the `verdict` on a `workflow_run`,
# shared with Conda Update and Vendor Update; cron-head.yml says when each
Expand Down Expand Up @@ -185,3 +190,64 @@ jobs:
clean: true # Automatically remove deleted files from the deploy branch
# Read back by the step above; keep the two in step.
commit-message: Publish the documentation of ${{ github.sha }}

# #2030: the run's report, the gate's, the build's and the publish's results,
# posted to the thread `deploy-pages: documentation build report`. As
# cron-vendor.yml's `discussion` job: a `workflow_run` follows a push to
# main, so it posts as a push run, only when it failed and once more on the
# next green run, and the schedule posts every run. It runs where
# `deploy-pages` ran or `head`'s gate failed, so never on a pull request's
# run or a verdict for another commit,
# which skip `deploy-pages` deliberately. It reads no artifact, and checks
# out the script alone. The job and its step are `continue-on-error`;
# util/discussion_report.py writes a warning rather than failing.
discussion:
name: Post the Pages report
needs: [ head, deploy-pages, publish-pages ]
if: >-
${{ always() && github.event_name != 'pull_request' && (needs.deploy-pages.result != 'skipped' ||
needs.head.result == 'failure' || needs.head.result == 'cancelled') }}
runs-on: ubuntu-latest
timeout-minutes: 10
continue-on-error: true
permissions:
contents: read
discussions: write

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
sparse-checkout: |
util/discussion_report.py
sparse-checkout-cone-mode: false
persist-credentials: false

# A skipped `deploy-pages` reports the gate's result; a skipped
# `publish-pages` after a green build, the skip.
- name: Post the report to Discussions
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
MODE: ${{ github.event_name == 'workflow_run' && 'push' || 'scheduled' }}
HEAD_RESULT: ${{ needs.head.result }}
BUILD_RESULT: ${{ needs.deploy-pages.result }}
PUBLISH_RESULT: ${{ needs.publish-pages.result }}
run: |
if [ "$BUILD_RESULT" = skipped ]; then
status="$HEAD_RESULT"
elif [ "$BUILD_RESULT" != success ]; then
status="$BUILD_RESULT"
else
status="$PUBLISH_RESULT"
fi
mkdir -p report
{
echo '### GitHub Pages'
echo
echo "- Gate: $HEAD_RESULT"
echo "- Build: $BUILD_RESULT"
echo "- Publish: $PUBLISH_RESULT"
} > report/pages-report.md
python3 util/discussion_report.py --category "$REPORT_CATEGORY" \
--title 'deploy-pages: documentation build report' --report report/pages-report.md \
--mode "$MODE" --status "$status"
123 changes: 115 additions & 8 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,18 @@ concurrency:
group: lint-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# #2030: one Discussions category holds every report with no pull request to
# comment on, one thread per job.
env:
REPORT_CATEGORY: CI reports

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
status: ${{ steps.run-report.outputs.status }}

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -97,9 +104,11 @@ jobs:
# run` prefix, so this and a developer's `make pylint` cannot drift apart.
# `continue-on-error: true` lets every tool run whatever another found;
# `set +e` and `status=` keep the summary lines running under the default
# `bash -e`.
# `bash -e`. Each block goes to lint-summary.md too, for the run report
# (#2030).

- name: bandit
id: bandit
continue-on-error: true
run: |
set +e
Expand All @@ -112,11 +121,12 @@ jobs:
echo '```'
sed -n '/^Run metrics:/,$p' bandit.log || echo 'no run metrics in output'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
} | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"

exit "$status"

- name: mypy
id: mypy
continue-on-error: true
run: |
set +e
Expand All @@ -129,7 +139,7 @@ jobs:
echo '```'
tail -n 1 mypy.log
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
} | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"

exit "$status"

Expand All @@ -141,6 +151,7 @@ jobs:
# code's real floor, 3.11 (`enum.StrEnum`, `enum.show_flag_values`).
# Raising either belongs in its own change.
- name: vermin
id: vermin
continue-on-error: true
run: |
set +e
Expand All @@ -154,13 +165,14 @@ jobs:
grep -E '^(Minimum required versions|Incompatible versions|Target versions not met):' vermin.log \
|| echo 'no verdict in output'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
} | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"

exit "$status"

# Last of the advisory steps because it is by far the slowest, so the
# quick verdicts reach the summary first.
- name: pylint
id: pylint
continue-on-error: true
run: |
set +e
Expand All @@ -174,7 +186,7 @@ jobs:
printf 'messages by class (C/E/R/W):\n'
grep -oE ': [A-Z][0-9]{4}:' pylint.log | grep -oE '[A-Z]' | sort | uniq -c || echo 'none'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
} | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"

exit "$status"

Expand All @@ -183,8 +195,8 @@ jobs:
# is safe because #2023, #2037 and #2038 fixed every finding it reported.
# It runs last, though it needs no Python, so that a finding skips none of
# the advisory steps above, and on `!cancelled()`, so that a failed Python
# setup does not skip it. Lint is not among ruleset 23497679's required
# checks, so a red Lint does not hold a merge.
# setup does not skip it. `Lint` is one of ruleset 23497679's required
# checks (#1967), so a red Lint holds a merge.
#
# The image's shellcheck is not pinned: a runner image that adds a check
# can flag unchanged code and turn Lint red on an unrelated pull request.
Expand All @@ -199,6 +211,7 @@ jobs:
# yet (rhysd/actionlint#657, fixed by rhysd/actionlint#654). Drop the
# pattern once a release with that fix is pinned.
- name: actionlint
id: actionlint
if: ${{ !cancelled() }}
timeout-minutes: 5
env:
Expand Down Expand Up @@ -228,6 +241,100 @@ jobs:
printf 'findings by rule:\n%s\n' "${rules:-none}"
printf 'shellcheck findings by code:\n%s\n' "${shellcheck:-none}"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
} | tee -a "$RUNNER_TEMP/lint-summary.md" >> "$GITHUB_STEP_SUMMARY"

exit "$status"

# #2030: the report the `discussion` job posts, on the schedule and on
# dispatch, never on a pull request, whose run has checks of its own:
# each tool's outcome, then the blocks the steps above wrote to the
# summary. `status=warning` where an advisory step failed. Both steps
# are `continue-on-error`, so a report never fails the run.
- name: Write the run report
id: run-report
if: ${{ !cancelled() && github.event_name != 'pull_request' }}
continue-on-error: true
env:
BANDIT: ${{ steps.bandit.outcome }}
MYPY: ${{ steps.mypy.outcome }}
VERMIN: ${{ steps.vermin.outcome }}
PYLINT: ${{ steps.pylint.outcome }}
ACTIONLINT: ${{ steps.actionlint.outcome }}
run: |
status=success
for outcome in "$BANDIT" "$MYPY" "$VERMIN" "$PYLINT"; do
if [ "$outcome" = failure ]; then
status=warning
fi
done
report="$RUNNER_TEMP/lint-report.md"
{
echo '### Lint'
echo
echo "- bandit, advisory: ${BANDIT:-not run}"
echo "- mypy, advisory: ${MYPY:-not run}"
echo "- vermin, advisory: ${VERMIN:-not run}"
echo "- pylint, advisory: ${PYLINT:-not run}"
echo "- actionlint, blocking: ${ACTIONLINT:-not run}"
if [ -s "$RUNNER_TEMP/lint-summary.md" ]; then
echo
cat "$RUNNER_TEMP/lint-summary.md"
fi
} > "$report"
echo "status=$status" >> "$GITHUB_OUTPUT"
cat "$report"

- name: Upload the run report
if: ${{ !cancelled() && github.event_name != 'pull_request' }}
continue-on-error: true
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: lint-report
path: ${{ runner.temp }}/lint-report.md
retention-days: 14

# #2030: posts the report `lint` uploaded to the thread `lint: weekly lint
# report`, every run, as a scheduled report: Lint has no push trigger, and a
# dispatch is a deliberate report, as in ci-timing.yml. Never on a pull
# request. A green job whose advisory steps failed reports `warning`. The job
# and its steps are `continue-on-error`; util/discussion_report.py writes a
# warning rather than failing.
discussion:
name: Post the lint report
needs: [ lint ]
if: ${{ always() && github.event_name != 'pull_request' && needs.lint.result != 'skipped' }}
runs-on: ubuntu-latest
timeout-minutes: 10
continue-on-error: true
permissions:
contents: read
discussions: write

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

# A run that never wrote a report uploaded none; the script then warns.
- name: Download the report
continue-on-error: true
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: lint-report
path: report

- name: Post the report to Discussions
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
LINT_RESULT: ${{ needs.lint.result }}
LINT_STATUS: ${{ needs.lint.outputs.status }}
run: |
if [ "$LINT_RESULT" = success ]; then
status="${LINT_STATUS:-success}"
else
status="$LINT_RESULT"
fi
python3 util/discussion_report.py --category "$REPORT_CATEGORY" \
--title 'lint: weekly lint report' --report report/lint-report.md \
--mode scheduled --status "$status"
27 changes: 23 additions & 4 deletions docs/source/contributing/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,9 @@ grepping every workflow file for its name:
* - Required context
- Emitted by
- Where
* - ``Lint``
- job ``lint``, required since `#1967 <https://github.com/JarryShaw/PyPCAPKit/issues/1967>`__
- ``lint.yml``
* - ``Required checks passed``
- job ``required-checks``
- ``unit-tests.yml``
Expand Down Expand Up @@ -1221,8 +1224,9 @@ does; each has tests under :file:`tests/project/` that run it.
``gate``, at ``error``.
* - :file:`util/discussion_report.py`
- ``Post the report to Discussions`` in the ``discussion`` job of
``ci-timing.yml``, ``cron-vendor.yml``, ``cron-conda.yml`` and
``cron-python-versions.yml`` (see `CI Reports`_).
``ci-timing.yml``, ``cron-vendor.yml``, ``cron-conda.yml``,
``cron-python-versions.yml``, ``lint.yml`` and ``deploy-pages.yml`` (see
`CI Reports`_).
* - :file:`util/apt_packages.py`
- The apt steps of ``unit-tests.yml``'s ``engine-tests`` and ``pypcap-parity``
(and so its KTLO twin): the plan, seed, install and collect steps, as
Expand Down Expand Up @@ -1306,6 +1310,20 @@ hand-edit note, for a failed audit of a changed tree, or for an unset
because ``pr`` waits for the owner's approval of ``github-release``, and the pull
request it opens is its own record.

``lint.yml`` has no push trigger, so it posts as ``scheduled`` to ``lint: weekly
lint report``, on ``schedule`` and ``workflow_dispatch``, never on a pull request,
whose run has checks of its own. Its report lists each tool step's outcome, then
the blocks those steps wrote to the job summary, which each also appends to a file
the report reads. It reports ``warning`` where an advisory step failed, as each
does while its findings stand, and ``failure`` where actionlint failed the job.
``deploy-pages.yml`` posts to ``deploy-pages: documentation build report`` by
``cron-vendor.yml``'s rules: ``push`` on its ``workflow_run``, ``scheduled`` on its
``schedule``, and never on a pull request or a verdict for another commit. Its
report is the results of ``head``, ``deploy-pages`` and ``publish-pages``, so it
uploads no artifact, and its ``discussion`` job checks out the script alone. The
``--status`` is the publish's result after a green build, the build's where the
build did not pass, and the gate's where the gate skipped the build.

The category is the workflow-level ``REPORT_CATEGORY``. The API cannot
create one, so it is made by hand, in Settings, Discussions.

Expand Down Expand Up @@ -1389,8 +1407,9 @@ actionlint runs the image's shellcheck over each ``run:`` block. Unlike Lint's
other steps, this one blocks: a finding fails the job, as the owner ruled on #1967
once #2023, #2037 and #2038 had fixed every finding it reported. It runs last and
on ``!cancelled()``, so a finding skips none of the advisory steps and a failed
Python setup does not skip it. Lint is not a required check of ruleset 23497679, so
a red Lint does not hold a merge. The image's shellcheck is not pinned, so a runner
Python setup does not skip it. ``Lint`` is a required check of ruleset 23497679
(`#1967 <https://github.com/JarryShaw/PyPCAPKit/issues/1967>`__), so a red Lint
holds a merge. The image's shellcheck is not pinned, so a runner
image that adds a check can turn Lint red on unchanged code; the fix is to the
script it flags. The step's run summary counts the findings by actionlint rule and
by shellcheck code.
Expand Down
5 changes: 4 additions & 1 deletion tests/project/test_actionlint_in_lint_1967.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@
),
}
#: The ``if:`` values under which a step still runs after an earlier step failed.
RUNS_AFTER_A_FAILURE = frozenset({'${{ !cancelled() }}', '${{ always() }}', '!cancelled()', 'always()'})
#: The last is the run report's (#2030), which a pull request skips whatever
#: actionlint found.
RUNS_AFTER_A_FAILURE = frozenset({'${{ !cancelled() }}', '${{ always() }}', '!cancelled()', 'always()',
"${{ !cancelled() && github.event_name != 'pull_request' }}"})
#: An ``-ignore`` argument in the step's script, quoted either way.
IGNORE = re.compile(r'''-ignore\s+(?P<quote>['"])(?P<pattern>.*?)(?P=quote)''')

Expand Down
Loading
Loading