Compare commits

..
12 Commits
Author SHA1 Message Date
debadityaandJordan Harband 80e700ec21 [Fix] nvm_alias: unset zsh's extendedglob while reading an alias file
Code scanning - action / CodeQL-Build (push) Waiting to run
Tests: `nvm install-latest-npm` / matrix (push) Waiting to run
latest-npm.yml / nvm install-latest-npm (${{ fromJson(needs.matrix.outputs.latest) }}) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (0.10) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (0.12) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (14.16) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (14.17) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (20.4) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (20.5) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (21) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.4) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.5) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.6) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (5.9) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (6.1) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.0) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.1) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.2) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (push) Blocked by required conditions
Tests: linting / eclint (push) Waiting to run
Tests: linting / dockerfile_lint (push) Waiting to run
Tests: linting / doctoc (push) Waiting to run
Tests: linting / test_naming (push) Waiting to run
Tests: linting / check_exec (push) Waiting to run
Tests: linting / all linting (push) Blocked by required conditions
Tests: nvm install with set -e / matrix (push) Waiting to run
nvm-install-test.yml / test (no nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 1 shlvl) (push) Blocked by required conditions
nvm-install-test.yml / test (no nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 2 shlvls) (push) Blocked by required conditions
nvm-install-test.yml / test (nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 1 shlvl) (push) Blocked by required conditions
nvm-install-test.yml / test (nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 2 shlvls) (push) Blocked by required conditions
Tests: nvm install with set -e / finisher (push) Blocked by required conditions
Tests: release process / release (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (bash_completion, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (install.sh, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm-exec, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, dash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, ksh) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, sh) (push) Waiting to run
Tests: shellcheck / shellcheck (push) Blocked by required conditions
Tests: alpine / fast arm64 (alpine 3, dash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3, sh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3, zsh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, bash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, dash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, sh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, zsh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, bash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, dash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, sh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, zsh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, bash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, dash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, sh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, zsh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, bash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, dash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, sh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, zsh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, bash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, dash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, sh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, zsh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3, bash) (push) Waiting to run
Tests: alpine / musl-binary arm64 (alpine 3, node v22.21.1) (push) Waiting to run
Tests: alpine / musl-binary arm64 (alpine 3, node v24.9.0) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3, node v22.12.0) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.10, node v10.24.1) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.10, node v8.17.0) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.12, node v12.22.12) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.15, node v14.21.3) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.16, node v16.20.2) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.18, node v18.20.4) (push) Waiting to run
Tests: alpine / musl-binary arm64 (alpine 3.20, node v20.20.1) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.20, node v20.18.1) (push) Waiting to run
Tests: alpine / all alpine tests (push) Blocked by required conditions
Tests: fast / all fast tests (push) Blocked by required conditions
Tests: fast / fast (bash, gawk) (push) Waiting to run
Tests: fast / fast (dash, gawk) (push) Waiting to run
Tests: fast / fast (sh, gawk) (push) Waiting to run
Tests: fast / fast (zsh, gawk) (push) Waiting to run
Tests: fast / fast (bash, mawk) (push) Waiting to run
Tests: fast / fast (dash, mawk) (push) Waiting to run
Tests: fast / fast (sh, mawk) (push) Waiting to run
Tests: fast / fast (zsh, mawk) (push) Waiting to run
Tests: installation_iojs / all installation_iojs tests (push) Blocked by required conditions
Tests: installation_iojs / actually compile io.js from source (gcc 4.9 container) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (bash) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (dash) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (sh) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (zsh) (push) Waiting to run
Tests: installation_node / installation_node (bash) (push) Waiting to run
Tests: installation_node / installation_node (bash, without curl) (push) Waiting to run
Tests: installation_node / installation_node (dash) (push) Waiting to run
Tests: installation_node / installation_node (dash, without curl) (push) Waiting to run
Tests: installation_node / installation_node (sh) (push) Waiting to run
Tests: installation_node / installation_node (sh, without curl) (push) Waiting to run
Tests: installation_node / installation_node (zsh) (push) Waiting to run
Tests: installation_node / installation_node (zsh, without curl) (push) Waiting to run
Tests: installation_node / all installation_node tests (push) Blocked by required conditions
Tests: xenial / xenial (bash) (push) Waiting to run
Tests: xenial / xenial (dash) (push) Waiting to run
Tests: xenial / xenial (sh) (push) Waiting to run
Tests: xenial / xenial (zsh) (push) Waiting to run
Tests: xenial / all xenial tests (push) Blocked by required conditions
urchin tests / tests (bash, install_script) (push) Waiting to run
urchin tests / tests (bash, installation_iojs) (push) Waiting to run
urchin tests / tests (bash, slow) (push) Waiting to run
urchin tests / tests (bash, sourcing) (push) Waiting to run
urchin tests / tests (dash, installation_iojs) (push) Waiting to run
urchin tests / tests (dash, slow) (push) Waiting to run
urchin tests / tests (dash, sourcing) (push) Waiting to run
urchin tests / tests (sh, installation_iojs) (push) Waiting to run
urchin tests / tests (sh, slow) (push) Waiting to run
urchin tests / tests (sh, sourcing) (push) Waiting to run
urchin tests / tests (zsh, installation_iojs) (push) Waiting to run
urchin tests / tests (zsh, slow) (push) Waiting to run
urchin tests / tests (zsh, sourcing) (push) Waiting to run
urchin tests / all test suites, all shells (push) Blocked by required conditions
update readme TOC / update readme TOC (push) Waiting to run
Tests on Windows: `nvm install` / MSYS fail prefix nvm install (push) Waiting to run
Tests on Windows: `nvm install` / MSYS nvm install (--default 12) (push) Waiting to run
Tests on Windows: `nvm install` / MSYS nvm install (--lts) (push) Waiting to run
Tests on Windows: `nvm install` / MSYS nvm install (--no-progress 10) (push) Waiting to run
Tests on Windows: `nvm install` / Cygwin nvm install (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 18, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 18, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 21, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 21, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, --lts, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 11, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 18, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 21, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, --lts, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 11, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / tests, on windows (push) Blocked by required conditions
`#` is a repetition operator under `extendedglob`,
so `${VAR%%#*}` is an invalid pattern:
zsh aborts the expansion and `nvm_alias` silently prints nothing,
leaving the `default` alias unresolved when nvm.sh is sourced.

The read loop moves into `nvm_print_alias_file` so that `local_options`
scopes the option change to that section alone,
rather than to everything `nvm_alias` does,
and restores whatever the caller had set.

Fixes #3885

Co-authored-by: debaditya <debaditya2005hait@gmail.com>
Co-authored-by: Jordan Harband <ljharb@gmail.com>
2026-09-03 20:54:28 -07:00
Jordan Harband 25b378323c [Fix] zsh: restore nomatch and markdirs rather than clobbering them
`unsetopt local_options nomatch` unsets both named options,
so LOCAL_OPTIONS is off by the time the function returns and the
`nomatch` change escapes into the caller's shell:
one `nvm ls` or `nvm alias` leaves NO_MATCH off for the rest of the session,
after which an unmatched glob is silently passed through as a literal.

`setopt local_options nonomatch`,
already used in `nvm_check_file_permissions`, restores on return.

The `markdirs` line also cancelled the snapshot
taken for `shwordsplit` on the line above it;
that one is latent,
as `nvm_ls` is only ever called inside a command substitution.
2026-09-03 20:44:59 -07:00
CJstate 3a72926f4a [Fix] nvm --help: render the set-colors legend in color
Every legend line is built inside a command substitution,
where `nvm_has_colors` can never succeed,
because `[ -t 1 ]` sees the capture pipe rather than the terminal.
The legend has therefore always printed plain,
on every terminal and in every shell.

Resolve color support once and pass it in,
the way `nvm_print_alias_path` already accepts the same flag.
A command-prefix assignment cannot carry it:
POSIX expands a command's arguments before applying its assignments,
so the substitutions would still see it unset,
and in sh and ksh it would outlive the call and override `--no-colors` for everything after.

The test needs no tty,
and fails against an unfixed `nvm.sh` in sh, bash, dash, and zsh.

Fixes #3896
2026-09-03 12:10:19 -07:00
Jordan Harband 38f0f2cfdf [Tests] nvm_print_color_legend: look up the function with PATH emptied
The presence checks were still satisfiable by this test file itself,
which is an executable named `nvm_print_color_legend`.
Filtering `command -v` output on a `/` was not enough:
dash and zsh report a bare name,
not a path,
for a current-directory match reached through an empty `PATH` component,
and that is indistinguishable from a function.
So `nvm unload` looked as though it had left the function behind in exactly the shells CI runs as `sh`, `dash`, and `zsh`,
while bash, which reports `./name`, passed.

Emptying `PATH` for the lookup asks about the function alone,
and is immune to whatever `PATH` happens to hold.

Reproduced first, then fixed:
with `PATH=":${PATH}"` the previous check fails under dash and zsh
and passes under bash, matching CI exactly.
The assertions now hold in sh, bash, dash, and zsh
with the directory absent from `PATH`, present on it, and via an empty entry,
and both dropping `nvm_print_color_legend` from `nvm unload`
and renaming the function out from under the guard
are still caught in all twelve combinations.
2026-09-03 15:12:34 -07:00
Jordan Harband 812a76bb41 [Fix] nvm unload: drop the undefined nvm_format_help_message_colors
`unset -f` was passed a name that has never been a function.
It arrived with `set-colors` in 6848c16d and has never existed.

That matters because shells disagree about what `unset -f` does with a name that is not a function.
bash unsets the rest of the list and carries on;
Linux dash and zsh appear to stop there,
which silently left every one of the ten names after it defined:
`nvm_echo_with_colors`, `nvm_err_with_colors`,
`nvm_get_artifact_compression`, `nvm_install_binary_extract`,
`nvm_extract_tarball`, `nvm_process_nvmrc`,
`nvm_process_nvmrc_content`, `nvm_nvmrc_invalid_msg`,
`nvm_write_nvmrc`, and most recently `nvm_print_color_legend`.

Nothing noticed until `nvm_print_color_legend` gained a test
asserting that `nvm unload` removes it,
because it sorts first among the survivors.

With the dead name gone,
every remaining entry is a real function,
so no shell has an undefined name to disagree about.
2026-09-03 13:57:58 -07:00
Jordan Harband 26fef4fe1a [Tests] nvm_print_color_legend: check for the function, not a same-named file
The presence checks used `command -v`,
which finds executables as readily as functions,
and this test file is itself an executable named `nvm_print_color_legend`.
Whenever its own directory is on `PATH`, as it is under CI,
`command -v` kept resolving to the file
long after `nvm unload` had removed the function,
so the unload assertion could never pass there
and the "is it defined" guard could never fail.

`command -v` prints a bare name for a function
and a path for an executable,
in sh, bash, dash, zsh, and ksh alike,
so requiring the result to contain no slash
distinguishes the two.

Verified both ways round:
with the test's directory on `PATH`
the suite passes in all four shells,
and removing `nvm_print_color_legend` from `nvm unload`'s list
is still caught in all four.
2026-09-03 12:08:18 -07:00
Jordan Harband 17f12a1207 [Tests] nvm_print_color_legend: fix the fast suite on GNU userlands
The `nvm unload` assertion failed every `fast` job on Linux
while passing on macOS,
and its shape made the cause impossible to read off the failure.
`set -e` aborts the command substitution
when `nvm unload` returns non-zero,
which leaves an empty string
that the assertion then reports as
"unload left nvm_print_color_legend defined".
Run the unload with `set +e` inside the subshell
and report its exit status next to the lookup,
so a failing unload can no longer be misread
as a function that survived it.

The accompanying `sed: couldn't flush stdout: Broken pipe`
came from the `grep -q` pipelines:
`grep -q` exits at its first match
and hands SIGPIPE to the `sed` feeding it,
which BSD sed swallows and GNU sed reports.
Match whole lines with `case` against the already-captured string instead,
which needs no pipe at all,
and capture `nvm --help` before stripping it
rather than piping it through two processes into `grep -q`.

Test stderr is now empty in sh, bash, dash, and zsh.
2026-09-03 11:54:45 -07:00
Jordan Harband ab83657ba6 [Refactor] nvm --help: extract the set-colors legend into nvm_print_color_legend 2026-09-03 10:17:16 -07:00
Jordan Harband 10a277ec47 [Fix] nvm_get_mirror: allow ports, userinfo, IPv6 hosts, and percent-encoding
Code scanning - action / CodeQL-Build (push) Waiting to run
Tests: `nvm install-latest-npm` / matrix (push) Waiting to run
latest-npm.yml / nvm install-latest-npm (${{ fromJson(needs.matrix.outputs.latest) }}) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (0.10) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (0.12) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (14.16) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (14.17) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (20.4) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (20.5) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (21) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.4) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.5) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.6) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (5.9) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (6.1) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.0) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.1) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.2) (push) Blocked by required conditions
Tests: `nvm install-latest-npm` / nvm install-latest-npm (push) Blocked by required conditions
Tests: linting / eclint (push) Waiting to run
Tests: linting / dockerfile_lint (push) Waiting to run
Tests: linting / doctoc (push) Waiting to run
Tests: linting / test_naming (push) Waiting to run
Tests: linting / check_exec (push) Waiting to run
Tests: linting / all linting (push) Blocked by required conditions
Tests: nvm install with set -e / matrix (push) Waiting to run
nvm-install-test.yml / test (no nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 1 shlvl) (push) Blocked by required conditions
nvm-install-test.yml / test (no nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 2 shlvls) (push) Blocked by required conditions
nvm-install-test.yml / test (nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 1 shlvl) (push) Blocked by required conditions
nvm-install-test.yml / test (nvmrc, ${{ fromJson(needs.matrix.outputs.matrix) }}, 2 shlvls) (push) Blocked by required conditions
Tests: nvm install with set -e / finisher (push) Blocked by required conditions
Tests: release process / release (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (bash_completion, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (install.sh, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm-exec, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, bash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, dash) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, ksh) (push) Waiting to run
Tests: shellcheck / shellcheck_matrix (nvm.sh, sh) (push) Waiting to run
Tests: shellcheck / shellcheck (push) Blocked by required conditions
Tests: alpine / fast arm64 (alpine 3, dash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3, sh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3, zsh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, bash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, dash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, sh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3, zsh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, bash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, dash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, sh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.15, zsh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, bash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, dash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, sh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.15, zsh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, bash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, dash) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, sh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3.19, zsh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, bash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, dash) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, sh) (push) Waiting to run
Tests: alpine / fast x64 (alpine 3.19, zsh) (push) Waiting to run
Tests: alpine / fast arm64 (alpine 3, bash) (push) Waiting to run
Tests: alpine / musl-binary arm64 (alpine 3, node v22.21.1) (push) Waiting to run
Tests: alpine / musl-binary arm64 (alpine 3, node v24.9.0) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3, node v22.12.0) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.10, node v10.24.1) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.10, node v8.17.0) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.12, node v12.22.12) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.15, node v14.21.3) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.16, node v16.20.2) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.18, node v18.20.4) (push) Waiting to run
Tests: alpine / musl-binary arm64 (alpine 3.20, node v20.20.1) (push) Waiting to run
Tests: alpine / musl-binary x64 (alpine 3.20, node v20.18.1) (push) Waiting to run
Tests: alpine / all alpine tests (push) Blocked by required conditions
Tests: fast / all fast tests (push) Blocked by required conditions
Tests: fast / fast (bash, gawk) (push) Waiting to run
Tests: fast / fast (dash, gawk) (push) Waiting to run
Tests: fast / fast (sh, gawk) (push) Waiting to run
Tests: fast / fast (zsh, gawk) (push) Waiting to run
Tests: fast / fast (bash, mawk) (push) Waiting to run
Tests: fast / fast (dash, mawk) (push) Waiting to run
Tests: fast / fast (sh, mawk) (push) Waiting to run
Tests: fast / fast (zsh, mawk) (push) Waiting to run
Tests: installation_iojs / all installation_iojs tests (push) Blocked by required conditions
Tests: installation_iojs / actually compile io.js from source (gcc 4.9 container) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (bash) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (dash) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (sh) (push) Waiting to run
Tests: installation_iojs / installation_iojs without curl (zsh) (push) Waiting to run
Tests: installation_node / installation_node (bash) (push) Waiting to run
Tests: installation_node / installation_node (bash, without curl) (push) Waiting to run
Tests: installation_node / installation_node (dash) (push) Waiting to run
Tests: installation_node / installation_node (dash, without curl) (push) Waiting to run
Tests: installation_node / installation_node (sh) (push) Waiting to run
Tests: installation_node / installation_node (sh, without curl) (push) Waiting to run
Tests: installation_node / installation_node (zsh) (push) Waiting to run
Tests: installation_node / installation_node (zsh, without curl) (push) Waiting to run
Tests: installation_node / all installation_node tests (push) Blocked by required conditions
Tests: xenial / xenial (bash) (push) Waiting to run
Tests: xenial / xenial (dash) (push) Waiting to run
Tests: xenial / xenial (sh) (push) Waiting to run
Tests: xenial / xenial (zsh) (push) Waiting to run
Tests: xenial / all xenial tests (push) Blocked by required conditions
urchin tests / tests (bash, install_script) (push) Waiting to run
urchin tests / tests (bash, installation_iojs) (push) Waiting to run
urchin tests / tests (bash, slow) (push) Waiting to run
urchin tests / tests (bash, sourcing) (push) Waiting to run
urchin tests / tests (dash, installation_iojs) (push) Waiting to run
urchin tests / tests (dash, slow) (push) Waiting to run
urchin tests / tests (dash, sourcing) (push) Waiting to run
urchin tests / tests (sh, installation_iojs) (push) Waiting to run
urchin tests / tests (sh, slow) (push) Waiting to run
urchin tests / tests (sh, sourcing) (push) Waiting to run
urchin tests / tests (zsh, installation_iojs) (push) Waiting to run
urchin tests / tests (zsh, slow) (push) Waiting to run
urchin tests / tests (zsh, sourcing) (push) Waiting to run
urchin tests / all test suites, all shells (push) Blocked by required conditions
update readme TOC / update readme TOC (push) Waiting to run
Tests on Windows: `nvm install` / MSYS fail prefix nvm install (push) Waiting to run
Tests on Windows: `nvm install` / MSYS nvm install (--default 12) (push) Waiting to run
Tests on Windows: `nvm install` / MSYS nvm install (--lts) (push) Waiting to run
Tests on Windows: `nvm install` / MSYS nvm install (--no-progress 10) (push) Waiting to run
Tests on Windows: `nvm install` / Cygwin nvm install (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 18, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 18, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 21, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 21, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Ubuntu-18.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Debian) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Ubuntu-20.04) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, --lts, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 10, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 11, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 12, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 14, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 16, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 18, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (, 21, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, --lts, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 11, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Alpine) (push) Waiting to run
Tests on Windows: `nvm install` / tests, on windows (push) Blocked by required conditions
3c480159 correctly made this validator actually enforce,
instead of the no-op `awk '{ $0 ~ "..." }'` it had been,
but its character class had never been audited against real mirror URLs:
it has no `:`,
so enabling enforcement silently rejected every mirror URL bearing a port.

    $ NVM_NODEJS_ORG_MIRROR=https://mirror.internal:8443/dist nvm_ls_remote
    $NVM_NODEJS_ORG_MIRROR and $NVM_IOJS_ORG_MIRROR may only contain a URL

