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.client import OrchestratorClient, RegisteredBottle
from ..orchestrator.registration import registration_inputs from ..orchestrator.registration import registration_inputs
from ..orchestrator.store.secret_store import new_env_var_secret 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( def provision_bottle(
@@ -2,7 +2,7 @@
How the launcher stages files + runs commands in the running gateway container: How the launcher stages files + runs commands in the running gateway container:
`docker exec` / `docker cp` over the docker socket. The backend-neutral `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 from __future__ import annotations
@@ -4,7 +4,7 @@
How the launcher stages files + runs commands in the running gateway: the How the launcher stages files + runs commands in the running gateway: the
docker exec/cp equivalents over SSH (dropbear + the stable infra key). The docker exec/cp equivalents over SSH (dropbear + the stable infra key). The
backend-neutral provisioning logic that drives it lives in backend-neutral provisioning logic that drives it lives in
`backend.docker.gateway_provision`. `backend.gateway_provision`.
""" """
from __future__ import annotations from __future__ import annotations
@@ -1,12 +1,14 @@
"""Provision one bottle's git-gate state into the running shared gateway """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>/` 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 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 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 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, credential files into the live gateway and runs the (namespaced, init-only)
init-only) provisioning script produced by `git_gate_render_provision`. provisioning script produced by `git_gate_render_provision`.
Isolating each bottle's creds dir + repo root by id is what keeps one 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. bottle's push credentials out of another's repos on the shared gateway.
@@ -16,8 +18,8 @@ from __future__ import annotations
import re import re
from ...git_gate import GitGatePlan, git_gate_render_provision from ..git_gate import GitGatePlan, git_gate_render_provision
from ...gateway import GatewayProvisionError, GatewayTransport from ..gateway import GatewayProvisionError, GatewayTransport
# bottle ids index the gateway's per-bottle repo + creds dirs; they land in # 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 # 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: How the launcher stages files + runs commands in the running gateway container:
the `container` CLI's exec/cp equivalents. The backend-neutral provisioning 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. `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 # `sh`, so the script needs the x bit. The gateway copy does not
# necessarily preserve this mode (`docker cp` does, the Apple `container # necessarily preserve this mode (`docker cp` does, the Apple `container
# cp` does not), so provisioning re-applies +x on the gateway side — see # 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) access_hook.chmod(0o700)
upstreams_with_files: list[GitGateUpstream] = [] upstreams_with_files: list[GitGateUpstream] = []
for u in upstreams: for u in upstreams:
+1 -1
View File
@@ -6,7 +6,7 @@ import unittest
from pathlib import Path from pathlib import Path
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from bot_bottle.backend.docker.gateway_provision import ( from bot_bottle.backend.gateway_provision import (
GatewayProvisionError, GatewayProvisionError,
deprovision_git_gate, deprovision_git_gate,
provision_git_gate, provision_git_gate,