From c4709601e76a39b6615a49e54ee130d218f2a442 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 21 Oct 2026 23:02:29 -0700 Subject: [PATCH] fix: reject stale upstream documentation publications --- README.md | 3 +++ scripts/import_docs.py | 7 +++++++ scripts/test_import_docs.py | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/README.md b/README.md index 215648c..af39f9e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ GitHub release asset. These downloads outlive the one-day Pages Actions artifact `scripts/import_docs.py` verifies the download and its embedded release metadata before adding the documentation to `_site/docs/`. Import errors fail the workflow before deployment, leaving the live website intact. +An upstream generation older than the deployed generation also fails the check, +preventing a stale GitHub Pages cache from rolling documentation backward. +Retry after cache propagation if this occurs. The existing publishing workflow checks daily at **10:47 UTC**. A changed publication triggers deployment, including an intentional rebuild of the same diff --git a/scripts/import_docs.py b/scripts/import_docs.py index 328a4b2..ecbaa88 100644 --- a/scripts/import_docs.py +++ b/scripts/import_docs.py @@ -72,6 +72,13 @@ def check_publication(upstream: str, current: str) -> tuple[dict, bool]: f"Deployed publication is unreadable; importing verified upstream: {error}" ) deployed = None + if deployed is not None: + upstream_generation = tuple(map(int, manifest["generation"].split("-"))) + deployed_generation = tuple(map(int, deployed["generation"].split("-"))) + if upstream_generation < deployed_generation: + raise ValueError( + "Upstream publication is older than deployed; retry after Pages cache propagation" + ) return manifest, manifest != deployed diff --git a/scripts/test_import_docs.py b/scripts/test_import_docs.py index cf8c1f8..99cc590 100644 --- a/scripts/test_import_docs.py +++ b/scripts/test_import_docs.py @@ -137,6 +137,11 @@ def test_actual_http_checks_skip_unchanged_and_detect_same_tag_rebuild(self): (self.root / "upstream.json").write_text(json.dumps(updated)) _, changed = check_publication(base + "/upstream.json", base + "/current.json") self.assertTrue(changed) + (self.root / "current.json").write_text(json.dumps(updated)) + (self.root / "upstream.json").write_text(json.dumps(self.manifest)) + with self.assertRaisesRegex(ValueError, "older than deployed"): + check_publication(base + "/upstream.json", base + "/current.json") + (self.root / "upstream.json").write_text(json.dumps(updated)) (self.root / "current.json").write_text("not JSON") _, changed = check_publication(base + "/upstream.json", base + "/current.json") self.assertTrue(changed)