GitNexus/.devcontainer/devcontainer.json
Gergo Magyar 326cb06fe7 fix(devcontainer): install gitnexus-web before gitnexus in postCreateCommand
The previous order (root → gitnexus-shared → gitnexus → gitnexus-web)
broke at the `gitnexus` install step because `gitnexus`'s `prepare`
script runs `scripts/build.js`, which compiles `gitnexus-web` whenever
its source tree exists. In the devcontainer the entire workspace is
bind-mounted, so `gitnexus-web/` is present from the start — but its
`node_modules/` wasn't yet, so `tsc -b` failed with:

  error TS2688: Cannot find type definition file for 'vite/client'
  error TS2688: Cannot find type definition file for 'node'

Reorder so `gitnexus-web` installs before `gitnexus`. Verified
end-to-end via `npx @devcontainers/cli up`: container builds clean,
all three CLIs (Claude 2.1.153, Codex 0.134.0, Cursor) respond, and
`npx tsc --noEmit` inside `/workspace/gitnexus` passes.

Production Dockerfiles (`Dockerfile.cli` etc.) don't hit this because
they only COPY `gitnexus/` + `gitnexus-shared/`, so `gitnexus-web/`
doesn't exist at install time and `scripts/build.js` skips the web
step. The devcontainer's full-tree bind mount changes that calculus.
2026-05-28 11:58:12 +01:00

116 lines
5 KiB
JSON

// Devcontainer for GitNexus. Pre-installs Claude Code, OpenAI Codex CLI,
// and Cursor CLI alongside the Node.js native build chain. Cross-platform
// (Win11+WSL2 + macOS + Linux), opened via the VS Code Dev Containers
// extension.
//
// First-time setup, auth flows, and troubleshooting: .devcontainer/README.md.
{
"name": "GitNexus AI CLI Devcontainer",
"build": {
"dockerfile": "Dockerfile",
"context": ".",
"args": {
"CLAUDE_CODE_VERSION": "2.1.153",
"CODEX_VERSION": "0.134.0",
"CURSOR_VERSION": "latest",
"TZ": "${localEnv:TZ:UTC}"
}
},
// Anthropic's official Claude Code Feature pulls the latest stable at
// build time; DISABLE_AUTOUPDATER below locks it inside the running
// container so rebuild is the only way the version changes.
"features": {
"ghcr.io/anthropics/devcontainer-features/claude-code:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"remoteUser": "node",
"updateRemoteUserUID": true,
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
"workspaceFolder": "/workspace",
// Named volumes scoped per-devcontainer keep auth tokens, history, and
// node_modules persistent across rebuilds without leaking between
// workspaces. Sub-workspace node_modules volumes keep tree-sitter native
// binaries and onnxruntime off the bind mount (the real Win/Mac perf win).
"mounts": [
"source=claude-config-${devcontainerId},target=/home/node/.claude,type=volume",
"source=codex-config-${devcontainerId},target=/home/node/.codex,type=volume",
"source=cursor-config-${devcontainerId},target=/home/node/.cursor,type=volume",
"source=commandhistory-${devcontainerId},target=/commandhistory,type=volume",
"source=npm-cache-${devcontainerId},target=/home/node/.npm,type=volume",
"source=${localWorkspaceFolderBasename}-root-node-modules,target=/workspace/node_modules,type=volume",
"source=${localWorkspaceFolderBasename}-gitnexus-node-modules,target=/workspace/gitnexus/node_modules,type=volume",
"source=${localWorkspaceFolderBasename}-gitnexus-web-node-modules,target=/workspace/gitnexus-web/node_modules,type=volume",
"source=${localWorkspaceFolderBasename}-gitnexus-shared-node-modules,target=/workspace/gitnexus-shared/node_modules,type=volume"
],
// Interactive login is the default auth path for all three CLIs;
// credentials persist in the per-devcontainer named volumes mounted above.
// API keys (ANTHROPIC_API_KEY / OPENAI_API_KEY / CURSOR_API_KEY) are NOT
// injected via containerEnv — `${localEnv:VAR}` resolves an unset host var
// to an empty string, and Cursor in particular treats `CURSOR_API_KEY=""`
// as "use this empty key" rather than "fall back to stored login", which
// would silently break `cursor-agent login`. Users who need API key auth
// should `export` the var in their container shell or carry it via their
// VS Code dotfiles repo (see .devcontainer/README.md).
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/home/node/.claude",
"DISABLE_AUTOUPDATER": "1",
"HISTFILE": "/commandhistory/.zsh_history"
},
"customizations": {
"vscode": {
"extensions": [
"anthropic.claude-code",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"eamodio.gitlens"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"files.eol": "\n",
"terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.profiles.linux": {
"bash": { "path": "bash", "icon": "terminal-bash" },
"zsh": { "path": "zsh" }
}
}
}
},
// 4747 (gitnexus serve) must not be remapped: gitnexus-web hardcodes
// http://localhost:4747 as the default backend URL.
"forwardPorts": [5173, 4747, 4173],
"portsAttributes": {
"5173": {
"label": "Vite dev (gitnexus-web)",
"onAutoForward": "notify"
},
"4747": {
"label": "gitnexus serve HTTP API",
"onAutoForward": "notify",
"requireLocalPort": true
},
"4173": {
"label": "Static web (Vite preview)",
"onAutoForward": "silent"
}
},
// Sequential setup: chown the workspace-side node_modules volumes (Docker
// creates them root-owned), then install in dependency order:
// root (husky) → gitnexus-shared (install + build, consumed via file:..)
// → gitnexus-web (must install BEFORE gitnexus, because gitnexus's
// `prepare` script runs scripts/build.js which compiles gitnexus-web)
// → gitnexus (last; its prepare hook needs gitnexus-web's node_modules).
"postCreateCommand": "sudo chown -R node:node /workspace/node_modules /workspace/gitnexus/node_modules /workspace/gitnexus-web/node_modules /workspace/gitnexus-shared/node_modules && cd /workspace && npm install && cd /workspace/gitnexus-shared && npm install && npm run build && cd /workspace/gitnexus-web && npm install && cd /workspace/gitnexus && npm install"
}