From 213390a4b5a6116fad189d01fd59e2e6be2919be Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Thu, 28 May 2026 14:14:36 +0100 Subject: [PATCH] fix(devcontainer): drop ~/.gitconfig bind mount; defer to VS Code auto-copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VS Code's Dev Containers extension auto-copies the host's gitconfig into the container at attach time using `(dd ...) >> /home/node/.gitconfig`. A read-only bind mount of ~/.gitconfig blocks that write, so attach failed with `cannot create /home/node/.gitconfig: Read-only file system`. Making it read-write would let the append succeed, but the bind mount means the host file and the container file are the same file — VS Code's append would double the host gitconfig contents on every container start. Drop the ~/.gitconfig bind mount entirely. VS Code's auto-copy is the purpose-built mechanism for this, gives the container the host's user.name / user.email transparently, and avoids both the read-only write failure and the append-duplication trap. The container ends up with a writable /home/node/.gitconfig that's a copy of the host's, not a mount. The remaining six bind mounts (.claude, .codex, .cursor, .ssh, .config/git, .config/gh) keep their existing modes — XDG-style git config under ~/.config/git is unaffected by VS Code's auto-copy (which only targets ~/.gitconfig), so its read-only bind mount stays. Also remove the `.gitconfig` touch from ensure-host-config-dirs.cjs (now unnecessary) and update the README CLI-state table, sharing explanation, and troubleshooting row to reflect that gitconfig flows in via VS Code auto-copy rather than the bind mount. --- .devcontainer/README.md | 7 ++++--- .devcontainer/devcontainer.json | 1 - .devcontainer/ensure-host-config-dirs.cjs | 10 ++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 28eb091df..c898676a7 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -83,16 +83,17 @@ The following directories inside the container are **bind-mounted directly from | `~/.claude` | `$HOME/.claude` | read-write | | `~/.codex` | `$HOME/.codex` | read-write | | `~/.cursor` | `$HOME/.cursor` | read-write | -| `~/.gitconfig` | `$HOME/.gitconfig` | **read-only** | | `~/.config/git` | `$HOME/.config/git` | **read-only** | | `~/.ssh` | `$HOME/.ssh` | **read-only** | | `~/.config/gh` | `$HOME/.config/gh` | read-write | +`~/.gitconfig` is **not** bind-mounted — VS Code's Dev Containers extension auto-copies the host's gitconfig into the container at attach time (this is built-in behavior, not something this devcontainer configures). The bind-mount approach conflicts with that auto-copy mechanism, so we let VS Code own it. The end result is the same: your host's `user.name` / `user.email` are available inside the container. + That means: - **Authentication is shared.** If you're already logged in on the host (`claude login`, `codex login`, `cursor-agent login`, `gh auth login`), you're already logged in inside the container. No second login step. - **Plugins, skills, agents, memory, and settings sync both ways.** Install a plugin from inside the container and it shows up on the host; add a custom agent on the host and the container sees it immediately. The auto-memory store at `~/.claude/projects//memory/` is the same file tree from both sides. -- **Git identity comes from the host.** Commits from inside the container use your host's `user.name` / `user.email` from `~/.gitconfig` and any XDG-style config under `~/.config/git/`. The mounts are read-only so container-side `git config --global` doesn't leak to host config — set those values from the host shell. +- **Git identity comes from the host.** Commits from inside the container use your host's `user.name` / `user.email` — VS Code's Dev Containers extension auto-copies your `~/.gitconfig` into the container at attach time. Any XDG-style config under `~/.config/git/` flows through via the read-only bind mount. To change git identity, edit `~/.gitconfig` on the host (container-side `git config --global` writes to a container-local file that's discarded on rebuild). - **SSH keys flow through (read-only).** Push over SSH remotes and SSH commit signing work inside the container using your host keys. The mount is read-only so container code can't exfiltrate or modify private keys — agent-perspective, this means you get git operations but the keys stay vendor-side. - **`gh` auth is shared.** `gh pr create`, `gh pr checks`, `gh issue create` work inside the container without re-authenticating. - **No per-workspace duplication.** All your devcontainers across all your projects see the same host CLI state, just like all your host shells do. @@ -223,5 +224,5 @@ Bump `CLAUDE_CODE_VERSION` and `CODEX_VERSION` in `.devcontainer/devcontainer.js | `npm install` fails on tree-sitter-swift / proto / dart | Native build toolchain missing | This shouldn't happen in the devcontainer — verify the apt layer installed `python3 make g++`. If iterating, set `GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1` to skip the vendored grammars | | Integration tests fail with `database busy` | LadybugDB single-writer constraint | Don't run host-side `gitnexus analyze` while the container is also analyzing the same repo; choose one writer | | API key env vars not visible inside the container | They are intentionally not auto-propagated from the host (so an empty/stale host var can't silently break `*-login` for everyone else) | `export ANTHROPIC_API_KEY=...` / `OPENAI_API_KEY=...` / `CURSOR_API_KEY=...` inside the container shell, or carry it via your VS Code [dotfiles repo](https://code.visualstudio.com/docs/devcontainers/containers#_personalizing-with-dotfile-repositories) for persistence | -| `git commit` produces commits with empty author | `~/.gitconfig` source path missing on the host | Set `git config --global user.name` / `user.email` from the host shell, then rebuild. The bind mount is read-only so the values come from the host | +| `git commit` produces commits with empty author | `~/.gitconfig` is missing or empty on the host (VS Code's auto-copy had nothing to copy) | Set `git config --global user.name "Your Name"` and `git config --global user.email "you@example.com"` from the host shell, then rebuild the container | | `gh: not logged in` inside the container | `~/.config/gh/` source path missing on the host | Run `gh auth login` from the host shell (or inside the container once); the auth file lands in the shared mount | diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 75eccc5c3..36d06c987 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -66,7 +66,6 @@ "source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind", "source=${localEnv:HOME}/.codex,target=/home/node/.codex,type=bind", "source=${localEnv:HOME}/.cursor,target=/home/node/.cursor,type=bind", - "source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind,readonly", "source=${localEnv:HOME}/.config/git,target=/home/node/.config/git,type=bind,readonly", "source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,readonly", "source=${localEnv:HOME}/.config/gh,target=/home/node/.config/gh,type=bind", diff --git a/.devcontainer/ensure-host-config-dirs.cjs b/.devcontainer/ensure-host-config-dirs.cjs index a320d31c3..285f1d465 100644 --- a/.devcontainer/ensure-host-config-dirs.cjs +++ b/.devcontainer/ensure-host-config-dirs.cjs @@ -5,8 +5,10 @@ // // Cross-platform via Node's `os.homedir()` (which reads $HOME on POSIX and // %USERPROFILE% on Windows) and `fs.mkdirSync({recursive: true})`. Idempotent -// — `recursive: true` is a no-op when a directory already exists, and the -// `.gitconfig` touch is gated on file existence. +// — each path is skipped if it already exists. `~/.gitconfig` is intentionally +// not handled here: VS Code's Dev Containers extension auto-copies the host +// gitconfig into the container at attach time, so a bind mount conflicts with +// that mechanism and was removed. // // Host prerequisite: Node.js on PATH. This is the only documented host // requirement beyond Docker Desktop and the VS Code Dev Containers @@ -94,7 +96,3 @@ for (const dir of [ fs.mkdirSync(path.join(home, dir), { recursive: true }); } -const gitconfig = path.join(home, ".gitconfig"); -if (!fs.existsSync(gitconfig)) { - fs.closeSync(fs.openSync(gitconfig, "a")); -}