feat(smolmachines): provision_ca + provision_git + provision_supervise (PRD 0023 chunk 4d)
End-to-end provisioning parity with the docker backend. After this
chunk a smolmachines bottle has a working trust store, git-gate
gitconfig, and supervise MCP registration — same shape as docker,
dispatched via `smolvm machine cp` / `smolvm machine exec` instead
of `docker cp` / `docker exec`.
Adds three new provision modules:
- ca.py: select egress vs pipelock CA (same logic as
docker), machine cp + update-ca-certificates,
log sha256 fingerprint.
- git.py: copy host .git when --cwd was passed; render
~/.gitconfig with insteadOf URLs. URL prefix is
`git://<bundle_ip>:9418/...` (no DNS in the
TSI-allowlisted guest) vs docker's
`git://git-gate/...`.
- supervise.py: `claude mcp add` via machine_exec; URL is
`http://<bundle_ip>:9100/`. Failure is logged but
non-fatal (matches docker).
Shared render: `render_git_gate_gitconfig` moves out of
backend/docker/provision/git.py into the platform-neutral
claude_bottle/git_gate.py (renamed to git_gate_render_gitconfig
for consistency with the existing git_gate_render_* helpers),
parameterized on a `gate_host` argument so both backends use the
same logic with different addresses.
Path/user fixups for the post-chunk-4c agent image (real
claude-bottle image, USER node, $HOME=/home/node):
- prompt.py default path moves from /root/... to
/home/node/.claude-bottle-prompt.txt; chown + chmod after
machine cp.
- skills.py default skills dir moves from /root/.claude/skills to
/home/node/.claude/skills; chown -R per skill.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit was merged in pull request #72.
This commit is contained in:
@@ -1,19 +1,28 @@
|
||||
"""Unit: render of ~/.gitconfig pushInsteadOf rules (PRD 0008)."""
|
||||
"""Unit: render of ~/.gitconfig insteadOf rules (PRD 0008).
|
||||
|
||||
The render moved to `claude_bottle.git_gate` so both backends
|
||||
share it; tests live here because docker's provision_git is the
|
||||
original consumer."""
|
||||
|
||||
import unittest
|
||||
|
||||
from claude_bottle.backend.docker.provision.git import render_git_gate_gitconfig
|
||||
from claude_bottle.git_gate import (
|
||||
GIT_GATE_HOSTNAME,
|
||||
git_gate_render_gitconfig,
|
||||
)
|
||||
from tests.fixtures import fixture_minimal, fixture_with_git
|
||||
|
||||
|
||||
class TestGitGateGitconfigRender(unittest.TestCase):
|
||||
def test_empty_entries_renders_nothing(self):
|
||||
bottle = fixture_minimal().bottles["dev"]
|
||||
self.assertEqual("", render_git_gate_gitconfig(bottle.git))
|
||||
self.assertEqual(
|
||||
"", git_gate_render_gitconfig(bottle.git, GIT_GATE_HOSTNAME),
|
||||
)
|
||||
|
||||
def test_one_block_per_entry(self):
|
||||
bottle = fixture_with_git().bottles["dev"]
|
||||
out = render_git_gate_gitconfig(bottle.git)
|
||||
out = git_gate_render_gitconfig(bottle.git, GIT_GATE_HOSTNAME)
|
||||
# Both entries map to a [url ...] block keyed on the gate's
|
||||
# short network alias (`git-gate`) inside the sidecar bundle.
|
||||
self.assertIn(
|
||||
@@ -37,10 +46,19 @@ class TestGitGateGitconfigRender(unittest.TestCase):
|
||||
# gate push and leave fetch on the original URL — exactly the
|
||||
# v1 design we've moved past.
|
||||
bottle = fixture_with_git().bottles["dev"]
|
||||
out = render_git_gate_gitconfig(bottle.git)
|
||||
out = git_gate_render_gitconfig(bottle.git, GIT_GATE_HOSTNAME)
|
||||
self.assertIn("\tinsteadOf", out)
|
||||
self.assertNotIn("pushInsteadOf", out)
|
||||
|
||||
def test_gate_host_can_be_ip_port_form(self):
|
||||
# The smolmachines backend's TSI-allowlisted guest has no
|
||||
# DNS, so it dials git-gate via `<bundle_ip>:<port>`.
|
||||
bottle = fixture_with_git().bottles["dev"]
|
||||
out = git_gate_render_gitconfig(bottle.git, "192.168.20.2:9418")
|
||||
self.assertIn(
|
||||
'[url "git://192.168.20.2:9418/claude-bottle.git"]', out,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user