mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
fix(devcontainer): drop single-file binds — fixes Codex batchWrite failed in TUI
On Docker Desktop Windows the named volumes are ext4 (`/dev/sdd`) while single-file bind mounts from the Windows host land as 9p (drvfs). Different filesystems → atomic config writes (write `foo.tmp`, then rename onto `foo`) trip EXDEV `inter-device move failed` / `Device or resource busy`. Codex's TUI surfaces this as `config/batchWrite failed in TUI` when saving model preference. Claude's writes to settings.json / .claude.json fail the same way, silently. Reproduction in container: $ echo x > /tmp/foo.toml; mv /tmp/foo.toml /home/node/.codex/config.toml mv: inter-device move failed: ... Device or resource busy Fix: drop the three single-file bind mounts. Sync host's versions into the named volume on container-create via `sync_from_host` (same pattern already used for credentials). Atomic rename within the volume works because everything is ext4. Trade-off: container writes to these files no longer propagate to host; they stay in the volume until next rebuild, which re-syncs from host. Host is source of truth on rebuild — same model as credentials. Plugin/ skill/agent/memory/command DIRS still bind-mount bidirectionally (atomic writes within a dir bind stay on one filesystem, no EXDEV). Files affected: - ~/.codex/config.toml - ~/.claude/settings.json - ~/.claude.json (HOME-level — added `/host/.claude.json` RO mount back for sync_from_host to read)
This commit is contained in:
parent
ed03ae3ea8
commit
2374e2c55b
2 changed files with 26 additions and 3 deletions
|
|
@ -108,17 +108,30 @@
|
|||
// `/Users/x/...` on macOS) so it CANNOT be shared as-is — stays in
|
||||
// the named volume, generated by post-create.sh from host's versions
|
||||
// with paths translated to the container's Linux paths.
|
||||
// DIRECTORY binds — bidirectional, atomic writes inside the dir work
|
||||
// fine (same filesystem). Sub-path writes from container go to host
|
||||
// immediately; host changes are visible to container immediately.
|
||||
"source=${localEnv:HOME}/.claude/plugins/marketplaces,target=/home/node/.claude/plugins/marketplaces,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/plugins/cache,target=/home/node/.claude/plugins/cache,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/skills,target=/home/node/.claude/skills,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/agents,target=/home/node/.claude/agents,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/memory,target=/home/node/.claude/memory,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/commands,target=/home/node/.claude/commands,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/settings.json,target=/home/node/.claude/settings.json,type=bind",
|
||||
"source=${localEnv:HOME}/.claude.json,target=/home/node/.claude.json,type=bind",
|
||||
"source=${localEnv:HOME}/.codex/config.toml,target=/home/node/.codex/config.toml,type=bind",
|
||||
"source=${localEnv:HOME}/.codex/memories,target=/home/node/.codex/memories,type=bind",
|
||||
"source=${localEnv:HOME}/.codex/skills,target=/home/node/.codex/skills,type=bind",
|
||||
//
|
||||
// SINGLE-FILE binds for settings.json / .claude.json / config.toml
|
||||
// are deliberately ABSENT. On Docker Desktop Windows the named-volume
|
||||
// is ext4 (/dev/sdd) and a single-file bind from host is 9p drvfs —
|
||||
// different filesystems. Atomic config writes (write `foo.tmp` ->
|
||||
// rename onto `foo`) trip EXDEV and fail with `Device or resource
|
||||
// busy` / `inter-device move failed`. Codex's TUI hits this as
|
||||
// "config/batchWrite failed in TUI"; Claude silently loses writes
|
||||
// through the same mechanism. Instead: host stage at /host/.claude
|
||||
// (RO) + post-create.sh copies these files into the named volume on
|
||||
// every container-create. Host changes propagate on rebuild;
|
||||
// container changes stay container-local until rebuild.
|
||||
"source=${localEnv:HOME}/.claude.json,target=/host/.claude.json,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",
|
||||
|
|
|
|||
|
|
@ -73,6 +73,16 @@ sync_from_host \
|
|||
sync_from_host \
|
||||
/host/.claude/.claude.json /home/node/.claude/.claude.json 644
|
||||
|
||||
# State files that USED to be single-file bind mounts but couldn't be: on
|
||||
# Docker Desktop Windows the named volume (ext4) and the host bind-mount
|
||||
# (9p drvfs) are different filesystems, so atomic config writes
|
||||
# (`tmp -> rename onto target`) trip EXDEV / Device-or-resource-busy.
|
||||
# Copy host's version into the named volume on container-create; container
|
||||
# can rewrite freely from there until next rebuild resyncs.
|
||||
sync_from_host /host/.claude/settings.json /home/node/.claude/settings.json 644
|
||||
sync_from_host /host/.claude.json /home/node/.claude.json 644
|
||||
sync_from_host /host/.codex/config.toml /home/node/.codex/config.toml 644
|
||||
|
||||
# Plugin registry path translation. Claude writes absolute OS-native paths
|
||||
# into known_marketplaces.json (`installLocation`), installed_plugins.json
|
||||
# (`installPath`), and plugin-catalog-cache.json — `C:\Users\X\.claude\...`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue