From bc69cb4ed860f4c038fba3a8442dec652b107c35 Mon Sep 17 00:00:00 2001 From: Arnaud Becheler <8360330+Becheler@users.noreply.github.com> Date: Sun, 7 Jun 2026 16:08:02 +0200 Subject: [PATCH] generate warnings counts in PR comment --- .github/scripts/warn_table.py | 48 ++++++++++++++++++ .github/workflows/warning-count-comment.yml | 54 +++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .github/scripts/warn_table.py create mode 100644 .github/workflows/warning-count-comment.yml diff --git a/.github/scripts/warn_table.py b/.github/scripts/warn_table.py new file mode 100644 index 000000000..2f875bc41 --- /dev/null +++ b/.github/scripts/warn_table.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +"""Print a markdown table comparing compiler-warning counts between two CI log archives. + +Usage: warn_table.py BASELINE.zip AFTER.zip + +A warning is a line matching 'file:line:col: warning:' (clang/gcc) or 'warning Cxxxx' +(msvc). Linker, "N warnings generated" summary, and CI-runner lines are not counted. +""" +import re +import sys +import zipfile + +WARNING = re.compile(r":\d+:\d+: warning:|warning C\d+") + + +def counts(zip_path): + """Map each CI job to its warning count, read from the run's log archive.""" + result = {} + with zipfile.ZipFile(zip_path) as archive: + for entry in archive.namelist(): + # Top-level per-job logs are named like "12_ubuntu (gcc-14, 14).txt". + if "/" in entry or not entry.endswith(".txt"): + continue + job = re.sub(r"^\d+_", "", entry[:-4]) + log = archive.read(entry).decode("utf-8", "replace") + result[job] = sum(1 for line in log.splitlines() if WARNING.search(line)) + return result + + +def main(baseline_zip, after_zip): + baseline = counts(baseline_zip) + after = counts(after_zip) + print("| Job | Baseline | After | Delta |") + print("|-----|---------:|------:|------:|") + for job in sorted(baseline.keys() | after.keys()): + if "cmake" in job: # cmake jobs build only the library, they never warn + continue + before = baseline.get(job, 0) + now = after.get(job, 0) + diff = now - before + delta = f"{diff:+d}" if diff else "0" + print(f"| {job} | {before} | {now} | {delta} |") + + +if __name__ == "__main__": + if len(sys.argv) != 3: + sys.exit("usage: warn_table.py BASELINE.zip AFTER.zip") + main(sys.argv[1], sys.argv[2]) diff --git a/.github/workflows/warning-count-comment.yml b/.github/workflows/warning-count-comment.yml new file mode 100644 index 000000000..aacb0d415 --- /dev/null +++ b/.github/workflows/warning-count-comment.yml @@ -0,0 +1,54 @@ +name: warning-count-comment + +on: + workflow_run: + workflows: ["CI"] + types: [completed] + +permissions: + actions: read + pull-requests: write + contents: read + +jobs: + comment: + if: github.event.workflow_run.event == 'pull_request' + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + PR_RUN: ${{ github.event.workflow_run.id }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + steps: + - uses: actions/checkout@v4 + + - name: Download this PR run's log archive + run: gh api "repos/$REPO/actions/runs/$PR_RUN/logs" > pr-logs.zip + + - name: Download latest successful develop run's log archive + run: | + DEV=$(gh run list --workflow CI --branch develop --status success \ + --limit 1 --json databaseId --jq '.[0].databaseId') + if [ -z "$DEV" ]; then echo "no develop baseline run found" >&2; exit 1; fi + echo "DEV_RUN=$DEV" >> "$GITHUB_ENV" + gh api "repos/$REPO/actions/runs/$DEV/logs" > base-logs.zip + + - name: Generate table + run: | + { + echo "Compiler-warning counts vs \`develop\` (auto-generated)." + echo "PR run \`$PR_RUN\` vs develop run \`$DEV_RUN\` (\`${HEAD_SHA:0:10}\`)." + echo + python3 .github/scripts/warn_table.py base-logs.zip pr-logs.zip + } > table.md + + - name: Resolve PR number + id: pr + run: echo "num=$(gh api "repos/$REPO/commits/$HEAD_SHA/pulls" --jq '.[0].number')" >> "$GITHUB_OUTPUT" + + - name: Post or update sticky comment + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: warning-counts + number: ${{ steps.pr.outputs.num }} + path: table.md