From d7a58e52fd2ba5a150b4c1b417e988b1d2fd5b66 Mon Sep 17 00:00:00 2001 From: didericis Date: Fri, 24 Jul 2026 12:53:52 -0400 Subject: [PATCH] refactor(store): consolidate the SQLite store family into bot_bottle.store Move the DbStore base (db_store), the shared schema-migration base (migrations / TableMigrations), the concrete stores (audit_store, config_store, queue_store), and StoreManager out of the package root into a bot_bottle/store/ package, so persistence lives in one place rather than scattered across the root. Callers use direct submodule imports (from bot_bottle.store.store_manager import StoreManager). Inside the moved modules the relative imports point back up to their non-store siblings (..paths, ..supervise_types) while co-moved siblings stay package-local; the flat-import fallbacks are untouched. The orchestrator's own stores (config_store, secret_store, registry) stay in orchestrator/ and repoint to the moved DbStore / TableMigrations. No behavior change; full unit suite green. Co-Authored-By: Claude Opus 4.8 --- bot_bottle/cli/__init__.py | 2 +- bot_bottle/image_cache.py | 2 +- bot_bottle/orchestrator/__main__.py | 2 +- bot_bottle/orchestrator/config_store.py | 4 ++-- bot_bottle/orchestrator/registry.py | 4 ++-- bot_bottle/store/__init__.py | 9 +++++++++ bot_bottle/{ => store}/audit_store.py | 4 ++-- bot_bottle/{ => store}/config_store.py | 2 +- bot_bottle/{ => store}/db_store.py | 0 bot_bottle/{ => store}/migrations.py | 0 bot_bottle/{ => store}/queue_store.py | 4 ++-- bot_bottle/{ => store}/store_manager.py | 2 +- bot_bottle/supervise.py | 6 +++--- tests/unit/test_cli_dispatch.py | 4 ++-- tests/unit/test_config_store.py | 4 ++-- tests/unit/test_db_store.py | 4 ++-- tests/unit/test_orchestrator_control_plane.py | 2 +- tests/unit/test_orchestrator_service.py | 2 +- tests/unit/test_supervise.py | 4 ++-- tests/unit/test_supervise_edge.py | 4 ++-- tests/unit/test_supervise_server.py | 4 ++-- 21 files changed, 39 insertions(+), 30 deletions(-) create mode 100644 bot_bottle/store/__init__.py rename bot_bottle/{ => store}/audit_store.py (97%) rename bot_bottle/{ => store}/config_store.py (98%) rename bot_bottle/{ => store}/db_store.py (100%) rename bot_bottle/{ => store}/migrations.py (100%) rename bot_bottle/{ => store}/queue_store.py (98%) rename bot_bottle/{ => store}/store_manager.py (97%) diff --git a/bot_bottle/cli/__init__.py b/bot_bottle/cli/__init__.py index cf62f68..c1618b6 100644 --- a/bot_bottle/cli/__init__.py +++ b/bot_bottle/cli/__init__.py @@ -10,7 +10,7 @@ import sys from ..errors import MissingEnvVarError from ..log import Die, die, error from ..manifest import ManifestError -from ..store_manager import StoreManager +from ..store.store_manager import StoreManager from ._common import PROG from . import list as _list_mod from .backend import cmd_backend diff --git a/bot_bottle/image_cache.py b/bot_bottle/image_cache.py index b2f802a..ff16544 100644 --- a/bot_bottle/image_cache.py +++ b/bot_bottle/image_cache.py @@ -6,7 +6,7 @@ from datetime import datetime, timezone from pathlib import Path try: - from .config_store import ConfigStore + from .store.config_store import ConfigStore except ImportError: from config_store import ConfigStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module diff --git a/bot_bottle/orchestrator/__main__.py b/bot_bottle/orchestrator/__main__.py index 0869c90..38d4e0c 100644 --- a/bot_bottle/orchestrator/__main__.py +++ b/bot_bottle/orchestrator/__main__.py @@ -16,7 +16,7 @@ import secrets from pathlib import Path from .. import log -from ..store_manager import StoreManager +from ..store.store_manager import StoreManager from .broker import LaunchBroker, StubBroker from .control_plane import make_server from .docker_broker import DockerBroker diff --git a/bot_bottle/orchestrator/config_store.py b/bot_bottle/orchestrator/config_store.py index 2cd6f85..7779b68 100644 --- a/bot_bottle/orchestrator/config_store.py +++ b/bot_bottle/orchestrator/config_store.py @@ -11,8 +11,8 @@ import os import sqlite3 from pathlib import Path -from ..db_store import DbStore -from ..migrations import TableMigrations +from ..store.db_store import DbStore +from ..store.migrations import TableMigrations from ..paths import host_db_path TEARDOWN_TIMEOUT_ENV = "BOT_BOTTLE_ORCHESTRATOR_TEARDOWN_TIMEOUT_SECONDS" diff --git a/bot_bottle/orchestrator/registry.py b/bot_bottle/orchestrator/registry.py index a7f34b1..a5afe4e 100644 --- a/bot_bottle/orchestrator/registry.py +++ b/bot_bottle/orchestrator/registry.py @@ -36,8 +36,8 @@ from collections.abc import Iterable from dataclasses import dataclass from pathlib import Path -from ..db_store import DbStore -from ..migrations import TableMigrations +from ..store.db_store import DbStore +from ..store.migrations import TableMigrations from ..paths import host_db_path # 256 bits of urandom, URL-safe — unguessable per-bottle identity token. diff --git a/bot_bottle/store/__init__.py b/bot_bottle/store/__init__.py new file mode 100644 index 0000000..ec5e39b --- /dev/null +++ b/bot_bottle/store/__init__.py @@ -0,0 +1,9 @@ +"""SQLite-backed persistence for bot-bottle. + +The store family: a shared `DbStore` base (versioned `migrations`) and the +concrete stores built on it — `ConfigStore`, `AuditStore`, `QueueStore` — plus +`StoreManager`, which migrates them together against the one per-host DB. + +Callers import the concrete module they need directly, e.g. +`from bot_bottle.store.store_manager import StoreManager`. +""" diff --git a/bot_bottle/audit_store.py b/bot_bottle/store/audit_store.py similarity index 97% rename from bot_bottle/audit_store.py rename to bot_bottle/store/audit_store.py index 2f48f7c..13de470 100644 --- a/bot_bottle/audit_store.py +++ b/bot_bottle/store/audit_store.py @@ -6,8 +6,8 @@ import sqlite3 from pathlib import Path try: - from .supervise_types import AuditEntry - from .paths import host_db_path + from ..supervise_types import AuditEntry + from ..paths import host_db_path from .db_store import DbStore from .migrations import TableMigrations except ImportError: diff --git a/bot_bottle/config_store.py b/bot_bottle/store/config_store.py similarity index 98% rename from bot_bottle/config_store.py rename to bot_bottle/store/config_store.py index bc2f607..d282d71 100644 --- a/bot_bottle/config_store.py +++ b/bot_bottle/store/config_store.py @@ -7,7 +7,7 @@ from pathlib import Path try: from .db_store import DbStore from .migrations import TableMigrations - from .paths import host_db_path + from ..paths import host_db_path except ImportError: from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module diff --git a/bot_bottle/db_store.py b/bot_bottle/store/db_store.py similarity index 100% rename from bot_bottle/db_store.py rename to bot_bottle/store/db_store.py diff --git a/bot_bottle/migrations.py b/bot_bottle/store/migrations.py similarity index 100% rename from bot_bottle/migrations.py rename to bot_bottle/store/migrations.py diff --git a/bot_bottle/queue_store.py b/bot_bottle/store/queue_store.py similarity index 98% rename from bot_bottle/queue_store.py rename to bot_bottle/store/queue_store.py index 2dae5c9..09ef1cf 100644 --- a/bot_bottle/queue_store.py +++ b/bot_bottle/store/queue_store.py @@ -7,8 +7,8 @@ import sqlite3 from pathlib import Path try: - from .supervise_types import Proposal, Response - from .paths import host_db_path + from ..supervise_types import Proposal, Response + from ..paths import host_db_path from .db_store import DbStore from .migrations import TableMigrations except ImportError: diff --git a/bot_bottle/store_manager.py b/bot_bottle/store/store_manager.py similarity index 97% rename from bot_bottle/store_manager.py rename to bot_bottle/store/store_manager.py index f8807a4..e7729cc 100644 --- a/bot_bottle/store_manager.py +++ b/bot_bottle/store/store_manager.py @@ -26,7 +26,7 @@ class StoreManager: def __init__(self, db_path: Path | None = None) -> None: if db_path is None: try: - from .paths import host_db_path + from ..paths import host_db_path except ImportError: from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module db_path = host_db_path() diff --git a/bot_bottle/supervise.py b/bot_bottle/supervise.py index 184500f..96d25a3 100644 --- a/bot_bottle/supervise.py +++ b/bot_bottle/supervise.py @@ -98,9 +98,9 @@ def audit_log_path(component: str, slug: str) -> Path: try: - from .queue_store import QueueStore - from .audit_store import AuditStore - from .store_manager import StoreManager + from .store.queue_store import QueueStore + from .store.audit_store import AuditStore + from .store.store_manager import StoreManager except ImportError: # Gateway: files are flat-copied under /app, not a package. from queue_store import QueueStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module diff --git a/tests/unit/test_cli_dispatch.py b/tests/unit/test_cli_dispatch.py index 2981f73..e5e10d5 100644 --- a/tests/unit/test_cli_dispatch.py +++ b/tests/unit/test_cli_dispatch.py @@ -12,10 +12,10 @@ from unittest.mock import patch import bot_bottle.cli as climod from bot_bottle.cli import main -from bot_bottle.db_store import DbStore +from bot_bottle.store.db_store import DbStore from bot_bottle.log import Die from bot_bottle.manifest import ManifestError -from bot_bottle.store_manager import StoreManager +from bot_bottle.store.store_manager import StoreManager class TestMainDispatch(unittest.TestCase): diff --git a/tests/unit/test_config_store.py b/tests/unit/test_config_store.py index 5987ae0..0f40576 100644 --- a/tests/unit/test_config_store.py +++ b/tests/unit/test_config_store.py @@ -7,11 +7,11 @@ import tempfile import unittest from pathlib import Path -from bot_bottle.config_store import ( +from bot_bottle.store.config_store import ( DEFAULT_CACHED_IMAGE_STALE_WARNING_DAYS, ConfigStore, ) -from bot_bottle.store_manager import StoreManager +from bot_bottle.store.store_manager import StoreManager class TestConfigStore(unittest.TestCase): diff --git a/tests/unit/test_db_store.py b/tests/unit/test_db_store.py index c447d1d..04046ee 100644 --- a/tests/unit/test_db_store.py +++ b/tests/unit/test_db_store.py @@ -7,8 +7,8 @@ import tempfile import unittest from pathlib import Path -from bot_bottle.db_store import DbStore -from bot_bottle.migrations import TableMigrations +from bot_bottle.store.db_store import DbStore +from bot_bottle.store.migrations import TableMigrations def _store(tmp: Path) -> DbStore: diff --git a/tests/unit/test_orchestrator_control_plane.py b/tests/unit/test_orchestrator_control_plane.py index b400bc5..1cd06b8 100644 --- a/tests/unit/test_orchestrator_control_plane.py +++ b/tests/unit/test_orchestrator_control_plane.py @@ -24,7 +24,7 @@ from bot_bottle.orchestrator.broker import StubBroker from bot_bottle.orchestrator.control_plane import dispatch, make_server from bot_bottle.orchestrator.registry import BottleRecord, RegistryStore from bot_bottle.orchestrator.service import Orchestrator -from bot_bottle.store_manager import StoreManager +from bot_bottle.store.store_manager import StoreManager from bot_bottle.supervise import ( Proposal, TOOL_EGRESS_ALLOW, diff --git a/tests/unit/test_orchestrator_service.py b/tests/unit/test_orchestrator_service.py index e76c1c8..9441654 100644 --- a/tests/unit/test_orchestrator_service.py +++ b/tests/unit/test_orchestrator_service.py @@ -15,7 +15,7 @@ from bot_bottle.orchestrator.broker import LaunchBroker, LaunchRequest, StubBrok from bot_bottle.orchestrator.registry import RegistryStore from bot_bottle.orchestrator.service import Orchestrator from bot_bottle.orchestrator.secret_store import new_env_var_secret -from bot_bottle.store_manager import StoreManager +from bot_bottle.store.store_manager import StoreManager from bot_bottle.supervise import ( Proposal, STATUS_APPROVED, diff --git a/tests/unit/test_supervise.py b/tests/unit/test_supervise.py index 6381af0..e3360a7 100644 --- a/tests/unit/test_supervise.py +++ b/tests/unit/test_supervise.py @@ -10,8 +10,8 @@ from pathlib import Path from bot_bottle import supervise from bot_bottle.paths import host_db_path from tests.unit import use_bottle_root -from bot_bottle.audit_store import AuditStore -from bot_bottle.queue_store import QueueStore +from bot_bottle.store.audit_store import AuditStore +from bot_bottle.store.queue_store import QueueStore from bot_bottle.supervise import ( AuditEntry, Proposal, diff --git a/tests/unit/test_supervise_edge.py b/tests/unit/test_supervise_edge.py index 4cedbfa..acbd420 100644 --- a/tests/unit/test_supervise_edge.py +++ b/tests/unit/test_supervise_edge.py @@ -11,9 +11,9 @@ from pathlib import Path from unittest.mock import patch from bot_bottle import supervise -from bot_bottle.audit_store import AuditStore +from bot_bottle.store.audit_store import AuditStore from bot_bottle.paths import bot_bottle_root -from bot_bottle.queue_store import QueueStore +from bot_bottle.store.queue_store import QueueStore from bot_bottle.supervise import ( AuditEntry, Proposal, diff --git a/tests/unit/test_supervise_server.py b/tests/unit/test_supervise_server.py index c666c55..3f596b0 100644 --- a/tests/unit/test_supervise_server.py +++ b/tests/unit/test_supervise_server.py @@ -19,8 +19,8 @@ from pathlib import Path from tests.unit import use_bottle_root from bot_bottle import supervise as _sv -from bot_bottle import queue_store as _qs -from bot_bottle import audit_store as _as +from bot_bottle.store import queue_store as _qs +from bot_bottle.store import audit_store as _as from bot_bottle import supervise_server # noqa: E402 from bot_bottle.supervise_server import (