mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-09 03:17:54 +00:00
feat(devcontainer): support Windows-native via auto setx HOME on first run
Reverses the "WSL2 required on Windows" posture. Windows-native now
works after a one-time auto-handled setup.
The root cause of the bind-mount failure: VS Code resolves
`${localEnv:HOME}` by reading its own process env, and Windows doesn't
set `HOME` by default — Windows uses `USERPROFILE`. So the bind sources
were collapsing to `/.claude`, `/.codex`, etc., and Docker rejected them.
`ensure-host-config-dirs.cjs` now handles this automatically on Windows
hosts where `HOME` is unset:
1. Runs `setx HOME "%USERPROFILE%"`, which writes to the user-level
Windows environment (HKCU\Environment) — no admin required. Every
future user process inherits HOME from there.
2. Prints a clear one-time setup banner explaining the user needs to
fully restart VS Code (File > Exit, not just close the window) for
VS Code to pick up the new env at its next startup.
3. Exits 1 so VS Code surfaces this as a clean container-create failure
instead of letting Docker error opaquely later.
On the second Reopen-in-Container attempt, `HOME` is now set in VS
Code's env, the script skips the setup block, creates the bind-mount
source dirs, and the container builds normally. Subsequent rebuilds
have no extra steps.
Mac, Linux, and WSL2 hosts have `HOME` set by the shell, so the new
block is a no-op there. Same `devcontainer.json` works across all
supported hosts.
README rewritten to reflect the new posture:
- Header lists Windows 11 (native) as a supported host alongside macOS,
Linux, and WSL2, with a note that Windows-native gets a one-time
HOME setup handled by the initializeCommand.
- New "Windows 11 setup" section walks through the auto-handled setup
flow + a manual `setx HOME "%USERPROFILE%"` fallback for users who
want to do it themselves.
- "Known trade-offs of Windows-native vs WSL2" subsection lays out the
Docker Desktop Windows bind-mount edge cases (file watchers, npm
install perf, husky/_ EPERM) so users opting into Windows-native do
so eyes-open. WSL2 remains documented as the faster path for users
who want it, but it's no longer the only supported one.
- Troubleshooting table gets two new rows: the one-time setup banner
(with "what to do" instructions) and the residual `bind source path
does not exist` case (run setx manually + fully exit VS Code).
This commit is contained in:
parent
f4dc20289a
commit
a29a1cfb9f
2 changed files with 92 additions and 37 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# GitNexus Devcontainer
|
||||
|
||||
A cross-platform Dev Container that pre-installs Claude Code, OpenAI Codex CLI, and Cursor CLI alongside the GitNexus native build chain. Supported hosts: **macOS, Linux, and Windows 11 via WSL2** (Windows-native is unsupported — the bind mounts require `$HOME` to be set on the host shell, which cmd.exe on Windows-native lacks).
|
||||
A cross-platform Dev Container that pre-installs Claude Code, OpenAI Codex CLI, and Cursor CLI alongside the GitNexus native build chain. Supported hosts: **macOS, Linux, Windows 11 (native), and Windows 11 via WSL2.** Windows-native needs a **one-time `HOME` env var setup** — handled automatically by the `initializeCommand` on first run (see [Windows 11 setup](#windows-11-setup)).
|
||||
|
||||
## Quick start
|
||||
|
||||
|
|
@ -11,11 +11,38 @@ A cross-platform Dev Container that pre-installs Claude Code, OpenAI Codex CLI,
|
|||
5. Wait for the first build (~3–6 minutes) and `postCreateCommand` to finish installing workspace dependencies.
|
||||
6. Authenticate the three CLIs once — see [First-time CLI authentication](#first-time-cli-authentication) below.
|
||||
|
||||
## Windows 11 — WSL2 is required
|
||||
## Windows 11 setup
|
||||
|
||||
**Windows-native is unsupported.** The host bind mounts use `${localEnv:HOME}/.claude` (and `.codex`, `.cursor`, `.ssh`, `.config/git`, `.config/gh`, `.gitconfig`). VS Code resolves `${localEnv:HOME}` by reading the host shell's `HOME` env var — and cmd.exe on Windows-native has no `HOME` set. The bind sources then collapse to filesystem-root paths (`/.claude`, `/.codex`, …) and Docker rejects them with `bind source path does not exist`. The `initializeCommand`'s Node script detects this case and fails fast with a pointer at this section instead of letting Docker error opaquely.
|
||||
### Windows-native (one-time setup, then "just works")
|
||||
|
||||
Beyond the `HOME` resolution issue, WSL2 also gives you reliable file watchers (Vite/jest `--watch` work), 3-5× faster `npm install` IO, and avoids the Docker Desktop Windows bind-mount permission edge cases (e.g., the husky `.husky/_/h` EPERM class).
|
||||
The host bind mounts use `${localEnv:HOME}/.claude` (and `.codex`, `.cursor`, `.ssh`, `.config/git`, `.config/gh`, `.gitconfig`). VS Code resolves `${localEnv:HOME}` by reading its own process env, and Windows doesn't set `HOME` by default — it uses `USERPROFILE`. So the bind mounts can't resolve until you tell Windows to also expose your profile as `HOME`.
|
||||
|
||||
The `initializeCommand` (`node .devcontainer/ensure-host-config-dirs.cjs`) handles this automatically:
|
||||
|
||||
1. **First time you Reopen in Container**, the script detects the missing `HOME`, runs `setx HOME "%USERPROFILE%"` (which writes to your user-level Windows env — no admin needed), prints a one-time setup banner, and exits.
|
||||
2. **Close all VS Code windows** (File → Exit) and reopen. VS Code picks up the new `HOME` at startup.
|
||||
3. **Reopen in Container again.** The script now sees `HOME=C:\Users\<you>`, skips the setup block, creates the bind-mount source dirs, and Docker brings the container up.
|
||||
|
||||
Subsequent rebuilds work normally with no extra steps. The `HOME` env var is set persistently in your Windows user environment, so it'll be there for every future VS Code session (and any other tool that wants `HOME`).
|
||||
|
||||
If you'd rather set it manually before opening the container:
|
||||
|
||||
```powershell
|
||||
setx HOME "%USERPROFILE%"
|
||||
# Close & reopen VS Code
|
||||
```
|
||||
|
||||
### Known trade-offs of Windows-native vs WSL2
|
||||
|
||||
Windows-native works, but Docker Desktop's Windows bind-mount layer has rough edges that WSL2 avoids:
|
||||
|
||||
- **File watchers can miss events.** Vite / jest `--watch` running inside the container watching workspace files mounted from `D:\...` may miss changes — chokidar polling (`CHOKIDAR_USEPOLLING=true`) is the usual workaround.
|
||||
- **`npm install` is 3-5× slower** through the Windows-to-Linux bind-mount translation than on a WSL2-native filesystem.
|
||||
- **Permission edge cases.** The husky `.husky/_/h` EPERM class we hit earlier in this PR is specific to Windows-side bind mounts changing UID ownership between container runs. `post-create.sh` clears the cache defensively to keep this from being fatal, but it's still a real source of friction.
|
||||
|
||||
If you hit any of those and want to migrate to WSL2 later, the steps are below.
|
||||
|
||||
### WSL2 (faster, fewer edge cases)
|
||||
|
||||
To clone and open the repo inside WSL2:
|
||||
|
||||
|
|
@ -187,7 +214,8 @@ Bump `CLAUDE_CODE_VERSION` and `CODEX_VERSION` in `.devcontainer/devcontainer.js
|
|||
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---------|--------------|-----|
|
||||
| `ERROR: GitNexus devcontainer requires WSL2 on Windows 11` from `initializeCommand` | You opened the repo from a Windows-native path; `cmd.exe` has no `$HOME`, so the bind mounts can't resolve | Clone the repo inside WSL2 and reopen — see [Windows 11 — WSL2 is required](#windows-11--wsl2-is-required) |
|
||||
| `GitNexus devcontainer one-time Windows setup` banner from `initializeCommand` | First-time Windows-native Reopen-in-Container; `HOME` env var was missing | The script just ran `setx HOME "%USERPROFILE%"` for you. Close ALL VS Code windows (File → Exit) and reopen — see [Windows 11 setup](#windows-11-setup) |
|
||||
| `bind source path does not exist: /.claude` (or similar) from Docker | Windows-native `HOME` env var is still missing even after one rebuild — `setx` may have failed or VS Code wasn't fully restarted | Run `setx HOME "%USERPROFILE%"` in a Windows shell manually, fully exit VS Code (check Task Manager that no `Code.exe` remains), reopen |
|
||||
| `EACCES` / `EPERM` writing into `~/.claude`, `~/.codex`, or `~/.cursor` inside the container | Stale state from a previous container with a different effective UID | Move the affected dir aside and let the CLI rebuild it (`mv ~/.claude ~/.claude.bak` and log in again). Long-term: WSL2 setup, which doesn't hit this class of issue |
|
||||
| `EPERM: operation not permitted, copyfile ... '.husky/_/h'` in `postCreateCommand` | Leftover `.husky/_/` from a previous container run on a Windows-side bind mount | `post-create.sh` already runs `rm -rf .husky/_` defensively. If you hit this on an older config, delete `.husky/_/` on the host and rebuild. Long-term: clone in WSL2 |
|
||||
| Vite never hot-reloads | Repo cloned on Windows side, not WSL2 | Re-clone inside WSL2 |
|
||||
|
|
|
|||
|
|
@ -16,39 +16,66 @@ const fs = require("fs");
|
|||
const os = require("os");
|
||||
const path = require("path");
|
||||
|
||||
// Fail-fast on Windows-native (no $HOME set). VS Code resolves the bind
|
||||
// mount sources via `${localEnv:HOME}` reading the host shell env, and
|
||||
// cmd.exe on Windows has no HOME variable — so bind sources collapse to
|
||||
// `/.claude`, `/.codex`, etc., and Docker rejects them with
|
||||
// `bind source path does not exist`. Surface the actual root cause here
|
||||
// instead of letting Docker error opaquely later.
|
||||
// Windows-native auto-setup. VS Code resolves the bind-mount sources via
|
||||
// `${localEnv:HOME}` reading its own process env, and Windows doesn't set
|
||||
// `HOME` by default (it uses `USERPROFILE`). Without `HOME`, the bind
|
||||
// sources collapse to filesystem-root paths (`/.claude`, `/.codex`, ...)
|
||||
// and Docker rejects them with `bind source path does not exist`.
|
||||
//
|
||||
// Fix: persist `HOME=%USERPROFILE%` to the user's environment via `setx`.
|
||||
// `setx` writes to `HKCU\Environment` and the new value is inherited by
|
||||
// every process the user launches after — including VS Code after a
|
||||
// restart. The current VS Code process can't see the update (its env was
|
||||
// fixed at launch), so we instruct the user to restart VS Code once.
|
||||
//
|
||||
// Subsequent runs detect `HOME` is set, skip this block, and proceed
|
||||
// normally. Mac/Linux/WSL hosts have `HOME` set by the shell, so this
|
||||
// block is a no-op on those platforms.
|
||||
if (process.platform === "win32" && !process.env.HOME) {
|
||||
console.error("ERROR: GitNexus devcontainer requires WSL2 on Windows 11.");
|
||||
console.error("");
|
||||
console.error(
|
||||
"You opened this from a Windows-native path. The host bind mounts use",
|
||||
);
|
||||
console.error(
|
||||
"`${localEnv:HOME}/.claude` etc., which VS Code resolves from the host shell",
|
||||
);
|
||||
console.error(
|
||||
"env. cmd.exe on Windows has no HOME variable, so the bind sources resolve",
|
||||
);
|
||||
console.error(
|
||||
"to filesystem-root paths (/.claude, /.codex, ...) and Docker rejects them.",
|
||||
);
|
||||
console.error("");
|
||||
console.error("Clone the repo inside WSL2 and re-open from there:");
|
||||
console.error(" wsl");
|
||||
console.error(
|
||||
" cd ~ && git clone https://github.com/abhigyanpatwari/GitNexus.git",
|
||||
);
|
||||
console.error(" cd GitNexus && code .");
|
||||
console.error("");
|
||||
console.error(
|
||||
"See .devcontainer/README.md - Windows 11 - WSL2 is required.",
|
||||
);
|
||||
process.exit(1);
|
||||
const userprofile = process.env.USERPROFILE;
|
||||
if (userprofile) {
|
||||
try {
|
||||
require("child_process").execFileSync("setx", ["HOME", userprofile], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
console.error("");
|
||||
console.error("=".repeat(70));
|
||||
console.error(" GitNexus devcontainer one-time Windows setup");
|
||||
console.error("=".repeat(70));
|
||||
console.error("");
|
||||
console.error(`HOME has been set to %USERPROFILE% (${userprofile}).`);
|
||||
console.error(
|
||||
"VS Code reads this at startup, so the current session can't pick it up.",
|
||||
);
|
||||
console.error("");
|
||||
console.error(
|
||||
" 1. Close ALL VS Code windows (File > Exit, not just the window).",
|
||||
);
|
||||
console.error(
|
||||
" 2. Reopen VS Code, open this folder, and re-run Reopen in Container.",
|
||||
);
|
||||
console.error("");
|
||||
console.error(
|
||||
"This is a one-time setup. Subsequent rebuilds work normally.",
|
||||
);
|
||||
console.error("=".repeat(70));
|
||||
process.exit(1);
|
||||
} catch (err) {
|
||||
console.error("ERROR: failed to set HOME automatically: " + err.message);
|
||||
console.error("");
|
||||
console.error("Run this in a Windows shell, then restart VS Code:");
|
||||
console.error(' setx HOME "%USERPROFILE%"');
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.error("ERROR: neither HOME nor USERPROFILE is set on this host.");
|
||||
console.error("");
|
||||
console.error(
|
||||
"Set HOME to your user profile directory and restart VS Code:",
|
||||
);
|
||||
console.error(' setx HOME "%USERPROFILE%"');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const home = os.homedir();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue