refactor(store): consolidate the SQLite store family into bot_bottle.store
lint / lint (push) Successful in 54s
test / integration-docker (pull_request) Successful in 38s
test / unit (pull_request) Successful in 45s
test / integration-firecracker (pull_request) Successful in 3m31s
test / coverage (pull_request) Successful in 18s
test / publish-infra (pull_request) Has been skipped
tracker-policy-pr / check-pr (pull_request) Failing after 6s

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 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 12:53:52 -04:00
parent dfce3d9505
commit d7a58e52fd
21 changed files with 39 additions and 30 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ import sys
from ..errors import MissingEnvVarError from ..errors import MissingEnvVarError
from ..log import Die, die, error from ..log import Die, die, error
from ..manifest import ManifestError from ..manifest import ManifestError
from ..store_manager import StoreManager from ..store.store_manager import StoreManager
from ._common import PROG from ._common import PROG
from . import list as _list_mod from . import list as _list_mod
from .backend import cmd_backend from .backend import cmd_backend
+1 -1
View File
@@ -6,7 +6,7 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
try: try:
from .config_store import ConfigStore from .store.config_store import ConfigStore
except ImportError: except ImportError:
from config_store import ConfigStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module from config_store import ConfigStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
+1 -1
View File
@@ -16,7 +16,7 @@ import secrets
from pathlib import Path from pathlib import Path
from .. import log from .. import log
from ..store_manager import StoreManager from ..store.store_manager import StoreManager
from .broker import LaunchBroker, StubBroker from .broker import LaunchBroker, StubBroker
from .control_plane import make_server from .control_plane import make_server
from .docker_broker import DockerBroker from .docker_broker import DockerBroker
+2 -2
View File
@@ -11,8 +11,8 @@ import os
import sqlite3 import sqlite3
from pathlib import Path from pathlib import Path
from ..db_store import DbStore from ..store.db_store import DbStore
from ..migrations import TableMigrations from ..store.migrations import TableMigrations
from ..paths import host_db_path from ..paths import host_db_path
TEARDOWN_TIMEOUT_ENV = "BOT_BOTTLE_ORCHESTRATOR_TEARDOWN_TIMEOUT_SECONDS" TEARDOWN_TIMEOUT_ENV = "BOT_BOTTLE_ORCHESTRATOR_TEARDOWN_TIMEOUT_SECONDS"
+2 -2
View File
@@ -36,8 +36,8 @@ from collections.abc import Iterable
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from ..db_store import DbStore from ..store.db_store import DbStore
from ..migrations import TableMigrations from ..store.migrations import TableMigrations
from ..paths import host_db_path from ..paths import host_db_path
# 256 bits of urandom, URL-safe — unguessable per-bottle identity token. # 256 bits of urandom, URL-safe — unguessable per-bottle identity token.
+9
View File
@@ -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`.
"""
@@ -6,8 +6,8 @@ import sqlite3
from pathlib import Path from pathlib import Path
try: try:
from .supervise_types import AuditEntry from ..supervise_types import AuditEntry
from .paths import host_db_path from ..paths import host_db_path
from .db_store import DbStore from .db_store import DbStore
from .migrations import TableMigrations from .migrations import TableMigrations
except ImportError: except ImportError:
@@ -7,7 +7,7 @@ from pathlib import Path
try: try:
from .db_store import DbStore from .db_store import DbStore
from .migrations import TableMigrations from .migrations import TableMigrations
from .paths import host_db_path from ..paths import host_db_path
except ImportError: except ImportError:
from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module 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 from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
@@ -7,8 +7,8 @@ import sqlite3
from pathlib import Path from pathlib import Path
try: try:
from .supervise_types import Proposal, Response from ..supervise_types import Proposal, Response
from .paths import host_db_path from ..paths import host_db_path
from .db_store import DbStore from .db_store import DbStore
from .migrations import TableMigrations from .migrations import TableMigrations
except ImportError: except ImportError:
@@ -26,7 +26,7 @@ class StoreManager:
def __init__(self, db_path: Path | None = None) -> None: def __init__(self, db_path: Path | None = None) -> None:
if db_path is None: if db_path is None:
try: try:
from .paths import host_db_path from ..paths import host_db_path
except ImportError: except ImportError:
from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
db_path = host_db_path() db_path = host_db_path()
+3 -3
View File
@@ -98,9 +98,9 @@ def audit_log_path(component: str, slug: str) -> Path:
try: try:
from .queue_store import QueueStore from .store.queue_store import QueueStore
from .audit_store import AuditStore from .store.audit_store import AuditStore
from .store_manager import StoreManager from .store.store_manager import StoreManager
except ImportError: except ImportError:
# Gateway: files are flat-copied under /app, not a package. # 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 from queue_store import QueueStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
+2 -2
View File
@@ -12,10 +12,10 @@ from unittest.mock import patch
import bot_bottle.cli as climod import bot_bottle.cli as climod
from bot_bottle.cli import main 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.log import Die
from bot_bottle.manifest import ManifestError 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): class TestMainDispatch(unittest.TestCase):
+2 -2
View File
@@ -7,11 +7,11 @@ import tempfile
import unittest import unittest
from pathlib import Path from pathlib import Path
from bot_bottle.config_store import ( from bot_bottle.store.config_store import (
DEFAULT_CACHED_IMAGE_STALE_WARNING_DAYS, DEFAULT_CACHED_IMAGE_STALE_WARNING_DAYS,
ConfigStore, ConfigStore,
) )
from bot_bottle.store_manager import StoreManager from bot_bottle.store.store_manager import StoreManager
class TestConfigStore(unittest.TestCase): class TestConfigStore(unittest.TestCase):
+2 -2
View File
@@ -7,8 +7,8 @@ import tempfile
import unittest import unittest
from pathlib import Path from pathlib import Path
from bot_bottle.db_store import DbStore from bot_bottle.store.db_store import DbStore
from bot_bottle.migrations import TableMigrations from bot_bottle.store.migrations import TableMigrations
def _store(tmp: Path) -> DbStore: def _store(tmp: Path) -> DbStore:
@@ -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.control_plane import dispatch, make_server
from bot_bottle.orchestrator.registry import BottleRecord, RegistryStore from bot_bottle.orchestrator.registry import BottleRecord, RegistryStore
from bot_bottle.orchestrator.service import Orchestrator 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 ( from bot_bottle.supervise import (
Proposal, Proposal,
TOOL_EGRESS_ALLOW, TOOL_EGRESS_ALLOW,
+1 -1
View File
@@ -15,7 +15,7 @@ from bot_bottle.orchestrator.broker import LaunchBroker, LaunchRequest, StubBrok
from bot_bottle.orchestrator.registry import RegistryStore from bot_bottle.orchestrator.registry import RegistryStore
from bot_bottle.orchestrator.service import Orchestrator from bot_bottle.orchestrator.service import Orchestrator
from bot_bottle.orchestrator.secret_store import new_env_var_secret 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 ( from bot_bottle.supervise import (
Proposal, Proposal,
STATUS_APPROVED, STATUS_APPROVED,
+2 -2
View File
@@ -10,8 +10,8 @@ from pathlib import Path
from bot_bottle import supervise from bot_bottle import supervise
from bot_bottle.paths import host_db_path from bot_bottle.paths import host_db_path
from tests.unit import use_bottle_root from tests.unit import use_bottle_root
from bot_bottle.audit_store import AuditStore from bot_bottle.store.audit_store import AuditStore
from bot_bottle.queue_store import QueueStore from bot_bottle.store.queue_store import QueueStore
from bot_bottle.supervise import ( from bot_bottle.supervise import (
AuditEntry, AuditEntry,
Proposal, Proposal,
+2 -2
View File
@@ -11,9 +11,9 @@ from pathlib import Path
from unittest.mock import patch from unittest.mock import patch
from bot_bottle import supervise 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.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 ( from bot_bottle.supervise import (
AuditEntry, AuditEntry,
Proposal, Proposal,
+2 -2
View File
@@ -19,8 +19,8 @@ from pathlib import Path
from tests.unit import use_bottle_root from tests.unit import use_bottle_root
from bot_bottle import supervise as _sv from bot_bottle import supervise as _sv
from bot_bottle import queue_store as _qs from bot_bottle.store import queue_store as _qs
from bot_bottle import audit_store as _as from bot_bottle.store import audit_store as _as
from bot_bottle import supervise_server # noqa: E402 from bot_bottle import supervise_server # noqa: E402
from bot_bottle.supervise_server import ( from bot_bottle.supervise_server import (