aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-05 05:30:32 -0500
committerCraig Jennings <c@cjennings.net>2026-05-05 05:30:32 -0500
commit4414d9e4d3c1a3113e38353e38ec11d6de6ba8d8 (patch)
tree56f80596311f400b13f0675ee8e85d780a696feb /scripts
parent35131cb72c08c657d2a3389338d0c049d57e69bd (diff)
downloademacs-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-xscripts/coverage-summary.py56
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))