feat(devcontainer): refresh dev image and auto-update Claude Code#154
feat(devcontainer): refresh dev image and auto-update Claude Code#154dpage wants to merge 4 commits into
Conversation
Updates the development container to base on Go 1.25 Trixie, add the Claude Code devcontainer feature, mount the host's gh config and Claude commands/settings into the container, forward the dev/docs/MCP ports, and pass GH_TOKEN through. postCreateCommand symlinks the mounted Claude config into ~/.claude, adds a `claude --dangerously-skip-permissions` alias, and runs `sudo env "PATH=$PATH" npm update -g @anthropic-ai/claude-code` so the update can write to the root-owned global node_modules while still locating the nvm-managed npm binary. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughUpdates ChangesDevcontainer Configuration Update
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Up to standards ✅🟢 Issues
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
.devcontainer/dev/devcontainer.json (1)
27-28: 💤 Low valueStale template comments are now misleading.
The commented-out
"features": {}(line 28) suggests features aren't configured, but they are (lines 7–15). Similarly, the commented-out"postCreateCommand": "go version"(line 48) shadows the activepostCreateCommandon line 25. Both should be removed.🧹 Suggested cleanup
- // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - // Use 'forwardPorts' to make a list of ports inside the container available locally.- // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "go version", - // Configure tool-specific properties.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.devcontainer/dev/devcontainer.json around lines 27 - 28, Remove the stale commented template properties that are misleading: delete the commented-out "features": {} entry and the commented-out "postCreateCommand": "go version" so only the active "features" block (lines with the actual features) and the active postCreateCommand remain; search for the literal strings "features": {} and "postCreateCommand": "go version" in .devcontainer/dev/devcontainer.json and remove their commented instances.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.devcontainer/dev/devcontainer.json:
- Line 25: The postCreateCommand currently force-adds an alias 'alias
claude="claude --dangerously-skip-permissions"' into ~/.bashrc which silently
disables safety prompts; remove that hardcoded alias from the
"postCreateCommand" invocation and instead implement an opt-in flow: either (A)
append a visible commented snippet or instructions to ~/.bashrc (not an active
alias) that explain how to enable the flag manually, and add a postAttachCommand
that prints a clear one-time notice about the bypass and how to opt in, or (B)
document the opt-in step in README and leave no changes to ~/.bashrc; update any
references to the alias in the repo accordingly and ensure the unique string
alias claude="claude --dangerously-skip-permissions" is no longer silently
injected.
- Line 19: The devcontainer bind uses
"source=${localEnv:HOME}/.claude/settings.json,target=/home/vscode/.claude-host/settings.json,type=bind,readonly"
which will fail or create a host dir if the file is missing; update
initializeCommand to check for the existence of
${localEnv:HOME}/.claude/settings.json and either create a sensible default file
or emit a clear error and abort container start, or alternatively make the
postCreateCommand symlink conditional (only create the symlink if the target
file exists) so the container start won't fail or corrupt the host filesystem.
- Around line 21-24: The initializeCommand export of GH_TOKEN won't propagate to
containerEnv because localEnv is resolved before initializeCommand runs; fix by
having initializeCommand write the token to a file (e.g., create a .env with
GH_TOKEN=<token>) and then pass that file into the container via runArgs (e.g.,
--env-file .env) so the container receives GH_TOKEN, or alternatively remove
initializeCommand and containerEnv and rely on the existing ~/.config/gh
bind-mount for authentication; adjust the keys initializeCommand, containerEnv,
GH_TOKEN, runArgs and the .env filename accordingly.
---
Nitpick comments:
In @.devcontainer/dev/devcontainer.json:
- Around line 27-28: Remove the stale commented template properties that are
misleading: delete the commented-out "features": {} entry and the commented-out
"postCreateCommand": "go version" so only the active "features" block (lines
with the actual features) and the active postCreateCommand remain; search for
the literal strings "features": {} and "postCreateCommand": "go version" in
.devcontainer/dev/devcontainer.json and remove their commented instances.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a77f92dc-2d2a-47db-b708-150b891cf9ef
📒 Files selected for processing (1)
.devcontainer/dev/devcontainer.json
- Drop trailing comma after the 8080 portsAttributes entry so the
Jackson Linter (strict JSON) stops flagging line 45.
- Make the ~/.claude/settings.json symlink in postCreateCommand
conditional on the bind-mounted file existing, so machines without
a host-side settings.json do not start with a dangling symlink.
- Replace the broken initializeCommand/containerEnv GH_TOKEN dance
(localEnv is resolved before initializeCommand runs, so the export
never reached the container) with an export appended to ~/.bashrc
that uses the already bind-mounted ~/.config/gh credentials.
- Surface the claude --dangerously-skip-permissions alias via a new
postAttachCommand notice and document the unalias / direct binary
escape hatch, so the bypass is no longer silently in effect.
- Remove the stale commented-out template stubs ("features": {} and
"postCreateCommand": "go version") that no longer matched reality.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
.devcontainer/dev/devcontainer.json (1)
18-19:⚠️ Potential issue | 🟠 Major | ⚡ Quick winOptional Claude bind mounts can still block container startup.
Line 18 and Line 19 are bind mounts, so those host paths must exist before the container starts. The
if [ -f ... ]check inpostCreateCommandruns too late; a machine without~/.claude/commandsor~/.claude/settings.jsonwill fail during startup before that script executes.How does the devcontainer.json "mounts" field map to Docker bind mounts, and what happens when a bind mount source path like ~/.claude/commands or ~/.claude/settings.json does not exist on the host?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.devcontainer/dev/devcontainer.json around lines 18 - 19, The two entries in .devcontainer/dev/devcontainer.json under "mounts" are translated to Docker bind mounts (source=${localEnv:HOME}/.claude/commands and source=${localEnv:HOME}/.claude/settings.json) and Docker will try to mount those host paths at container start — if the host file/path doesn't exist the bind will fail and block startup. Fix this by removing those unconditional bind mounts and instead handle them at runtime: delete the two mount lines and add logic in postCreateCommand (or postStartCommand) to check for ${localEnv:HOME}/.claude/commands and ${localEnv:HOME}/.claude/settings.json and copy or link them into /home/vscode/.claude-host only when present, or document that users must create those host files; reference the exact mount entries shown ("source=${localEnv:HOME}/.claude/commands,target=/home/vscode/.claude-host/commands,type=bind,readonly" and "source=${localEnv:HOME}/.claude/settings.json,target=/home/vscode/.claude-host/settings.json,type=bind,readonly") when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.devcontainer/dev/devcontainer.json:
- Line 21: The current postCreateCommand appends "export GH_TOKEN=$(gh auth
token ...)" to ~/.bashrc which doesn't expose GH_TOKEN to non-interactive
processes; instead remove that append and set GH_TOKEN using devcontainer.json's
remoteEnv or containerEnv so the variable is available container-wide; update
the postCreateCommand (the "postCreateCommand" entry) to stop relying on
~/.bashrc and, if you need to obtain the token at creation time via gh, capture
it in the devcontainer build step and populate remoteEnv/containerEnv GH_TOKEN
accordingly so VS Code tasks and extensions can read it.
---
Duplicate comments:
In @.devcontainer/dev/devcontainer.json:
- Around line 18-19: The two entries in .devcontainer/dev/devcontainer.json
under "mounts" are translated to Docker bind mounts
(source=${localEnv:HOME}/.claude/commands and
source=${localEnv:HOME}/.claude/settings.json) and Docker will try to mount
those host paths at container start — if the host file/path doesn't exist the
bind will fail and block startup. Fix this by removing those unconditional bind
mounts and instead handle them at runtime: delete the two mount lines and add
logic in postCreateCommand (or postStartCommand) to check for
${localEnv:HOME}/.claude/commands and ${localEnv:HOME}/.claude/settings.json and
copy or link them into /home/vscode/.claude-host only when present, or document
that users must create those host files; reference the exact mount entries shown
("source=${localEnv:HOME}/.claude/commands,target=/home/vscode/.claude-host/commands,type=bind,readonly"
and
"source=${localEnv:HOME}/.claude/settings.json,target=/home/vscode/.claude-host/settings.json,type=bind,readonly")
when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: acd3d016-91c6-4b6c-9caa-69379017b7a5
📒 Files selected for processing (1)
.devcontainer/dev/devcontainer.json
CodeRabbit correctly noted that appending `export GH_TOKEN=...` to ~/.bashrc only sets the variable for interactive shells; VS Code tasks, extensions, and other non-interactive processes never see it. No tool inside the dev container actually reads $GH_TOKEN — every reference in the repo is in GitHub Actions workflows that run on hosted runners, not here. The bind-mounted ~/.config/gh already authenticates the `gh` CLI itself, and consumers that need the raw token can call `gh auth token` explicitly. Remove the bashrc export and add a short comment documenting how auth flows so the next reader does not re-add a plumbing line that does not work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.devcontainer/dev/devcontainer.json:
- Line 24: The postCreateCommand currently appends "alias claude=\"claude
--dangerously-skip-permissions\"" to ~/.bashrc unconditionally, causing
duplicate entries on rebuilds; modify the postCreateCommand so it checks for the
exact alias line first (e.g., grep -qxF or test with grep) and only appends it
if missing, ensuring the alias addition is idempotent while leaving the rest of
the command sequence (mkdir/ln -sf/npm update) unchanged and still referencing
the same alias text used in the current command.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 25b4f174-2898-45f2-b3a8-e2ed5c0ccf6f
📒 Files selected for processing (1)
.devcontainer/dev/devcontainer.json
Guard the bashrc alias append with `grep -qxF` so re-running postCreateCommand on container rebuilds does not stack duplicate `alias claude="claude --dangerously-skip-permissions"` lines. Addresses the CodeRabbit follow-up on fbb6092. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
mcr.microsoft.com/devcontainers/go:2-1.25-trixieand replace the Node/docker-in-docker features with thegithub-cliandclaude-codedevcontainer features.~/.config/gh,~/.claude/commands, and~/.claude/settings.jsoninto the container, passGH_TOKENthrough, and forward the Vite (5173), docs (8000), and MCP (8080) ports.postCreateCommand, symlink the mounted Claude config under~/.claude, add aclaude --dangerously-skip-permissionsalias, and auto-update@anthropic-ai/claude-codeviasudo env "PATH=$PATH" npm update -g …so the update can write to the root-owned globalnode_moduleswhile still locating the nvm-managednpm.Test plan
postCreateCommandcompletes without the priorEACCES/npm: command not founderrors.claude --versioninside the container reflects the latest published release after rebuild.gh auth statusworks inside the container (mounted gh config +GH_TOKEN).🤖 Generated with Claude Code
Summary by CodeRabbit