Conversation
Installs the runtime from the official prebuilt tarball into /usr/local and verifies it against SHASUMS256.txt, rather than following the upstream node feature's nvm install. nvm exists to switch versions at runtime, which a pinned dev container does not need, and it would be the only such layer in this repo: every other feature here resolves a version, checks it, and drops a binary on PATH. npm's global prefix defaults to the dev user's ~/.local so `npm i -g` needs no root, matching the ~/.local/bin convention claude-code, codex and uv already set. With a stateDir it moves to the volume alongside npm's cache and user config, so globally installed CLIs survive a rebuild. The npmrc option follows settingsJson/configToml/daemonJson: baked at build, written by the run-once hook, and restored if something overwrites it. Note the tarball ships node, npm and npx only. There is no compiler, so packages needing node-gyp will not build, and corepack is gone as of Node 26, so yarn and pnpm need an explicit npm i -g.
sops was the only feature claiming ", verified against its published checksums" in its description, though uv verifies checksums too and says nothing. The claim belongs in install.sh, where it already is, not in the feature listing. The stateDir options had split into two idioms. sops now uses the "If set, exports X ... (persist it by mounting a volume there). Empty ..." shape that claude-code and codex already used. install.sh comments use a closed vocabulary of section tags. docker-in-docker had "Repo:", the only tag outside it, and tagged the entrypoint install as "Install:" after a "Configure:" block, which broke the shared order. It is the same ship-a-script step the other features label "Hook:". The init.sh hooks said "once its volume is mounted" in claude-code and codex but "once the volume is mounted" in sops and uv. No version bumps: these change prose only, following 5b2b440.
Every install.sh tags its sections from the same vocabulary and runs them in the order Dependencies, Resolve, Fetch, Install, Configure, Hook, Verify. Two features broke it. uv baked its default Python in a "Configure:" block after the "Hook:" block. The block only needs the profile, which is written earlier, so it moves up. latex wrote config.env and installed lib.sh and init.sh under "Configure:" before the install branch. That is the hook install, so it is retagged and moved after the branch. install_texlive() reads SCHEME, REPO and INSTALLER_DIR from install.sh's own scope rather than from config.env, so nothing in the branch depends on what the moved block writes, and the hook is not needed until postCreate. Its state dir group block also lost the "Configure:" tag when nested in the else branch. latex keeps Fetch before Resolve: install-tl -print-platform cannot run until the installer is downloaded, as the comment says. Both also pick up the stateDir description idiom. Patch bumps because the shipped install.sh changed and the publish action only pushes new versions.
The claude-code test resolves its target as ${CLAUDE_CONFIG_DIR:-$HOME/.claude},
so it follows the state dir when one is set. Its codex twin hardcoded
$HOME/.codex, which happens to work only because the scenario sets no stateDir.
Node keeps several supported lines alive at once (today 26 Current, 24 Active LTS, 22/20 Maintenance), so "newest, or one exact patch" did not cover the normal ask. lts resolved to the tag vlts and 24 to v24, both 404s at download time, leaving an exact patch as the only way to hold a line -- and Renovate bumps the feature version, not an option value inside it, so that pin had to be moved by hand. index.json carries "lts": false | "<codename>" per release, so the channel and the lines fall out of the file install.sh already downloads. No nvm: that layer exists to switch versions at runtime, which a pinned dev container does not need. The default moves from latest to lts. latest is Node's Current line, which upstream does not recommend for production, so it sat where headless-chrome puts dev and canary rather than where that feature puts its default: latest|stable both resolve to key="Stable" there, two majors behind Canary. The invariant across this repo is that the default gives you the upstream's stable track. Aliasing latest to LTS would have kept the uniform default, but nodejs.org/dist/latest really does mean Current and contradicting upstream's own vocabulary is the worse surprise. Partial versions come free: the three-part glob does not match 24.19, which falls through to the line branch and pins a minor line. Also fixes a SIGPIPE this branch shipped in 0aba5f5. The tag came from `grep -oP ... | head -n 1`, and grep emits 860 lines into a head that exits after one; under `set -euo pipefail` the closed pipe kills grep with 141 and aborts the install. It went unnoticed because the feature was never built here -- no Docker in this environment -- and because a `printf | grep -m1` variant fails the same way while a pre-filtered pipeline survives on buffering alone. grep -m1 now reads a here-string, which bash backs with a temp file, and `|| true` lets a no-match reach the explicit error instead of dying silently under set -e. No other feature has the pattern; age, sops and headless-chrome pipe a source that emits a single match. Scenarios cover both new forms with assertions that survive a floating patch: the 24 line by prefix, and lts by the resolved major being even, which is the property that identifies an LTS line under Node's release policy.
The greps matched one release per line, which held only because nodejs.org serves index.json pretty-printed. Nothing documents that. Against a minified index the lts branch returned v26.8.1, the Current line, silently mislabelled as LTS -- it fails by answering wrongly rather than by erroring, which is the failure mode worth removing. tr '}' '\n' puts one release per line whichever way the file arrives, so both shapes now resolve identically. curl | tr cannot SIGPIPE, since tr reads to EOF. latest was the only resolve branch without a scenario, and the branch that shipped the pipe bug in 847fe90. Its assertion is deliberately weak because the build is the real check: a resolution mistake aborts the install before the script runs. The description said "on a selectable channel", which stopped being true when the option grew release lines.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.