|
4 | 4 | "dockerComposeFile": "docker-compose.yml", |
5 | 5 | "service": "devcontainer", |
6 | 6 | "workspaceFolder": "/workspace", |
7 | | - // initializeCommand: see the default variant. |
| 7 | + // Pull the prebuilt image on the HOST before the build, so "Rebuild Without |
| 8 | + // Cache" always layers on the freshest base. Replaces `pull_policy: always` in |
| 9 | + // docker-compose.yml, which breaks on Linux/WSL2 when `updateRemoteUserUID` |
| 10 | + // builds a local-only image (issue #109). `|| exit 0` keeps offline/outage opens |
| 11 | + // working (valid in POSIX sh and Windows cmd.exe). Keep in sync with the compose image. |
8 | 12 | "initializeCommand": "docker pull ghcr.io/gatezh/devcontainers/claude-code-sandbox:latest || exit 0", |
9 | | - // For the iptables firewall. |
| 13 | + // Capabilities required for iptables firewall setup |
10 | 14 | "capAdd": ["NET_ADMIN", "NET_RAW"], |
11 | 15 | "init": true, |
12 | 16 | "updateRemoteUserUID": true, |
|
16 | 20 | "vscode": { |
17 | 21 | "extensions": [ |
18 | 22 | // **AI Agents** |
| 23 | + // Claude Code - AI coding assistant with chat, inline edits, and terminal integration |
19 | 24 | "anthropic.claude-code", |
20 | 25 |
|
21 | 26 | // **Runtime** |
| 27 | + // Bun - Bun runtime support (debugging, lockfile viewer, bundler integration) |
22 | 28 | "oven.bun-vscode", |
23 | 29 |
|
24 | 30 | // **Code Quality** |
| 31 | + // OXC - Fast linter and formatter for JavaScript/TypeScript (Rust-based) |
25 | 32 | "oxc.oxc-vscode", |
26 | 33 |
|
27 | 34 | // **Tailwind** |
| 35 | + // Tailwind CSS IntelliSense - autocomplete, syntax highlighting, linting for Tailwind classes |
28 | 36 | "bradlc.vscode-tailwindcss", |
| 37 | + // Tailwind Fold - collapse long Tailwind class strings in the editor for readability |
29 | 38 | "stivo.tailwind-fold", |
30 | 39 |
|
31 | 40 | // **General** |
| 41 | + // YAML - YAML language support (for workflows and docker-compose) |
32 | 42 | "redhat.vscode-yaml", |
| 43 | + // Markdown Preview Github Styles - renders Markdown preview with GitHub's CSS |
33 | 44 | "bierner.markdown-preview-github-styles", |
| 45 | + // Markdown Preview Mermaid Support - renders Mermaid diagrams in Markdown preview |
34 | 46 | "bierner.markdown-mermaid", |
| 47 | + // Docker - Dockerfile and Compose syntax, linting, and image management |
35 | 48 | "ms-azuretools.vscode-docker", |
36 | 49 |
|
37 | 50 | // **Spelling** |
| 51 | + // Code Spell Checker - catches typos in code and comments |
38 | 52 | "streetsidesoftware.code-spell-checker", |
39 | 53 |
|
40 | 54 | // **JavaScript / TypeScript** |
| 55 | + // npm IntelliSense - autocomplete npm imports in import statements |
41 | 56 | "christian-kohler.npm-intellisense", |
| 57 | + // TypeScript Error Translator - human-readable TS error messages |
42 | 58 | "mattpocock.ts-error-translator", |
43 | 59 |
|
44 | 60 | // **CSS / Colors** |
| 61 | + // Color Info - preview CSS colors on hover |
45 | 62 | "bierner.color-info", |
| 63 | + // Colorize - visualize CSS colors inline in the editor |
46 | 64 | "kamikillerto.vscode-colorize", |
47 | 65 |
|
48 | 66 | // **Frameworks** |
| 67 | + // Drizzle ORM Snippets - code snippets for Drizzle ORM |
49 | 68 | "drizzle-team.drizzle-orm-snippets", |
50 | 69 |
|
51 | 70 | // **Testing** |
| 71 | + // Playwright - run, debug, and generate Playwright tests from VS Code |
52 | 72 | "ms-playwright.playwright", |
53 | 73 |
|
54 | 74 | // **Sandbox Theme** |
| 75 | + // Claude Theme - provides "Claude Dark" referenced by workbench.colorTheme |
55 | 76 | "SamiHindi.claude-theme-sami-hindi" |
56 | 77 | ], |
57 | 78 | "settings": { |
|
68 | 89 | "source.organizeImports": "explicit" |
69 | 90 | }, |
70 | 91 | "window.title": "${localWorkspaceFolderBasename}", |
71 | | - // Tells a sandbox window apart from the default variant at a glance. |
| 92 | + // Sandbox visual identity — Claude Dark theme with coral remote indicator |
72 | 93 | "workbench.colorTheme": "Claude Dark", |
73 | 94 | "workbench.colorCustomizations": { |
74 | 95 | "statusBarItem.remoteBackground": "#C15F3C", |
|
78 | 99 | } |
79 | 100 | } |
80 | 101 | }, |
81 | | - // Volume names end in ${localWorkspaceFolderBasename} so the default and sandbox |
82 | | - // variants share them. Replace "myproject" with your project name in both variants. |
| 102 | + // Named volumes keep node_modules OFF the host machine and persist across rebuilds. |
| 103 | + // Uses ${localWorkspaceFolderBasename} so default and sandbox variants share volumes. |
| 104 | + // Replace "myproject" with your project name (must match across both variants). |
83 | 105 | "mounts": [ |
84 | | - // Every workspace with a package.json needs its own node_modules volume, or |
85 | | - // node_modules lands in the bind mount and shows up on the host. |
| 106 | + // ── node_modules isolation (one per workspace) ───────────────────── |
| 107 | + // Each workspace with a package.json needs its own volume mount. |
| 108 | + // Without one, node_modules lands in the bind mount and shows up on the host. |
86 | 109 | "source=myproject-node-modules-root-${localWorkspaceFolderBasename},target=/workspace/node_modules,type=volume", |
| 110 | + // ── Monorepo: uncomment and customize for your structure ────────── |
87 | 111 | // "source=myproject-node-modules-api-${localWorkspaceFolderBasename},target=/workspace/services/api/node_modules,type=volume", |
88 | 112 | // "source=myproject-node-modules-web-${localWorkspaceFolderBasename},target=/workspace/apps/web/node_modules,type=volume", |
| 113 | + // ── Persistent config ────────────────────────────────────────────── |
89 | 114 | "source=myproject-claude-config-${localWorkspaceFolderBasename},target=/home/node/.claude,type=volume", |
90 | 115 | "source=myproject-zsh-history-${localWorkspaceFolderBasename},target=/commandhistory,type=volume", |
| 116 | + // gh CLI auth/state (~/.config/gh/hosts.yml) — keeps `gh auth login` across rebuilds |
91 | 117 | "source=myproject-gh-config-${localWorkspaceFolderBasename},target=/home/node/.config/gh,type=volume", |
92 | | - // The image has iptables, ipset and the sudo rule but not the script: each project |
93 | | - // mounts its own, with its own domain allowlist. |
| 118 | + // ── Firewall script ──────────────────────────────────────────────── |
| 119 | + // The image provides iptables/ipset packages and sudo rule but NOT the script itself. |
| 120 | + // Each project provides its own script via bind mount to customize the domain allowlist. |
94 | 121 | "source=${localWorkspaceFolder}/.devcontainer/claude-sandbox/init-firewall.sh,target=/usr/local/bin/init-firewall.sh,type=bind" |
95 | 122 | ], |
96 | 123 | "containerEnv": { |
97 | 124 | "TZ": "${localEnv:TZ:America/Edmonton}", |
98 | 125 | "DEVCONTAINER": "true", |
99 | 126 | "NODE_OPTIONS": "--max-old-space-size=4096", |
100 | 127 | "CLAUDE_CONFIG_DIR": "/home/node/.claude", |
101 | | - // Only needed without the default variant, whose sign-in lands on the shared |
102 | | - // ~/.claude volume. See "Sandbox Authentication" in the README. |
| 128 | + // Optional — only needed when the sandbox is used without the default variant, whose |
| 129 | + // sign-in lands on the shared ~/.claude volume. Resolves to "" when the host var is |
| 130 | + // unset. See "Sandbox Authentication" section in README. |
103 | 131 | "CLAUDE_CODE_OAUTH_TOKEN": "${localEnv:CLAUDE_CODE_OAUTH_TOKEN}" |
104 | 132 | }, |
105 | | - // Plugins install here, before postStartCommand brings the firewall up (which is also |
106 | | - // why Chromium comes from the image). The chowns are the same safety net as in the |
107 | | - // default variant. |
| 133 | + // The find command chowns all node_modules volume mount points in one pass. |
| 134 | + // Chromium is baked into the sandbox image (firewall blocks runtime install). |
| 135 | + // bun install and init-plugins.sh are skipped when their file is absent. Plugins |
| 136 | + // install here, before postStartCommand brings the firewall up. |
108 | 137 | "postCreateCommand": "sudo find /workspace -maxdepth 4 -name node_modules -type d -exec chown node {} + && sudo chown -R node /home/node/.claude /commandhistory /home/node/.config/gh && mise install && if [ -f package.json ]; then bun install; fi && if [ -f .devcontainer/init-plugins.sh ]; then bash .devcontainer/init-plugins.sh; fi", |
109 | | - // patch-playwright-mcp: see the default variant. |
| 138 | + // Firewall init (bind-mounted from project) + re-patch the Playwright MCP |
| 139 | + // plugin's .mcp.json. The patch is defense in depth alongside the SessionStart |
| 140 | + // hook in /etc/claude-code/managed-settings.json, which handles the case where |
| 141 | + // the plugin auto-updates mid-container-run. See issues #85, #87, #98. |
110 | 142 | "postStartCommand": "sudo /usr/local/bin/init-firewall.sh && /usr/local/bin/patch-playwright-mcp", |
111 | 143 | "waitFor": "postStartCommand" |
112 | 144 | } |
0 commit comments