Switch to native Claude Code installer and add update command

Replace the deprecated npm installation with the native installer. The
binary is installed to /usr/local/bin so it survives the home directory
bind mount at runtime.

Add a 'claude update' subcommand that rebuilds the image with the latest
Claude Code binary and removes all existing containers.

Disable the in-container auto-updater since the binary lives in the
read-only image layer.
This commit is contained in:
Jeena 2026-03-18 03:53:55 +00:00
parent bfcb79a890
commit 4605a62d90
3 changed files with 64 additions and 2 deletions

View file

@ -41,6 +41,9 @@ class ClaudeContainer:
return self._get_xdg_data_home() / 'claude-container' / 'container-home'
def run(self, args: list[str]) -> None:
if args and args[0] == "update":
self.update()
return
self.container_home_path.mkdir(parents=True, exist_ok=True)
if self.container_exists() and self.container_running():
logger.error(f"Project '{self.project_path.name}' already has a running Claude container.")
@ -86,6 +89,40 @@ class ClaudeContainer:
str(self.docker_context_dir),
])
def update(self) -> None:
logger.info("Updating Claude Code...")
logger.info("Removing existing containers...")
result = subprocess.run(
["docker", "ps", "-a", "--filter", "name=^cc-", "--format", "{{.Names}}"],
capture_output=True,
text=True,
)
for name in result.stdout.strip().splitlines():
if name:
logger.info(f" Removing container '{name}'")
subprocess.run(
["docker", "rm", "-f", name],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
if self.image_exists():
logger.info(f"Removing image '{self.IMAGE}'...")
subprocess.run(
["docker", "rmi", self.IMAGE],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
logger.info("Rebuilding image with latest Claude Code...")
self._run([
"docker", "build", "--no-cache",
"--build-arg", f"USERNAME={self.host_username}",
"--build-arg", f"UID={self.host_uid}",
"--build-arg", f"GID={self.host_gid}",
"-t", self.IMAGE,
str(self.docker_context_dir),
])
logger.info("Update complete. Containers will be recreated on next run.")
def create_container(self) -> None:
logger.info(f"Creating container '{self.container_name}'")
self._run([
@ -118,7 +155,7 @@ class ClaudeContainer:
)
def exec_claude(self, args: list[str]) -> None:
env_args = []
env_args = ["-e", "DISABLE_AUTOUPDATER=1"]
for var in ("ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"):
val = os.environ.get(var)
if val: