mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-30 01:51:20 +00:00
refactor(devcontainer): split workspace-deps to updateContentCommand
post-create.sh was doing two unrelated jobs: workspace dependency install (four `npm install` runs in topological order) and AI CLI credential sync. They have different lifecycle needs — deps should re-run when lockfiles change, AI sync should run once per container — but both were gated on container-create. Per Dev Container spec lifecycle, `updateContentCommand` is the right hook for workspace deps: runs at container-create AND on content changes (lockfile updates). `postCreateCommand` is right for AI CLI sync: container-create only. Move steps 3-7 (husky cleanup + four `npm install` runs) into install-deps.sh wired as `updateContentCommand`. Split the chown step too — install-deps owns workspace-side dirs (node_modules volumes, ~/.npm), post-create owns AI-side dirs (~/.claude, ~/.codex, ~/.cursor, /commandhistory, ~/.local). Each script now has one concern. post-create.sh drops from ~187 lines to 148; install-deps.sh is 56 lines new. Faster rebuilds when nothing about deps changed (the credential sync + path translation work still runs every container-create, but the npm install dance no longer does). Research backing (no other simplification applies): - Anthropic's reference devcontainer uses pure named volumes; no host-state inheritance pattern is published. - Path translation has no upstream fix (issues #21916, #10379 closed without resolution). Our Node rewrite is the workaround. - pnpm workspaces (`pnpm -r install`) would replace the four installs with one command, but that's a real refactor (touches gitnexus/scripts/build.js + 4 package.json files); deferred. - `HUSKY=0` in containerEnv would drop the `rm -rf .husky/_` hack, but would also stop pre-commit hooks from firing inside the container; deferred.
This commit is contained in:
parent
6bcc2fe207
commit
ed03ae3ea8
3 changed files with 82 additions and 55 deletions
|
|
@ -208,9 +208,18 @@
|
|||
}
|
||||
},
|
||||
|
||||
// Driver script with labeled steps lives at .devcontainer/post-create.sh
|
||||
// so each step's success/failure is visible in the log without parsing
|
||||
// an &&-chain. Run via `bash` explicitly so the script doesn't depend
|
||||
// on its executable bit surviving the workspace bind mount.
|
||||
// Lifecycle split (per Dev Container spec):
|
||||
// - `updateContentCommand` runs on container-create AND whenever
|
||||
// workspace content changes (e.g. lockfile updates). It owns
|
||||
// workspace dependency installation — re-installing on every
|
||||
// container-create wastes time when nothing changed, but it must
|
||||
// re-run when deps shift.
|
||||
// - `postCreateCommand` runs once on container-create. It owns AI CLI
|
||||
// credential + identity sync from the host — work that should
|
||||
// happen exactly once per container instance, not on every content
|
||||
// update.
|
||||
// Run both via explicit `bash` so they don't depend on the script's
|
||||
// executable bit surviving the workspace bind mount.
|
||||
"updateContentCommand": "bash .devcontainer/install-deps.sh",
|
||||
"postCreateCommand": "bash .devcontainer/post-create.sh"
|
||||
}
|
||||
|
|
|
|||
56
.devcontainer/install-deps.sh
Normal file
56
.devcontainer/install-deps.sh
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env bash
|
||||
# Devcontainer updateContentCommand — runs on container-create AND whenever
|
||||
# workspace content changes (lockfile updates etc., per the Dev Container
|
||||
# spec). Handles workspace dependency installation only; AI CLI state sync
|
||||
# lives in post-create.sh which runs once after this.
|
||||
#
|
||||
# Why split out: `updateContentCommand` re-runs on content updates, while
|
||||
# `postCreateCommand` runs only on container-create. Putting `npm install`
|
||||
# here means a rebuild after pulling new dependencies refreshes them
|
||||
# without re-running the AI CLI credential/path-translation work each time.
|
||||
|
||||
set -euo pipefail
|
||||
cd /workspace
|
||||
|
||||
echo "[install-deps] 1/4: chown workspace node_modules + npm cache mount points"
|
||||
# Named volumes (workspace/*/node_modules, ~/.npm) created at first mount
|
||||
# inherit ownership from the image's pre-realignment UID. After
|
||||
# `updateRemoteUserUID: true` shifts the `node` user, the volumes end up
|
||||
# owned by the stale UID — npm install can't write. Re-chown
|
||||
# post-realignment; idempotent on subsequent runs.
|
||||
sudo chown -R node:node \
|
||||
/workspace/node_modules \
|
||||
/workspace/gitnexus/node_modules \
|
||||
/workspace/gitnexus-web/node_modules \
|
||||
/workspace/gitnexus-shared/node_modules \
|
||||
/home/node/.npm
|
||||
|
||||
echo "[install-deps] 2/4: clear stale .husky/_ runtime cache"
|
||||
# Docker Desktop's Windows bind-mount permission translation refuses to
|
||||
# let the new container's `node` user overwrite a `.husky/_/h` left by a
|
||||
# prior container with a different effective UID. `.husky/_` is
|
||||
# gitignored runtime cache; husky regenerates it during the root
|
||||
# `npm install`. Upstream husky has no fix for this UID-clash case.
|
||||
rm -rf .husky/_
|
||||
|
||||
echo "[install-deps] 3/4: npm install at root, then gitnexus-shared (build required)"
|
||||
# Install order: root first (lint-staged + husky + prettier), then
|
||||
# gitnexus-shared (build needed BEFORE gitnexus-web or gitnexus install
|
||||
# because both consume it via `file:../gitnexus-shared`).
|
||||
npm install
|
||||
cd /workspace/gitnexus-shared
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
echo "[install-deps] 4/4: npm install gitnexus-web, then gitnexus"
|
||||
# gitnexus-web before gitnexus: gitnexus's `prepare` script runs
|
||||
# scripts/build.js, which compiles gitnexus-web when the directory is
|
||||
# present. In the devcontainer the full workspace is bind-mounted, so
|
||||
# gitnexus-web/ is present at gitnexus install time (not the case in the
|
||||
# production Dockerfiles, which COPY selectively).
|
||||
cd /workspace/gitnexus-web
|
||||
npm install
|
||||
cd /workspace/gitnexus
|
||||
npm install
|
||||
|
||||
echo "[install-deps] done"
|
||||
|
|
@ -1,35 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
# Devcontainer postCreate driver. Runs once after the container is created
|
||||
# (per devcontainer.json `postCreateCommand`). Each labeled step is its own
|
||||
# command, so a failure log line names the step that failed instead of an
|
||||
# opaque `&&`-chain index.
|
||||
# (per devcontainer.json `postCreateCommand`). Workspace dependency
|
||||
# installation lives in install-deps.sh (`updateContentCommand`) which
|
||||
# runs BEFORE this script — see the spec lifecycle. This script only
|
||||
# handles AI CLI credential + identity sync from the host.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd /workspace
|
||||
|
||||
echo "[post-create] 1/7: chown workspace node_modules + named-volume mount points"
|
||||
# `updateRemoteUserUID: true` realigns the `node` user's UID/GID at runtime
|
||||
# on Linux hosts (no-op on Mac/Windows where Docker Desktop translates UIDs
|
||||
# via its VM layer). The Dockerfile chown at build time targets the original
|
||||
# UID; empty named volumes created at first mount inherit that ownership and
|
||||
# end up owned by the stale UID after realignment. Re-chown here, post-
|
||||
# realignment, so npm install can write to ~/.npm, the AI CLIs can write
|
||||
# to their config dirs, and zsh history writes to /commandhistory succeed
|
||||
# on hosts with non-1000 UIDs.
|
||||
echo "[post-create] 1/2: chown AI CLI named-volume mount points"
|
||||
# Named volumes (~/.claude, ~/.codex, ~/.cursor, /commandhistory,
|
||||
# ~/.local) inherit ownership from the image's pre-realignment UID at
|
||||
# first mount. After `updateRemoteUserUID: true` shifts the `node` user,
|
||||
# these end up owned by the stale UID — writes inside the volume fail.
|
||||
# install-deps.sh handles the workspace-side chown; this script handles
|
||||
# the AI CLI side so each lifecycle hook owns its own concern.
|
||||
sudo chown -R node:node \
|
||||
/workspace/node_modules \
|
||||
/workspace/gitnexus/node_modules \
|
||||
/workspace/gitnexus-web/node_modules \
|
||||
/workspace/gitnexus-shared/node_modules \
|
||||
/home/node/.npm \
|
||||
/home/node/.local \
|
||||
/home/node/.claude \
|
||||
/home/node/.codex \
|
||||
/home/node/.cursor \
|
||||
/home/node/.local \
|
||||
/commandhistory
|
||||
|
||||
echo "[post-create] 2/7: sync AI CLI credentials + identity from host"
|
||||
echo "[post-create] 2/2: sync AI CLI credentials + identity from host"
|
||||
# Defensive cleanup for users upgrading from an earlier devcontainer
|
||||
# design (Option B) where these paths were symlinks into the read-only
|
||||
# host stage (e.g. /home/node/.claude/plugins -> /host/.claude/plugins).
|
||||
|
|
@ -153,34 +145,4 @@ sync_from_host \
|
|||
sync_from_host \
|
||||
/host/.cursor/cli-config.json /home/node/.cursor/cli-config.json
|
||||
|
||||
echo "[post-create] 3/7: clear stale .husky/_ runtime cache"
|
||||
# Docker Desktop's Windows bind-mount permission translation refuses to let
|
||||
# the new container's `node` user overwrite a `.husky/_/h` left by a prior
|
||||
# container with a different effective UID. `.husky/_` is gitignored runtime
|
||||
# cache; husky regenerates it during npm install.
|
||||
rm -rf .husky/_
|
||||
|
||||
echo "[post-create] 4/7: npm install at root (husky + lint-staged + prettier + eslint)"
|
||||
npm install
|
||||
|
||||
echo "[post-create] 5/7: npm install + build gitnexus-shared"
|
||||
# gitnexus and gitnexus-web both consume gitnexus-shared via
|
||||
# file:../gitnexus-shared, so it must be built before either installs.
|
||||
cd /workspace/gitnexus-shared
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
echo "[post-create] 6/7: npm install gitnexus-web"
|
||||
# Must install BEFORE gitnexus: gitnexus's `prepare` script runs
|
||||
# scripts/build.js, which compiles gitnexus-web when the directory is
|
||||
# present. In the devcontainer the full workspace is bind-mounted, so
|
||||
# gitnexus-web/ is present at gitnexus install time even though it
|
||||
# wouldn't be in the production Dockerfiles (which COPY selectively).
|
||||
cd /workspace/gitnexus-web
|
||||
npm install
|
||||
|
||||
echo "[post-create] 7/7: npm install gitnexus (triggers prepare -> scripts/build.js)"
|
||||
cd /workspace/gitnexus
|
||||
npm install
|
||||
|
||||
echo "[post-create] done"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue