feat: resolve a program's pinned node version for build + runtime
Frontend builds triggered from the castle web app run inside the castle-api systemd service, whose PATH omits nvm's versioned node dir — so `pnpm build` died with `node: not found` even though it worked from an interactive shell. Introduce a per-program node convention: a program declares its version the ecosystem-standard way (.node-version / .nvmrc / package.json engines.node), and castle_core.toolchains.resolve_node_bin() maps it to a concrete nvm bin dir (CASTLE_NODE_VERSIONS_DIR, default ~/.nvm/versions/node; newest match wins). The same resolver feeds both sites that run a program's node: - build time: stacks._build_env() prepends the pinned node for the dev-verb subprocess (keyed on the source dir), so `castle program build` uses the program's node regardless of caller. - run time: deploy._build_deployed() stores it in Deployment.path_prepend, which the systemd generator puts ahead of the default unit PATH — so a `launcher: node` service runs its program's node. A pinned-but-uninstalled version fails loud with an `nvm install` hint instead of a cryptic `node: not found`. Unpinned → no injection (no guessing). Also: the systemd generator now honors an explicit PATH in defaults.env as a full override instead of clobbering it with a trailing Environment=PATH line (systemd's last-assignment-wins rule had silently defeated the documented escape hatch). Pins app/.node-version and documents the convention in the react-vite stack. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -75,7 +75,10 @@ REPOS_DIR = _resolve_repos_dir()
|
||||
# Order matters: pnpm's modern standalone installer puts its shim in
|
||||
# $PNPM_HOME/bin, which must win over the bare dir (older installs leave a stale
|
||||
# version wrapper there). nvm/node is intentionally omitted — it's versioned and
|
||||
# brittle; a service needing a specific node should pin it via defaults.env.
|
||||
# brittle to hardcode. A program's node is resolved per-program from its declared
|
||||
# pin (.node-version/.nvmrc/engines) and prepended ahead of these dirs at both the
|
||||
# build subprocess (stacks._build_env) and the runtime unit PATH (Deployment
|
||||
# .path_prepend) — see castle_core.toolchains.
|
||||
USER_TOOL_PATH_DIRS = [
|
||||
Path.home() / ".local" / "bin",
|
||||
Path.home() / ".local" / "share" / "pnpm" / "bin",
|
||||
|
||||
Reference in New Issue
Block a user