diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-05 05:30:32 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-05 05:30:32 -0500 |
| commit | 4414d9e4d3c1a3113e38353e38ec11d6de6ba8d8 (patch) | |
| tree | 56f80596311f400b13f0675ee8e85d780a696feb /scripts | |
| parent | 35131cb72c08c657d2a3389338d0c049d57e69bd (diff) | |
| download | emacs-wttrin-4414d9e4d3c1a3113e38353e38ec11d6de6ba8d8.tar.gz emacs-wttrin-4414d9e4d3c1a3113e38353e38ec11d6de6ba8d8.zip | |
ci: add github actions for tests, lint, and coverage
Tests run across an Emacs version matrix (26.3, 27.2, 28.2, 29.4, snapshot) on every push to main and every PR. Lint and coverage run once each on the latest stable Emacs.
The coverage job runs `make coverage`, prints the per-file and overall percentages via `scripts/coverage-summary.py`, and uploads the simplecov JSON as a build artifact (30-day retention). I'm leaving the artifact-only path in place for now and we'll wire up Coveralls in a follow-up once the repo is registered there.
The matrix floor is 26.3 even though Package-Requires says 24.4. The setup-emacs action doesn't reliably support 24.x or early 25.x anymore, and the recent if-let find shows we hadn't actually been testing the stated minimum. Honest CI floor here is more useful than an aspirational one.
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/coverage-summary.py | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/scripts/coverage-summary.py b/scripts/coverage-summary.py new file mode 100755 index 0000000..9b7bc99 --- /dev/null +++ b/scripts/coverage-summary.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Print a per-file and overall coverage summary from undercover's simplecov JSON. + +Usage: + python3 scripts/coverage-summary.py [path] + +If `path` is omitted, defaults to `.coverage/simplecov.json`. +Exit code is 0 on success, 1 if the JSON is missing or malformed. +""" + +import json +import os +import sys + + +def main(path: str) -> int: + try: + with open(path) as f: + data = json.load(f) + except FileNotFoundError: + print(f"error: {path} not found; run `make coverage` first", file=sys.stderr) + return 1 + except json.JSONDecodeError as exc: + print(f"error: {path} is not valid JSON: {exc}", file=sys.stderr) + return 1 + + try: + suite = data["undercover.el"]["coverage"] + except (KeyError, TypeError): + print(f"error: {path} does not look like an undercover simplecov report", + file=sys.stderr) + return 1 + + print(f'{"File":<30} {"Lines":>7} {"Covered":>8} {"Coverage":>10}') + print("-" * 60) + + total_lines = 0 + total_covered = 0 + for fname, lines in suite.items(): + relevant = [l for l in lines if l is not None] + covered = sum(1 for l in relevant if l > 0) + pct = 100.0 * covered / len(relevant) if relevant else 0.0 + total_lines += len(relevant) + total_covered += covered + short = os.path.basename(fname) + print(f"{short:<30} {len(relevant):>7} {covered:>8} {pct:>9.2f}%") + + print("-" * 60) + overall = 100.0 * total_covered / total_lines if total_lines else 0.0 + print(f'{"TOTAL":<30} {total_lines:>7} {total_covered:>8} {overall:>9.2f}%') + return 0 + + +if __name__ == "__main__": + target = sys.argv[1] if len(sys.argv) > 1 else ".coverage/simplecov.json" + sys.exit(main(target)) |
