diff --git a/scripts/check_buildbot_system.py b/scripts/check_buildbot_system.py index 3346f98a..4958def2 100644 --- a/scripts/check_buildbot_system.py +++ b/scripts/check_buildbot_system.py @@ -205,10 +205,17 @@ def main() -> None: sys.exit(1) # Unreachable upstream means the freshness question was not answered, and - # a zero exit says it was answered "fresh". - if report.get("error"): + # a zero exit says it was answered "fresh". A new asset nobody tracks, or + # a tracked one the buildbot dropped, is a change to act on: a zero exit + # there let the online pipeline call the system directory current. + if report.get("error") or pending(report): raise SystemExit(1) +def pending(report: dict) -> list[dict]: + """Entries a maintainer has to act on: not tracked yet, or gone.""" + return [e for e in report.get("entries", []) if e["status"] in ("NEW", "STALE")] + + if __name__ == "__main__": main() diff --git a/scripts/check_freshness.py b/scripts/check_freshness.py index 5ba10aeb..801a0c69 100644 --- a/scripts/check_freshness.py +++ b/scripts/check_freshness.py @@ -685,9 +685,33 @@ def check_data(offline: bool) -> list[Finding]: findings.append(Finding("data", key, STALE, detail="upstream changed, refresh_data_dirs.py fetches it")) else: findings.append(Finding("data", key, OK)) + findings.append(_check_buildbot_system()) return findings +def _check_buildbot_system() -> Finding: + """The buildbot's system directory against what _data_dirs.yml tracks. + + The registry entries are refreshed above; an asset published since, or + one the buildbot no longer serves, is only seen by reading its .index. + """ + import check_buildbot_system # noqa: PLC0415 + + with contextlib.redirect_stdout(io.StringIO()): + report = check_buildbot_system.check( + str(REPO_ROOT / "platforms" / "_data_dirs.yml") + ) + if report.get("error"): + return Finding("data", "buildbot system", UNKNOWN, detail="index unreachable") + todo = check_buildbot_system.pending(report) + if not todo: + return Finding("data", "buildbot system", OK) + names = ", ".join(f"{e['status'].lower()} {e['zip']}" for e in todo[:SHOWN_NAMES]) + more = ", ..." if len(todo) > SHOWN_NAMES else "" + return Finding("data", "buildbot system", STALE, local=f"{len(todo)} to track", + detail=f"{names}{more} (_data_dirs.yml)") + + # --- catalogs and recipes --------------------------------------------------- diff --git a/tests/test_check_freshness.py b/tests/test_check_freshness.py index 4117a9ea..97add807 100644 --- a/tests/test_check_freshness.py +++ b/tests/test_check_freshness.py @@ -381,5 +381,28 @@ class AStaticTargetStillNamesItsCores(unittest.TestCase): self.assertLess(static, source.index("_declared_cores(platform)")) + +class TheBuildbotIndexIsRead(unittest.TestCase): + """A new asset in buildbot's assets/system was seen only by + check_buildbot_system, which exited 0, as a non-blocking step.""" + + def test_new_and_dropped_assets_are_stale(self): + from unittest import mock + + import check_buildbot_system as cbs + + report = {"entries": [ + {"zip": "New.zip", "status": "NEW", "key": None}, + {"zip": "Old.zip", "status": "OK", "key": "old"}, + ]} + with mock.patch.object(cbs, "check", lambda registry: report): + finding = cf._check_buildbot_system() + self.assertEqual(finding.status, cf.STALE) + self.assertIn("new New.zip", finding.detail) + with mock.patch.object(cbs, "check", lambda registry: {"error": "x", "entries": []}): + self.assertEqual(cf._check_buildbot_system().status, cf.UNKNOWN) + self.assertEqual([e["zip"] for e in cbs.pending(report)], ["New.zip"]) + + if __name__ == "__main__": unittest.main()