mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-30 01:51:20 +00:00
Walkthrough resolution of the 16-finding ce-code-review on PR #1875. 15 of 16 findings applied; one (F12, Anthropic Feature floating tag) was superseded by F6's Feature removal. P0 - F1: WSL2 is now REQUIRED for Windows hosts, not just recommended. ${localEnv:HOME} resolves to empty string on Windows-native (no HOME env var) — bind mounts then point at /.claude, /.codex etc. and silently break. ensure-host-config-dirs.cjs wrote to USERPROFILE-derived paths via os.homedir(), so the two surfaces disagreed about which env var was "home" on Windows. README header reframed; "Windows 11 — WSL2 is required" section explains the mismatch concretely. P1 - F2: Workspace `node_modules` volume names now include `-${devcontainerId}` so two GitNexus checkouts on the same host (~/work/GitNexus and ~/projects/GitNexus) don't share volumes and corrupt each other's installs. - F3 + F5: `postCreateCommand` extracted to `.devcontainer/post-create.sh` with `set -euo pipefail` and six labeled echo steps so failure logs name the step instead of an opaque &&-chain index. Chown step extended to cover /home/node/.npm, /commandhistory, and /home/node/.local — these named-volume mount points were owned by build-time UID 1000 but the container's `node` is re-IDed at runtime by updateRemoteUserUID on non-1000 Linux hosts, leaving them unwritable until now. - F4: Cursor installer downloaded to a temp file with curl --retry + --max-time; sha256 logged to build output before execution so drift across rebuilds is visible in CI logs. Full hard-pin (to a versioned downloads.cursor.com tarball with verified sha256) tracked as a follow-up in README "What's not included". P2 - F6: Anthropic Feature replaced with a direct `npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}` so CLAUDE_CODE_VERSION actually pins the installed binary (the Feature ignored the ARG and pulled latest at install time). Honors the earlier "pin known-good versions" decision and resolves F12's floating-tag concern for this Feature. - F7: Dockerfile ARG defaults dropped for the three version vars; `devcontainer.json` `build.args` is now the single source of truth. Standalone `docker build .devcontainer/` must pass --build-arg. - F8: ensure-host-config-dirs.cjs deleted; `initializeCommand` now uses POSIX `mkdir -p` + `touch ~/.gitconfig` directly, dropping the host-Node-on-PATH prerequisite that broke on fresh Windows+Docker Desktop installs without Node. - F9: ~/.gitconfig bind-mounted read-only so `git commit` inside the container uses the host's user.name / user.email. Read-only so container-side `git config --global` doesn't leak to host. - F10: ~/.config/gh bind-mounted (read-write) so `gh pr create` / `gh pr checks` / `gh issue create` work inside the container without re-auth. AGENTS.md's commit + PR workflow now fully functional for agents inside the container. - F11: CLAUDE_CONFIG_DIR removed from Dockerfile ENV; canonical value lives only in devcontainer.json containerEnv. Eliminates the two-file edit risk. - F13: Mounts comment now documents per-instance vs per-workspace-name scoping rationale so future contributors don't guess. - F14: README "Trust boundary, concretely" paragraph names the exfil path explicitly (malicious npm postinstall → OAuth tokens → ~/.claude/projects/<workspace>/memory/MEMORY.md secrets) and lists vendor-side rotation runbook entries. P3 - F15: Dockerfile pre-create + chown of /home/node/.claude, .codex, .cursor dropped — those paths are bind-mounted, which fully shadows any image-side ownership. Only .npm, .local, /commandhistory still benefit from the pre-create. - F16: README "Bumping CLI versions" section rewritten against the post-F6 reality: CLAUDE_CODE_VERSION and CODEX_VERSION are real pins; CURSOR_VERSION is informational only. Verified locally: `docker build .devcontainer/ --build-arg ...` succeeds. Smoke-tested image: `claude --version` (2.1.153), `codex --version` (0.134.0), `cursor-agent --version` all resolve as the non-root `node` user; named-volume mount points (/home/node/.npm, /commandhistory) are node-owned at build time so non-1000 host UIDs get the post-create.sh chown fix instead of EACCES.
80 lines
3.6 KiB
Docker
80 lines
3.6 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
# Base image: Microsoft's TypeScript+Node devcontainer image. Multi-arch
|
|
# (linux/amd64, linux/arm64), monthly security patching, ships the non-root
|
|
# `node` user (UID 1000), zsh + Oh My Zsh, eslint global, `gh` CLI.
|
|
FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm
|
|
|
|
# Build args. Version defaults are NOT set here — devcontainer.json
|
|
# `build.args` is the single source of truth. Standalone `docker build
|
|
# .devcontainer/` (e.g., CI smoke) must pass each version via --build-arg
|
|
# or the build will fail loudly rather than silently drift from the
|
|
# devcontainer-canonical pin.
|
|
ARG CLAUDE_CODE_VERSION
|
|
ARG CODEX_VERSION
|
|
ARG CURSOR_VERSION
|
|
ARG TZ=UTC
|
|
ARG USERNAME=node
|
|
|
|
# Promote build-only ARGs into runtime ENV so shells and lifecycle scripts
|
|
# can read them. CLAUDE_CONFIG_DIR is intentionally NOT set here — the
|
|
# canonical value lives in devcontainer.json `containerEnv` (single source
|
|
# of truth; runtime-time wins anyway).
|
|
ENV CLAUDE_CODE_VERSION=${CLAUDE_CODE_VERSION} \
|
|
CODEX_VERSION=${CODEX_VERSION} \
|
|
CURSOR_VERSION=${CURSOR_VERSION} \
|
|
TZ=${TZ} \
|
|
DEVCONTAINER=true \
|
|
NODE_OPTIONS=--max-old-space-size=4096 \
|
|
POWERLEVEL9K_DISABLE_GITSTATUS=true
|
|
|
|
# Native build toolchain required by gitnexus/postinstall: tree-sitter
|
|
# native bindings, vendored Dart/Proto/Swift grammars, @ladybugdb/core
|
|
# N-API addon. python3/make/g++ are non-negotiable; mirrors the apt block
|
|
# in the existing Dockerfile.cli / gitnexus/Dockerfile.test images.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
python3 make g++ git curl ca-certificates bash \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Pre-create + chown the named-volume mount points (~/.npm, ~/.local,
|
|
# /commandhistory) so empty volumes inherit `node:node` ownership on first
|
|
# mount. The three CLI config dirs (~/.claude, ~/.codex, ~/.cursor) are
|
|
# bind-mounted from the host — bind mounts fully shadow image-side
|
|
# ownership, so no chown is needed for those paths here.
|
|
RUN mkdir -p \
|
|
/home/${USERNAME}/.npm \
|
|
/home/${USERNAME}/.local/bin \
|
|
/commandhistory \
|
|
&& chown -R ${USERNAME}:${USERNAME} \
|
|
/home/${USERNAME}/.npm \
|
|
/home/${USERNAME}/.local \
|
|
/commandhistory
|
|
|
|
USER ${USERNAME}
|
|
|
|
# Install Claude Code and Codex CLI globally as the `node` user. The base
|
|
# image configures /usr/local/share/npm-global as the npm-global prefix
|
|
# with the `npm` group writable by `node`, so `npm install -g` works
|
|
# without sudo. Both versions are pinned via build args — bump in
|
|
# devcontainer.json and rebuild to upgrade.
|
|
RUN npm install -g \
|
|
@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \
|
|
@openai/codex@${CODEX_VERSION}
|
|
|
|
# Cursor CLI install. The official cursor.com/install script does not
|
|
# expose version pinning or a checksum, so we download to a temp file,
|
|
# log the sha256 to build output (so drift across rebuilds shows up in
|
|
# CI logs), then execute. Trust assumption: cursor.com's TLS chain is
|
|
# reliable. Long-term hardening (tracked as a follow-up): pin a specific
|
|
# downloads.cursor.com/lab/<version>/<arch>/agent-cli-package.tar.gz URL
|
|
# with a hard sha256 verification and skip the install script entirely.
|
|
RUN curl -fsS --retry 3 --max-time 60 -o /tmp/cursor-install.sh https://cursor.com/install \
|
|
&& echo "Cursor installer sha256:" \
|
|
&& sha256sum /tmp/cursor-install.sh \
|
|
&& bash /tmp/cursor-install.sh \
|
|
&& rm -f /tmp/cursor-install.sh
|
|
|
|
# ~/.local/bin (where Cursor's installer drops `agent` and `cursor-agent`
|
|
# symlinks) on PATH for interactive shells and lifecycle scripts.
|
|
ENV PATH=/home/${USERNAME}/.local/bin:${PATH}
|