From 4beddc902ddf726bb1579b56d83700c2184429a1 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Sun, 1 Nov 2026 00:19:06 -0500 Subject: [PATCH] Build the index again: a package's former repository, named by an older pydevices release, maps to its new one --- docs/how-it-works/index.md | 2 ++ pins.json | 10 ++++++++-- tests/test_pins.py | 34 ++++++++++++++++++++++++++++++++++ tools/pins.py | 6 +++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/docs/how-it-works/index.md b/docs/how-it-works/index.md index 618af8c..9a74843 100644 --- a/docs/how-it-works/index.md +++ b/docs/how-it-works/index.md @@ -10,6 +10,8 @@ A release in any of the source repositories sends this repository a request nami [`publish.yml`](../../.github/workflows/publish.yml) records each request in [`pins.json`](../../pins.json) as soon as it arrives. It then rebuilds every package from the releases pinned there with pydevices' `tools/libbuild.py`, and deploys the result to GitHub Pages. +When a package moves to a new repository, its entry in `pins.json` names the new one and lists the old under `formerly`. A pydevices release made before the move still names the old repository in its `lib-lock.json`; the build takes the package from the new one at the pinned tag. + Builds queue, one at a time, and a newer build can replace one that's still waiting. Nothing is lost when that happens: every build starts from the pins as recorded so far, so each request is in the index by the next build that finishes. You can also start a run by hand from the Actions tab, to rebuild from the pins as they are. diff --git a/pins.json b/pins.json index e8706c3..97cd7e2 100644 --- a/pins.json +++ b/pins.json @@ -21,10 +21,16 @@ }, "audioinstruments": { "repository": "PyDevices/audioinstruments", - "ref": "v0.5.0" + "ref": "v0.5.0", + "formerly": [ + "PyDevices/audiocomponents" + ] }, "audioeffects": { "repository": "PyDevices/audioeffects", - "ref": "v0.5.0" + "ref": "v0.5.0", + "formerly": [ + "PyDevices/audiocomponents" + ] } } diff --git a/tests/test_pins.py b/tests/test_pins.py index 55f52f1..3b70734 100644 --- a/tests/test_pins.py +++ b/tests/test_pins.py @@ -176,3 +176,37 @@ def test_a_refused_request_pushes_nothing(self): if __name__ == "__main__": unittest.main() + + +class Locking(unittest.TestCase): + """lock() writes pins.json's tags into a root's lib-lock.json.""" + + def setUp(self): + self._tmp = tempfile.TemporaryDirectory() + self.addCleanup(self._tmp.cleanup) + self.dir = Path(self._tmp.name) + self.saved = pins.PINS + self.addCleanup(lambda: setattr(pins, "PINS", self.saved)) + pins.PINS = self.dir / "pins.json" + + def lock(self, pin, locked): + pins.save({"audioinstruments": pin}) + root = self.dir / "root" + root.mkdir() + (root / "lib-lock.json").write_text(json.dumps({"pinned": {"audioinstruments": locked}})) + pins.lock(root) + return json.loads((root / "lib-lock.json").read_text())["pinned"]["audioinstruments"] + + def test_a_root_that_names_a_former_repository_is_locked_to_the_new_one(self): + entry = self.lock( + {"repository": "PyDevices/audioinstruments", "ref": "v0.5.0", "formerly": ["PyDevices/audiocomponents"]}, + {"repository": "PyDevices/audiocomponents", "ref": "v0.4.0"}, + ) + self.assertEqual(entry, {"repository": "PyDevices/audioinstruments", "ref": "v0.5.0"}) + + def test_a_root_that_names_another_repository_is_refused(self): + with self.assertRaises(SystemExit): + self.lock( + {"repository": "PyDevices/audioinstruments", "ref": "v0.5.0", "formerly": ["PyDevices/audiocomponents"]}, + {"repository": "PyDevices/somewhere-else", "ref": "v0.4.0"}, + ) diff --git a/tools/pins.py b/tools/pins.py index 2d13f08..e3d2bf4 100644 --- a/tools/pins.py +++ b/tools/pins.py @@ -186,7 +186,11 @@ def lock(root: Path) -> None: if pin is None or not pin["ref"]: raise SystemExit(f"lib-lock.json pins {name!r}, which pins.json does not") if pin["repository"] != entry["repository"]: - raise SystemExit(f"{name}: pins.json says {pin['repository']}, lib-lock.json says {entry['repository']}") + # A root released before a package moved still names its old + # repository: pins.json lists that as the package's "formerly". + if entry["repository"] not in pin.get("formerly", ()): + raise SystemExit(f"{name}: pins.json says {pin['repository']}, lib-lock.json says {entry['repository']}") + entry["repository"] = pin["repository"] entry["ref"] = pin["ref"] path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")