refactor(docker): split provision into provision_prompt / _ssh / _git
test / run tests/run_tests.py (pull_request) Successful in 13s

provision now orchestrates three focused sub-methods. Each sub-method
self-gates: provision_ssh is a no-op when the bottle has no SSH
entries; provision_git is a no-op when --cwd was not set. The prompt
copy + chown always runs (so the path always exists in-container);
the return is gated on whether the agent has a non-empty prompt.
This commit is contained in:
2026-05-11 00:20:22 -04:00
parent 133a7a39e7
commit 5a024259a6
+27 -6
View File
@@ -269,6 +269,20 @@ class DockerBottleBackend(BottleBackend):
f"got {type(plan).__name__}" f"got {type(plan).__name__}"
) )
container = target container = target
prompt_path = self.provision_prompt(plan, container)
agent = plan.spec.manifest.agents[plan.spec.agent_name]
if agent.skills:
skills_mod.skills_copy_into(container, list(agent.skills))
self.provision_ssh(plan, container)
self.provision_git(plan, container)
return prompt_path
def provision_prompt(self, plan: DockerBottlePlan, container: str) -> str | None:
"""Copy the prompt file into the container, fix ownership/mode.
Returns the in-container path if the agent has a non-empty
prompt (drives --append-system-prompt-file), else None. The
file is copied either way so the path always exists."""
container_home = os.environ.get("CLAUDE_BOTTLE_CONTAINER_HOME", "/home/node") container_home = os.environ.get("CLAUDE_BOTTLE_CONTAINER_HOME", "/home/node")
in_container_prompt_path = f"{container_home}/.claude-bottle-prompt.txt" in_container_prompt_path = f"{container_home}/.claude-bottle-prompt.txt"
@@ -291,15 +305,24 @@ class DockerBottleBackend(BottleBackend):
) )
agent = plan.spec.manifest.agents[plan.spec.agent_name] agent = plan.spec.manifest.agents[plan.spec.agent_name]
if agent.skills: return in_container_prompt_path if agent.prompt else None
skills_mod.skills_copy_into(container, list(agent.skills))
def provision_ssh(self, plan: DockerBottlePlan, container: str) -> None:
"""If the bottle has SSH entries, set up the in-container
ssh-agent and config so node can authenticate without ever
seeing the key bytes. No-op when the bottle has no SSH."""
bottle = plan.spec.manifest.bottle_for(plan.spec.agent_name) bottle = plan.spec.manifest.bottle_for(plan.spec.agent_name)
if bottle.ssh: if not bottle.ssh:
return
proxy_host_port = pipelock.pipelock_proxy_host_port(plan.slug) proxy_host_port = pipelock.pipelock_proxy_host_port(plan.slug)
ssh_mod.ssh_setup(container, plan.stage_dir, proxy_host_port, bottle.ssh) ssh_mod.ssh_setup(container, plan.stage_dir, proxy_host_port, bottle.ssh)
if plan.spec.copy_cwd and Path(plan.spec.user_cwd, ".git").is_dir(): def provision_git(self, plan: DockerBottlePlan, container: str) -> None:
"""If --cwd was set and the host cwd has a .git directory, copy
it into /home/node/workspace/.git and fix ownership. No-op
otherwise."""
if not (plan.spec.copy_cwd and Path(plan.spec.user_cwd, ".git").is_dir()):
return
info(f"copying {plan.spec.user_cwd}/.git -> {container}:/home/node/workspace/.git") info(f"copying {plan.spec.user_cwd}/.git -> {container}:/home/node/workspace/.git")
subprocess.run( subprocess.run(
["docker", "cp", f"{plan.spec.user_cwd}/.git", f"{container}:/home/node/workspace/.git"], ["docker", "cp", f"{plan.spec.user_cwd}/.git", f"{container}:/home/node/workspace/.git"],
@@ -315,8 +338,6 @@ class DockerBottleBackend(BottleBackend):
check=True, check=True,
) )
return in_container_prompt_path if agent.prompt else None
# --- Cleanup --- # --- Cleanup ---
def prepare_cleanup(self) -> DockerBottleCleanupPlan: def prepare_cleanup(self) -> DockerBottleCleanupPlan: