errands
small jobs the runtime does for the studio.
git clone https://owenrusk.dev/errands.git
commit 2cbf7d78b8c80f8dfb26b428750c88975032b1f7 parent d3a252e97cda58a35c4b188a8dfc608eebe0326b author Owen Rusk <owen@papermothgames.com> date 2026-03-17 15:05:57 -0500
vault: unlock again, once, when bw has dropped the session
| errands/vault.py | +11 | -1 |
| tests/test_vault.py | +12 | -0 |
diff --git a/errands/vault.py b/errands/vault.py index f42617a..aa5f78d 100644 --- a/errands/vault.py +++ b/errands/vault.py @@ -35,12 +35,22 @@ class Vault: def item(self, name: str) -> dict: self.unlock()- found = json.loads(self.bw("list", "items", "--search", name))+ try: + found = json.loads(self.bw("list", "items", "--search", name)) + except subprocess.CalledProcessError: + # bw forgets its sessions when it updates. + self.forget() + self.unlock() + found = json.loads(self.bw("list", "items", "--search", name)) exact = [item for item in found if item["name"] == name] if len(exact) != 1: raise LookupError(f"{len(exact)} items called {name}") return exact[0] + def forget(self) -> None: + self.session = None + (settings.STATE / "bw-session").unlink(missing_ok=True) + def password(self, name: str) -> str: return self.item(name)["login"]["password"] diff --git a/tests/test_vault.py b/tests/test_vault.py index e855872..8f1563a 100644 --- a/tests/test_vault.py +++ b/tests/test_vault.py @@ -16,9 +16,13 @@ ITEMS = [ class FakeBw: def __init__(self): self.calls = [] + self.stale = set() def __call__(self, args, **kwargs): self.calls.append(args[1:]) + session = (kwargs.get("env") or {}).get("BW_SESSION") + if session in self.stale: + raise subprocess.CalledProcessError(1, args, "", "Vault is locked.") if args[1] == "unlock": out = "session-key" elif args[1:3] == ["list", "items"]: @@ -64,6 +68,14 @@ class VaultTest(unittest.TestCase): self.assertEqual(self.vault.note("wifi"), "office network: see the sticker on the router") self.assertEqual(self.vault.note("stillwater api"), "") + def test_stale_session(self): + self.vault.password("stillwater api") + self.bw.stale.add("session-key") + with self.assertRaises(subprocess.CalledProcessError): + # the fake hands out the same key, so the second try fails too and gives up. + self.vault.password("stillwater api") + self.assertEqual(sum(1 for call in self.bw.calls if call[0] == "unlock"), 2) + def test_missing(self): with self.assertRaises(LookupError): self.vault.password("nothing like it")