From b23f89494dcbd943f667b734b401349af2728379 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Fri, 13 Nov 2026 12:46:51 +0100 Subject: [PATCH 1/2] Match packageManager pins without their hash Scripts pass NODE_MODULE straight from package.json's packageManager, e.g. pnpm@12.14.0+sha512.. npm list prints 12.14.0, so the pin never matched and pnpm was reinstalled on every update. The build metadata is dropped for the npm comparison and install; corepack still gets the hash. --- lib/runtime.func | 5 +++ tests/nodejs-module-pin.sh | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/nodejs-module-pin.sh diff --git a/lib/runtime.func b/lib/runtime.func index f4030dd..1f79a37 100644 --- a/lib/runtime.func +++ b/lib/runtime.func @@ -1741,6 +1741,11 @@ setup_nodejs() { continue fi + # packageManager pins carry a hash (pnpm@12.14.0+sha512.) that npm + # list never prints, so the module was reinstalled on every run. Corepack + # above still gets the hash and checks it. + MODULE_REQ_VERSION="${MODULE_REQ_VERSION%%+*}" + # The corepack shim is dropped by _cs_drop_pm_shim immediately before each # npm install below, never here. Dropping it up front deleted a working # binary and then let the version check decide there was nothing to diff --git a/tests/nodejs-module-pin.sh b/tests/nodejs-module-pin.sh new file mode 100644 index 0000000..6ed2a4a --- /dev/null +++ b/tests/nodejs-module-pin.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# NODE_MODULE="$(jq -r .packageManager package.json)" passes pnpm@12.14.0+sha512.. +# npm list prints 12.14.0, so the pin never matched and pnpm was reinstalled on +# every update. +set -uo pipefail + +CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +eval "$(awk '/^setup_nodejs[(][)] [{]/,/^}/' "$CORE_DIR/lib/runtime.func")" + +TEST_DIR="$(mktemp -d)" +trap 'rm -rf -- "$TEST_DIR"' EXIT +CALLS="$TEST_DIR/calls" +LOG="$TEST_DIR/log" +passed=0 failed=0 + +export STD="" +msg_info() { echo "info: $*" >>"$LOG"; } +msg_ok() { echo "ok: $*" >>"$LOG"; } +msg_warn() { echo "warn: $*" >>"$LOG"; } +msg_error() { echo "error: $*" >>"$LOG"; } + +HAVE="node npm jq pnpm" +command() { + if [[ "${1:-}" == "-v" ]]; then + [[ " $HAVE " == *" $2 "* ]] && { echo "/usr/bin/$2"; return 0; } + return 1 + fi + builtin command "$@" +} + +# Node 22 installed, Node 22 wanted, pnpm 12.14.0 already global. +is_tool_installed() { echo 22; } +cleanup_legacy_install() { :; } +ensure_apt_working() { :; } +download_gpg_key() { :; } +cache_installed_version() { :; } +_cs_drop_pm_shim() { :; } +_setup_npm() { + # shellcheck disable=SC2034 # read by the extracted setup_nodejs + NPM_SETUP_VERSION="12.2.1" +} +apt() { :; } +apt-get() { :; } +node() { [[ "$1" == "-v" ]] && echo v22.23.4; return 0; } +corepack() { :; } +npm() { + case "${1:-}" in + -v) echo "12.2.1" ;; + list) echo "/usr/lib"; echo "\`-- pnpm@12.14.0" ;; + install) echo "npm $*" >>"$CALLS" ;; + esac + return 0 +} +mkdir() { :; } +cd() { [[ "$1" == "/opt" ]] || builtin cd "$@" || return; } + +run() { + : >"$CALLS" + : >"$LOG" + NODE_VERSION=22 NODE_MODULE="$1" setup_nodejs +} +expect() { + if [[ "$2" == "$3" ]]; then + echo "PASS $1" + passed=$((passed + 1)) + else + echo "FAIL $1: expected <$2>, got <$3>" >&2 + failed=$((failed + 1)) + fi +} + +run "pnpm@12.14.0+sha512.0123abcd" +expect "the installed pin with a hash is not reinstalled" "" "$(cat "$CALLS")" + +run "pnpm@12.14.0" +expect "the installed pin without a hash is not reinstalled" "" "$(cat "$CALLS")" + +run "pnpm@12.15.0+sha512.0123abcd" +expect "a new pin with a hash installs that version" "npm install -g pnpm@12.15.0" "$(head -n1 "$CALLS")" + +echo "${passed} passed, ${failed} failed" +((failed == 0)) From 2edae4834175520b764d4d9e83918b1b1ce44c30 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Fri, 13 Nov 2026 15:39:34 +0100 Subject: [PATCH 2/2] Trim the comments to what a reader would get wrong --- lib/runtime.func | 4 +--- tests/nodejs-module-pin.sh | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/runtime.func b/lib/runtime.func index 1f79a37..924a2e1 100644 --- a/lib/runtime.func +++ b/lib/runtime.func @@ -1741,9 +1741,7 @@ setup_nodejs() { continue fi - # packageManager pins carry a hash (pnpm@12.14.0+sha512.) that npm - # list never prints, so the module was reinstalled on every run. Corepack - # above still gets the hash and checks it. + # npm list never prints the +sha512 hash of a packageManager pin; Corepack still checks it. MODULE_REQ_VERSION="${MODULE_REQ_VERSION%%+*}" # The corepack shim is dropped by _cs_drop_pm_shim immediately before each diff --git a/tests/nodejs-module-pin.sh b/tests/nodejs-module-pin.sh index 6ed2a4a..fb9bac8 100644 --- a/tests/nodejs-module-pin.sh +++ b/tests/nodejs-module-pin.sh @@ -1,7 +1,4 @@ #!/usr/bin/env bash -# NODE_MODULE="$(jq -r .packageManager package.json)" passes pnpm@12.14.0+sha512.. -# npm list prints 12.14.0, so the pin never matched and pnpm was reinstalled on -# every update. set -uo pipefail CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" @@ -28,7 +25,6 @@ command() { builtin command "$@" } -# Node 22 installed, Node 22 wanted, pnpm 12.14.0 already global. is_tool_installed() { echo 22; } cleanup_legacy_install() { :; } ensure_apt_working() { :; }