audit: say what was found, for the workflow's badge

be6c752 · gmorell · 2026-11-10 00:12

8 files +31 -7
Message
{commit_body(@commit)}

Files changed

modified .forgejo/workflows/audit.yml
+7 −1
@@ -49,6 +49,9 @@ jobs:
49 49 # GitHub and Forgejo read it as an ordinary output.
50 50 outputs:
51 51 gitgud-conclusion: ${{ steps.scan.outputs.conclusion }}
52 + # "2 to fix, 1 no fix": what the workflow's badge says, rather
53 + # than just "warning" (GitGud.Badges).
54 + gitgud-summary: ${{ steps.scan.outputs.summary }}
52 55 steps:
53 56 - uses: actions/checkout@v4
54 57 with:
@@ -108,6 +111,7 @@ jobs:
108 111 runs-on: self-hosted
109 112 outputs:
110 113 gitgud-conclusion: ${{ steps.pending.outputs.conclusion }}
114 + gitgud-summary: ${{ steps.pending.outputs.summary }}
111 115 steps:
112 116 # The job's own token reads its repository's image, as build.yml's
113 117 # pushes it.
@@ -127,9 +131,11 @@ jobs:
127 131 pending=$(docker run --rm --user root --entrypoint sh "$IMAGE" -c \
128 132 'apt-get update -qq >/dev/null && apt list --upgradable 2>/dev/null | grep -- -security' || true)
129 133 if [ -n "$pending" ]; then
134 + count=$(echo "$pending" | wc -l)
130 135 echo "$pending"
131 − echo "::warning::$(echo "$pending" | wc -l) Debian security update(s) not in the published image: bump RUNNER_DEBIAN_VERSION, or rebuild."
136 + echo "::warning::$count Debian security update(s) not in the published image: bump RUNNER_DEBIAN_VERSION, or rebuild."
132 137 echo "conclusion=warning" >> "$GITHUB_OUTPUT"
138 + echo "summary=$count image updates" >> "$GITHUB_OUTPUT"
133 139 exit 0
134 140 fi
135 141 echo "No Debian security updates pending."
modified scripts/osv-audit
+19 −3
@@ -25,6 +25,10 @@ failure:
25 25 outputs:
26 26 gitgud-conclusion: ${{ steps.scan.outputs.conclusion }}
27 27
28 +Alongside it, `summary=` says what was found in a few words ("2 to fix,
29 +1 no fix"), for the job's `gitgud-summary` output: the badge for the
30 +workflow shows it (GitGud.Badges). Nothing found writes no summary.
31 +
28 32 `conclusion=success` when nothing was found. osv-scanner's own errors
29 33 are passed on: 127 (it failed) as 127, and 128 (no lockfiles found) as
30 34 128 unless `--allow-no-lockfiles` was given, when it's nothing found.
@@ -151,11 +155,23 @@ def report(fixable, unfixable):
151 155 )
152 156
153 157
154 −def set_output(conclusion):
158 +def set_output(conclusion, summary=None):
155 159 path = os.environ.get("GITHUB_OUTPUT")
156 160 if path:
157 161 with open(path, "a") as f:
158 162 f.write(f"conclusion={conclusion}\n")
163 + if summary:
164 + f.write(f"summary={summary}\n")
165 +
166 +
167 +def summary(fixable, unfixable):
168 + """A badge's worth: `2 to fix, 1 no fix`, `1 no fix`."""
169 + parts = []
170 + if fixable:
171 + parts.append(f"{len(fixable)} to fix")
172 + if unfixable:
173 + parts.append(f"{len(unfixable)} no fix")
174 + return ", ".join(parts) or None
159 175
160 176
161 177 def main(argv):
@@ -199,11 +215,11 @@ def main(argv):
199 215
200 216 if fixable:
201 217 print(f"\n{len(fixable)} package(s) have a fixed version: update them.")
202 − set_output("failure")
218 + set_output("failure", summary(fixable, unfixable))
203 219 return 1
204 220 if unfixable:
205 221 print(f"\nNothing to update yet: {len(unfixable)} package(s) have no fixed version.")
206 − set_output("warning")
222 + set_output("warning", summary(fixable, unfixable))
207 223 return 0
208 224 print("\nNo known vulnerabilities.")
209 225 set_output("success")
modified test/scripts/osv_audit_test.exs
+5 −3
@@ -7,7 +7,7 @@ defmodule GitGud.Scripts.OsvAuditTest do
7 7 What it covers is the sorting — a fixed version listed for the
8 8 package means "fix now", none means "blocked" — and what each outcome
9 9 exits with and says in `$GITHUB_OUTPUT`, which a job's
10 − `gitgud-conclusion` output reads.
10 + `gitgud-conclusion` and `gitgud-summary` outputs read.
11 11 """
12 12 use ExUnit.Case, async: true
13 13
@@ -91,7 +91,9 @@ defmodule GitGud.Scripts.OsvAuditTest do
91 91 ])
92 92 ])
93 93
94 − assert {out, 1, {:ok, "conclusion=failure\n"}, args} = audit(dir, report, 1)
94 + assert {out, 1, {:ok, "conclusion=failure\nsummary=1 to fix, 1 no fix\n"}, args} =
95 + audit(dir, report, 1)
96 +
95 97 assert args == ~w(scan source --format json -r .)
96 98
97 99 [fix_now, blocked] = String.split(out, "No fix available / blocked")
@@ -108,7 +110,7 @@ defmodule GitGud.Scripts.OsvAuditTest do
108 110 ])
109 111 ])
110 112
111 − assert {out, 0, {:ok, "conclusion=warning\n"}, _} = audit(dir, report, 1)
113 + assert {out, 0, {:ok, "conclusion=warning\nsummary=1 no fix\n"}, _} = audit(dir, report, 1)
112 114 assert out =~ "Fix now: 0"
113 115 assert out =~ "No fix available / blocked: 1"
114 116 end

Parents: 0d6bdf0