refactor(backend): move git-gate provisioning to a neutral module

`backend/docker/gateway_provision.py` had no docker-specific code left once
`DockerGatewayTransport` moved out — `provision_git_gate` / `deprovision_git_gate`
drive any `GatewayTransport`, and the guest-side paths they write
(`/git-gate/creds/<id>`, `/git/<id>`, `/etc/git-gate/...`) are identical inside
every backend's gateway. Yet the neutral `backend/consolidated_util.py` reached
into the docker package to import them.

Move it to `backend/gateway_provision.py`, a sibling of its only importer. The
gateway *package* can't host it (git_gate already imports gateway.git_gate,
so gateway importing git_gate would cycle), but the backend layer uses git_gate
freely. Docstrings + the git_gate/service.py pointer updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 13:59:11 -04:00
parent e1fd8ef1b4
commit 343f3a0735
7 changed files with 13 additions and 11 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ from ..git_gate import GitGatePlan
from ..orchestrator.client import OrchestratorClient, RegisteredBottle
from ..orchestrator.registration import registration_inputs
from ..orchestrator.store.secret_store import new_env_var_secret
from .docker.gateway_provision import GatewayTransport, deprovision_git_gate, provision_git_gate
from .gateway_provision import GatewayTransport, deprovision_git_gate, provision_git_gate
def provision_bottle(
@@ -2,7 +2,7 @@
How the launcher stages files + runs commands in the running gateway container:
`docker exec` / `docker cp` over the docker socket. The backend-neutral
provisioning logic that drives it lives in `gateway_provision`.
provisioning logic that drives it lives in `backend.gateway_provision`.
"""
from __future__ import annotations
@@ -4,7 +4,7 @@
How the launcher stages files + runs commands in the running gateway: the
docker exec/cp equivalents over SSH (dropbear + the stable infra key). The
backend-neutral provisioning logic that drives it lives in
`backend.docker.gateway_provision`.
`backend.gateway_provision`.
"""
from __future__ import annotations
@@ -1,12 +1,14 @@
"""Provision one bottle's git-gate state into the running shared gateway
(PRD 0070, docker slice).
(PRD 0070). Backend-neutral: it drives any `GatewayTransport` (docker/apple
exec+cp, firecracker SSH), and the guest-side paths it writes are identical
inside every backend's gateway because they all run the same gateway image.
The consolidated gateway serves every bottle's repos under `/git/<bottle_id>/`
with per-repo credentials under `/git-gate/creds/<bottle_id>/`. When a bottle
is registered the launcher must place *its* deploy keys + known_hosts into
that per-bottle creds dir and init its bare repos there so this copies the
credential files into the live gateway container and runs the (namespaced,
init-only) provisioning script produced by `git_gate_render_provision`.
credential files into the live gateway and runs the (namespaced, init-only)
provisioning script produced by `git_gate_render_provision`.
Isolating each bottle's creds dir + repo root by id is what keeps one
bottle's push credentials out of another's repos on the shared gateway.
@@ -16,8 +18,8 @@ from __future__ import annotations
import re
from ...git_gate import GitGatePlan, git_gate_render_provision
from ...gateway import GatewayProvisionError, GatewayTransport
from ..git_gate import GitGatePlan, git_gate_render_provision
from ..gateway import GatewayProvisionError, GatewayTransport
# bottle ids index the gateway's per-bottle repo + creds dirs; they land in
# exec/cp path arguments, so validate before any path is built (a traversal
@@ -2,7 +2,7 @@
How the launcher stages files + runs commands in the running gateway container:
the `container` CLI's exec/cp equivalents. The backend-neutral provisioning
logic that drives it lives in `backend.docker.gateway_provision`; Docker uses
logic that drives it lives in `backend.gateway_provision`; Docker uses
`docker exec`/`docker cp` and Firecracker uses SSH.
"""
+1 -1
View File
@@ -60,7 +60,7 @@ class GitGate:
# `sh`, so the script needs the x bit. The gateway copy does not
# necessarily preserve this mode (`docker cp` does, the Apple `container
# cp` does not), so provisioning re-applies +x on the gateway side — see
# backend/docker/gateway_provision.py.
# backend/gateway_provision.py.
access_hook.chmod(0o700)
upstreams_with_files: list[GitGateUpstream] = []
for u in upstreams:
+1 -1
View File
@@ -6,7 +6,7 @@ import unittest
from pathlib import Path
from unittest.mock import Mock, patch
from bot_bottle.backend.docker.gateway_provision import (
from bot_bottle.backend.gateway_provision import (
GatewayProvisionError,
deprovision_git_gate,
provision_git_gate,