No request is made at all,
so an internal mirror on a non-default port has been unusable since v0.40.5.

Allow RFC 3986 `unreserved` plus the authority and path punctuation
a mirror URL legitimately needs:
`:` for a port,
`@` for userinfo,
`[`/`]` for an IPv6 literal host,
and `%` for percent-encoding.

`sub-delims` and the query/fragment delimiters stay rejected:
nvm appends a path to this value,
so a query string could never have worked,
and several of those characters are shell metacharacters.
2026-09-02 19:40:47 -07:00
Andres Mejia Sanchez dd0f702fb1 [Fix] nvm_sanitize_auth_header: allow ~, completing RFC 7235 token68
`~` is a member of RFC 7235 §2.1 `token68`,
and thus of RFC 6750 §2.1 `b64token`,
but the allowlist stripped it,
silently corrupting any opaque Bearer credential containing it:

    Bearer mF_9.B5f-4.1JqM~+/=  ->  Bearer mF_9.B5f-4.1JqM+/=

`Basic` credentials were never affected,
since RFC 4648 §4 base64 cannot emit `~`;
`;` stays stripped, as it belongs to no auth-scheme production.

Cover the charset in the unit test,
and assert the credential reaches the downloader intact via the existing fake-`wget` harness,
which needs no container.
2026-09-02 19:36:04 -07:00
Jordan Harband 6798d1dbc9 [actions] release.yml: provide GH_TOKEN so release-notes PR lookup can run
Tests: installation_node / installation_node (bash) (push) Canceled after 0s
Tests: installation_node / installation_node (bash, without curl) (push) Canceled after 0s
Tests: installation_node / installation_node (dash) (push) Canceled after 0s
Tests: installation_node / installation_node (dash, without curl) (push) Canceled after 0s
Tests: installation_node / installation_node (sh) (push) Canceled after 0s
Tests: installation_node / installation_node (sh, without curl) (push) Canceled after 0s
Tests: installation_node / installation_node (zsh) (push) Canceled after 0s
Tests: installation_node / installation_node (zsh, without curl) (push) Canceled after 0s
Tests: xenial / xenial (bash) (push) Canceled after 0s
Tests: xenial / xenial (dash) (push) Canceled after 0s
Tests: xenial / xenial (sh) (push) Canceled after 0s
Tests: xenial / xenial (zsh) (push) Canceled after 0s
urchin tests / tests (bash, install_script) (push) Canceled after 0s
urchin tests / tests (bash, installation_iojs) (push) Canceled after 0s
urchin tests / tests (bash, slow) (push) Canceled after 0s
urchin tests / tests (bash, sourcing) (push) Canceled after 0s
urchin tests / tests (dash, installation_iojs) (push) Canceled after 0s
urchin tests / tests (dash, slow) (push) Canceled after 0s
urchin tests / tests (dash, sourcing) (push) Canceled after 0s
urchin tests / tests (sh, installation_iojs) (push) Canceled after 0s
urchin tests / tests (sh, slow) (push) Canceled after 0s
urchin tests / tests (sh, sourcing) (push) Canceled after 0s
urchin tests / tests (zsh, installation_iojs) (push) Canceled after 0s
urchin tests / tests (zsh, slow) (push) Canceled after 0s
urchin tests / tests (zsh, sourcing) (push) Canceled after 0s
update readme TOC / update readme TOC (push) Canceled after 0s
Tests on Windows: `nvm install` / MSYS fail prefix nvm install (push) Canceled after 0s
Tests on Windows: `nvm install` / MSYS nvm install (--default 12) (push) Canceled after 0s
Tests on Windows: `nvm install` / MSYS nvm install (--lts) (push) Canceled after 0s
Tests on Windows: `nvm install` / MSYS nvm install (--no-progress 10) (push) Canceled after 0s
Tests on Windows: `nvm install` / Cygwin nvm install (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 10, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 10, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 10, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 12, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 12, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 12, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 14, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 14, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 14, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 16, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 16, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 16, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 18, Debian) (push) Canceled after 0s
Tests: alpine / musl-binary arm64 (alpine 3, node v24.9.0) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 18, Ubuntu-20.04) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3, node v22.12.0) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3.10, node v10.24.1) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 21, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 21, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Debian) (push) Canceled after 0s
Code scanning - action / CodeQL-Build (push) Canceled after 0s
Tests: `nvm install-latest-npm` / matrix (push) Canceled after 0s
Tests: linting / eclint (push) Canceled after 0s
Tests: linting / dockerfile_lint (push) Canceled after 0s
Tests: linting / doctoc (push) Canceled after 0s
Tests: linting / test_naming (push) Canceled after 0s
Tests: linting / check_exec (push) Canceled after 0s
Tests: release process / release (push) Canceled after 0s
Tests: shellcheck / shellcheck_matrix (bash_completion, bash) (push) Canceled after 0s
Tests: shellcheck / shellcheck_matrix (install.sh, bash) (push) Canceled after 0s
Tests: shellcheck / shellcheck_matrix (nvm-exec, bash) (push) Canceled after 0s
Tests: shellcheck / shellcheck_matrix (nvm.sh, bash) (push) Canceled after 0s
Tests: shellcheck / shellcheck_matrix (nvm.sh, dash) (push) Canceled after 0s
Tests: shellcheck / shellcheck_matrix (nvm.sh, ksh) (push) Canceled after 0s
Tests: shellcheck / shellcheck_matrix (nvm.sh, sh) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3, bash) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3, dash) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3, sh) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3, zsh) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3, bash) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3, dash) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3, sh) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3, zsh) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.15, bash) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.15, dash) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.15, sh) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.15, zsh) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.15, bash) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.15, dash) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.15, sh) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.15, zsh) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.19, bash) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.19, dash) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.19, sh) (push) Canceled after 0s
Tests: alpine / fast arm64 (alpine 3.19, zsh) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.19, bash) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.19, dash) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.19, sh) (push) Canceled after 0s
Tests: alpine / fast x64 (alpine 3.19, zsh) (push) Canceled after 0s
Tests: alpine / musl-binary arm64 (alpine 3, node v22.21.1) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3.10, node v8.17.0) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3.12, node v12.22.12) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3.15, node v14.21.3) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3.16, node v16.20.2) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3.18, node v18.20.4) (push) Canceled after 0s
Tests: alpine / musl-binary arm64 (alpine 3.20, node v20.20.1) (push) Canceled after 0s
Tests: alpine / musl-binary x64 (alpine 3.20, node v20.18.1) (push) Canceled after 0s
Tests: installation_iojs / actually compile io.js from source (gcc 4.9 container) (push) Canceled after 0s
Tests: fast / fast (bash, gawk) (push) Canceled after 0s
Tests: fast / fast (dash, gawk) (push) Canceled after 0s
Tests: fast / fast (sh, gawk) (push) Canceled after 0s
Tests: fast / fast (zsh, gawk) (push) Canceled after 0s
Tests: fast / fast (bash, mawk) (push) Canceled after 0s
Tests: fast / fast (dash, mawk) (push) Canceled after 0s
Tests: fast / fast (sh, mawk) (push) Canceled after 0s
Tests: fast / fast (zsh, mawk) (push) Canceled after 0s
Tests: installation_iojs / installation_iojs without curl (bash) (push) Canceled after 0s
Tests: installation_iojs / installation_iojs without curl (dash) (push) Canceled after 0s
Tests: installation_iojs / installation_iojs without curl (sh) (push) Canceled after 0s
Tests: installation_iojs / installation_iojs without curl (zsh) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Ubuntu-18.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Debian) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Ubuntu-20.04) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, --lts, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 10, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 11, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 12, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 14, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 16, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 18, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (, 21, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, --lts, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 10, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 11, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 12, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 14, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 16, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 18, Alpine) (push) Canceled after 0s
Tests on Windows: `nvm install` / WSL nvm install (script, 21, Alpine) (push) Canceled after 0s
Tests: installation_iojs / all installation_iojs tests (push) Canceled after 0s
Tests: installation_node / all installation_node tests (push) Canceled after 0s
Tests: xenial / all xenial tests (push) Canceled after 0s
urchin tests / all test suites, all shells (push) Canceled after 0s
Tests: fast / all fast tests (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (${{ fromJson(needs.matrix.outputs.latest) }}) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (0.10) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (0.12) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (14.16) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (14.17) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (20.4) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (20.5) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (21) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.4) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.5) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (4.6) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (5.9) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (6.1) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.0) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.1) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (9.2) (push) Canceled after 0s
Tests: `nvm install-latest-npm` / nvm install-latest-npm (push) Canceled after 0s
Tests: linting / all linting (push) Canceled after 0s
Tests: shellcheck / shellcheck (push) Canceled after 0s
Tests: alpine / all alpine tests (push) Canceled after 0s
Tests on Windows: `nvm install` / tests, on windows (push) Canceled after 0s
In GitHub Actions, `gh` refuses to run without a token,
and `release-notes.sh` fails closed on `gh` errors,
so the `make release` dry run died once the script was checked in.
The workflow already permits `api.github.com` egress and only needs the default read-only token.
2026-08-17 23:33:17 -07:00
Jordan Harband df7a936157 [meta] add release-notes.sh, the release notes generator
The `release` target has invoked `./release-notes.sh` since 0b8ed9fa,
but the script itself was never committed,
so the "Tests: release process" workflow fails on a fresh checkout.
2026-08-17 23:21:32 -07:00
10 changed files with 598 additions and 29 deletions
+1
View File
@@ -35,5 +35,6 @@ jobs:
run: echo proceed | make TAG=99.99.99 release
env:
GIT_EDITOR: "sed -i '1 s/^/99.99.99 make release test/'"
GH_TOKEN: ${{ github.token }}
- name: Ensure tag is created
run: git tag | grep v99.99.99
+64 -29
View File
@@ -190,9 +190,10 @@ nvm_sanitize_auth_header() {
# and base64url (A-Za-z0-9-_=) charsets, plus the space, colon, dot, and
# underscore the previous allowlist already permitted, so that values like
# `Basic <base64>` and `Bearer <token>` survive intact.
# token68's '~' is still stripped.
# '~' is also allowed, completing RFC 7235 `token68` (and thus RFC 6750
# `b64token`), so opaque Bearer tokens containing it are not corrupted.
# Note: '-' must be at the end of the bracket expression to be treated as a literal.
nvm_echo "$1" | command sed 's/[^a-zA-Z0-9 :_.+/=-]//g'
nvm_echo "$1" | command sed 's/[^a-zA-Z0-9 :_.+/=~-]//g'
}
nvm_has_system_node() {
@@ -1128,6 +1129,8 @@ nvm_set_colors() {
local CURRENT_COLOR
local NOT_INSTALLED_COLOR
local DEFAULT_COLOR
local NVM_HAS_COLORS
NVM_HAS_COLORS=0
INSTALLED_COLOR="$(echo "$1" | awk '{ print substr($0, 1, 1); }')"
LTS_AND_SYSTEM_COLOR="$(echo "$1" | awk '{ print substr($0, 2, 1); }')"
@@ -1138,6 +1141,7 @@ nvm_set_colors() {
nvm_echo "Setting colors to: ${INSTALLED_COLOR} ${LTS_AND_SYSTEM_COLOR} ${CURRENT_COLOR} ${NOT_INSTALLED_COLOR} ${DEFAULT_COLOR}"
nvm_echo "WARNING: Colors may not display because they are not supported in this shell."
else
NVM_HAS_COLORS=1
nvm_echo_with_colors "Setting colors to: $(nvm_wrap_with_color_code "${INSTALLED_COLOR}" "${INSTALLED_COLOR}")$(nvm_wrap_with_color_code "${LTS_AND_SYSTEM_COLOR}" "${LTS_AND_SYSTEM_COLOR}")$(nvm_wrap_with_color_code "${CURRENT_COLOR}" "${CURRENT_COLOR}")$(nvm_wrap_with_color_code "${NOT_INSTALLED_COLOR}" "${NOT_INSTALLED_COLOR}")$(nvm_wrap_with_color_code "${DEFAULT_COLOR}" "${DEFAULT_COLOR}")"
fi
export NVM_COLORS="$1"
@@ -1175,13 +1179,38 @@ nvm_wrap_with_color_code() {
CODE="$(nvm_print_color_code "${1}" 2>/dev/null ||:)"
local TEXT
TEXT="${2-}"
if nvm_has_colors && [ -n "${CODE}" ]; then
# `nvm_has_colors` cannot answer for itself inside a command substitution,
# where `[ -t 1 ]` sees the capture pipe: callers that already checked pass
# the answer in, as `nvm_print_alias_path` also accepts it.
if { [ "${NVM_HAS_COLORS-}" = 1 ] || nvm_has_colors; } && [ -n "${CODE}" ]; then
nvm_echo_with_colors "\033[${CODE}${TEXT}\033[0m"
else
nvm_echo "${TEXT}"
fi
}
nvm_print_color_legend() {
# Every line below builds its text in a command substitution, which cannot
# detect color support itself, so resolve it once here. `local` matters:
# leaking the flag would override `--no-colors` for every later call.
local NVM_HAS_COLORS
NVM_HAS_COLORS=0
if nvm_has_colors; then
NVM_HAS_COLORS=1
fi
nvm_echo ' Initial colors are:'
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'b' 'b')$(nvm_wrap_with_color_code 'y' 'y')$(nvm_wrap_with_color_code 'g' 'g')$(nvm_wrap_with_color_code 'r' 'r')$(nvm_wrap_with_color_code 'e' 'e')"
nvm_echo ' Color codes:'
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'r' 'r')/$(nvm_wrap_with_color_code 'R' 'R') = $(nvm_wrap_with_color_code 'r' 'red') / $(nvm_wrap_with_color_code 'R' 'bold red')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'g' 'g')/$(nvm_wrap_with_color_code 'G' 'G') = $(nvm_wrap_with_color_code 'g' 'green') / $(nvm_wrap_with_color_code 'G' 'bold green')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'b' 'b')/$(nvm_wrap_with_color_code 'B' 'B') = $(nvm_wrap_with_color_code 'b' 'blue') / $(nvm_wrap_with_color_code 'B' 'bold blue')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'c' 'c')/$(nvm_wrap_with_color_code 'C' 'C') = $(nvm_wrap_with_color_code 'c' 'cyan') / $(nvm_wrap_with_color_code 'C' 'bold cyan')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'm' 'm')/$(nvm_wrap_with_color_code 'M' 'M') = $(nvm_wrap_with_color_code 'm' 'magenta') / $(nvm_wrap_with_color_code 'M' 'bold magenta')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'y' 'y')/$(nvm_wrap_with_color_code 'Y' 'Y') = $(nvm_wrap_with_color_code 'y' 'yellow') / $(nvm_wrap_with_color_code 'Y' 'bold yellow')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'k' 'k')/$(nvm_wrap_with_color_code 'K' 'K') = $(nvm_wrap_with_color_code 'k' 'black') / $(nvm_wrap_with_color_code 'K' 'bold black')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'e' 'e')/$(nvm_wrap_with_color_code 'W' 'W') = $(nvm_wrap_with_color_code 'e' 'light grey') / $(nvm_wrap_with_color_code 'W' 'white')"
}
nvm_print_color_code() {
case "${1-}" in
'0') return 0 ;;
@@ -1360,7 +1389,7 @@ nvm_list_aliases() {
NVM_HAS_COLORS=1
fi
nvm_is_zsh && unsetopt local_options nomatch
nvm_is_zsh && setopt local_options nonomatch
(
local ALIAS_PATH
for ALIAS_PATH in "${NVM_ALIAS_DIR}/${ALIAS}"*; do
@@ -1398,6 +1427,23 @@ nvm_list_aliases() {
return
}
nvm_print_alias_file() {
# a `#` pattern is a repetition operator under zsh's `extendedglob`, so it is
# unset for the length of this function and restored by `local_options`
nvm_is_zsh && setopt local_options noextendedglob
local NVM_ALIAS_LINE
while IFS= read -r NVM_ALIAS_LINE || [ -n "${NVM_ALIAS_LINE}" ]; do
NVM_ALIAS_LINE="${NVM_ALIAS_LINE%%#*}"
case "${NVM_ALIAS_LINE}" in
*[![:space:]]*) ;;
*) continue ;;
esac
NVM_ALIAS_LINE="${NVM_ALIAS_LINE%"${NVM_ALIAS_LINE##*[![:space:]]}"}"
nvm_echo "${NVM_ALIAS_LINE}"
done < "$1"
}
nvm_alias() {
local ALIAS
ALIAS="${1-}"
@@ -1428,16 +1474,7 @@ nvm_alias() {
return 0
fi
local NVM_ALIAS_LINE
while IFS= read -r NVM_ALIAS_LINE || [ -n "${NVM_ALIAS_LINE}" ]; do
NVM_ALIAS_LINE="${NVM_ALIAS_LINE%%#*}"
case "${NVM_ALIAS_LINE}" in
*[![:space:]]*) ;;
*) continue ;;
esac
NVM_ALIAS_LINE="${NVM_ALIAS_LINE%"${NVM_ALIAS_LINE##*[![:space:]]}"}"
nvm_echo "${NVM_ALIAS_LINE}"
done < "${NVM_ALIAS_PATH}"
nvm_print_alias_file "${NVM_ALIAS_PATH}"
}
nvm_ls_current() {
@@ -1656,7 +1693,7 @@ nvm_ls() {
esac
nvm_is_zsh && setopt local_options shwordsplit
nvm_is_zsh && unsetopt local_options markdirs
nvm_is_zsh && setopt local_options nomarkdirs
local NVM_DIRS_TO_SEARCH1
NVM_DIRS_TO_SEARCH1=''
@@ -2387,7 +2424,14 @@ nvm_get_mirror() {
esac
if ! nvm_echo "${NVM_MIRROR}" | command awk '{ if ($0 !~ /^https?:\/\/[a-zA-Z0-9.\/_-]+$/) exit 1 }'; then
# Allow RFC 3986 `unreserved` (A-Za-z0-9-._~) plus the authority and path
# punctuation a mirror URL legitimately needs: `:` for a port, `@` for
# userinfo, `[`/`]` for an IPv6 literal host, `%` for percent-encoding.
# `sub-delims` (!$&'()*+,;=) and the query/fragment delimiters (?#) stay out:
# nvm appends a path to this value, so a query string could never work anyway,
# and several of those characters are shell metacharacters.
# Note: in a bracket expression `]` must come first and `-` last to be literal.
if ! nvm_echo "${NVM_MIRROR}" | command awk '{ if ($0 !~ /^https?:\/\/[]a-zA-Z0-9._~:\/@%[-]+$/) exit 1 }'; then
nvm_err '$NVM_NODEJS_ORG_MIRROR and $NVM_IOJS_ORG_MIRROR may only contain a URL'
return 2
fi
@@ -3492,17 +3536,7 @@ nvm() {
nvm_echo ' nvm cache dir Display path to the cache directory for nvm'
nvm_echo ' nvm cache clear Empty cache directory for nvm'
nvm_echo ' nvm set-colors [<color codes>] Set five text colors using format "yMeBg". Available when supported.'
nvm_echo ' Initial colors are:'
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'b' 'b')$(nvm_wrap_with_color_code 'y' 'y')$(nvm_wrap_with_color_code 'g' 'g')$(nvm_wrap_with_color_code 'r' 'r')$(nvm_wrap_with_color_code 'e' 'e')"
nvm_echo ' Color codes:'
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'r' 'r')/$(nvm_wrap_with_color_code 'R' 'R') = $(nvm_wrap_with_color_code 'r' 'red') / $(nvm_wrap_with_color_code 'R' 'bold red')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'g' 'g')/$(nvm_wrap_with_color_code 'G' 'G') = $(nvm_wrap_with_color_code 'g' 'green') / $(nvm_wrap_with_color_code 'G' 'bold green')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'b' 'b')/$(nvm_wrap_with_color_code 'B' 'B') = $(nvm_wrap_with_color_code 'b' 'blue') / $(nvm_wrap_with_color_code 'B' 'bold blue')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'c' 'c')/$(nvm_wrap_with_color_code 'C' 'C') = $(nvm_wrap_with_color_code 'c' 'cyan') / $(nvm_wrap_with_color_code 'C' 'bold cyan')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'm' 'm')/$(nvm_wrap_with_color_code 'M' 'M') = $(nvm_wrap_with_color_code 'm' 'magenta') / $(nvm_wrap_with_color_code 'M' 'bold magenta')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'y' 'y')/$(nvm_wrap_with_color_code 'Y' 'Y') = $(nvm_wrap_with_color_code 'y' 'yellow') / $(nvm_wrap_with_color_code 'Y' 'bold yellow')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'k' 'k')/$(nvm_wrap_with_color_code 'K' 'K') = $(nvm_wrap_with_color_code 'k' 'black') / $(nvm_wrap_with_color_code 'K' 'bold black')"
nvm_echo_with_colors " $(nvm_wrap_with_color_code 'e' 'e')/$(nvm_wrap_with_color_code 'W' 'W') = $(nvm_wrap_with_color_code 'e' 'light grey') / $(nvm_wrap_with_color_code 'W' 'white')"
nvm_print_color_legend
nvm_echo 'Example:'
nvm_echo ' nvm install 8.0.0 Install a specific version number'
nvm_echo ' nvm use 8.0 Use the latest available 8.0.x release'
@@ -4935,13 +4969,14 @@ nvm() {
nvm_has_solaris_binary nvm_is_merged_node_version \
nvm_is_natural_num nvm_is_version_installed nvm_validate_install \
nvm_install_lock_name nvm_acquire_install_lock nvm_release_install_lock \
nvm_list_aliases nvm_make_alias nvm_print_alias_path \
nvm_list_aliases nvm_make_alias nvm_print_alias_file nvm_print_alias_path \
nvm_print_default_alias nvm_print_formatted_alias nvm_resolve_local_alias \
nvm_sanitize_path nvm_has_colors nvm_process_parameters \
nvm_node_version_has_solaris_binary nvm_iojs_version_has_solaris_binary \
nvm_curl_libz_support nvm_command_info nvm_is_zsh nvm_stdout_is_terminal \
nvm_npmrc_bad_news_bears nvm_sanitize_auth_header \
nvm_get_colors nvm_set_colors nvm_print_color_code nvm_wrap_with_color_code nvm_format_help_message_colors \
nvm_get_colors nvm_set_colors nvm_print_color_code nvm_wrap_with_color_code \
nvm_print_color_legend \
nvm_echo_with_colors nvm_err_with_colors \
nvm_get_artifact_compression nvm_install_binary_extract nvm_extract_tarball \
nvm_process_nvmrc nvm_process_nvmrc_content nvm_nvmrc_invalid_msg \
+153
View File
@@ -0,0 +1,153 @@
#!/bin/sh
# Generates release notes for an nvm release, in the format used in the
# annotated release tags and version bump commits.
#
# Usage: ./release-notes.sh <new-version> [<prev-ref>] [<end-ref>]
# <new-version> the version being released, e.g. `v0.40.7` (the leading `v` is optional)
# <prev-ref> list commits since this ref; defaults to the most recent `vX.Y.Z` tag
# <end-ref> list commits up to this ref; defaults to HEAD
#
# Commits from <prev-ref> to <end-ref> are grouped into sections by the leading
# `[bracket]` tag of their subject line, in `git log` order (newest first).
# Category tags ([New], [Fix], ...) are stripped from the bullet text;
# context tags ([readme], [actions], [meta], ...) are kept.
# Commits without a recognized tag land under "Misc", with a warning on stderr.
#
# Each bullet gets a ` (#123)` reference to the pull request that introduced
# the commit, looked up via `gh` from GitHub's commit->PR association, unless
# the subject already ends with one. Set NVM_RELEASE_NOTES_SKIP_PRS to a
# nonempty value to skip the lookup (references will be omitted).
NEW_VERSION="${1-}"
PREV_REF="${2-}"
END_REF="${3:-HEAD}"
if [ -z "${NEW_VERSION}" ]; then
printf >&2 'Usage: %s <new-version> [<prev-ref>] [<end-ref>]\n' "$0"
exit 2
fi
case "${NEW_VERSION}" in
v*) ;;
*) NEW_VERSION="v${NEW_VERSION}" ;;
esac
if [ -z "${PREV_REF}" ]; then
PREV_REF="$(git describe --abbrev=0 --tags --match 'v[0-9]*.[0-9]*.[0-9]*')" || {
printf >&2 'release-notes: failed to determine the previous version tag.\n'
exit 1
}
fi
for REF in "${PREV_REF}" "${END_REF}"; do
git rev-parse --quiet --verify "${REF}^{commit}" >/dev/null || {
# shellcheck disable=SC2016
printf >&2 'release-notes: `%s` is not a valid ref.\n' "${REF}"
exit 1
}
done
LOOKUP_PRS=1
if [ -n "${NVM_RELEASE_NOTES_SKIP_PRS-}" ]; then
LOOKUP_PRS=0
elif ! command -v gh >/dev/null 2>&1; then
# shellcheck disable=SC2016
printf >&2 'release-notes: `gh` is not available; PR references will be omitted.\n'
LOOKUP_PRS=0
fi
# the repo to look up commit->PR associations in, e.g. `nvm-sh/nvm`
REPO_SLUG="${GH_REPO-}"
if [ -z "${REPO_SLUG}" ]; then
ORIGIN_URL="$(git remote get-url origin 2>/dev/null)" || ORIGIN_URL=''
case "${ORIGIN_URL}" in
*github.com[:/]*) REPO_SLUG="$(printf '%s' "${ORIGIN_URL}" | sed 's#^.*github\.com[:/]##;s#\.git$##')" ;;
esac
fi
: "${REPO_SLUG:=nvm-sh/nvm}"
TAB="$(printf '\t')"
COMMITS_FILE="$(mktemp)" || exit 1
GH_ERR_FILE="$(mktemp)" || exit 1
trap 'rm -f "${COMMITS_FILE}" "${GH_ERR_FILE}"' EXIT
# each line: <section key> TAB <sha> TAB <bullet text>, in git log order
git log --no-merges --format='%H %s' "${PREV_REF}..${END_REF}" | while IFS=' ' read -r SHA SUBJECT; do
LOWERED="$(printf '%s' "${SUBJECT}" | tr '[:upper:]' '[:lower:]')"
STRIPPED="${SUBJECT#\[*\] }"
case "${LOWERED}" in
v[0-9]*.[0-9]*.[0-9]*)
case "${SUBJECT}" in
*' '*) # not a bump commit, just a subject that starts with a version
printf >&2 'release-notes: no category prefix, filing under Misc: %s\n' "${SUBJECT}"
KEY='misc' TEXT="${SUBJECT}"
;;
*) continue ;; # a version bump commit
esac
;;
'[new] '*) KEY='new' TEXT="${STRIPPED}" ;;
'[fix] '* | '[patch] '*) KEY='fix' TEXT="${STRIPPED}" ;;
'[refactor] '*) KEY='refactor' TEXT="${STRIPPED}" ;;
'[shellcheck] '*) KEY='refactor' TEXT="${SUBJECT}" ;;
'[robustness] '*) KEY='robustness' TEXT="${STRIPPED}" ;;
'[perf] '* | '[performance] '*) KEY='perf' TEXT="${STRIPPED}" ;;
'[dockerfile] '*) KEY='dockerfile' TEXT="${STRIPPED}" ;;
'[docs] '* | '[doc] '*) KEY='docs' TEXT="${STRIPPED}" ;;
'[readme] '*) KEY='docs' TEXT="${SUBJECT}" ;;
'[meta] '* | '[security] '* | '[dev deps] '*)
KEY='misc' TEXT="${SUBJECT}" ;;
'[tests] '* | '[test] '*) KEY='tests' TEXT="${STRIPPED}" ;;
'[actions] '* | '[debug] '*) KEY='tests' TEXT="${SUBJECT}" ;;
*)
printf >&2 'release-notes: no category prefix, filing under Misc: %s\n' "${SUBJECT}"
KEY='misc' TEXT="${SUBJECT}"
;;
esac
printf '%s\t%s\t%s\n' "${KEY}" "${SHA}" "${TEXT}"
done > "${COMMITS_FILE}"
if ! [ -s "${COMMITS_FILE}" ]; then
printf >&2 'release-notes: no commits found in %s..HEAD.\n' "${PREV_REF}"
fi
print_section() {
if ! grep -q "^$1${TAB}" "${COMMITS_FILE}"; then
return 0
fi
printf '\n%s\n\n' "$2"
grep "^$1${TAB}" "${COMMITS_FILE}" | while IFS="${TAB}" read -r _KEY SHA TEXT; do
REF=''
case "${TEXT}" in
*'(#'[0-9]*')') ;; # the subject already carries a reference
*)
if [ "${LOOKUP_PRS}" = 1 ]; then
PR="$(gh api "repos/${REPO_SLUG}/commits/${SHA}/pulls" --jq '.[0].number // empty' 2>"${GH_ERR_FILE}")" || {
if grep -q 'No commit found' "${GH_ERR_FILE}"; then
PR='' # not pushed to GitHub yet, so it can not have come from a PR
else
printf >&2 'release-notes: failed to look up the PR for commit %s:\n' "${SHA}"
cat >&2 "${GH_ERR_FILE}"
exit 1
fi
}
case "${PR}" in
'' | *[!0-9]*) ;;
*) REF=" (#${PR})" ;;
esac
fi
;;
esac
printf ' - %s%s\n' "${TEXT}" "${REF}"
done || exit 1
}
printf '%s\n' "${NEW_VERSION}"
print_section 'new' 'New Stuff' &&
print_section 'fix' 'Bug Fixes' &&
print_section 'refactor' 'Refactors' &&
print_section 'robustness' 'Robustness' &&
print_section 'perf' 'Performance' &&
print_section 'dockerfile' 'Dockerfile' &&
print_section 'docs' 'Docs' &&
print_section 'misc' 'Misc' &&
print_section 'tests' 'Tests'
@@ -39,5 +39,11 @@ NVM_AUTH_HEADER='Bearer test-token' nvm_download "https://nodejs.org/dist/x" -o
grep -Fxqe '--header' "$ARGV_LOG" || die "wget did not receive --header; got: $(cat "$ARGV_LOG")"
grep -Fxq 'Authorization: Bearer test-token' "$ARGV_LOG" || die "wget did not receive a well-formed Authorization header; got: $(cat "$ARGV_LOG")"
# given a credential using every character RFC 7235 token68 permits, including '~'
# when nvm_download uses the wget path
NVM_AUTH_HEADER='Bearer mF_9.B5f-4.1JqM~+/=' nvm_download "https://nodejs.org/dist/x" -o - || die 'nvm_download (wget) returned nonzero for a token68 credential'
# then it reaches wget byte-for-byte, with no character silently dropped
grep -Fxq 'Authorization: Bearer mF_9.B5f-4.1JqM~+/=' "$ARGV_LOG" || die "wget did not receive the token68 credential intact; got: $(cat "$ARGV_LOG")"
cleanup
echo "nvm_download wget Authorization header: passed"
+23
View File
@@ -52,3 +52,26 @@ testMirrors 'not a url'
testMirrors 'ftp://wrong-scheme'
testMirrors 'http://'
testMirrors 'javascript:alert(1)'
# a query string can never work, since nvm appends a path to the mirror
testMirrors 'https://nodejs.org/dist?token=abc'
testMirrors 'https://nodejs.org/dist#frag'
testAcceptedMirrors() {
NVM_NODEJS_ORG_MIRROR="${1-}"
[ "$(nvm_get_mirror node std)" = "${1-}" ] || die "NVM_NODEJS_ORG_MIRROR should accept ${1-}"
unset NVM_NODEJS_ORG_MIRROR
NVM_IOJS_ORG_MIRROR="${1-}"
[ "$(nvm_get_mirror iojs std)" = "${1-}" ] || die "NVM_IOJS_ORG_MIRROR should accept ${1-}"
unset NVM_IOJS_ORG_MIRROR
}
# a private mirror on a non-default port is a URL, and must not be rejected
testAcceptedMirrors 'https://mirror.internal:8443/dist'
testAcceptedMirrors 'http://localhost:8080/dist'
# an IPv6 literal host, userinfo, percent-encoding, and `~` are all legal too
testAcceptedMirrors 'http://[::1]:8080/dist'
testAcceptedMirrors 'https://user:pw@nexus.corp.example.com/repository/nodejs-dist'
testAcceptedMirrors 'https://mirror.internal/a%20b/dist'
testAcceptedMirrors 'https://mirror.internal/~dist'
+53
View File
@@ -0,0 +1,53 @@
#!/bin/sh
die () { echo "$@" ; cleanup ; exit 1; }
cleanup () {
rm -rf "${TEST_DIR-}"
unset TEST_DIR OUTPUT EXPECTED
}
: nvm.sh
\. ../../../nvm.sh
TEST_DIR="${PWD}/nvm_print_alias_file_tmp"
mkdir -p "${TEST_DIR}" || die 'failed to create test dir'
printf 'v0.10 # inline comment\n' > "${TEST_DIR}/inline"
OUTPUT="$(nvm_print_alias_file "${TEST_DIR}/inline")"
[ "_${OUTPUT}" = '_v0.10' ] || die "inline comment not stripped; got '${OUTPUT}'"
printf '# leading comment\nv0.11\n' > "${TEST_DIR}/comment-first"
OUTPUT="$(nvm_print_alias_file "${TEST_DIR}/comment-first")"
[ "_${OUTPUT}" = '_v0.11' ] || die "comment-only line not skipped; got '${OUTPUT}'"
printf 'v0.12\t \nv0.13\n' > "${TEST_DIR}/trailing-space"
OUTPUT="$(nvm_print_alias_file "${TEST_DIR}/trailing-space")"
EXPECTED="$(printf 'v0.12\nv0.13')"
[ "_${OUTPUT}" = "_${EXPECTED}" ] || die "trailing whitespace not stripped, or a line was dropped; got '${OUTPUT}'"
printf '\n \n\t\n# only a comment\n' > "${TEST_DIR}/nothing-usable"
OUTPUT="$(nvm_print_alias_file "${TEST_DIR}/nothing-usable")"
[ "_${OUTPUT}" = '_' ] || die "blank and comment-only lines should produce no output; got '${OUTPUT}'"
nvm_print_alias_file "${TEST_DIR}/nothing-usable" >/dev/null 2>&1 \
|| die 'a file with no usable lines should still exit zero'
printf 'v0.14' > "${TEST_DIR}/no-trailing-newline"
OUTPUT="$(nvm_print_alias_file "${TEST_DIR}/no-trailing-newline")"
[ "_${OUTPUT}" = '_v0.14' ] || die "a final line with no trailing newline should be read; got '${OUTPUT}'"
# a `#` pattern is a repetition operator under zsh's `extendedglob`, which is what
# made `nvm_alias` print nothing at all: https://github.com/nvm-sh/nvm/issues/3885
if [ -n "${ZSH_VERSION-}" ]; then
setopt extendedglob || die 'enabling extendedglob failed'
OUTPUT="$(nvm_print_alias_file "${TEST_DIR}/inline")"
[ "_${OUTPUT}" = '_v0.10' ] || die "with extendedglob set, expected 'v0.10', got '${OUTPUT}'"
[[ -o extendedglob ]] || die 'nvm_print_alias_file left extendedglob unset'
unsetopt extendedglob
nvm_print_alias_file "${TEST_DIR}/inline" > /dev/null
[[ -o extendedglob ]] && die 'nvm_print_alias_file left extendedglob set'
fi
cleanup
+197
View File
@@ -0,0 +1,197 @@
#!/bin/sh
ONESHOT=''
cleanup () {
if [ -n "${ONESHOT}" ]; then
command rm -f "${ONESHOT}"
fi
}
die () { echo "$@" ; cleanup ; exit 1; }
\. ../../../nvm.sh
set -e
# assert about nvm, not about the caller's environment
NVM_HAS_COLORS_WAS="${NVM_HAS_COLORS-}"
unset NVM_HAS_COLORS
ESC="$(printf '\033')"
# The legend's letters are wrapped in color codes when the terminal supports
# them, so every text assertion below compares against the output with any
# escapes removed. That keeps these checks about structure and alignment only.
strip_colors() { command sed "s/${ESC}\[[0-9;]*m//g"; }
# Exact whole-line match against an already-captured string. Deliberately
# pipe-free: `grep -q` exits at its first match and hands SIGPIPE to whatever
# feeds it, which BSD sed swallows but GNU sed reports as "couldn't flush
# stdout", polluting the test's stderr.
contains_line () {
case "
${2}
" in
*"
${1}
"*) return 0 ;;
esac
return 1
}
# This test file is itself an executable named `nvm_print_color_legend`, so a
# plain `command -v` keeps finding it long after the function is gone. Emptying
# `PATH` for the lookup is the only reliable way to ask about the function
# alone: filtering on a `/` is not enough, because dash and zsh report a bare
# name for a current-directory match reached through an empty `PATH` component.
is_function () {
[ -n "$(PATH=/nonexistent command -v "${1}" 2>/dev/null)" ]
}
is_function nvm_print_color_legend \
|| die 'nvm_print_color_legend is not defined'
OUTPUT="$(nvm_print_color_legend)"
LINES="$(nvm_echo "${OUTPUT}" | command wc -l | command tr -d ' ')"
[ "${LINES}" = 11 ] || die "expected 11 legend lines, got ${LINES}: ${OUTPUT}"
PLAIN="$(nvm_echo "${OUTPUT}" | strip_colors)"
# the two labels are literal, and their indentation lines the legend up under
# the `nvm set-colors` entry in the help output
contains_line ' Initial colors are:' "${PLAIN}" \
|| die "the 'Initial colors are:' label is missing or misaligned: ${PLAIN}"
contains_line ' Color codes:' "${PLAIN}" \
|| die "the 'Color codes:' label is missing or misaligned: ${PLAIN}"
# the default-palette sample, indented one level deeper than its label
contains_line ' bygre' "${PLAIN}" \
|| die "the initial-colors sample is missing or misaligned: ${PLAIN}"
# all eight rows of the code table, in order, each with both letter forms
for ROW in \
'r/R = red / bold red' \
'g/G = green / bold green' \
'b/B = blue / bold blue' \
'c/C = cyan / bold cyan' \
'm/M = magenta / bold magenta' \
'y/Y = yellow / bold yellow' \
'k/K = black / bold black' \
'e/W = light grey / white' \
; do
contains_line " ${ROW}" "${PLAIN}" \
|| die "color code row '${ROW}' is missing or misaligned: ${PLAIN}"
done
# the extracted function has to stay wired into the help output
HELP_PLAIN="$(nvm --help | strip_colors)"
contains_line ' Color codes:' "${HELP_PLAIN}" \
|| die 'nvm --help no longer includes the color legend'
# --- the fix: colors have to survive command substitution ------------------
# the bug condition: inside `$( )`, `[ -t 1 ]` sees the capture pipe, so
# `nvm_has_colors` always fails
nvm_has_colors () { return 1; }
WRAPPED="$(nvm_wrap_with_color_code 'b' 'b')"
case "${WRAPPED}" in
*"${ESC}"*) die "expected plain text when colors are unsupported, got >${WRAPPED}<" ;;
esac
NVM_HAS_COLORS=1
export NVM_HAS_COLORS
WRAPPED="$(nvm_wrap_with_color_code 'b' 'b')"
[ "${WRAPPED}" = "${ESC}[0;34mb${ESC}[0m" ] \
|| die "expected a colored 'b' with NVM_HAS_COLORS=1, got >${WRAPPED}<"
WRAPPED="$(nvm_wrap_with_color_code 'X' 'X')"
[ "${WRAPPED}" = 'X' ] \
|| die "expected plain text for an unknown color code, got >${WRAPPED}<"
unset NVM_HAS_COLORS
# `0` is what the resolving callers start from, so it must not read as truthy
NVM_HAS_COLORS=0
export NVM_HAS_COLORS
WRAPPED="$(nvm_wrap_with_color_code 'b' 'b')"
case "${WRAPPED}" in
*"${ESC}"*) die "NVM_HAS_COLORS=0 must not enable colors, got >${WRAPPED}<" ;;
esac
unset NVM_HAS_COLORS
UNSUPPORTED="$(nvm_print_color_legend)"
case "${UNSUPPORTED}" in
*"${ESC}"*) die "the legend emitted colors while unsupported: ${UNSUPPORTED}" ;;
esac
# Model the real asymmetry: yes to the first, direct call, no to every later
# one. The state has to live in a file, because each `$( )` is a fresh subshell
# that would take its own copy of an in-shell counter and answer yes every time.
ONESHOT="$(command mktemp)"
oneshot_reset () { nvm_echo 0 > "${ONESHOT}"; }
nvm_has_colors () {
if [ "$(command cat "${ONESHOT}")" = 0 ]; then
nvm_echo 1 > "${ONESHOT}"
return 0
fi
return 1
}
oneshot_reset
COLORED_OUTPUT="$(nvm_print_color_legend)"
# `grep -c` drains its input, so it cannot SIGPIPE its producer
COLORED="$(nvm_echo "${COLORED_OUTPUT}" | nvm_grep -c "${ESC}" || true)"
[ "${COLORED}" = 9 ] \
|| die "expected all 9 legend lines colored, got ${COLORED}: ${COLORED_OUTPUT}"
# the sample specifically, which is the case #3896 reports
case "${COLORED_OUTPUT}" in
*"${ESC}[0;34mb${ESC}[0m${ESC}[0;33my${ESC}[0m"*) : ;;
*) die "the initial-colors sample was not colored: ${COLORED_OUTPUT}" ;;
esac
# in a substitution, so `nvm_set_colors`' `export NVM_COLORS` stays contained
oneshot_reset
SET_COLORS_OUTPUT="$(nvm_set_colors 'bygre')"
case "${SET_COLORS_OUTPUT}" in
*"${ESC}"*) : ;;
*) die "nvm_set_colors did not color its confirmation line: ${SET_COLORS_OUTPUT}" ;;
esac
# coloring must change only the escapes, never the text or the alignment
COLORED_STRIPPED="$(nvm_echo "${COLORED_OUTPUT}" | strip_colors)"
[ "${COLORED_STRIPPED}" = "${PLAIN}" ] \
|| die 'the colored legend does not match the plain legend once escapes are stripped'
# called directly rather than in a substitution, so a leak is observable at all
oneshot_reset
nvm_print_color_legend >/dev/null
[ -z "${NVM_HAS_COLORS-}" ] \
|| die "nvm_print_color_legend leaked NVM_HAS_COLORS=${NVM_HAS_COLORS}"
# And `nvm unload` has to clean it up, the way it does its sibling helpers.
# `set +e` inside the subshell so a non-zero `nvm unload` cannot abort it before
# the lookup runs; its status is reported separately, so "unload failed" can
# never be misreported as "the function survived".
UNLOAD_OUT="$(
set +e
nvm unload >/dev/null 2>&1
# plain `echo`: `nvm unload` has just unset `nvm_echo`
echo "unload_rc=$?"
if is_function nvm_print_color_legend; then
echo present
else
echo gone
fi
)"
case "${UNLOAD_OUT}" in
*gone*) : ;;
*) die "nvm unload did not remove nvm_print_color_legend (${UNLOAD_OUT})" ;;
esac
if [ -n "${NVM_HAS_COLORS_WAS}" ]; then
NVM_HAS_COLORS="${NVM_HAS_COLORS_WAS}"
export NVM_HAS_COLORS
fi
cleanup
echo "nvm_print_color_legend: passed"
@@ -35,4 +35,18 @@ case "${RESULT}" in
;;
esac
# Test 6: every character of RFC 7235 §2.1 `token68` is preserved.
# token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
# This is a superset of both base64 and base64url; note the '~', which no
# base64 variant emits but which an opaque token may legitimately contain.
TOKEN68="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~+/="
RESULT=$(nvm_sanitize_auth_header "${TOKEN68}")
[ "${RESULT}" = "${TOKEN68}" ] || die "FAIL: token68 chars were stripped. Got: '${RESULT}'"
# Test 7: the RFC 6750 §2.1 example Bearer token, extended with the remaining
# valid `b64token` characters, survives intact.
B64TOKEN="Bearer mF_9.B5f-4.1JqM~+/="
RESULT=$(nvm_sanitize_auth_header "${B64TOKEN}")
[ "${RESULT}" = "${B64TOKEN}" ] || die "FAIL: b64token chars were stripped. Got: '${RESULT}'"
echo "All nvm_sanitize_auth_header tests passed"
@@ -0,0 +1,54 @@
#!/bin/sh
die () { echo "$@" ; cleanup ; exit 1; }
cleanup () {
rm -rf "${TEST_DIR-}"
unset TEST_DIR NVM_DIR
}
: nvm.sh
\. ../../../nvm.sh
\. ../../common.sh
# `local_options` is zsh-only, so no other shell can regress this
[ -n "${ZSH_VERSION-}" ] || exit 0
# an isolated NVM_DIR with a single known version, so that ambient versions
# and aliases can not affect any assertions. `alias/lts` is deliberately absent:
# globbing it is why `nvm_list_aliases` has to suppress `nomatch` at all.
TEST_DIR="${PWD}/nvm_zsh_options_tmp"
mkdir -p "${TEST_DIR}/alias" "${TEST_DIR}/versions/node/v24.13.0/bin" || die 'failed to create test dirs'
make_echo "${TEST_DIR}/versions/node/v24.13.0/bin/node" 'v24.13.0' || die 'failed to create test node binary'
echo '24' > "${TEST_DIR}/alias/default" || die 'failed to create default alias'
NVM_DIR="${TEST_DIR}"
setopt nomatch
unsetopt markdirs
unsetopt shwordsplit
nvm_list_aliases >/dev/null 2>&1
[[ -o nomatch ]] || die 'nvm_list_aliases left nomatch unset'
nvm alias >/dev/null 2>&1
[[ -o nomatch ]] || die '"nvm alias" left nomatch unset'
nvm ls >/dev/null 2>&1
[[ -o nomatch ]] || die '"nvm ls" left nomatch unset'
# called directly rather than in a command substitution, so that anything
# `nvm_ls` fails to restore is visible here
nvm_ls 24 >/dev/null 2>&1
[[ -o markdirs ]] && die 'nvm_ls left markdirs set'
[[ -o shwordsplit ]] && die 'nvm_ls left shwordsplit set'
# and the inverse: a caller that wants these on has to get them back
setopt markdirs
setopt shwordsplit
nvm_ls 24 >/dev/null 2>&1
[[ -o markdirs ]] || die 'nvm_ls left markdirs unset'
[[ -o shwordsplit ]] || die 'nvm_ls left shwordsplit unset'
cleanup
@@ -0,0 +1,33 @@
#!/bin/sh
die () { echo "$@" ; exit 1; }
\. ../common.sh
# `#` is a pattern operator only under zsh's `extendedglob`
[ -n "${ZSH_VERSION-}" ] || exit 0
# We need to unload nvm again first, as by the time this test is run,
# despite being unloaded in setup, the inherited PATH still contains
# an nvm-installed node version. `nvm unload` unsets NVM_DIR, so cache it.
\. ../../nvm.sh || die 'sourcing returned nonzero exit code'
NVM_DIR_CACHED="${NVM_DIR}"
nvm unload || die 'unloading returned nonzero exit code'
NVM_DIR="${NVM_DIR_CACHED}"
# the comment is the point: stripping it is what needs a `#` pattern
echo '0.10.1 # comment' > "${NVM_DIR}/alias/default" || die 'creation of default alias failed'
setopt extendedglob || die 'enabling extendedglob failed'
\. ../../nvm.sh || die 'sourcing returned nonzero exit code'
[[ -o extendedglob ]] || die 'sourcing nvm.sh disabled extendedglob'
NVM_LS_CURRENT_NOT_GREPPED="$(nvm ls current | strip_colors)"
NVM_LS_CURRENT="$(nvm ls current | strip_colors | \grep -o 'v0\.10\.1')"
[ "_${NVM_LS_CURRENT}" = '_v0.10.1' ] || die "'nvm ls current' did not return '-> v0.10.1', got '${NVM_LS_CURRENT_NOT_GREPPED}'"
NVM_ALIAS_DEFAULT="$(nvm alias default | strip_colors)"
[ "_${NVM_ALIAS_DEFAULT}" = '_default -> 0.10.1 (-> v0.10.1 *)' ] \
|| die "'nvm alias default' did not return 'default -> 0.10.1 (-> v0.10.1 *)', got '${NVM_ALIAS_DEFAULT}'"