Install the pinned node when the installed one is another major version (closes #118)
check / check (push) Canceled after 0s

script/bootstrap used whatever node was installed, so on the CI runner
image, which ships node 24, the pinned yarn 1.22.22 ran under node 24
and printed the deprecation warning DEP0169 on every bootstrap. It now
uses the installed node only when its major version is the pinned one,
and otherwise installs the pinned node with nvm, as it already did when
node was missing, with yarn and the packages under it. Only the major is
compared because the Dockerfile stages use a node 22 alpine image that
nvm cannot replace. script/fmt and script/fmt-check run yarn under nvm's
pinned node when nvm has it installed, and otherwise the yarn on PATH.

Model: opus-5-5
This commit is contained in:
2026-10-08 00:33:44 +00:00
parent c55a0cb2f0
commit 42af5e06e1
7 changed files with 103 additions and 69 deletions
+27 -11
View File
@@ -2,10 +2,12 @@
# script/bootstrap: install all dependencies needed to build and develop
# this repo. Idempotent: every install is guarded by a check so already
# installed tools are skipped. Base tooling comes from nix, apt, brew,
# or apk (detected in that order); assumes nothing is present. Node is
# used directly if installed; otherwise it is installed at a pinned
# version via nvm (installing nvm itself first, from a hash-verified
# release archive, never curl | sh).
# or apk (detected in that order); assumes nothing is present. The
# installed node is used only when its major version is the pinned one;
# otherwise (node missing, or another major) the pinned version is
# installed via nvm (installing nvm itself first, from a hash-verified
# release archive, never curl | sh), and yarn and the packages are
# installed under it.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
@@ -100,31 +102,45 @@ ensure_nvm() {
rm -rf "$tmp"
}
# True when the node on PATH has the pinned major version. Only the
# major is compared: the Dockerfile stages start from a node 22 alpine
# image whose exact version is not the pin, and nvm cannot install a
# prebuilt node on alpine. Another major is not used: the pinned yarn 1
# prints a deprecation warning under node 24.
node_is_pinned_major() {
if missing node; then return 1; fi
installed="$(node --version)"
installed="${installed#v}"
[ "${installed%%.*}" = "${NODE_VERSION%%.*}" ]
}
ensure_node() {
if ! missing node; then return 0; fi
if node_is_pinned_major; then return 0; fi
ensure_nvm
nvm_sh "nvm install $NODE_VERSION"
}
ensure_yarn() {
if ! node_is_pinned_major; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && corepack enable && \
corepack prepare yarn@$YARN_VERSION --activate"
return 0
fi
if ! missing yarn; then return 0; fi
if ! missing corepack; then
corepack enable
corepack prepare "yarn@$YARN_VERSION" --activate
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && corepack enable && \
corepack prepare yarn@$YARN_VERSION --activate"
else
npm install -g "yarn@$YARN_VERSION"
fi
}
install_js_deps() {
if missing yarn && [ -s "$HOME/.nvm/nvm.sh" ]; then
if node_is_pinned_major; then
yarn install --frozen-lockfile
else
nvm_sh "nvm use $NODE_VERSION >/dev/null && cd \"$ROOT\" && \
yarn install --frozen-lockfile"
else
yarn install --frozen-lockfile
fi
}
+10 -9
View File
@@ -7,20 +7,21 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Must match the pin in script/bootstrap.
NODE_VERSION="22.17.0"
# script/bootstrap installs node and yarn under nvm and leaves neither
# on the PATH of the shell that called it, so resolve the pinned
# toolchain here the way bootstrap's own install step does. nvm is a
# bash script, hence the subshell.
# When the installed node is not the pinned major version,
# script/bootstrap installs the pinned node and yarn under nvm and
# leaves neither on the PATH of the shell that called it. So yarn runs
# under nvm's pinned node when nvm has it installed, and otherwise is
# the yarn on PATH. nvm is a bash script, hence the subshell.
run_yarn() {
if command -v yarn >/dev/null 2>&1; then
exec yarn "$@"
if [ -d "$HOME/.nvm/versions/node/v$NODE_VERSION" ]; then
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
if ! command -v yarn >/dev/null 2>&1; then
echo "fmt: no yarn; run script/bootstrap first" >&2
exit 1
fi
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
exec yarn "$@"
}
main() {
+10 -9
View File
@@ -7,20 +7,21 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Must match the pin in script/bootstrap.
NODE_VERSION="22.17.0"
# script/bootstrap installs node and yarn under nvm and leaves neither
# on the PATH of the shell that called it, so resolve the pinned
# toolchain here the way bootstrap's own install step does. nvm is a
# bash script, hence the subshell.
# When the installed node is not the pinned major version,
# script/bootstrap installs the pinned node and yarn under nvm and
# leaves neither on the PATH of the shell that called it. So yarn runs
# under nvm's pinned node when nvm has it installed, and otherwise is
# the yarn on PATH. nvm is a bash script, hence the subshell.
run_yarn() {
if command -v yarn >/dev/null 2>&1; then
exec yarn "$@"
if [ -d "$HOME/.nvm/versions/node/v$NODE_VERSION" ]; then
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
if ! command -v yarn >/dev/null 2>&1; then
echo "fmt-check: no yarn; run script/bootstrap first" >&2
exit 1
fi
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
exec yarn "$@"
}
main() {