coder-init: carry global npm packages over when the Node patch version changes #35

Open
opened 2026-10-26 07:03:33 +00:00 by GuillaumeHemmen · 0 comments

Problem

coder-init runs nvm install "${NODE_MAJOR}" and then nvm alias default "${NODE_MAJOR}" at
every boot. When a new patch release of that major version comes out, nvm installs it into a
new folder and makes it the default. nvm keeps global npm packages separately for each
version, so anything installed with npm i -g stays behind under the old version and drops off
PATH. Only yarn comes back, because coder-init reinstalls it explicitly.

Example from one workspace: there are now seven v24 folders (v24.16.0 to v24.22.1), and a
global CLI installed once ended up spread across three of them. The current default didn't
have it. A Claude Code plugin hook that calls that CLI failed at every session start until it
was reinstalled by hand.

Proposed change

Reinstall globals from the previous default, but only when a new version is actually being
installed:

NVM_INSTALL_ARGS=()
PREVIOUS_NODE="$(nvm version default 2>/dev/null || true)"
TARGET_NODE="$(nvm version-remote "${NODE_MAJOR}")"
if [[ "${PREVIOUS_NODE}" == v* && "${PREVIOUS_NODE}" != "${TARGET_NODE}" ]]; then
  NVM_INSTALL_ARGS+=("--reinstall-packages-from=${PREVIOUS_NODE}")
fi
nvm install "${NODE_MAJOR}" "${NVM_INSTALL_ARGS[@]}"

The guard is required. When the target version is already installed, nvm install 24
--reinstall-packages-from=default fails with exit code 4 ("You can't reinstall global packages
from the same version of node you're installing"). Because coder-init runs under set -euo
pipefail, that would abort the boot on every day without a new Node release. On a first boot
there is no previous version, so the flag is skipped.

Workaround until then

List the packages in ~/.nvm/default-packages. nvm 0.40.1 installs them whenever it installs a
new version.

### Problem coder-init runs nvm install "${NODE_MAJOR}" and then nvm alias default "${NODE_MAJOR}" at every boot. When a new patch release of that major version comes out, nvm installs it into a new folder and makes it the default. nvm keeps global npm packages separately for each version, so anything installed with npm i -g stays behind under the old version and drops off PATH. Only yarn comes back, because coder-init reinstalls it explicitly. Example from one workspace: there are now seven v24 folders (v24.16.0 to v24.22.1), and a global CLI installed once ended up spread across three of them. The current default didn't have it. A Claude Code plugin hook that calls that CLI failed at every session start until it was reinstalled by hand. ### Proposed change Reinstall globals from the previous default, but only when a new version is actually being installed: ``` NVM_INSTALL_ARGS=() PREVIOUS_NODE="$(nvm version default 2>/dev/null || true)" TARGET_NODE="$(nvm version-remote "${NODE_MAJOR}")" if [[ "${PREVIOUS_NODE}" == v* && "${PREVIOUS_NODE}" != "${TARGET_NODE}" ]]; then NVM_INSTALL_ARGS+=("--reinstall-packages-from=${PREVIOUS_NODE}") fi nvm install "${NODE_MAJOR}" "${NVM_INSTALL_ARGS[@]}" ``` The guard is required. When the target version is already installed, nvm install 24 --reinstall-packages-from=default fails with exit code 4 ("You can't reinstall global packages from the same version of node you're installing"). Because coder-init runs under set -euo pipefail, that would abort the boot on every day without a new Node release. On a first boot there is no previous version, so the flag is skipped. ### Workaround until then List the packages in ~/.nvm/default-packages. nvm 0.40.1 installs them whenever it installs a new version.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
actions/sindri#35
No description provided.