From ea58f6f5cecd53e3303e255d44a80a55bff600d0 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Fri, 6 Nov 2026 08:55:51 +0100 Subject: [PATCH] fix: refresh_stale --dry-run honours --json --dry-run printed its text plan and returned before --json was read, so a caller parsing the output as JSON failed. With both flags the plan is printed as JSON: planned jobs with their commands and follow-ups, the rows left to a human, and the count ignored. --- scripts/refresh_stale.py | 18 +++++++++++++++++- tests/test_refresh_stale.py | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/scripts/refresh_stale.py b/scripts/refresh_stale.py index 4e79e70a5..8e1221ec2 100755 --- a/scripts/refresh_stale.py +++ b/scripts/refresh_stale.py @@ -579,7 +579,7 @@ def main() -> int: parser.add_argument( "--json", action="store_true", - help="machine-readable summary (jobs, surfaced, ignored)", + help="machine-readable summary (jobs, surfaced, ignored), the plan with --dry-run", ) parser.add_argument( "--findings", @@ -618,6 +618,22 @@ def main() -> int: registry = _load_platform_registry() jobs, surfaced, ignored = plan_jobs(findings, registry) + if args.dry_run and args.json: + print(json.dumps({ + "planned": [ + { + "area": job.area, + "subject": job.subject, + "command": job.command, + "after": sorted(job.after), + "then": [list(command) for command in job.then], + } + for job in sorted(jobs, key=lambda j: (j.area, j.subject)) + ], + "surfaced": surfaced, + "ignored_count": len(ignored), + }, indent=2)) + return 0 if args.dry_run: print(f"\n[plan] {len(jobs)} refreshable, {len(surfaced)} manual, " f"{len(ignored)} already clean/skipped") diff --git a/tests/test_refresh_stale.py b/tests/test_refresh_stale.py index 3310d068e..dedd6629b 100644 --- a/tests/test_refresh_stale.py +++ b/tests/test_refresh_stale.py @@ -234,6 +234,28 @@ class SavedFindings(unittest.TestCase): findings = rs.load_findings(path, ("data", "catalogs")) self.assertEqual([f["subject"] for f in findings], ["dolphin-sys"]) + def test_dry_run_json_prints_the_plan_as_json(self): + """--dry-run --json printed the text plan, and a caller parsing it + as JSON failed on the first line.""" + import json # noqa: PLC0415 + import subprocess # noqa: PLC0415 + + with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp" / "tests") as directory: + path = Path(directory) / "freshness.json" + path.write_text(json.dumps([ + finding("catalogs", "mame recipes"), finding("coreinfo", "anybor"), + ]), encoding="utf-8") + proc = subprocess.run( + [sys.executable, "scripts/refresh_stale.py", "--findings", str(path), + "--dry-run", "--json"], + cwd=REPO_ROOT, capture_output=True, text=True, check=False, timeout=120, + ) + self.assertEqual(proc.returncode, 0, proc.stderr) + plan = json.loads(proc.stdout) + self.assertEqual([(j["area"], j["subject"]) for j in plan["planned"]], + [("catalogs", "mame recipes")]) + self.assertEqual([f["subject"] for f in plan["surfaced"]], ["anybor"]) + def test_a_report_that_is_not_a_list_is_refused(self): with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp" / "tests") as directory: path = Path(directory) / "freshness.json"