diff --git a/bot_bottle/bottle_state.py b/bot_bottle/bottle_state.py index f611fd1..4f3b04d 100644 --- a/bot_bottle/bottle_state.py +++ b/bot_bottle/bottle_state.py @@ -31,6 +31,7 @@ from __future__ import annotations import dataclasses import json import secrets +import socket import string from dataclasses import dataclass from pathlib import Path @@ -87,6 +88,14 @@ def bottle_identity(agent_name: str) -> str: return f"{slug}-{suffix}" +def globalize_slug(slug: str) -> str: + """Return a globally-unique slug qualified with the current hostname. + + Assumes slug is a value returned from mint_slug. Use wherever a slug + must be unique across hosts (e.g. deploy-key titles).""" + return f"{socket.gethostname()}-{slug}" + + @dataclass(frozen=True) class BottleMetadata: """Persistent record of how a bottle was launched, written at @@ -340,6 +349,7 @@ __all__ = [ "BottleMetadata", "agent_state_dir", "bottle_identity", + "globalize_slug", "bottle_state_dir", "cleanup_state", "clear_preserve_marker", diff --git a/bot_bottle/git_gate_provision.py b/bot_bottle/git_gate_provision.py index 2078c15..00f42fc 100644 --- a/bot_bottle/git_gate_provision.py +++ b/bot_bottle/git_gate_provision.py @@ -10,10 +10,10 @@ from __future__ import annotations import os import dataclasses -import socket from pathlib import Path from typing import TYPE_CHECKING +from .bottle_state import globalize_slug from .errors import MissingEnvVarError from .log import info from .manifest import ManifestBottle, ManifestGitEntry @@ -47,7 +47,7 @@ def _provision_dynamic_key( owner_repo = entry.UpstreamPath if owner_repo.endswith(".git"): owner_repo = owner_repo[:-4] - title = f"bot-bottle:{socket.gethostname()}:{slug}:{entry.Name}" + title = f"bot-bottle:{globalize_slug(slug)}:{entry.Name}" info(f"provisioning deploy key for git-gate.repos[{entry.Name!r}]") key_id, private_key_bytes = provisioner.create(owner_repo, title) diff --git a/tests/unit/test_git_gate_render_provision.py b/tests/unit/test_git_gate_render_provision.py index afe5992..481a63c 100644 --- a/tests/unit/test_git_gate_render_provision.py +++ b/tests/unit/test_git_gate_render_provision.py @@ -127,9 +127,9 @@ class TestProvisionDynamicKey(unittest.TestCase): self.assertEqual(b"PRIVATE-KEY-BYTES", key_file.read_bytes()) id_file = Path(d) / "repo-deploy-key-id" self.assertEqual("kid123", id_file.read_text()) - # owner_repo had .git stripped; title carries hostname + slug + name + # owner_repo had .git stripped; title carries globalize_slug(slug) + name hostname = socket.gethostname() - self.assertEqual([("o/r", f"bot-bottle:{hostname}:myslug:repo")], fake.created) + self.assertEqual([("o/r", f"bot-bottle:{hostname}-myslug:repo")], fake.created) def test_missing_token_raises(self) -> None: with tempfile.TemporaryDirectory() as d, \