mirror of
https://github.com/nvm-sh/nvm.git
synced 2026-08-26 00:00:09 +08:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42bb45f289 |
@@ -107,35 +107,3 @@ Additionally, the maintainer of a third-party dependency might introduce a vulne
|
||||
**Recommendation**: Third-party libraries should be kept up-to-date, applying patches to address publicly known vulnerabilities in a timely fashion.
|
||||
Monitoring and logging capabilities should also be in place to detect and respond to potential attacks.
|
||||
SLSA compliance may also be considered for further supply chain security hardening.
|
||||
|
||||
## Trust boundary: mirror payloads vs. mirror metadata
|
||||
|
||||
`nvm` fetches two very different kinds of data from a Node.js/io.js mirror (`nodejs.org`/`iojs.org` by default, or whatever `$NVM_NODEJS_ORG_MIRROR` and `$NVM_IOJS_ORG_MIRROR` point at), and they sit on opposite sides of a trust boundary:
|
||||
|
||||
- **Payloads** — the Node.js/io.js binaries and source tarballs that `nvm install` downloads, unpacks, compiles (for source installs), and runs.
|
||||
- **Metadata** — everything `nvm` parses *about* those payloads rather than executing: the `index.tab` version list (including each release's LTS codename), and the `SHASUMS`/`SHASUMS256` checksum files.
|
||||
|
||||
Conflating the two leads to mis-scoped reports, so the project draws the line explicitly.
|
||||
|
||||
### Payloads are trusted, by construction
|
||||
|
||||
The entire purpose of `nvm` is to download a mirror's build of Node.js and run it.
|
||||
A mirror that serves a backdoored binary has arbitrary code execution the moment you `nvm install` and invoke `node`, and no validation inside `nvm` can prevent that - you have chosen to execute that code.
|
||||
Installing from source (`nvm install -s`) is if anything more direct: the mirror-supplied source tarball is unpacked and its `configure`/`make` build runs arbitrary code on your machine *at install time*, before `node` is ever invoked.
|
||||
Checksum verification protects **integrity** (a corrupted or truncated download, or a network intermediary that cannot also forge the same-origin `SHASUMS`), not **authenticity** against the mirror itself, since the checksums come from the same origin as the payload.
|
||||
Selecting a mirror is therefore equivalent to selecting whom you trust for arbitrary code execution in your account.
|
||||
A malicious payload from the configured mirror is consequently **out of scope**: no privilege boundary is crossed, so there is no privilege to escalate.
|
||||
|
||||
### Metadata is not trusted
|
||||
|
||||
Parsing a version list is a pure data operation.
|
||||
A user who runs `nvm ls-remote` to browse available versions - and installs nothing - has not opted into running any code from the mirror.
|
||||
Metadata can also be attacker-controlled with no mirror misconfiguration at all, via a compromised mirror/CDN or a man-in-the-middle of the channel (see *Threat ID 3*), so it is treated as hostile input.
|
||||
The invariant `nvm` maintains is:
|
||||
|
||||
> Mirror-supplied metadata must never reach a shell/command evaluator, an `awk`/`sed` program body, or an unvalidated filesystem path (see *Threat ID 2*).
|
||||
|
||||
This is why version strings from `index.tab` are passed to the downloader as literal `argv` elements rather than re-parsed by the shell ([CVE-2026-10796](https://github.com/advisories/GHSA-3c52-35h2-gfmm), [CVE-2026-1665](https://github.com/advisories/GHSA-4fc5-r4vr-8rp7)); why checksum comparisons pass the mirror's values as `awk -v` **data** and never as program text; and why LTS codenames are constrained to safe alias filenames before naming a file under `$NVM_DIR/alias/lts`, so a hostile codename such as `../../../.bashrc` cannot traverse out of the alias directory.
|
||||
|
||||
The point is not that metadata is "more dangerous" than a payload — a trusted payload can obviously do anything.
|
||||
It is that metadata carries **no** implied grant of code execution, so any code-execution or arbitrary-write primitive reachable purely by parsing it is a defect worth removing on its own merits, independent of how much the payload channel is trusted.
|
||||
|
||||
@@ -71,29 +71,11 @@ jobs:
|
||||
- name: check tests filenames
|
||||
run: ./rename_test.sh --check
|
||||
|
||||
check_exec:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: step-security/harden-runner@v2
|
||||
with:
|
||||
allowed-endpoints:
|
||||
github.com:443
|
||||
raw.githubusercontent.com:443
|
||||
nodejs.org:443
|
||||
registry.npmjs.org:443
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'nvm install lts/* && npm install'
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- name: check that test files are executable
|
||||
run: npm run test:check-exec
|
||||
|
||||
all:
|
||||
permissions:
|
||||
contents: none
|
||||
name: 'all linting'
|
||||
needs: [eclint, dockerfile_lint, doctoc, test_naming, check_exec]
|
||||
needs: [eclint, dockerfile_lint, doctoc, test_naming]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: true
|
||||
|
||||
@@ -49,6 +49,7 @@ jobs:
|
||||
contents: read
|
||||
needs: [matrix]
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{ matrix.ref == 'v0.40.0' }} # https://github.com/nvm-sh/nvm/issues/3405
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -80,19 +81,12 @@ jobs:
|
||||
set -e
|
||||
export NVM_INSTALL_VERSION="${ref}"
|
||||
curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/${ref}/install.sh" | bash
|
||||
# v0.40.0 with an .nvmrc present is expected to fail (https://github.com/nvm-sh/nvm/issues/3405):
|
||||
# the steps below tolerate failure for that combination, and the final step
|
||||
# turns the job red only if the expected failure did NOT happen.
|
||||
- name: nvm --version
|
||||
id: nvm-version
|
||||
continue-on-error: ${{ matrix.ref == 'v0.40.0' && matrix.has-nvmrc == 'nvmrc' }}
|
||||
run: |
|
||||
set +e
|
||||
. $NVM_DIR/nvm.sh && nvm --version
|
||||
- name: nvm install in 1 shell level, ${{ matrix.has-nvmrc }}
|
||||
id: install-1
|
||||
if: ${{ matrix.shell-level == '1 shlvl' }}
|
||||
continue-on-error: ${{ matrix.ref == 'v0.40.0' && matrix.has-nvmrc == 'nvmrc' }}
|
||||
run: |
|
||||
set -ex
|
||||
. $NVM_DIR/nvm.sh
|
||||
@@ -102,29 +96,13 @@ jobs:
|
||||
nvm install
|
||||
fi
|
||||
- name: nvm install in 2 shell levels, ${{ matrix.has-nvmrc }}
|
||||
id: install-2
|
||||
if: ${{ matrix.shell-level == '2 shlvls' }}
|
||||
continue-on-error: ${{ matrix.ref == 'v0.40.0' && matrix.has-nvmrc == 'nvmrc' }}
|
||||
run: |
|
||||
if [ '${{ matrix.has-nvmrc }}' == 'nvmrc' ]; then
|
||||
bash -c "set -ex && . $NVM_DIR/nvm.sh && echo nvm.sh sourced && nvm --version && nvm install"
|
||||
else
|
||||
bash -c "set -ex && . $NVM_DIR/nvm.sh && echo nvm.sh sourced && nvm --version"
|
||||
fi
|
||||
- name: 'confirm the expected failure happened (v0.40.0 + nvmrc)'
|
||||
if: ${{ matrix.ref == 'v0.40.0' && matrix.has-nvmrc == 'nvmrc' }}
|
||||
env:
|
||||
SOURCE_OUTCOME: ${{ steps.nvm-version.outcome }}
|
||||
INSTALL_1_OUTCOME: ${{ steps.install-1.outcome }}
|
||||
INSTALL_2_OUTCOME: ${{ steps.install-2.outcome }}
|
||||
run: |
|
||||
echo "nvm --version: ${SOURCE_OUTCOME}; install (1 shlvl): ${INSTALL_1_OUTCOME}; install (2 shlvls): ${INSTALL_2_OUTCOME}"
|
||||
if [ "${SOURCE_OUTCOME}" = 'failure' ] || [ "${INSTALL_1_OUTCOME}" = 'failure' ] || [ "${INSTALL_2_OUTCOME}" = 'failure' ]; then
|
||||
echo 'v0.40.0 failed as expected: https://github.com/nvm-sh/nvm/issues/3405'
|
||||
else
|
||||
echo '::error::v0.40.0 with an .nvmrc was expected to fail (https://github.com/nvm-sh/nvm/issues/3405), but every step succeeded. If v0.40.0 somehow works now, remove the expected-failure handling from this workflow.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
finisher:
|
||||
permissions:
|
||||
|
||||
@@ -35,6 +35,5 @@ 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
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
name: 'Tests: alpine'
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
fast:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Native runners per arch (no QEMU): arm64 uses the ubuntu-24.04-arm image.
|
||||
name: 'fast ${{ matrix.arch }} (alpine ${{ matrix.alpine }}, ${{ matrix.shell }})'
|
||||
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
alpine:
|
||||
- '3.15'
|
||||
- '3.19'
|
||||
- '3'
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
shell:
|
||||
- sh
|
||||
- bash
|
||||
- dash
|
||||
- zsh
|
||||
# - ksh (#574)
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@v2
|
||||
with:
|
||||
allowed-endpoints:
|
||||
github.com:443
|
||||
registry.npmjs.org:443
|
||||
raw.githubusercontent.com:443
|
||||
nodejs.org:443
|
||||
iojs.org:443
|
||||
unofficial-builds.nodejs.org:443
|
||||
dl-cdn.alpinelinux.org:443
|
||||
dl-cdn.alpinelinux.org:80
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
- uses: actions/checkout@v6
|
||||
id: checkout
|
||||
continue-on-error: true
|
||||
with:
|
||||
submodules: true
|
||||
- name: 'nvmrc submodule fallback (forks without their own nvmrc)'
|
||||
if: steps.checkout.outcome == 'failure'
|
||||
shell: bash
|
||||
run: |
|
||||
git submodule set-url test/fixtures/nvmrc https://github.com/nvm-sh/nvmrc.git
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'npm install (on host; node_modules is mounted into the container)'
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
skip-ls-check: true
|
||||
- run: npm ls urchin
|
||||
- name: 'Run fast tests on Alpine ${{ matrix.alpine }} (${{ matrix.arch }})'
|
||||
run: |
|
||||
for i in 1 2 3 4 5; do
|
||||
docker pull alpine:${{ matrix.alpine }} && break
|
||||
echo "docker pull failed, attempt $i/5"; sleep $((i * 5))
|
||||
done
|
||||
docker run --rm \
|
||||
-v "${{ github.workspace }}:/workspace" \
|
||||
-w /workspace \
|
||||
-e "TEST_SHELL=${{ matrix.shell }}" \
|
||||
-e "TERM=xterm-256color" \
|
||||
-e "GITHUB_ACTIONS=true" \
|
||||
alpine:${{ matrix.alpine }} \
|
||||
sh -c '
|
||||
set -ex
|
||||
cat /etc/alpine-release; uname -m
|
||||
for i in 1 2 3 4 5; do
|
||||
apk add --no-cache \
|
||||
make bash zsh dash \
|
||||
grep sed gawk coreutils util-linux findutils ncurses \
|
||||
curl wget ca-certificates openssl tar xz gzip git \
|
||||
sudo su-exec libstdc++ libgcc && break
|
||||
echo "apk add failed, attempt $i/5"; sleep $((i * 5))
|
||||
done
|
||||
# Mirror the ubuntu runner: run the suite as a non-root user with
|
||||
# passwordless sudo and a PTY so the permission/terminal tests
|
||||
# behave the same. The user takes the mounted checkout uid so
|
||||
# files stay host-owned (no chown; post-checkout cleanup works).
|
||||
# No node is installed, so no active version skews the output.
|
||||
HOST_UID="$(stat -c %u /workspace)"
|
||||
adduser -D -u "$HOST_UID" tester 2>/dev/null || adduser -D tester
|
||||
echo "tester ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/tester
|
||||
chmod 0440 /etc/sudoers.d/tester
|
||||
export NVM_DIR=/workspace HOME=/home/tester
|
||||
unset NVM_BIN NVM_INC NVM_CD_FLAGS
|
||||
su-exec tester script -q -e -c "make TEST_SUITE=fast SHELL=$TEST_SHELL URCHIN=/workspace/node_modules/.bin/urchin test-$TEST_SHELL" /dev/null
|
||||
'
|
||||
|
||||
musl-binary:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Regression lock, same-era diagonal only: modern musl node needs a newer
|
||||
# libstdc++ than old Alpine ships, and arm64-musl exists only from
|
||||
# v20.20.1/v22.21.1/v24.9.0 (built on modern Alpine), so arm64 pins modern Alpine.
|
||||
name: 'musl-binary ${{ matrix.arch }} (alpine ${{ matrix.alpine }}, node ${{ matrix.node }})'
|
||||
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { arch: x64, alpine: '3.10', node: v8.17.0 }
|
||||
- { arch: x64, alpine: '3.10', node: v10.24.1 }
|
||||
- { arch: x64, alpine: '3.12', node: v12.22.12 }
|
||||
- { arch: x64, alpine: '3.15', node: v14.21.3 }
|
||||
- { arch: x64, alpine: '3.16', node: v16.20.2 }
|
||||
- { arch: x64, alpine: '3.18', node: v18.20.4 }
|
||||
- { arch: x64, alpine: '3.20', node: v20.18.1 }
|
||||
- { arch: x64, alpine: '3', node: v22.12.0 }
|
||||
- { arch: arm64, alpine: '3.20', node: v20.20.1 }
|
||||
- { arch: arm64, alpine: '3', node: v22.21.1 }
|
||||
- { arch: arm64, alpine: '3', node: v24.9.0 }
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@v2
|
||||
with:
|
||||
allowed-endpoints:
|
||||
github.com:443
|
||||
registry.npmjs.org:443
|
||||
unofficial-builds.nodejs.org:443
|
||||
dl-cdn.alpinelinux.org:443
|
||||
dl-cdn.alpinelinux.org:80
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: false
|
||||
- name: 'Install node ${{ matrix.node }} from a musl binary on Alpine ${{ matrix.alpine }}'
|
||||
run: |
|
||||
for i in 1 2 3 4 5; do
|
||||
docker pull alpine:${{ matrix.alpine }} && break
|
||||
echo "docker pull failed, attempt $i/5"; sleep $((i * 5))
|
||||
done
|
||||
docker run --rm \
|
||||
-v "${{ github.workspace }}:/workspace" \
|
||||
-w /workspace \
|
||||
-e "NODE_VERSION=${{ matrix.node }}" \
|
||||
-e "TERM=xterm-256color" \
|
||||
alpine:${{ matrix.alpine }} \
|
||||
sh -c '
|
||||
set -ex
|
||||
cat /etc/alpine-release; uname -m
|
||||
# libstdc++/libgcc are load-bearing: the unofficial musl node
|
||||
# binary dynamically links them. No build toolchain: -b forbids
|
||||
# the source fallback, so a missing binary is a hard failure.
|
||||
for i in 1 2 3 4 5; do
|
||||
apk add --no-cache \
|
||||
bash ca-certificates curl wget tar xz gzip \
|
||||
grep sed coreutils libstdc++ libgcc && break
|
||||
echo "apk add failed, attempt $i/5"; sleep $((i * 5))
|
||||
done
|
||||
export NVM_DIR=/workspace
|
||||
unset NVM_BIN NVM_INC NVM_CD_FLAGS
|
||||
# The default nodejs.org mirror serves no -musl artifacts; this one does.
|
||||
export NVM_NODEJS_ORG_MIRROR=https://unofficial-builds.nodejs.org/download/release
|
||||
. /workspace/nvm.sh --no-use
|
||||
nvm install -b --skip-default-packages "$NODE_VERSION"
|
||||
nvm use "$NODE_VERSION"
|
||||
[ "$(node -v)" = "$NODE_VERSION" ] || { echo "version mismatch: $(node -v)"; exit 1; }
|
||||
node -e "process.exit(0)"
|
||||
ldd "$(command -v node)" | grep -qi musl
|
||||
'
|
||||
|
||||
all:
|
||||
permissions:
|
||||
contents: none
|
||||
name: 'all alpine tests'
|
||||
needs: [fast, musl-binary]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: true
|
||||
@@ -98,45 +98,11 @@ jobs:
|
||||
run: sudo apt-get install curl -y
|
||||
shell: bash
|
||||
|
||||
installation_iojs_source_compile:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
name: 'actually compile io.js from source (gcc 4.9 container)'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@v2
|
||||
with:
|
||||
allowed-endpoints:
|
||||
github.com:443
|
||||
mirror.gcr.io:443
|
||||
storage.googleapis.com:443
|
||||
iojs.org:443
|
||||
- uses: actions/checkout@v6
|
||||
# io.js requires python 2 and gcc <= 5 to compile; the gcc:4.9 image
|
||||
# (debian jessie) has both, plus curl/wget/xz and CA certificates that
|
||||
# still validate iojs.org - so the source-install tests run unmodified,
|
||||
# resolving, downloading, checksumming, and compiling for real.
|
||||
# The image is pulled through Google's Docker Hub mirror: it serves
|
||||
# manifests and blobs from a single stable hostname (Docker Hub's blob
|
||||
# CDN hostnames rotate, which the blocked-egress policy can not allow),
|
||||
# and it is not subject to Docker Hub's anonymous pull rate limits.
|
||||
- name: compile io.js from source, for real
|
||||
run: |
|
||||
docker run --rm -v "${PWD}:/nvm-under-test" -e NVM_DIR=/nvm-under-test mirror.gcr.io/library/gcc:4.9 bash -ec '
|
||||
cd "${NVM_DIR}/test/installation_iojs"
|
||||
sh "./install from source"
|
||||
sh "./install version specified in .nvmrc from source"
|
||||
'
|
||||
|
||||
all:
|
||||
permissions:
|
||||
contents: none
|
||||
name: 'all installation_iojs tests'
|
||||
needs: [installation_iojs_without_curl, installation_iojs_source_compile]
|
||||
needs: [installation_iojs_without_curl]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: true
|
||||
|
||||
@@ -72,35 +72,18 @@ endif
|
||||
_ensure-clean:
|
||||
@[ -z "$$(git status --porcelain --untracked-files=no || echo err)" ] || { echo "Workspace is not clean; please commit changes first." >&2; exit 2; }
|
||||
|
||||
# Resolves $(TAG) against the most recent version tag; sets $$old_ver and $$new_ver in the recipe's shell.
|
||||
define RESOLVE_VERSIONS
|
||||
old_ver=`git describe --abbrev=0 --tags --match 'v[0-9]*.[0-9]*.[0-9]*'` || { echo "Failed to determine current version." >&2; exit 1; }; old_ver=$${old_ver#v}; \
|
||||
new_ver=`echo "$(TAG)" | sed 's/^v//'`; new_ver=$${new_ver:-patch}; \
|
||||
if printf "$$new_ver" | grep -q '^[0-9]'; then \
|
||||
semver "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be major.minor.patch' >&2; exit 2; }; \
|
||||
semver -r "> $$old_ver" "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be HIGHER than current one.' >&2; exit 2; } \
|
||||
else \
|
||||
new_ver=`semver -i "$$new_ver" "$$old_ver"` || { echo 'Invalid version-increment specifier: $(TAG)' >&2; exit 2; } \
|
||||
fi
|
||||
endef
|
||||
|
||||
# Prints the release notes that `make TAG=<versionOrIncrementSpec> release` would use; invoke the same way.
|
||||
.PHONY: release-notes
|
||||
release-notes: _ensure-tag _ensure-current-version
|
||||
@$(RESOLVE_VERSIONS); \
|
||||
./release-notes.sh "v$$new_ver"
|
||||
|
||||
# Makes a release; invoke with `make TAG=<versionOrIncrementSpec> release`.
|
||||
.PHONY: release
|
||||
release: _ensure-tag _ensure-clean _ensure-current-version
|
||||
@$(RESOLVE_VERSIONS); \
|
||||
notes_file=`mktemp` || { echo 'Failed to create a temp file for the release notes.' >&2; exit 1; }; \
|
||||
trap 'rm -f "$$notes_file"' EXIT; \
|
||||
./release-notes.sh "v$$new_ver" > "$$notes_file" || { echo 'Failed to generate release notes.' >&2; exit 1; }; \
|
||||
echo '=== Release notes: ==='; \
|
||||
cat "$$notes_file"; \
|
||||
printf "\n=== Bumping version **$$old_ver** to **$$new_ver** before committing and tagging with the above notes:\n=== TYPE 'proceed' TO PROCEED, anything else to abort: " && read response && [ "$$response" = 'proceed' ] || { echo 'Aborted.' >&2; exit 2; }; \
|
||||
old_ver_re=`printf '%s' "$$old_ver" | sed 's/[.]/\\\\./g'` && \
|
||||
replace "$$old_ver_re" "$$new_ver" $(VERSIONED_FILES) && \
|
||||
git commit -F "$$notes_file" $(VERSIONED_FILES) && \
|
||||
git tag -a -F "$$notes_file" "v$$new_ver"
|
||||
@old_ver=`git describe --abbrev=0 --tags --match 'v[0-9]*.[0-9]*.[0-9]*'` || { echo "Failed to determine current version." >&2; exit 1; }; old_ver=$${old_ver#v}; \
|
||||
new_ver=`echo "$(TAG)" | sed 's/^v//'`; new_ver=$${new_ver:-patch}; \
|
||||
if printf "$$new_ver" | grep -q '^[0-9]'; then \
|
||||
semver "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be major.minor.patch' >&2; exit 2; }; \
|
||||
semver -r "> $$old_ver" "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be HIGHER than current one.' >&2; exit 2; } \
|
||||
else \
|
||||
new_ver=`semver -i "$$new_ver" "$$old_ver"` || { echo 'Invalid version-increment specifier: $(TAG)' >&2; exit 2; } \
|
||||
fi; \
|
||||
printf "=== Bumping version **$$old_ver** to **$$new_ver** before committing and tagging:\n=== TYPE 'proceed' TO PROCEED, anything else to abort: " && read response && [ "$$response" = 'proceed' ] || { echo 'Aborted.' >&2; exit 2; }; \
|
||||
replace "$$old_ver" "$$new_ver" $(VERSIONED_FILES) && \
|
||||
git commit -m "v$$new_ver" $(VERSIONED_FILES) && \
|
||||
git tag -a "v$$new_ver"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</a>
|
||||
|
||||
|
||||
# Node Version Manager [][3] [][4] [](https://bestpractices.dev/projects/684)
|
||||
# Node Version Manager [][3] [][4] [](https://bestpractices.dev/projects/684)
|
||||
|
||||
<!-- To update this table of contents, ensure you have run `npm install` then `npm run doctoc` -->
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
## Intro
|
||||
|
||||
`nvm` allows you to quickly install and use different versions of [node](https://nodejs.org) via the command line.
|
||||
`nvm` allows you to quickly install and use different versions of node via the command line.
|
||||
|
||||
**Example:**
|
||||
```sh
|
||||
@@ -96,7 +96,7 @@ Simple as that!
|
||||
|
||||
|
||||
## About
|
||||
nvm is a version manager for [node.js](https://nodejs.org/en/), designed to be installed per-user, and invoked per-shell. `nvm` works on any POSIX-compliant shell (sh, [dash](https://git.kernel.org/pub/scm/utils/dash/dash.git), [ksh](https://github.com/ksh93/ksh), [zsh](https://www.zsh.org), [bash](https://www.gnu.org/software/bash/)), in particular on these platforms: unix, [macOS](https://www.apple.com/macos/), and [Windows WSL](https://github.com/nvm-sh/nvm#important-notes).
|
||||
nvm is a version manager for [node.js](https://nodejs.org/en/), designed to be installed per-user, and invoked per-shell. `nvm` works on any POSIX-compliant shell (sh, dash, ksh, zsh, bash), in particular on these platforms: unix, macOS, and [Windows WSL](https://github.com/nvm-sh/nvm#important-notes).
|
||||
|
||||
<a id="installation-and-update"></a>
|
||||
<a id="install-script"></a>
|
||||
@@ -104,12 +104,12 @@ nvm is a version manager for [node.js](https://nodejs.org/en/), designed to be i
|
||||
|
||||
### Install & Update Script
|
||||
|
||||
To **install** or **update** nvm, you should run the [install script][2]. To do that, you may either download and run the script manually, or use the following [cURL](https://curl.se) or [Wget](https://www.gnu.org/software/wget/) command:
|
||||
To **install** or **update** nvm, you should run the [install script][2]. To do that, you may either download and run the script manually, or use the following cURL or Wget command:
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
```sh
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
|
||||
Running either of the above commands downloads a script and runs it. The script clones the nvm repository to `~/.nvm`, and attempts to add the source lines from the snippet below to the correct profile file (`~/.bashrc`, `~/.bash_profile`, `~/.zshrc`, or `~/.profile`). If you find the install script is updating the wrong profile file, set the `$PROFILE` env var to the profile file’s path, and then rerun the installation script.
|
||||
@@ -134,13 +134,13 @@ export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || pr
|
||||
- You can customize the install source, directory, profile, and version using the `NVM_SOURCE`, `NVM_DIR`, `PROFILE`, and `NODE_VERSION` variables.
|
||||
Eg: `curl ... | NVM_DIR="path/to/nvm"`. Ensure that the `NVM_DIR` does not contain a trailing slash.
|
||||
|
||||
- The installer can use [`git`](https://git-scm.com/), `curl`, or `wget` to download `nvm`, whichever is available.
|
||||
- The installer can use `git`, `curl`, or `wget` to download `nvm`, whichever is available.
|
||||
|
||||
- You can instruct the installer to not edit your shell config (for example if you already get completions via a [zsh nvm plugin](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/nvm)) by setting `PROFILE=/dev/null` before running the `install.sh` script. Here's an example one-line command to do that: `PROFILE=/dev/null bash -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash'`
|
||||
- You can instruct the installer to not edit your shell config (for example if you already get completions via a [zsh nvm plugin](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/nvm)) by setting `PROFILE=/dev/null` before running the `install.sh` script. Here's an example one-line command to do that: `PROFILE=/dev/null bash -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash'`
|
||||
|
||||
#### Installing in Docker
|
||||
|
||||
When invoking bash as a non-interactive shell, like in a [Docker](https://www.docker.com) container, none of the regular profile files are sourced. In order to use `nvm`, `node`, and [`npm`](https://www.npmjs.com) like normal, you can instead specify the special `BASH_ENV` variable, which bash sources when invoked non-interactively.
|
||||
When invoking bash as a non-interactive shell, like in a Docker container, none of the regular profile files are sourced. In order to use `nvm`, `node`, and `npm` like normal, you can instead specify the special `BASH_ENV` variable, which bash sources when invoked non-interactively.
|
||||
|
||||
```Dockerfile
|
||||
# Use bash for the shell
|
||||
@@ -152,7 +152,7 @@ RUN touch "${BASH_ENV}"
|
||||
RUN echo '. "${BASH_ENV}"' >> ~/.bashrc
|
||||
|
||||
# Download and install nvm
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | PROFILE="${BASH_ENV}" bash
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | PROFILE="${BASH_ENV}" bash
|
||||
RUN echo node > .nvmrc
|
||||
RUN nvm install
|
||||
```
|
||||
@@ -170,7 +170,7 @@ ARG NODE_VERSION=20
|
||||
RUN apt update && apt install curl -y
|
||||
|
||||
# install nvm
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
|
||||
# set env
|
||||
ENV NVM_DIR=/root/.nvm
|
||||
@@ -196,7 +196,7 @@ After creation of the image you can start container interactively and run comman
|
||||
docker run --rm -it nvmimage
|
||||
|
||||
root@0a6b5a237c14:/# nvm -v
|
||||
0.40.7
|
||||
0.40.5
|
||||
|
||||
root@0a6b5a237c14:/# node -v
|
||||
v19.9.0
|
||||
@@ -215,7 +215,7 @@ user@host:/tmp/test $ docker run --rm -it nvmimage npm -v
|
||||
|
||||
#### Troubleshooting on Linux
|
||||
|
||||
On [Linux](https://www.kernel.org/), after running the install script, if you get `nvm: command not found` or see no feedback from your terminal after you type `command -v nvm`, simply close your current terminal, open a new terminal, and try verifying again.
|
||||
On Linux, after running the install script, if you get `nvm: command not found` or see no feedback from your terminal after you type `command -v nvm`, simply close your current terminal, open a new terminal, and try verifying again.
|
||||
Alternatively, you can run the following commands for the different shells on the command line:
|
||||
|
||||
*bash*: `source ~/.bashrc`
|
||||
@@ -228,7 +228,7 @@ These should pick up the `nvm` command.
|
||||
|
||||
#### Troubleshooting on macOS
|
||||
|
||||
Since OS X 10.9, `/usr/bin/git` has been preset by [Xcode](https://developer.apple.com/xcode/) command line tools, which means we can't properly detect if Git is installed or not. You need to manually install the Xcode command line tools before running the install script, otherwise, it'll fail. (see [#1782](https://github.com/nvm-sh/nvm/issues/1782))
|
||||
Since OS X 10.9, `/usr/bin/git` has been preset by Xcode command line tools, which means we can't properly detect if Git is installed or not. You need to manually install the Xcode command line tools before running the install script, otherwise, it'll fail. (see [#1782](https://github.com/nvm-sh/nvm/issues/1782))
|
||||
|
||||
If you get `nvm: command not found` after running the install script, one of the following might be the reason:
|
||||
|
||||
@@ -259,7 +259,7 @@ You can use a task:
|
||||
```yaml
|
||||
- name: Install nvm
|
||||
ansible.builtin.shell: >
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
args:
|
||||
creates: "{{ ansible_env.HOME }}/.nvm/nvm.sh"
|
||||
```
|
||||
@@ -278,9 +278,9 @@ which should output `nvm` if the installation was successful. Please note that `
|
||||
|
||||
### Important Notes
|
||||
|
||||
If you're running a system without prepackaged binary available, which means you're going to install node or [io.js](https://iojs.org) from its source code, you need to make sure your system has a C++ compiler. For OS X, Xcode will work, for [Debian](https://www.debian.org)/[Ubuntu](https://ubuntu.com) based GNU/Linux, the `build-essential` and `libssl-dev` packages work.
|
||||
If you're running a system without prepackaged binary available, which means you're going to install node or io.js from its source code, you need to make sure your system has a C++ compiler. For OS X, Xcode will work, for Debian/Ubuntu based GNU/Linux, the `build-essential` and `libssl-dev` packages work.
|
||||
|
||||
**Note:** `nvm` also supports Windows in some cases. It should work through WSL (Windows Subsystem for Linux) depending on the version of WSL. It should also work with [Git Bash](https://gitforwindows.org/) ([MSYS](https://www.msys2.org/)) or [Cygwin](https://cygwin.com). Otherwise, for Windows, a few alternatives exist, which are neither supported nor developed by us:
|
||||
**Note:** `nvm` also supports Windows in some cases. It should work through WSL (Windows Subsystem for Linux) depending on the version of WSL. It should also work with [Git Bash](https://gitforwindows.org/) (MSYS) or [Cygwin](https://cygwin.com). Otherwise, for Windows, a few alternatives exist, which are neither supported nor developed by us:
|
||||
|
||||
- [nvm-windows](https://github.com/coreybutler/nvm-windows)
|
||||
- [nodist](https://github.com/marcelklehr/nodist)
|
||||
@@ -294,7 +294,7 @@ If you're running a system without prepackaged binary available, which means you
|
||||
- [nvm.fish](https://github.com/jorgebucaran/nvm.fish) - The Node.js version manager you'll adore, crafted just for Fish
|
||||
- [fish-nvm](https://github.com/FabioAntunes/fish-nvm) - Wrapper around nvm for fish, delays sourcing nvm until it's actually used.
|
||||
|
||||
**Note:** We still have some problems with [FreeBSD](https://www.freebsd.org), because there is no official pre-built binary for FreeBSD, and building from source may need [patches](https://www.freshports.org/www/node/files/patch-deps_v8_src_base_platform_platform-posix.cc); see the issue ticket:
|
||||
**Note:** We still have some problems with FreeBSD, because there is no official pre-built binary for FreeBSD, and building from source may need [patches](https://www.freshports.org/www/node/files/patch-deps_v8_src_base_platform_platform-posix.cc); see the issue ticket:
|
||||
|
||||
- [[#900] [Bug] node on FreeBSD may need to be patched](https://github.com/nvm-sh/nvm/issues/900)
|
||||
- [nodejs/node#3716](https://github.com/nodejs/node/issues/3716)
|
||||
@@ -309,11 +309,11 @@ If you're running a system without prepackaged binary available, which means you
|
||||
- If you have an `~/.npmrc` file, make sure it does not contain any `prefix` settings (which is not compatible with `nvm`)
|
||||
- You can (but should not?) keep your previous "system" node install, but `nvm` will only be available to your user account (the one used to install nvm). This might cause version mismatches, as other users will be using `/usr/local/lib/node_modules/*` VS your user account using `~/.nvm/versions/node/vX.X.X/lib/node_modules/*`
|
||||
|
||||
[Homebrew](https://brew.sh) installation is not supported. If you have issues with homebrew-installed `nvm`, please `brew uninstall` it, and install it using the instructions below, before filing an issue.
|
||||
Homebrew installation is not supported. If you have issues with homebrew-installed `nvm`, please `brew uninstall` it, and install it using the instructions below, before filing an issue.
|
||||
|
||||
**Note:** If you're using `zsh` you can easily install `nvm` as a zsh plugin. Install [`zsh-nvm`](https://github.com/lukechilds/zsh-nvm) and run `nvm upgrade` to upgrade ([you can set](https://github.com/lukechilds/zsh-nvm#auto-use) `NVM_AUTO_USE=true` to have it automatically detect and use `.nvmrc` files).
|
||||
|
||||
**Note:** Git versions before v1.7 may face a problem of cloning `nvm` source from [GitHub](https://github.com) via https protocol, and there is also different behavior of git before v1.6, and git prior to [v1.17.10](https://github.com/git/git/commit/5a7d5b683f869d3e3884a89775241afa515da9e7) can not clone tags, so the minimum required git version is v1.7.10. If you are interested in the problem we mentioned here, please refer to GitHub's [HTTPS cloning errors](https://help.github.com/articles/https-cloning-errors/) article.
|
||||
**Note:** Git versions before v1.7 may face a problem of cloning `nvm` source from GitHub via https protocol, and there is also different behavior of git before v1.6, and git prior to [v1.17.10](https://github.com/git/git/commit/5a7d5b683f869d3e3884a89775241afa515da9e7) can not clone tags, so the minimum required git version is v1.7.10. If you are interested in the problem we mentioned here, please refer to GitHub's [HTTPS cloning errors](https://help.github.com/articles/https-cloning-errors/) article.
|
||||
|
||||
### Git Install
|
||||
|
||||
@@ -321,7 +321,7 @@ If you have `git` installed (requires git v1.7.10+):
|
||||
|
||||
1. clone this repo in the root of your user profile
|
||||
- `cd ~/` from anywhere then `git clone https://github.com/nvm-sh/nvm.git .nvm`
|
||||
1. `cd ~/.nvm` and check out the latest version with `git checkout v0.40.7`
|
||||
1. `cd ~/.nvm` and check out the latest version with `git checkout v0.40.5`
|
||||
1. activate `nvm` by sourcing it from your shell: `. ./nvm.sh`
|
||||
|
||||
Now add these lines to your `~/.bashrc`, `~/.profile`, or `~/.zshrc` file to have it automatically sourced upon login:
|
||||
@@ -429,7 +429,7 @@ In place of a version pointer like "14.7" or "16.3" or "12.22.1", you can use th
|
||||
- `node`: this installs the latest version of [`node`](https://nodejs.org/en/)
|
||||
- `iojs`: this installs the latest version of [`io.js`](https://iojs.org/en/)
|
||||
- `stable`: this alias is deprecated, and only truly applies to `node` `v0.12` and earlier. Currently, this is an alias for `node`.
|
||||
- `unstable`: this alias points to `node` `v0.11` - the last "unstable" node release, since post-1.0, all node versions are stable. (in [SemVer](https://semver.org), versions communicate breakage, not stability).
|
||||
- `unstable`: this alias points to `node` `v0.11` - the last "unstable" node release, since post-1.0, all node versions are stable. (in SemVer, versions communicate breakage, not stability).
|
||||
- `current`: the version currently active in this shell (i.e. what `node` resolves to via `$PATH`). It is **not** affected by `.nvmrc`. Useful when you want to refer to the active version explicitly — e.g. `nvm which current` always prints the path to the active `node`, regardless of whether an `.nvmrc` file is present.
|
||||
|
||||
### Long-term Support
|
||||
@@ -872,14 +872,6 @@ nvm exposes the following environment variables:
|
||||
|
||||
Additionally, nvm modifies `PATH`, and, if present, `MANPATH` and `NODE_PATH` when changing versions.
|
||||
|
||||
The following environment variables can be set to configure `nvm install`:
|
||||
|
||||
- `NVM_NO_SOURCE_FALLBACK` - when `1`, a failed binary download aborts instead of silently falling back to a (much slower) from-source compile; the persistent equivalent of the `-b` flag, and mutually exclusive with `-s`.
|
||||
- `NVM_INSTALL_LOCK_TIMEOUT` - seconds to wait for a concurrent install of the same version to finish before giving up (default `600`). On timeout, nvm prints the lock path so a lock left behind by a killed install can be removed.
|
||||
- `NVM_INSTALL_LOCK_STALE` - minutes after which an install lock is assumed abandoned and stolen automatically; `0` (the default) never steals.
|
||||
|
||||
`nvm install <version>` takes a per-version advisory lock (a directory under `$NVM_DIR/.cache/locks`), so two shells installing the same version at once cannot corrupt its version directory; installs of *different* versions never block each other.
|
||||
|
||||
|
||||
## Bash Completion
|
||||
|
||||
@@ -955,9 +947,9 @@ set -e
|
||||
|
||||
## Installing nvm on Alpine Linux
|
||||
|
||||
In order to provide the best performance (and other optimizations), nvm will download and install pre-compiled binaries for Node (and npm) when you run `nvm install X`. The Node project compiles, tests and hosts/provides these pre-compiled binaries which are built for mainstream/traditional Linux distributions (such as Debian, Ubuntu, [CentOS](https://www.centos.org), [RedHat](https://www.redhat.com) et al).
|
||||
In order to provide the best performance (and other optimizations), nvm will download and install pre-compiled binaries for Node (and npm) when you run `nvm install X`. The Node project compiles, tests and hosts/provides these pre-compiled binaries which are built for mainstream/traditional Linux distributions (such as Debian, Ubuntu, CentOS, RedHat et al).
|
||||
|
||||
[Alpine Linux](https://www.alpinelinux.org), unlike mainstream/traditional Linux distributions, is based on [BusyBox](https://www.busybox.net/), a very compact (~5MB) Linux distribution. BusyBox (and thus Alpine Linux) uses a different C/C++ stack to most mainstream/traditional Linux distributions - [musl](https://www.musl-libc.org/). This makes binary programs built for such mainstream/traditional incompatible with Alpine Linux, thus we cannot simply `nvm install X` on Alpine Linux and expect the downloaded binary to run correctly - you'll likely see "...does not exist" errors if you try that.
|
||||
Alpine Linux, unlike mainstream/traditional Linux distributions, is based on [BusyBox](https://www.busybox.net/), a very compact (~5MB) Linux distribution. BusyBox (and thus Alpine Linux) uses a different C/C++ stack to most mainstream/traditional Linux distributions - [musl](https://www.musl-libc.org/). This makes binary programs built for such mainstream/traditional incompatible with Alpine Linux, thus we cannot simply `nvm install X` on Alpine Linux and expect the downloaded binary to run correctly - you'll likely see "...does not exist" errors if you try that.
|
||||
|
||||
There is a `-s` flag for `nvm install` which requests nvm download Node source and compile it locally.
|
||||
|
||||
@@ -966,20 +958,20 @@ If installing nvm on Alpine Linux *is* still what you want or need to do, you sh
|
||||
### Alpine Linux 3.13+
|
||||
```sh
|
||||
apk add -U curl bash ca-certificates openssl ncurses coreutils python3 make gcc g++ libgcc linux-headers grep util-linux binutils findutils
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
|
||||
### Alpine Linux 3.5 - 3.12
|
||||
```sh
|
||||
apk add -U curl bash ca-certificates openssl ncurses coreutils python2 make gcc g++ libgcc linux-headers grep util-linux binutils findutils
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
|
||||
_Note: Alpine 3.5 can only install NodeJS versions up to v6.9.5, Alpine 3.6 can only install versions up to v6.10.3, Alpine 3.7 installs versions up to v8.9.3, Alpine 3.8 installs versions up to v8.14.0, Alpine 3.9 installs versions up to v10.19.0, Alpine 3.10 installs versions up to v10.24.1, Alpine 3.11 installs versions up to v12.22.6, Alpine 3.12 installs versions up to v12.22.12, Alpine 3.13 & 3.14 install versions up to v14.20.0, Alpine 3.15 & 3.16 install versions up to v16.16.0 (**These are all versions on the main branch**). Alpine 3.5 - 3.12 required the package [`python2`](https://www.python.org/) to build NodeJS, as they are older versions to build. Alpine 3.13+ requires `python3` to successfully build newer NodeJS versions, but you can use `python2` with Alpine 3.13+ if you need to build versions of node supported in Alpine 3.5 - 3.15, you just need to specify what version of NodeJS you need to install in the package install script._
|
||||
_Note: Alpine 3.5 can only install NodeJS versions up to v6.9.5, Alpine 3.6 can only install versions up to v6.10.3, Alpine 3.7 installs versions up to v8.9.3, Alpine 3.8 installs versions up to v8.14.0, Alpine 3.9 installs versions up to v10.19.0, Alpine 3.10 installs versions up to v10.24.1, Alpine 3.11 installs versions up to v12.22.6, Alpine 3.12 installs versions up to v12.22.12, Alpine 3.13 & 3.14 install versions up to v14.20.0, Alpine 3.15 & 3.16 install versions up to v16.16.0 (**These are all versions on the main branch**). Alpine 3.5 - 3.12 required the package `python2` to build NodeJS, as they are older versions to build. Alpine 3.13+ requires `python3` to successfully build newer NodeJS versions, but you can use `python2` with Alpine 3.13+ if you need to build versions of node supported in Alpine 3.5 - 3.15, you just need to specify what version of NodeJS you need to install in the package install script._
|
||||
|
||||
The Node project has some desire but no concrete plans (due to the overheads of building, testing and support) to offer Alpine-compatible binaries.
|
||||
|
||||
As a potential alternative, [@mhart](https://github.com/mhart) (a Node contributor) has some [Docker images for Alpine Linux with Node and optionally, npm, pre-installed](https://github.com/mhart/alpine-node).
|
||||
As a potential alternative, @mhart (a Node contributor) has some [Docker images for Alpine Linux with Node and optionally, npm, pre-installed](https://github.com/mhart/alpine-node).
|
||||
|
||||
<a id="removal"></a>
|
||||
## Uninstalling / Removal
|
||||
@@ -1006,13 +998,13 @@ export NVM_DIR="$HOME/.nvm"
|
||||
|
||||
## Docker For Development Environment
|
||||
|
||||
To make development and testing work easier we supply a Dockerfile for development usage. It's based on an Ubuntu base image prepared with essential and useful tools for `nvm` development. To build the docker image of the environment, do a Docker build at the root of `nvm` repository:
|
||||
To make the development and testing work easier, we have a Dockerfile for development usage, which is based on Ubuntu 18.04 base image, prepared with essential and useful tools for `nvm` development, to build the docker image of the environment, run the docker command at the root of `nvm` repository:
|
||||
|
||||
```sh
|
||||
$ docker build -t nvm-dev .
|
||||
```
|
||||
|
||||
This will package your current nvm working copy with our pre-defined development environment into a Docker image named `nvm-dev`. After the build you should see it appear in the list of images:
|
||||
This will package your current nvm repository with our pre-defined development environment into a docker image named `nvm-dev`, once it's built with success, validate your image via `docker images`:
|
||||
|
||||
```sh
|
||||
$ docker images
|
||||
@@ -1021,7 +1013,7 @@ REPOSITORY TAG IMAGE ID CREATED S
|
||||
nvm-dev latest 9ca4c57a97d8 7 days ago 650 MB
|
||||
```
|
||||
|
||||
To start and enter a container based on this image:
|
||||
If you got no error message, now you can easily involve in:
|
||||
|
||||
```sh
|
||||
$ docker run -h nvm-dev -it nvm-dev
|
||||
@@ -1029,9 +1021,12 @@ $ docker run -h nvm-dev -it nvm-dev
|
||||
nvm@nvm-dev:~/.nvm$
|
||||
```
|
||||
|
||||
It takes several minutes to build the image and the image size is about 650MB, so it's not suitable for production usage.
|
||||
Please note that it'll take about 8 minutes to build the image and the image size would be about 650MB, so it's not suitable for production usage.
|
||||
|
||||
For more information and documentation about Docker, please refer to its [official website][docker-www] and [documentation][docker-docs]:
|
||||
For more information and documentation about docker, please refer to its official website:
|
||||
|
||||
- https://www.docker.com/
|
||||
- https://docs.docker.com/
|
||||
|
||||
## Problems
|
||||
|
||||
@@ -1051,7 +1046,7 @@ nvm install -s 0.8.6
|
||||
|
||||
## macOS Troubleshooting
|
||||
|
||||
**nvm node version not found in [vim](https://www.vim.org) shell**
|
||||
**nvm node version not found in vim shell**
|
||||
|
||||
If you set node version to a version other than your system node version `nvm use 6.2.1` and open vim and run `:!node -v` you should see `v6.2.1` if you see your system version `v0.12.7`. You need to run:
|
||||
|
||||
@@ -1072,9 +1067,9 @@ You have to make sure that the user directory name in `$HOME` and the user direc
|
||||
To change the user directory and/or account name follow the instructions [here](https://support.apple.com/en-us/HT201548)
|
||||
|
||||
[1]: https://github.com/nvm-sh/nvm.git
|
||||
[2]: https://github.com/nvm-sh/nvm/blob/v0.40.7/install.sh
|
||||
[2]: https://github.com/nvm-sh/nvm/blob/v0.40.5/install.sh
|
||||
[3]: https://github.com/nvm-sh/nvm/actions/workflows/tests-fast.yml
|
||||
[4]: https://github.com/nvm-sh/nvm/releases/tag/v0.40.7
|
||||
[4]: https://github.com/nvm-sh/nvm/releases/tag/v0.40.5
|
||||
[Urchin]: https://git.sdf.org/tlevine/urchin
|
||||
[Fish]: https://fishshell.com
|
||||
|
||||
@@ -1092,7 +1087,7 @@ Homebrew causes insecure directories like `/usr/local/share/zsh/site-functions`
|
||||
Experimental support for the Apple Silicon chip architecture was added in node.js v15.3 and full support was added in v16.0.
|
||||
Because of this, if you try to install older versions of node as usual, you will probably experience either compilation errors when installing node or out-of-memory errors while running your code.
|
||||
|
||||
So, if you want to run a version prior to v16.0 on an Apple Silicon Mac, it may be best to compile node targeting the `x86_64` Intel architecture so that [Rosetta 2](https://support.apple.com/en-us/HT211861) can translate the `x86_64` processor instructions to ARM-based Apple Silicon instructions.
|
||||
So, if you want to run a version prior to v16.0 on an Apple Silicon Mac, it may be best to compile node targeting the `x86_64` Intel architecture so that Rosetta 2 can translate the `x86_64` processor instructions to ARM-based Apple Silicon instructions.
|
||||
Here's what you will need to do:
|
||||
|
||||
- Install Rosetta, if you haven't already done so
|
||||
@@ -1110,7 +1105,7 @@ Here's what you will need to do:
|
||||
$ arch -x86_64 zsh
|
||||
```
|
||||
|
||||
Note: This same thing can also be accomplished by finding the Terminal or [iTerm](https://iterm2.com) App in Finder, right clicking, selecting "Get Info", and then checking the box labeled "Open using Rosetta".
|
||||
Note: This same thing can also be accomplished by finding the Terminal or iTerm App in Finder, right clicking, selecting "Get Info", and then checking the box labeled "Open using Rosetta".
|
||||
|
||||
Note: This terminal session is now running in `zsh`.
|
||||
If `zsh` is not the shell you typically use, `nvm` may not be `source`'d automatically like it probably is for your usual shell through your dotfiles.
|
||||
@@ -1128,11 +1123,11 @@ Here's what you will need to do:
|
||||
```
|
||||
|
||||
Note: You're probably curious why `--shared-zlib` is included.
|
||||
There's a bug in recent versions of Apple's system [`clang`](https://clang.llvm.org/) compiler.
|
||||
There's a bug in recent versions of Apple's system `clang` compiler.
|
||||
If one of these broken versions is installed on your system, the above step will likely still succeed even if you didn't include the `--shared-zlib` flag.
|
||||
However, later, when you attempt to `npm install` something using your old version of node.js, you will see `incorrect data check` errors.
|
||||
If you want to avoid the possible hassle of dealing with this, include that flag.
|
||||
For more details, see [this issue](https://github.com/nodejs/node/issues/39313) and [this comment](https://github.com/nodejs/node/issues/39313#issuecomment-902395576)
|
||||
For more details, see [this issue](https://github.com/nodejs/node/issues/39313) and [this comment](https://github.com/nodejs/node/issues/39313#issuecomment-90.40.576)
|
||||
|
||||
- Exit back to your native shell.
|
||||
|
||||
@@ -1159,7 +1154,7 @@ Now you should be able to use node as usual.
|
||||
If you've encountered this error on WSL-2:
|
||||
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
% Total % Received % Xferd Average Speed Time Time Time Current
|
||||
Dload Upload Total Spent Left Speed
|
||||
0 0 0 0 0 0 0 0 --:--:-- 0:00:09 --:--:-- 0curl: (6) Could not resolve host: raw.githubusercontent.com
|
||||
@@ -1194,7 +1189,7 @@ Currently, the sole maintainer is [@ljharb](https://github.com/ljharb) - more ma
|
||||
|
||||
## Project Support
|
||||
|
||||
Only the latest version (v0.40.7 at this time) is supported.
|
||||
Only the latest version (v0.40.5 at this time) is supported.
|
||||
|
||||
## Enterprise Support
|
||||
|
||||
@@ -1210,7 +1205,3 @@ See [LICENSE.md](./LICENSE.md).
|
||||
|
||||
Copyright [OpenJS Foundation](https://openjsf.org) and `nvm` contributors. All rights reserved. The [OpenJS Foundation](https://openjsf.org) has registered trademarks and uses trademarks. For a list of trademarks of the [OpenJS Foundation](https://openjsf.org), please see our [Trademark Policy](https://trademark-policy.openjsf.org/) and [Trademark List](https://trademark-list.openjsf.org/). Trademarks and logos not indicated on the [list of OpenJS Foundation trademarks](https://trademark-list.openjsf.org) are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
|
||||
[The OpenJS Foundation](https://openjsf.org/) | [Terms of Use](https://terms-of-use.openjsf.org/) | [Privacy Policy](https://privacy-policy.openjsf.org/) | [Bylaws](https://bylaws.openjsf.org/) | [Code of Conduct](https://code-of-conduct.openjsf.org) | [Trademark Policy](https://trademark-policy.openjsf.org/) | [Trademark List](https://trademark-list.openjsf.org/) | [Cookie Policy](https://www.linuxfoundation.org/cookies/)
|
||||
|
||||
<!-------------------------------------------------------------------->
|
||||
[docker-docs]: https://docs.docker.com/
|
||||
[docker-www]: https://www.docker.com/
|
||||
|
||||
+18
-44
@@ -6,18 +6,6 @@ nvm_has() {
|
||||
type "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# resolves like `command "${1}"` does: only executables on the PATH,
|
||||
# ignoring shell functions and aliases
|
||||
nvm_has_executable() {
|
||||
(
|
||||
# `|| true` so that shells with errexit-style options (eg, zsh's ERR_RETURN)
|
||||
# do not abort the subshell when the name is not an alias or a function
|
||||
unalias "${1-}" 2>/dev/null || true
|
||||
unset -f "${1-}" 2>/dev/null || true
|
||||
command -v "${1-}" > /dev/null 2>&1
|
||||
)
|
||||
}
|
||||
|
||||
nvm_echo() {
|
||||
command printf %s\\n "$*" 2>/dev/null
|
||||
}
|
||||
@@ -45,7 +33,7 @@ nvm_install_dir() {
|
||||
}
|
||||
|
||||
nvm_latest_version() {
|
||||
nvm_echo "v0.40.7"
|
||||
nvm_echo "v0.40.5"
|
||||
}
|
||||
|
||||
nvm_profile_is_bash_or_zsh() {
|
||||
@@ -117,35 +105,21 @@ nvm_node_version() {
|
||||
}
|
||||
|
||||
nvm_download() {
|
||||
if nvm_has_executable "curl"; then
|
||||
command curl --fail --compressed -q "$@"
|
||||
elif nvm_has_executable "wget"; then
|
||||
if nvm_has "curl"; then
|
||||
curl --fail --compressed -q "$@"
|
||||
elif nvm_has "wget"; then
|
||||
# Emulate curl with wget
|
||||
local NVM_DOWNLOAD_WGET_COUNT
|
||||
NVM_DOWNLOAD_WGET_COUNT=$#
|
||||
local NVM_DOWNLOAD_WGET_SKIP
|
||||
NVM_DOWNLOAD_WGET_SKIP=0
|
||||
local NVM_DOWNLOAD_WGET_ARG
|
||||
for NVM_DOWNLOAD_WGET_ARG in "$@"; do
|
||||
if [ "${NVM_DOWNLOAD_WGET_SKIP}" = '1' ]; then
|
||||
NVM_DOWNLOAD_WGET_SKIP=0
|
||||
continue
|
||||
fi
|
||||
case "${NVM_DOWNLOAD_WGET_ARG}" in
|
||||
'--progress-bar') set -- "$@" '--progress=bar' ;;
|
||||
'--compressed') : ;;
|
||||
'--fail') : ;;
|
||||
'-L') : ;;
|
||||
'-I') set -- "$@" '--server-response' ;;
|
||||
'-s') set -- "$@" '-q' ;;
|
||||
'-sS') set -- "$@" '-nv' ;;
|
||||
'-o') set -- "$@" '-O' ;;
|
||||
'-C') NVM_DOWNLOAD_WGET_SKIP=1; set -- "$@" '-c' ;;
|
||||
*) set -- "$@" "${NVM_DOWNLOAD_WGET_ARG}" ;;
|
||||
esac
|
||||
done
|
||||
shift "${NVM_DOWNLOAD_WGET_COUNT}"
|
||||
command wget "$@"
|
||||
ARGS=$(nvm_echo "$@" | command sed -e 's/--progress-bar /--progress=bar /' \
|
||||
-e 's/--compressed //' \
|
||||
-e 's/--fail //' \
|
||||
-e 's/-L //' \
|
||||
-e 's/-I /--server-response /' \
|
||||
-e 's/-s /-q /' \
|
||||
-e 's/-sS /-nv /' \
|
||||
-e 's/-o /-O /' \
|
||||
-e 's/-C - /-c /')
|
||||
# shellcheck disable=SC2086
|
||||
eval wget $ARGS
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -427,7 +401,7 @@ nvm_do_install() {
|
||||
# Autodetect install method
|
||||
if nvm_has git; then
|
||||
install_nvm_from_git
|
||||
elif nvm_has_executable curl || nvm_has_executable wget; then
|
||||
elif nvm_has curl || nvm_has wget; then
|
||||
install_nvm_as_script
|
||||
else
|
||||
nvm_echo >&2 'You need git, curl, or wget to install nvm'
|
||||
@@ -440,7 +414,7 @@ nvm_do_install() {
|
||||
fi
|
||||
install_nvm_from_git
|
||||
elif [ "${METHOD}" = 'script' ]; then
|
||||
if ! nvm_has_executable curl && ! nvm_has_executable wget; then
|
||||
if ! nvm_has curl && ! nvm_has wget; then
|
||||
nvm_echo >&2 "You need curl or wget to install nvm"
|
||||
exit 1
|
||||
fi
|
||||
@@ -522,7 +496,7 @@ nvm_do_install() {
|
||||
# during the execution of the install script
|
||||
#
|
||||
nvm_reset() {
|
||||
unset -f nvm_has nvm_has_executable nvm_install_dir nvm_latest_version nvm_profile_is_bash_or_zsh \
|
||||
unset -f nvm_has nvm_install_dir nvm_latest_version nvm_profile_is_bash_or_zsh \
|
||||
nvm_source nvm_node_version nvm_download install_nvm_from_git nvm_install_node \
|
||||
install_nvm_as_script nvm_try_profile nvm_detect_profile nvm_check_global_modules \
|
||||
nvm_do_install nvm_reset nvm_default_install_dir nvm_grep
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{ # this ensures the entire script is downloaded #
|
||||
|
||||
# shellcheck disable=SC3028
|
||||
NVM_SCRIPT_SOURCE="${_:-}"
|
||||
NVM_SCRIPT_SOURCE="$_"
|
||||
|
||||
nvm_is_zsh() {
|
||||
[ -n "${ZSH_VERSION-}" ]
|
||||
@@ -49,18 +49,6 @@ nvm_has() {
|
||||
type "${1-}" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# resolves like `command "${1}"` does: only executables on the PATH,
|
||||
# ignoring shell functions and aliases
|
||||
nvm_has_executable() {
|
||||
(
|
||||
# `|| true` so that shells with errexit-style options (eg, zsh's ERR_RETURN)
|
||||
# do not abort the subshell when the name is not an alias or a function
|
||||
unalias "${1-}" 2>/dev/null || true
|
||||
unset -f "${1-}" 2>/dev/null || true
|
||||
command -v "${1-}" >/dev/null 2>&1
|
||||
)
|
||||
}
|
||||
|
||||
nvm_has_non_aliased() {
|
||||
nvm_has "${1-}" && ! nvm_is_alias "${1-}"
|
||||
}
|
||||
@@ -99,7 +87,7 @@ nvm_has_colors() {
|
||||
}
|
||||
|
||||
nvm_curl_libz_support() {
|
||||
command curl -V 2>/dev/null | nvm_grep "^Features:" | nvm_grep -q "libz"
|
||||
curl -V 2>/dev/null | nvm_grep "^Features:" | nvm_grep -q "libz"
|
||||
}
|
||||
|
||||
nvm_curl_use_compression() {
|
||||
@@ -109,13 +97,13 @@ nvm_curl_use_compression() {
|
||||
nvm_get_latest() {
|
||||
local NVM_LATEST_URL
|
||||
local CURL_COMPRESSED_FLAG
|
||||
if nvm_has_executable "curl"; then
|
||||
if nvm_has "curl"; then
|
||||
if nvm_curl_use_compression; then
|
||||
CURL_COMPRESSED_FLAG="--compressed"
|
||||
fi
|
||||
NVM_LATEST_URL="$(command curl ${CURL_COMPRESSED_FLAG:-} -q -w "%{url_effective}\\n" -L -s -S https://latest.nvm.sh -o /dev/null)"
|
||||
elif nvm_has_executable "wget"; then
|
||||
NVM_LATEST_URL="$(command wget -q https://latest.nvm.sh --server-response -O /dev/null 2>&1 | command awk '/^ Location: /{DEST=$2} END{ print DEST }')"
|
||||
NVM_LATEST_URL="$(curl ${CURL_COMPRESSED_FLAG:-} -q -w "%{url_effective}\\n" -L -s -S https://latest.nvm.sh -o /dev/null)"
|
||||
elif nvm_has "wget"; then
|
||||
NVM_LATEST_URL="$(wget -q https://latest.nvm.sh --server-response -O /dev/null 2>&1 | command awk '/^ Location: /{DEST=$2} END{ print DEST }')"
|
||||
else
|
||||
nvm_err 'nvm needs curl or wget to proceed.'
|
||||
return 1
|
||||
@@ -139,13 +127,13 @@ nvm_download() {
|
||||
|
||||
local NVM_DOWNLOADER
|
||||
NVM_DOWNLOADER=''
|
||||
if nvm_has_executable "curl"; then
|
||||
if nvm_has "curl"; then
|
||||
NVM_DOWNLOADER='curl'
|
||||
set -- -q --fail "$@"
|
||||
if nvm_curl_use_compression; then
|
||||
set -- --compressed "$@"
|
||||
fi
|
||||
elif nvm_has_executable "wget"; then
|
||||
elif nvm_has "wget"; then
|
||||
NVM_DOWNLOADER='wget'
|
||||
# Emulate curl with wget
|
||||
local NVM_DOWNLOAD_WGET_COUNT
|
||||
@@ -182,17 +170,12 @@ nvm_download() {
|
||||
set -- "$@" --header "Authorization: ${sanitized_header}"
|
||||
fi
|
||||
|
||||
command "${NVM_DOWNLOADER}" "$@"
|
||||
"${NVM_DOWNLOADER}" "$@"
|
||||
}
|
||||
|
||||
nvm_sanitize_auth_header() {
|
||||
# Remove potentially dangerous characters; allow the full base64 (A-Za-z0-9+/=)
|
||||
# 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.
|
||||
# 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'
|
||||
# Remove potentially dangerous characters
|
||||
nvm_echo "$1" | command sed 's/[^a-zA-Z0-9:;_. -]//g'
|
||||
}
|
||||
|
||||
nvm_has_system_node() {
|
||||
@@ -218,52 +201,6 @@ nvm_is_version_installed() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Sanity-check an installed version's layout: a non-empty, executable bin/node
|
||||
# and, if present, an npm entry that resolves. nvm_is_version_installed only
|
||||
# tests the bin/node exec bit, which a zero-byte binary and a dangling npm
|
||||
# symlink both pass; this catches those, so a partial install is not reported
|
||||
# as a success. It deliberately does NOT execute node: a correctly installed
|
||||
# binary can still fail to run on an incompatible host (e.g. a newer node on an
|
||||
# older glibc), which is not a broken install - and a corrupt download is
|
||||
# already rejected by the checksum check before extraction.
|
||||
nvm_validate_install() {
|
||||
local VERSION
|
||||
VERSION="${1-}"
|
||||
if [ -z "${VERSION}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local VERSION_PATH
|
||||
VERSION_PATH="$(nvm_version_path "${VERSION}" 2>/dev/null)"
|
||||
if [ -z "${VERSION_PATH}" ] || [ ! -d "${VERSION_PATH}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local NVM_NODE_BINARY
|
||||
NVM_NODE_BINARY='node'
|
||||
if [ "_$(nvm_get_os)" = '_win' ]; then
|
||||
NVM_NODE_BINARY='node.exe'
|
||||
fi
|
||||
|
||||
local NVM_NODE_PATH
|
||||
NVM_NODE_PATH="${VERSION_PATH}/bin/${NVM_NODE_BINARY}"
|
||||
# A zero-byte file with the exec bit set still passes `[ -x ]` (the shell
|
||||
# would run it as an empty script), so require a non-empty executable.
|
||||
if [ ! -s "${NVM_NODE_PATH}" ] || [ ! -x "${NVM_NODE_PATH}" ]; then
|
||||
nvm_err "The installed node binary at ${NVM_NODE_PATH} is missing or empty."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# npm ships with every supported node/io.js version; if its entry is a
|
||||
# symlink it must resolve (one pointing at a removed target counts as broken).
|
||||
if [ -h "${VERSION_PATH}/bin/npm" ] && [ ! -e "${VERSION_PATH}/bin/npm" ]; then
|
||||
nvm_err "npm for ${VERSION} is a dangling symlink."
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
nvm_print_npm_version() {
|
||||
if nvm_has "npm"; then
|
||||
local NPM_VERSION
|
||||
@@ -611,12 +548,12 @@ ${1}"
|
||||
$(nvm_wrap_with_color_code 'y' "${warn_text}")"
|
||||
}
|
||||
|
||||
nvm_process_nvmrc_content() {
|
||||
local NVMRC_CONTENT
|
||||
NVMRC_CONTENT="${1-}"
|
||||
nvm_process_nvmrc() {
|
||||
local NVMRC_PATH
|
||||
NVMRC_PATH="$1"
|
||||
local lines
|
||||
|
||||
lines=$(nvm_echo "${NVMRC_CONTENT}" | command sed 's/#.*//' | command sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | nvm_grep -v '^$')
|
||||
lines=$(command sed 's/#.*//' "$NVMRC_PATH" | command sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | nvm_grep -v '^$')
|
||||
|
||||
if [ -z "$lines" ]; then
|
||||
nvm_nvmrc_invalid_msg "${lines}"
|
||||
@@ -680,13 +617,6 @@ EOF
|
||||
nvm_echo "${unpaired_line}"
|
||||
}
|
||||
|
||||
nvm_process_nvmrc() {
|
||||
local NVMRC_PATH
|
||||
NVMRC_PATH="$1"
|
||||
|
||||
nvm_process_nvmrc_content "$(command cat "${NVMRC_PATH}")"
|
||||
}
|
||||
|
||||
nvm_rc_version() {
|
||||
local NVMRC_PATH
|
||||
NVMRC_PATH="$(nvm_find_nvmrc)"
|
||||
@@ -719,7 +649,7 @@ nvm_clang_version() {
|
||||
}
|
||||
|
||||
nvm_curl_version() {
|
||||
command curl -V | command awk '{ if ($1 == "curl") print $2 }' | command sed 's/-.*$//g'
|
||||
curl -V | command awk '{ if ($1 == "curl") print $2 }' | command sed 's/-.*$//g'
|
||||
}
|
||||
|
||||
nvm_version_greater() {
|
||||
@@ -1329,13 +1259,6 @@ nvm_make_alias() {
|
||||
nvm_err "an alias target version is required"
|
||||
return 2
|
||||
fi
|
||||
# slashes are legal (eg `lts/iron`), but a `..` component would escape the alias dir
|
||||
case "/${ALIAS}/" in
|
||||
*/../*)
|
||||
nvm_err "invalid alias name: ${ALIAS}"
|
||||
return 3
|
||||
;;
|
||||
esac
|
||||
nvm_echo "${VERSION}" | tee "$(nvm_alias_path)/${ALIAS}" >/dev/null
|
||||
}
|
||||
|
||||
@@ -1420,24 +1343,7 @@ nvm_alias() {
|
||||
return 2
|
||||
fi
|
||||
|
||||
if [ ! -r "${NVM_ALIAS_PATH}" ]; then
|
||||
# an existing-but-unreadable alias file yields empty output with a success
|
||||
# status - a nonzero status here would make `nvm_ensure_default_set`
|
||||
# overwrite an existing default alias it merely could not read
|
||||
nvm_err "Alias file is not readable: ${NVM_ALIAS_PATH}"
|
||||
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}"
|
||||
command sed 's/#.*//; s/[[:space:]]*$//' "${NVM_ALIAS_PATH}" | command awk 'NF'
|
||||
}
|
||||
|
||||
nvm_ls_current() {
|
||||
@@ -1470,32 +1376,24 @@ nvm_resolve_alias() {
|
||||
local ALIAS
|
||||
ALIAS="${PATTERN}"
|
||||
local ALIAS_TEMP
|
||||
local ALIAS_OUTPUT
|
||||
|
||||
local SEEN_ALIASES
|
||||
SEEN_ALIASES="
|
||||
${ALIAS}
|
||||
"
|
||||
SEEN_ALIASES="${ALIAS}"
|
||||
local NVM_ALIAS_INDEX
|
||||
NVM_ALIAS_INDEX=1
|
||||
while true; do
|
||||
ALIAS_OUTPUT="$(nvm_alias "${ALIAS}" 2>/dev/null)" || ALIAS_OUTPUT=''
|
||||
ALIAS_TEMP="${ALIAS_OUTPUT%%
|
||||
*}"
|
||||
ALIAS_TEMP="$( (nvm_alias "${ALIAS}" 2>/dev/null | command head -n "${NVM_ALIAS_INDEX}" | command tail -n 1) || nvm_echo)"
|
||||
|
||||
if [ -z "${ALIAS_TEMP}" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
case "${SEEN_ALIASES}" in
|
||||
*"
|
||||
${ALIAS_TEMP}
|
||||
"*)
|
||||
ALIAS="∞"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
if command printf '%b' "${SEEN_ALIASES}" | nvm_grep -q -e "^${ALIAS_TEMP}$"; then
|
||||
ALIAS="∞"
|
||||
break
|
||||
fi
|
||||
|
||||
SEEN_ALIASES="${SEEN_ALIASES}${ALIAS_TEMP}
|
||||
"
|
||||
SEEN_ALIASES="${SEEN_ALIASES}\\n${ALIAS_TEMP}"
|
||||
ALIAS="${ALIAS_TEMP}"
|
||||
done
|
||||
|
||||
@@ -1576,17 +1474,6 @@ nvm_strip_iojs_prefix() {
|
||||
nvm_ls() {
|
||||
local PATTERN
|
||||
PATTERN="${1-}"
|
||||
case "${PATTERN}" in
|
||||
*'#'* | *'
|
||||
'*)
|
||||
local NVMRC_PATTERN
|
||||
if ! NVMRC_PATTERN="$(nvm_process_nvmrc_content "${PATTERN}" 2>/dev/null)"; then
|
||||
nvm_echo 'N/A'
|
||||
return 3
|
||||
fi
|
||||
PATTERN="${NVMRC_PATTERN}"
|
||||
;;
|
||||
esac
|
||||
local VERSIONS
|
||||
VERSIONS=''
|
||||
if [ "${PATTERN}" = 'current' ]; then
|
||||
@@ -1850,7 +1737,6 @@ nvm_ls_remote_index_tab() {
|
||||
command mkdir -p "$(nvm_alias_path)/lts"
|
||||
{ command awk '{
|
||||
if ($10 ~ /^\-?$/) { next }
|
||||
if (tolower($10) !~ /^[a-z0-9][a-z0-9._-]*$/) { next }
|
||||
if ($10 && !a[tolower($10)]++) {
|
||||
if (alias) { print alias, version }
|
||||
alias_name = "lts/" tolower($10)
|
||||
@@ -2285,7 +2171,6 @@ nvm_get_arch() {
|
||||
x86_64 | amd64) NVM_ARCH="x64" ;;
|
||||
i*86) NVM_ARCH="x86" ;;
|
||||
aarch64 | armv8l) NVM_ARCH="arm64" ;;
|
||||
loongarch64) NVM_ARCH="loong64" ;;
|
||||
*) NVM_ARCH="${HOST_ARCH}" ;;
|
||||
esac
|
||||
|
||||
@@ -2305,12 +2190,10 @@ nvm_get_arch() {
|
||||
HOST_ARCH=armv7l
|
||||
fi
|
||||
|
||||
if [ -f "/etc/alpine-release" ] && [ "_${NVM_OS}" = "_linux" ]; then
|
||||
# Alpine Linux uses musl libc; map to musl variants where available
|
||||
# See https://unofficial-builds.nodejs.org/download/release/
|
||||
if [ -f "/etc/alpine-release" ]; then
|
||||
# Alpine Linux uses musl libc; only x64-musl binaries are available
|
||||
case "${NVM_ARCH}" in
|
||||
x64) NVM_ARCH=x64-musl ;;
|
||||
arm64) NVM_ARCH=arm64-musl ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -2423,39 +2306,22 @@ nvm_install_binary_extract() {
|
||||
if [ "${NVM_OS}" = 'win' ]; then
|
||||
VERSION_PATH="${VERSION_PATH}/bin"
|
||||
command unzip -q "${TARBALL}" -d "${TMPDIR}" || return 1
|
||||
# Replace any pre-existing (possibly broken or partial) install so the
|
||||
# move below cannot collide with leftover files. Safe here: the archive
|
||||
# has already downloaded and unzipped successfully into TMPDIR.
|
||||
command rm -rf "${VERSION_PATH}"
|
||||
command mkdir -p "${VERSION_PATH}" || return 1
|
||||
# For non Windows system (including WSL running on Windows)
|
||||
else
|
||||
nvm_extract_tarball "${NVM_OS}" "${VERSION}" "${TARBALL}" "${TMPDIR}"
|
||||
fi
|
||||
|
||||
command mkdir -p "${VERSION_PATH}" || return 1
|
||||
|
||||
if [ "${NVM_OS}" = 'win' ]; then
|
||||
command mv "${TMPDIR}/"*/* "${VERSION_PATH}/" || return 1
|
||||
command chmod +x "${VERSION_PATH}"/node.exe || return 1
|
||||
command chmod +x "${VERSION_PATH}"/npm || return 1
|
||||
command chmod +x "${VERSION_PATH}"/npx 2>/dev/null
|
||||
command rm -rf "${TMPDIR}"
|
||||
return 0
|
||||
else
|
||||
command mv "${TMPDIR}/"* "${VERSION_PATH}" || return 1
|
||||
fi
|
||||
|
||||
# For non-Windows systems (including WSL running on Windows)
|
||||
nvm_extract_tarball "${NVM_OS}" "${VERSION}" "${TARBALL}" "${TMPDIR}" || return 1
|
||||
|
||||
# Install atomically: replace any pre-existing version directory with a
|
||||
# single rename, so a partial or broken tree is never observed as installed.
|
||||
# A leftover directory without a working bin/node otherwise wedges the
|
||||
# install - the per-entry `mv` refuses to overwrite the non-empty bin/, lib/,
|
||||
# ... subdirectories and leaves a half-updated tree behind. Removing it first
|
||||
# is safe: the tarball has already downloaded and extracted into TMPDIR.
|
||||
command rm -rf "${VERSION_PATH}" || return 1
|
||||
command mkdir -p "$(dirname "${VERSION_PATH}")" || return 1
|
||||
if command mv "${TMPDIR}" "${VERSION_PATH}" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fall back to a per-entry move when a single rename is not possible (e.g.
|
||||
# TMPDIR and the versions directory are on different filesystems).
|
||||
command rm -rf "${VERSION_PATH}"
|
||||
command mkdir -p "${VERSION_PATH}" || return 1
|
||||
command mv "${TMPDIR}/"* "${VERSION_PATH}" || return 1
|
||||
command rm -rf "${TMPDIR}"
|
||||
|
||||
return 0
|
||||
@@ -2572,10 +2438,15 @@ nvm_get_download_slug() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# If running MAC M1 :: ARM64 binaries are not available for Node < 16.0.0
|
||||
# https://github.com/nodejs/node/issues/40126 (binary distributions aren't available until v16)
|
||||
if nvm_version_greater '16.0.0' "${VERSION}" && [ "_${NVM_OS}" = '_darwin' ] && [ "${NVM_ARCH}" = 'arm64' ]; then
|
||||
NVM_ARCH=x64
|
||||
# If running MAC M1 :: Node v14.17.0 was the first version to offer official experimental support:
|
||||
# https://github.com/nodejs/node/issues/40126 (although binary distributions aren't available until v16)
|
||||
if \
|
||||
nvm_version_greater '14.17.0' "${VERSION}" \
|
||||
|| (nvm_version_greater_than_or_equal_to "${VERSION}" '15.0.0' && nvm_version_greater '16.0.0' "${VERSION}") \
|
||||
; then
|
||||
if [ "_${NVM_OS}" = '_darwin' ] && [ "${NVM_ARCH}" = 'arm64' ]; then
|
||||
NVM_ARCH=x64
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${KIND}" = 'binary' ]; then
|
||||
@@ -3230,108 +3101,27 @@ nvm_cache_dir() {
|
||||
nvm_echo "${NVM_DIR}/.cache"
|
||||
}
|
||||
|
||||
# Turn a version into a filesystem-safe lock name. Versions that reach here are
|
||||
# already restricted to [0-9A-Za-z._+-], but be defensive about anything else.
|
||||
nvm_install_lock_name() {
|
||||
command printf '%s' "${1-}" | command tr -c '0-9A-Za-z._+-' '_'
|
||||
}
|
||||
|
||||
# Acquire an advisory, per-version install lock so two concurrent
|
||||
# `nvm install <same version>` runs cannot race on the same version directory
|
||||
# (one removing/replacing it while the other reads or writes it). The lock is a
|
||||
# directory created with `mkdir`, which is atomic across POSIX filesystems.
|
||||
#
|
||||
# Tunables (env vars):
|
||||
# NVM_INSTALL_LOCK_TIMEOUT seconds to wait for a held lock (default 600)
|
||||
# NVM_INSTALL_LOCK_STALE minutes after which a lock is assumed abandoned
|
||||
# and stolen; 0 (default) never steals
|
||||
#
|
||||
# On success the lock path is recorded in NVM_INSTALL_LOCK for the matching
|
||||
# nvm_release_install_lock.
|
||||
nvm_acquire_install_lock() {
|
||||
local VERSION
|
||||
VERSION="${1-}"
|
||||
if [ -z "${VERSION}" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local LOCK_ROOT
|
||||
LOCK_ROOT="$(nvm_cache_dir)/locks"
|
||||
# If the lock directory can't be created, don't block the install over it.
|
||||
command mkdir -p "${LOCK_ROOT}" 2>/dev/null || return 0
|
||||
|
||||
local LOCK
|
||||
LOCK="${LOCK_ROOT}/$(nvm_install_lock_name "${VERSION}")"
|
||||
|
||||
local TIMEOUT
|
||||
TIMEOUT="${NVM_INSTALL_LOCK_TIMEOUT:-600}"
|
||||
local STALE
|
||||
STALE="${NVM_INSTALL_LOCK_STALE:-0}"
|
||||
|
||||
local WAITED
|
||||
WAITED=0
|
||||
local ANNOUNCED
|
||||
ANNOUNCED=0
|
||||
while ! command mkdir "${LOCK}" 2>/dev/null; do
|
||||
# Steal a lock left behind by a crashed install once it is old enough.
|
||||
if [ "${STALE}" != '0' ] && [ -n "$(command find "${LOCK}" -maxdepth 0 -type d -mmin "+${STALE}" 2>/dev/null)" ]; then
|
||||
nvm_err "Removing stale install lock for ${VERSION} (older than ${STALE} minute(s))"
|
||||
command rm -rf "${LOCK}" 2>/dev/null
|
||||
continue
|
||||
fi
|
||||
if [ "${WAITED}" -ge "${TIMEOUT}" ]; then
|
||||
nvm_err "Timed out after ${TIMEOUT}s waiting for another install of ${VERSION} to finish."
|
||||
nvm_err "If no other install is running, remove ${LOCK} and try again."
|
||||
return 1
|
||||
fi
|
||||
if [ "${ANNOUNCED}" -eq 0 ]; then
|
||||
nvm_err "Waiting for another install of ${VERSION} to finish..."
|
||||
ANNOUNCED=1
|
||||
fi
|
||||
command sleep 1
|
||||
WAITED=$((WAITED + 1))
|
||||
done
|
||||
|
||||
NVM_INSTALL_LOCK="${LOCK}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Release the lock acquired by nvm_acquire_install_lock, if any.
|
||||
nvm_release_install_lock() {
|
||||
if [ -n "${NVM_INSTALL_LOCK-}" ]; then
|
||||
command rmdir "${NVM_INSTALL_LOCK}" 2>/dev/null || command rm -rf "${NVM_INSTALL_LOCK}" 2>/dev/null || true
|
||||
unset NVM_INSTALL_LOCK
|
||||
fi
|
||||
}
|
||||
|
||||
# args: pattern
|
||||
# Lists versions available in the local cache (not yet installed).
|
||||
# Returns version numbers like "v18.20.4", one per line, sorted.
|
||||
nvm_ls_cached() {
|
||||
local PATTERN
|
||||
PATTERN="${1-}"
|
||||
local NVM_CACHE_DIR
|
||||
NVM_CACHE_DIR="$(nvm_cache_dir)"
|
||||
local CACHE_BIN_DIR
|
||||
CACHE_BIN_DIR="$(nvm_cache_dir)/bin"
|
||||
if [ ! -d "${CACHE_BIN_DIR}" ]; then
|
||||
return
|
||||
fi
|
||||
local NVM_OS
|
||||
NVM_OS="$(nvm_get_os)"
|
||||
local NVM_ARCH
|
||||
NVM_ARCH="$(nvm_get_arch)"
|
||||
local SUFFIX
|
||||
SUFFIX="${NVM_OS}-${NVM_ARCH}"
|
||||
{
|
||||
if [ -d "${NVM_CACHE_DIR}/bin" ]; then
|
||||
# shellcheck disable=SC2010
|
||||
command ls -1 "${NVM_CACHE_DIR}/bin" \
|
||||
| nvm_grep "^\\(node\\|iojs\\)-v[0-9][0-9.]*-${SUFFIX}\$" \
|
||||
| command sed "s/-${SUFFIX}\$//"
|
||||
fi
|
||||
if [ -d "${NVM_CACHE_DIR}/src" ]; then
|
||||
# shellcheck disable=SC2010
|
||||
command ls -1 "${NVM_CACHE_DIR}/src" \
|
||||
| nvm_grep "^\\(node\\|iojs\\)-v[0-9][0-9.]*\$"
|
||||
fi
|
||||
} \
|
||||
| command sed 's/^node-//' \
|
||||
# shellcheck disable=SC2010
|
||||
command ls -1 "${CACHE_BIN_DIR}" \
|
||||
| nvm_grep "^node-v.*-${SUFFIX}\$" \
|
||||
| command sed "s/^node-\\(v[0-9][0-9.]*\\)-${SUFFIX}\$/\\1/" \
|
||||
| nvm_grep "$(nvm_ensure_version_prefix "${PATTERN}")" \
|
||||
| command sort -t. -u -k 1.2,1n -k 2,2n -k 3,3n
|
||||
}
|
||||
@@ -3436,7 +3226,6 @@ nvm() {
|
||||
nvm_echo ' The following optional arguments, if provided, must appear directly after `nvm install`:'
|
||||
nvm_echo ' -s Skip binary download, install from source only.'
|
||||
nvm_echo ' -b Skip source download, install from binary only.'
|
||||
nvm_echo ' (set NVM_NO_SOURCE_FALLBACK=1 to make this the default for every install)'
|
||||
nvm_echo ' --reinstall-packages-from=<version> When installing, reinstall packages installed in <node|iojs|node version number>'
|
||||
nvm_echo ' --lts When installing, only select from LTS (long-term support) versions'
|
||||
nvm_echo ' --lts=<LTS name> When installing, only select from versions for a specific LTS line'
|
||||
@@ -3785,18 +3574,7 @@ nvm() {
|
||||
esac
|
||||
done
|
||||
|
||||
# NVM_NO_SOURCE_FALLBACK=1 makes `nvm install` behave as if `-b` were always passed:
|
||||
# a failed binary download aborts instead of silently falling back to a from-source compile.
|
||||
# It is a persistent policy so callers need not thread `-b` through every invocation.
|
||||
if [ "${NVM_NO_SOURCE_FALLBACK-}" = '1' ] && [ $nosource -ne 1 ]; then
|
||||
if [ $nobinary -eq 1 ]; then
|
||||
nvm_err '-s cannot be combined with NVM_NO_SOURCE_FALLBACK=1 since that would skip install from both binary and source'
|
||||
return 6
|
||||
fi
|
||||
nosource=1
|
||||
fi
|
||||
|
||||
if [ "${NVM_OFFLINE}" != 1 ] && ! nvm_has_executable "curl" && ! nvm_has_executable "wget"; then
|
||||
if [ "${NVM_OFFLINE}" != 1 ] && ! nvm_has "curl" && ! nvm_has "wget"; then
|
||||
nvm_err 'nvm needs curl or wget to proceed.'
|
||||
return 1
|
||||
fi
|
||||
@@ -3949,7 +3727,7 @@ nvm() {
|
||||
|
||||
EXIT_CODE=0
|
||||
|
||||
if nvm_is_version_installed "${VERSION}" && nvm_validate_install "${VERSION}"; then
|
||||
if nvm_is_version_installed "${VERSION}"; then
|
||||
nvm_err "${VERSION} is already installed."
|
||||
nvm use "${VERSION}"
|
||||
EXIT_CODE=$?
|
||||
@@ -4007,11 +3785,6 @@ nvm() {
|
||||
fi
|
||||
EXIT_CODE=0
|
||||
else
|
||||
# Serialize concurrent installs of this version so two runs cannot race
|
||||
# on its version directory (one replacing it while the other reads it).
|
||||
if ! nvm_acquire_install_lock "${VERSION}"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "_${NVM_OS}" = "_freebsd" ]; then
|
||||
# node.js and io.js do not have a FreeBSD binary
|
||||
@@ -4054,13 +3827,6 @@ nvm() {
|
||||
EXIT_CODE=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
nvm_release_install_lock
|
||||
fi
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ] && ! nvm_validate_install "${VERSION}"; then
|
||||
nvm_err "The install of ${VERSION} reported success but failed verification; not activating it."
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
@@ -4898,7 +4664,7 @@ nvm() {
|
||||
NVM_VERSION_ONLY=true NVM_LTS="${NVM_LTS-}" nvm_remote_version "${PATTERN:-node}"
|
||||
;;
|
||||
"--version" | "-v")
|
||||
nvm_echo '0.40.7'
|
||||
nvm_echo '0.40.5'
|
||||
;;
|
||||
"unload")
|
||||
nvm deactivate >/dev/null 2>&1
|
||||
@@ -4927,14 +4693,13 @@ nvm() {
|
||||
nvm_version_greater nvm_version_greater_than_or_equal_to \
|
||||
nvm_print_npm_version nvm_install_latest_npm nvm_npm_global_modules \
|
||||
nvm_has_system_node nvm_has_system_iojs \
|
||||
nvm_download nvm_get_latest nvm_has nvm_has_executable nvm_install_default_packages nvm_get_default_packages \
|
||||
nvm_download nvm_get_latest nvm_has nvm_install_default_packages nvm_get_default_packages \
|
||||
nvm_curl_use_compression nvm_curl_version \
|
||||
nvm_auto nvm_supports_xz \
|
||||
nvm_echo nvm_err nvm_grep nvm_cd \
|
||||
nvm_die_on_prefix nvm_get_make_jobs nvm_get_minor_version \
|
||||
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_is_natural_num nvm_is_version_installed \
|
||||
nvm_list_aliases nvm_make_alias 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 \
|
||||
@@ -4944,11 +4709,11 @@ nvm() {
|
||||
nvm_get_colors nvm_set_colors nvm_print_color_code nvm_wrap_with_color_code nvm_format_help_message_colors \
|
||||
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_process_nvmrc nvm_nvmrc_invalid_msg \
|
||||
nvm_write_nvmrc \
|
||||
>/dev/null 2>&1
|
||||
unset NVM_NODEJS_ORG_MIRROR NVM_IOJS_ORG_MIRROR NVM_DIR \
|
||||
NVM_CD_FLAGS NVM_BIN NVM_INC NVM_MAKE_JOBS NVM_INSTALL_LOCK \
|
||||
NVM_CD_FLAGS NVM_BIN NVM_INC NVM_MAKE_JOBS \
|
||||
NVM_COLORS INSTALLED_COLOR SYSTEM_COLOR \
|
||||
CURRENT_COLOR NOT_INSTALLED_COLOR DEFAULT_COLOR LTS_COLOR \
|
||||
>/dev/null 2>&1
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nvm",
|
||||
"version": "0.40.7",
|
||||
"version": "0.40.5",
|
||||
"description": "Node Version Manager - Simple bash script to manage multiple active node.js versions",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
@@ -14,7 +14,7 @@
|
||||
"test/installation/node": "shell=$(basename -- $(ps -o comm= $(ps -o ppid= -p $PPID)) | sed 's/^-//'); make TEST_SUITE=installation_node test-$shell",
|
||||
"test/installation/iojs": "shell=$(basename -- $(ps -o comm= $(ps -o ppid= -p $PPID)) | sed 's/^-//'); make TEST_SUITE=installation_iojs test-$shell",
|
||||
"test/sourcing": "shell=$(basename -- $(ps -o comm= $(ps -o ppid= -p $PPID)) | sed 's/^-//'); make TEST_SUITE=sourcing test-$shell",
|
||||
"test:check-exec": "git -c core.quotePath=false ls-files test | while IFS= read -r file; do if [ ! -x \"$file\" ]; then case \"$file\" in test/fixtures/*|*.json|*.txt|*.sh|*.js|*.log|*.tab|*.gitkeep) ;; *) echo \"$file\" ;; esac; fi; done | tee /dev/stderr | awk 'END {if (NR > 0) exit 1}'",
|
||||
"test:check-exec": "(IFS=$'\\n'; for file in $(git ls-files test); do if [ ! -x \"$file\" ] && [[ \"$file\" != *.* ]] && [[ \"$file\" != test/fixtures/* ]]; then echo \"$file\"; fi; done) | tee /dev/stderr | awk 'END {if (NR > 0) exit 1}'",
|
||||
"test:check-nonexec": "(IFS=$'\\n'; for file in $(git ls-files test); do if [ -x \"$file\" ] && [ ! -d \"$file\" ] && { [[ \"$file\" =~ '\\.(json|txt|sh|js|log)$' ]] || [[ \"$file\" =~ '^test/(mocks|fixtures)/.*' ]]; }; then echo \"$file\"; fi; done) | tee /dev/stderr | awk 'END {if (NR > 0) exit 1}'",
|
||||
"doctoc": "doctoc --title='## Table of Contents' --github README.md",
|
||||
"predoctoc:check": "cp README.md v-README.md.orig && npm run doctoc",
|
||||
@@ -45,7 +45,7 @@
|
||||
"dockerfile_lint": "^0.3.4",
|
||||
"doctoc": "^2.5.0",
|
||||
"eclint": "^2.8.1",
|
||||
"markdown-link-check": "^3.15.0",
|
||||
"markdown-link-check": "^3.14.2",
|
||||
"replace": "^1.2.2",
|
||||
"semver": "^7.8.5",
|
||||
"urchin": "^0.0.5"
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
#!/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'
|
||||
@@ -50,39 +50,6 @@ make_echo() {
|
||||
chmod a+x "$1"
|
||||
}
|
||||
|
||||
# `command curl` bypasses shell functions and aliases, so tests that mock curl
|
||||
# must provide an actual executable on the PATH: this creates one in the given
|
||||
# directory. Invoked as `curl -V`, it prints ${VERSION_MESSAGE} (exported here
|
||||
# on the mock's behalf); any other invocation runs the body given as the
|
||||
# second argument, or fails.
|
||||
make_fake_curl() {
|
||||
local FAKE_BIN_DIR
|
||||
FAKE_BIN_DIR="${1-}"
|
||||
[ -n "${FAKE_BIN_DIR}" ] || return 1
|
||||
|
||||
local FAKE_CURL_BODY
|
||||
FAKE_CURL_BODY="${2-}"
|
||||
if [ -z "${FAKE_CURL_BODY}" ]; then
|
||||
FAKE_CURL_BODY='echo >&2 "This fake curl only takes one parameter, -V"
|
||||
exit 1'
|
||||
fi
|
||||
|
||||
mkdir -p "${FAKE_BIN_DIR}" || return 2
|
||||
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo 'if [ "$#" -eq 1 ] && [ "$1" = "-V" ]; then'
|
||||
echo ' echo "${VERSION_MESSAGE}"'
|
||||
echo ' exit 0'
|
||||
echo 'fi'
|
||||
printf '%s\n' "${FAKE_CURL_BODY}"
|
||||
} > "${FAKE_BIN_DIR}/curl"
|
||||
chmod +x "${FAKE_BIN_DIR}/curl"
|
||||
|
||||
# the fake curl reads VERSION_MESSAGE from the environment
|
||||
export VERSION_MESSAGE
|
||||
}
|
||||
|
||||
make_fake_node() {
|
||||
local VERSION
|
||||
VERSION="${1-}"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file whose name contains spaces
|
||||
mkdir -p ../../../alias
|
||||
printf 'v22.1.0\n' > "../../../alias/test edge spaces"
|
||||
ACTUAL="$(nvm_alias "test edge spaces")"
|
||||
EXPECTED='v22.1.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
|
||||
rm -f "../../../alias/test edge spaces"
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file where the first line has a valid version
|
||||
# followed by a second line of binary-like data
|
||||
printf 'v22.1.0\n' > ../../../alias/test-edge-binary
|
||||
printf '\001\002\003\377\n' >> ../../../alias/test-edge-binary
|
||||
ACTUAL="$(nvm_alias test-edge-binary)"
|
||||
# nvm_alias emits every non-blank, non-comment line — first line should be the version
|
||||
FIRST_LINE="$(nvm_echo "${ACTUAL}" | command head -n 1)"
|
||||
EXPECTED='v22.1.0'
|
||||
[ "${FIRST_LINE}" = "${EXPECTED}" ] || die "expected first line >${EXPECTED}<, got >${FIRST_LINE}<"
|
||||
|
||||
rm -f ../../../alias/test-edge-binary
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file with Windows-style line endings (CRLF)
|
||||
printf 'v22.1.0\r\n' > ../../../alias/test-edge-cr
|
||||
ACTUAL="$(nvm_alias test-edge-cr)"
|
||||
EXPECTED='v22.1.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
|
||||
# Create an alias file with bare carriage return (no newline)
|
||||
printf 'v22.2.0\r' > ../../../alias/test-edge-cr-bare
|
||||
ACTUAL="$(nvm_alias test-edge-cr-bare)"
|
||||
EXPECTED='v22.2.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for bare CR, got >${ACTUAL}<"
|
||||
|
||||
rm -f ../../../alias/test-edge-cr ../../../alias/test-edge-cr-bare
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file containing only comments
|
||||
printf '# this is a comment\n# another comment\n' > ../../../alias/test-edge-comments
|
||||
ACTUAL="$(nvm_alias test-edge-comments 2>/dev/null)"
|
||||
EXIT_CODE="$(nvm_alias test-edge-comments 2>/dev/null; echo $?)"
|
||||
[ -z "${ACTUAL}" ] || die "expected empty output for comment-only alias file, got >${ACTUAL}<"
|
||||
[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}"
|
||||
|
||||
rm -f ../../../alias/test-edge-comments
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file with an embedded NUL byte after the version.
|
||||
# NUL handling varies by shell: some stop at NUL, others read through it.
|
||||
# The function must not crash, and must emit output starting with the version.
|
||||
printf 'v22.1.0\000garbage\n' > ../../../alias/test-edge-nul
|
||||
ACTUAL="$(nvm_alias test-edge-nul)"
|
||||
EXIT_CODE=$?
|
||||
[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}"
|
||||
case "${ACTUAL}" in
|
||||
v22.1.0*) ;; # OK — starts with the version regardless of NUL handling
|
||||
*) die "expected output starting with >v22.1.0<, got >${ACTUAL}<" ;;
|
||||
esac
|
||||
|
||||
rm -f ../../../alias/test-edge-nul
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an empty alias file
|
||||
printf '' > ../../../alias/test-edge-empty
|
||||
ACTUAL="$(nvm_alias test-edge-empty 2>/dev/null)"
|
||||
EXIT_CODE="$(nvm_alias test-edge-empty 2>/dev/null; echo $?)"
|
||||
[ -z "${ACTUAL}" ] || die "expected empty output for empty alias file, got >${ACTUAL}<"
|
||||
[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}"
|
||||
|
||||
rm -f ../../../alias/test-edge-empty
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file with trailing form feed
|
||||
printf 'v22.1.0\f\n' > ../../../alias/test-edge-ff
|
||||
ACTUAL="$(nvm_alias test-edge-ff)"
|
||||
EXPECTED='v22.1.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for form feed, got >${ACTUAL}<"
|
||||
|
||||
# Create an alias file with trailing vertical tab
|
||||
printf 'v22.2.0\v\n' > ../../../alias/test-edge-vt
|
||||
ACTUAL="$(nvm_alias test-edge-vt)"
|
||||
EXPECTED='v22.2.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for vertical tab, got >${ACTUAL}<"
|
||||
|
||||
rm -f ../../../alias/test-edge-ff ../../../alias/test-edge-vt
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file with no trailing newline
|
||||
printf 'v22.1.0' > ../../../alias/test-edge-no-newline
|
||||
ACTUAL="$(nvm_alias test-edge-no-newline)"
|
||||
EXPECTED='v22.1.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
|
||||
rm -f ../../../alias/test-edge-no-newline
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
cleanup() {
|
||||
chmod 644 ../../../alias/test-edge-unreadable 2>/dev/null
|
||||
rm -f ../../../alias/test-edge-unreadable
|
||||
}
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file that exists but can not be read
|
||||
echo 'v0.0.1' > ../../../alias/test-edge-unreadable
|
||||
chmod 000 ../../../alias/test-edge-unreadable
|
||||
|
||||
# root (and some containers) can read mode-000 files; nothing to assert there
|
||||
if [ -r ../../../alias/test-edge-unreadable ]; then
|
||||
cleanup
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# like the sed/awk pipeline this replaced: empty output, success status, so
|
||||
# that `nvm_ensure_default_set` does not overwrite an unreadable default alias
|
||||
OUTPUT="$(nvm_alias test-edge-unreadable 2>/dev/null)"
|
||||
EXIT_CODE=$?
|
||||
[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0 for unreadable alias file, got ${EXIT_CODE}"
|
||||
[ -z "${OUTPUT}" ] || die "expected empty output for unreadable alias file, got >${OUTPUT}<"
|
||||
|
||||
STDERR="$(nvm_alias test-edge-unreadable 2>&1 >/dev/null)"
|
||||
case "${STDERR}" in
|
||||
*'not readable'*) ;;
|
||||
*) die "expected a not-readable warning on stderr, got >${STDERR}<" ;;
|
||||
esac
|
||||
|
||||
cleanup
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file with a very long line (version followed by a long comment)
|
||||
LONG_COMMENT="$(printf '%0*d' 10000 0 | command tr '0' 'x')"
|
||||
printf 'v22.1.0 #%s\n' "${LONG_COMMENT}" > ../../../alias/test-edge-long
|
||||
ACTUAL="$(nvm_alias test-edge-long)"
|
||||
EXPECTED='v22.1.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
|
||||
rm -f ../../../alias/test-edge-long
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias file with trailing spaces and tabs
|
||||
printf '22.1.0 \t \n' > ../../../alias/test-edge-trailing-ws
|
||||
ACTUAL="$(nvm_alias test-edge-trailing-ws)"
|
||||
EXPECTED='22.1.0'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
|
||||
rm -f ../../../alias/test-edge-trailing-ws
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# 1-hop self-reference: alias points to itself
|
||||
echo 'self' > '../../../alias/self'
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias self)"
|
||||
EXPECTED='∞'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for self-reference, got >${ACTUAL}<"
|
||||
|
||||
rm -f '../../../alias/self'
|
||||
|
||||
# Multi-hop loop: link1 -> link2 -> link3 -> link1
|
||||
echo 'link2' > '../../../alias/link1'
|
||||
echo 'link3' > '../../../alias/link2'
|
||||
echo 'link1' > '../../../alias/link3'
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias link1)"
|
||||
EXPECTED='∞'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for 3-hop cycle, got >${ACTUAL}<"
|
||||
|
||||
rm -f '../../../alias/link1' '../../../alias/link2' '../../../alias/link3'
|
||||
|
||||
# Cycle through an alias name containing a space
|
||||
echo 'midway' > '../../../alias/foo bar'
|
||||
echo 'foo bar' > '../../../alias/midway'
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias 'foo bar')"
|
||||
EXPECTED='∞'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for space-name cycle, got >${ACTUAL}<"
|
||||
|
||||
rm -f '../../../alias/foo bar' '../../../alias/midway'
|
||||
|
||||
# Non-cycle through an alias name containing a space.
|
||||
# Guards the newline-delimited SEEN_ALIASES storage: with space- or
|
||||
# token-delimited storage, seen name 'foo bar' would falsely match 'bar'.
|
||||
# Resolves: 'foo bar' -> 'midway' -> 'bar' -> 0.0.99
|
||||
echo 'midway' > '../../../alias/foo bar'
|
||||
echo 'bar' > '../../../alias/midway'
|
||||
echo '0.0.99' > '../../../alias/bar'
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias 'foo bar')"
|
||||
EXPECTED='v0.0.99'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for space-name chain, got >${ACTUAL}<"
|
||||
|
||||
rm -f '../../../alias/foo bar' '../../../alias/midway' '../../../alias/bar'
|
||||
|
||||
# Non-cycle through an alias name containing a regex metacharacter.
|
||||
# Guards the literal `case` matching: the previous grep-based detection
|
||||
# interpolated the resolved name into an anchored regex, so resolving 'axb'
|
||||
# matched the seen name 'axb' against the pattern 'a.b' and falsely
|
||||
# reported a cycle.
|
||||
# Resolves: 'axb' -> 'a.b' -> 0.0.99
|
||||
echo 'a.b' > '../../../alias/axb'
|
||||
echo '0.0.99' > '../../../alias/a.b'
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias axb)"
|
||||
EXPECTED='v0.0.99'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for metachar-name chain, got >${ACTUAL}<"
|
||||
|
||||
# ... while a genuine cycle through a metachar name is still detected
|
||||
echo 'axb' > '../../../alias/a.b'
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias axb)"
|
||||
EXPECTED='∞'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for metachar-name cycle, got >${ACTUAL}<"
|
||||
|
||||
rm -f '../../../alias/axb' '../../../alias/a.b'
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create a 4-deep alias chain: hop1 -> hop2 -> hop3 -> hop4 -> 0.0.99
|
||||
echo 'hop2' > ../../../alias/hop1
|
||||
echo 'hop3' > ../../../alias/hop2
|
||||
echo 'hop4' > ../../../alias/hop3
|
||||
echo '0.0.99' > ../../../alias/hop4
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias hop1)"
|
||||
EXPECTED='v0.0.99'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for 4-deep chain, got >${ACTUAL}<"
|
||||
|
||||
rm -f ../../../alias/hop1 ../../../alias/hop2 ../../../alias/hop3 ../../../alias/hop4
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
export NVM_DIR="$(cd ../../.. && pwd)"
|
||||
|
||||
: nvm.sh
|
||||
\. "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# Create an alias pointing to a version that does not exist as an alias
|
||||
echo '99.99.99' > ../../../alias/test-edge-noexist
|
||||
|
||||
ACTUAL="$(nvm_resolve_alias test-edge-noexist)"
|
||||
EXPECTED='v99.99.99'
|
||||
EXIT_CODE="$(nvm_resolve_alias test-edge-noexist >/dev/null 2>&1; echo $?)"
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}"
|
||||
|
||||
rm -f ../../../alias/test-edge-noexist
|
||||
@@ -17,8 +17,3 @@ rm -f "../../../alias/iojs"
|
||||
rm -f "../../../alias/default"
|
||||
rm -f "../../../alias/test-blank-lines"
|
||||
rm -f "../../../alias/test-multi-lines"
|
||||
rm -f "../../../alias/test-edge-"* "../../../alias/test edge spaces"
|
||||
rm -f "../../../alias/self" "../../../alias/link1" "../../../alias/link2" "../../../alias/link3"
|
||||
rm -f "../../../alias/foo bar" "../../../alias/midway" "../../../alias/bar"
|
||||
rm -f "../../../alias/hop1" "../../../alias/hop2" "../../../alias/hop3" "../../../alias/hop4"
|
||||
rm -f "../../../alias/axb" "../../../alias/a.b"
|
||||
|
||||
Executable → Regular
+3
-6
@@ -12,10 +12,8 @@ nvm_download() {
|
||||
}
|
||||
|
||||
# --offline with an already-installed version should succeed
|
||||
# (`nvm ls` ends with the alias listing, so use `nvm_ls`, which emits bare
|
||||
# versions, and skip its trailing `system <version>` line)
|
||||
INSTALLED_VERSION="$(nvm_ls | command grep -v '^system' | command tail -n1)"
|
||||
if [ -n "${INSTALLED_VERSION}" ] && [ "_${INSTALLED_VERSION}" != '_N/A' ]; then
|
||||
INSTALLED_VERSION="$(nvm ls | command tail -1 | command awk '{print $1}' | command sed 's/\x1b\[[0-9;]*m//g')"
|
||||
if [ -n "${INSTALLED_VERSION}" ] && [ "_${INSTALLED_VERSION}" != '_N/A' ] && [ "_${INSTALLED_VERSION}" != '_system' ]; then
|
||||
try nvm install --offline "${INSTALLED_VERSION}"
|
||||
[ "_$CAPTURED_EXIT_CODE" = "_0" ] \
|
||||
|| die "nvm install --offline with installed version '${INSTALLED_VERSION}' should succeed, got exit code $CAPTURED_EXIT_CODE"
|
||||
@@ -32,10 +30,9 @@ nvm_echo "$CAPTURED_STDERR" | nvm_grep -q "${EXPECTED_ERR}" \
|
||||
|
||||
# --offline should not require curl or wget
|
||||
nvm_has() { return 1; }
|
||||
nvm_has_executable() { return 1; }
|
||||
try_err nvm install --offline 999.999.999
|
||||
# Should fail with "not found" not "nvm needs curl or wget"
|
||||
nvm_echo "$CAPTURED_STDERR" | nvm_grep -q "curl or wget" \
|
||||
&& die "nvm install --offline should not require curl or wget"
|
||||
alias nvm_has='\nvm_has'
|
||||
unset -f nvm_has nvm_has_executable
|
||||
unset -f nvm_has
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup() {
|
||||
[ -n "${NVM_DIR}" ] && [ -d "${NVM_DIR}" ] && rm -rf "${NVM_DIR}"
|
||||
unset -f die cleanup nvm_remote_version nvm_has_executable nvm_binary_available \
|
||||
nvm_install_binary nvm_install_source nvm_get_make_jobs
|
||||
unset NVM_DIR version SOURCE_MARKER OUTPUT EXIT_CODE
|
||||
}
|
||||
|
||||
die() { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
unset npm_config_prefix NPM_CONFIG_PREFIX
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
[ -n "${NVM_DIR}" ] || die 'unable to create temp NVM_DIR'
|
||||
version='v20.0.0'
|
||||
SOURCE_MARKER="${NVM_DIR}/source-was-attempted"
|
||||
|
||||
# Stay offline; make the binary download always fail and record any attempt to
|
||||
# compile from source.
|
||||
nvm_remote_version() { nvm_echo "${version}"; }
|
||||
nvm_has_executable() { return 0; }
|
||||
nvm_binary_available() { return 0; }
|
||||
nvm_get_make_jobs() { NVM_MAKE_JOBS=1; }
|
||||
nvm_install_binary() { return 1; }
|
||||
nvm_install_source() { command touch "${SOURCE_MARKER}"; return 1; }
|
||||
|
||||
# Default: a failed binary download falls back to a source compile.
|
||||
rm -f "${SOURCE_MARKER}"
|
||||
nvm install "${version}" > /dev/null 2>&1
|
||||
[ -f "${SOURCE_MARKER}" ] || die 'default behaviour should fall back to source when the binary fails'
|
||||
|
||||
# NVM_NO_SOURCE_FALLBACK=1: a failed binary download must NOT compile from source.
|
||||
rm -f "${SOURCE_MARKER}"
|
||||
NVM_NO_SOURCE_FALLBACK=1 nvm install "${version}" > /dev/null 2>&1; EXIT_CODE=$?
|
||||
[ ! -f "${SOURCE_MARKER}" ] || die 'NVM_NO_SOURCE_FALLBACK=1 should not fall back to source'
|
||||
[ "${EXIT_CODE}" != "0" ] || die 'a failed binary install with NVM_NO_SOURCE_FALLBACK=1 should be non-zero'
|
||||
|
||||
# NVM_NO_SOURCE_FALLBACK=1 combined with -s would skip both methods: a conflict.
|
||||
OUTPUT="$(NVM_NO_SOURCE_FALLBACK=1 nvm install -s "${version}" 2>&1)"; EXIT_CODE=$?
|
||||
[ "${EXIT_CODE}" = "6" ] || die "expected exit code 6 for -s with NVM_NO_SOURCE_FALLBACK=1, got ${EXIT_CODE}"
|
||||
case "${OUTPUT}" in
|
||||
*'skip install from both binary and source'*) ;;
|
||||
*) die "expected a conflict message, got >${OUTPUT}<" ;;
|
||||
esac
|
||||
|
||||
cleanup
|
||||
@@ -1,71 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup() {
|
||||
[ -n "${NVM_DIR}" ] && [ -d "${NVM_DIR}" ] && rm -rf "${NVM_DIR}"
|
||||
unset -f die cleanup make_healthy make_broken \
|
||||
nvm_remote_version nvm_has_executable nvm_binary_available \
|
||||
nvm_install_binary nvm_install_source nvm_use_if_needed \
|
||||
nvm_install_npm_if_needed nvm_install_default_packages nvm_ensure_default_set
|
||||
unset NVM_DIR version version_path REINSTALL_MARKER OUTPUT EXIT_CODE
|
||||
}
|
||||
|
||||
die() { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
unset npm_config_prefix NPM_CONFIG_PREFIX
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
[ -n "${NVM_DIR}" ] || die 'unable to create temp NVM_DIR'
|
||||
|
||||
version='v20.0.0'
|
||||
version_path="${NVM_DIR}/versions/node/${version}"
|
||||
REINSTALL_MARKER="${NVM_DIR}/reinstalled"
|
||||
|
||||
# Stay offline and keep the post-install activation path inert; only the
|
||||
# verification behaviour is under test here.
|
||||
nvm_remote_version() { nvm_echo "${version}"; }
|
||||
nvm_has_executable() { return 0; }
|
||||
nvm_binary_available() { return 0; }
|
||||
nvm_install_source() { return 1; }
|
||||
nvm_use_if_needed() { return 0; }
|
||||
nvm_install_npm_if_needed() { return 0; }
|
||||
nvm_install_default_packages() { return 0; }
|
||||
nvm_ensure_default_set() { return 0; }
|
||||
|
||||
make_healthy() {
|
||||
mkdir -p "${version_path}/bin"
|
||||
printf '#!/bin/sh\necho "%s"\n' "${version}" > "${version_path}/bin/node"
|
||||
chmod +x "${version_path}/bin/node"
|
||||
}
|
||||
make_broken() {
|
||||
mkdir -p "${version_path}/bin"
|
||||
: > "${version_path}/bin/node" # zero-byte but +x: passes nvm_is_version_installed
|
||||
chmod +x "${version_path}/bin/node"
|
||||
}
|
||||
|
||||
# 1) An install that reports success but leaves a broken node must fail loudly
|
||||
# and must not be activated.
|
||||
rm -rf "${version_path}"
|
||||
nvm_install_binary() { make_broken; return 0; }
|
||||
OUTPUT="$(nvm install -b "${version}" 2>&1)"; EXIT_CODE=$?
|
||||
[ "${EXIT_CODE}" != "0" ] || die "a broken install should fail; got exit 0, output >${OUTPUT}<"
|
||||
case "${OUTPUT}" in
|
||||
*'failed verification'*) ;;
|
||||
*) die "expected a verification-failure message; got >${OUTPUT}<" ;;
|
||||
esac
|
||||
|
||||
# 2) An already-present but broken version must not short-circuit as "already
|
||||
# installed"; it must be reinstalled.
|
||||
rm -rf "${version_path}"
|
||||
make_broken
|
||||
nvm_install_binary() { command touch "${REINSTALL_MARKER}"; make_healthy; return 0; }
|
||||
OUTPUT="$(nvm install -b "${version}" 2>&1)"; EXIT_CODE=$?
|
||||
case "${OUTPUT}" in
|
||||
*'is already installed'*) die "a broken version should not be treated as already installed: >${OUTPUT}<" ;;
|
||||
esac
|
||||
[ -f "${REINSTALL_MARKER}" ] || die 'a broken already-present version was not reinstalled'
|
||||
[ "${EXIT_CODE}" = "0" ] || die "the healing reinstall should succeed; got ${EXIT_CODE}, output >${OUTPUT}<"
|
||||
|
||||
cleanup
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup() {
|
||||
[ -n "${NVM_DIR}" ] && [ -d "${NVM_DIR}" ] && rm -rf "${NVM_DIR}"
|
||||
unset -f die cleanup
|
||||
unset NVM_DIR NVM_INSTALL_LOCK NVM_INSTALL_LOCK_TIMEOUT NVM_INSTALL_LOCK_STALE \
|
||||
version lock OUTPUT EXIT_CODE
|
||||
}
|
||||
|
||||
die() { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
type nvm_acquire_install_lock > /dev/null 2>&1 || die 'nvm_acquire_install_lock is not available'
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
[ -n "${NVM_DIR}" ] || die 'unable to create temp NVM_DIR'
|
||||
version='v20.0.0'
|
||||
lock="$(nvm_cache_dir)/locks/$(nvm_install_lock_name "${version}")"
|
||||
|
||||
# An empty version is a no-op that succeeds and takes no lock.
|
||||
unset NVM_INSTALL_LOCK
|
||||
nvm_acquire_install_lock '' || die 'acquiring with an empty version should succeed as a no-op'
|
||||
[ -z "${NVM_INSTALL_LOCK-}" ] || die 'an empty-version acquire should not record a lock'
|
||||
|
||||
# Acquiring a free lock succeeds, creates the lock dir, and records its path.
|
||||
nvm_acquire_install_lock "${version}" || die 'acquiring a free lock should succeed'
|
||||
[ -d "${lock}" ] || die 'acquire should create the lock directory'
|
||||
[ "${NVM_INSTALL_LOCK}" = "${lock}" ] || die 'acquire should record NVM_INSTALL_LOCK'
|
||||
|
||||
# Release it so the following contention checks start from a held-by-someone-else state.
|
||||
command rmdir "${lock}" 2>/dev/null
|
||||
unset NVM_INSTALL_LOCK
|
||||
|
||||
# A lock held by "another process", with no wait budget, fails and names the path.
|
||||
command mkdir -p "${lock}"
|
||||
OUTPUT="$(NVM_INSTALL_LOCK_TIMEOUT=0 nvm_acquire_install_lock "${version}" 2>&1)"; EXIT_CODE=$?
|
||||
[ "${EXIT_CODE}" != "0" ] || die 'acquire should fail when the lock is held and the timeout is 0'
|
||||
case "${OUTPUT}" in
|
||||
*"${lock}"*) ;;
|
||||
*) die "the timeout message should name the lock path; got >${OUTPUT}<" ;;
|
||||
esac
|
||||
[ -d "${lock}" ] || die 'a failed acquire must not remove the held lock'
|
||||
|
||||
# By default, a held lock is never stolen no matter how old it is.
|
||||
touch -t 202001010000 "${lock}" 2>/dev/null || die 'unable to age the lock dir'
|
||||
NVM_INSTALL_LOCK_TIMEOUT=0 nvm_acquire_install_lock "${version}" 2>/dev/null && die 'an old lock must not be stolen unless NVM_INSTALL_LOCK_STALE is set'
|
||||
[ -d "${lock}" ] || die 'the old lock should still be held'
|
||||
|
||||
# With NVM_INSTALL_LOCK_STALE set, a sufficiently old lock is stolen and re-acquired.
|
||||
unset NVM_INSTALL_LOCK
|
||||
NVM_INSTALL_LOCK_STALE=1 NVM_INSTALL_LOCK_TIMEOUT=0 nvm_acquire_install_lock "${version}" || die 'a stale lock should be stolen when NVM_INSTALL_LOCK_STALE is set'
|
||||
[ "${NVM_INSTALL_LOCK}" = "${lock}" ] || die 'stealing a stale lock should acquire it'
|
||||
|
||||
cleanup
|
||||
@@ -1,11 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="$PWD/nvm_curl_libz_support-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
unset -f curl
|
||||
}
|
||||
|
||||
die() { cleanup; echo "$@" ; exit 1; }
|
||||
@@ -13,28 +9,32 @@ die() { cleanup; echo "$@" ; exit 1; }
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
make_fake_curl "$TEST_BIN"
|
||||
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
|
||||
# curl with libz feature
|
||||
VERSION_MESSAGE="
|
||||
curl() {
|
||||
# curl with libz feature
|
||||
if [ $# -ne 1 ] || [ "$1" != "-V" ]; then
|
||||
die "This fake curl only takes one parameter -V"
|
||||
fi
|
||||
echo "
|
||||
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3
|
||||
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
|
||||
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets"
|
||||
}
|
||||
|
||||
nvm_curl_libz_support || die "nvm_curl_libz_support should return 0"
|
||||
|
||||
# curl without libz feature
|
||||
VERSION_MESSAGE="
|
||||
unset -f curl
|
||||
|
||||
curl() {
|
||||
# curl without libz feature
|
||||
if [ "$#" -ne 1 ] || [ "$1" != "-V" ]; then
|
||||
die "This fake curl only takes one parameter -V"
|
||||
fi
|
||||
echo "
|
||||
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.32
|
||||
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
|
||||
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL TLS-SRP UnixSockets"
|
||||
}
|
||||
|
||||
! nvm_curl_libz_support || die "nvm_curl_libz_support should return 1"
|
||||
|
||||
cleanup
|
||||
unset -f curl
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="$PWD/nvm_curl_use_compression-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
|
||||
cleanup () {
|
||||
unset -f die
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
}
|
||||
|
||||
die () { echo -e "$@" ; cleanup ; exit 1; }
|
||||
|
||||
NVM_ENV=testing \. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
make_fake_curl "$TEST_BIN"
|
||||
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
curl() {
|
||||
if [ "$1" = "-V" ]; then
|
||||
echo "${VERSION_MESSAGE}"
|
||||
fi
|
||||
}
|
||||
|
||||
CURL_VERSION_ON_ARCHLINUX_WITH_LIBZ="curl 7.54.0 (x86_64-pc-linux-gnu) libcurl/7.54.0 OpenSSL/1.1.0f zlib/1.2.11 libpsl/0.17.0 (+libicu/59.1) libssh2/1.8.0 nghttp2/1.22.0
|
||||
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="$PWD/nvm_curl_version-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
|
||||
cleanup () {
|
||||
unset -f die cleanup assert_version_is
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
unset -f die
|
||||
unset -f curl
|
||||
}
|
||||
|
||||
die () { echo -e "$@" ; cleanup ; exit 1; }
|
||||
|
||||
NVM_ENV=testing \. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
make_fake_curl "$TEST_BIN"
|
||||
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
curl() {
|
||||
if [ "$1" = "-V" ]; then
|
||||
echo "${VERSION_MESSAGE}"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_version_is() {
|
||||
if [ "${1}" != "${2}" ]; then
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="$PWD/shadowed-curl-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
ARGV_LOG="$WORK/argv.log"
|
||||
|
||||
cleanup () {
|
||||
unset -f die cleanup curl wget
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
}
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
NVM_ENV=testing \. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
# real-looking curl on PATH; calling the shadowing shell function below instead is a failure
|
||||
make_fake_curl "$TEST_BIN" ': > "$ARGV_LOG"
|
||||
for a in "$@"; do printf "%s\n" "$a" >> "$ARGV_LOG"; done'
|
||||
|
||||
VERSION_MESSAGE="curl 7.99.0 (fake)
|
||||
Features: libz"
|
||||
|
||||
export ARGV_LOG
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
|
||||
# shadowing shell functions, like aliases baked into a user's shell, must be
|
||||
# bypassed in favor of the executables on PATH (https://github.com/nvm-sh/nvm/issues/2923)
|
||||
curl() {
|
||||
echo 'curl 0.0.0-shadowed'
|
||||
return 1
|
||||
}
|
||||
wget() {
|
||||
return 1
|
||||
}
|
||||
|
||||
[ "$(nvm_curl_version)" = '7.99.0' ] || die "nvm_curl_version used the shadowing curl function; got $(nvm_curl_version)"
|
||||
|
||||
nvm_curl_libz_support || die 'nvm_curl_libz_support used the shadowing curl function'
|
||||
|
||||
nvm_download -s 'http://example.com/' -o /dev/null || die 'nvm_download used the shadowing curl function and failed'
|
||||
grep -Fxq 'http://example.com/' "$ARGV_LOG" || die "nvm_download did not invoke the curl executable; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# when no curl executable exists, a shadowing curl shell function must not fool
|
||||
# downloader selection: nvm_download must fall back to the wget executable
|
||||
WGET_ONLY_BIN="$WORK/wget-only-bin"
|
||||
mkdir -p "$WGET_ONLY_BIN"
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo ': > "$ARGV_LOG"'
|
||||
echo 'for a in "$@"; do printf "%s\n" "$a" >> "$ARGV_LOG"; done'
|
||||
} > "$WGET_ONLY_BIN/wget"
|
||||
chmod +x "$WGET_ONLY_BIN/wget"
|
||||
|
||||
(
|
||||
PATH="$WGET_ONLY_BIN"
|
||||
export PATH
|
||||
nvm_download -s 'http://wget-fallback.example.com/' -o /dev/null
|
||||
) || die 'nvm_download did not fall back to wget when curl is only a shell function'
|
||||
grep -Fxq 'http://wget-fallback.example.com/' "$ARGV_LOG" || die "nvm_download did not invoke the wget executable; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
cleanup
|
||||
@@ -6,7 +6,7 @@ TEST_BIN="$WORK/bin"
|
||||
ARGV_LOG="$WORK/argv.log"
|
||||
|
||||
cleanup() {
|
||||
unset -f die cleanup nvm_has_executable
|
||||
unset -f die cleanup nvm_has
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
}
|
||||
@@ -30,7 +30,7 @@ chmod +x "$TEST_BIN/wget"
|
||||
export ARGV_LOG
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
# force the wget path while keeping system tools (sed) available for sanitization
|
||||
nvm_has_executable() { [ "$1" != curl ] && command -v "$1" >/dev/null 2>&1; }
|
||||
nvm_has() { [ "$1" != curl ] && command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# given an Authorization credential in NVM_AUTH_HEADER
|
||||
# when nvm_download uses the wget path
|
||||
|
||||
@@ -90,14 +90,7 @@ run_test amd64 smartos x64 no_pkg_info
|
||||
run_test x86 osx x86
|
||||
run_test amd64 osx x64
|
||||
|
||||
# These smartos cases have no arch-specific uname mock, so nvm_get_os falls
|
||||
# through to the real host; on Alpine that host is linux and picks up the musl
|
||||
# suffix, which these non-musl assertions do not expect. Skip them there.
|
||||
if [ ! -f "/etc/alpine-release" ]; then
|
||||
run_test arm64 smartos x64
|
||||
run_test armv8l smartos x64
|
||||
fi
|
||||
|
||||
run_test loongarch64 linux loong64
|
||||
run_test arm64 smartos x64
|
||||
run_test armv8l smartos x64
|
||||
|
||||
cleanup
|
||||
|
||||
@@ -15,8 +15,8 @@ die () { cleanup; echo "$@" ; exit 1; }
|
||||
MOCKS_DIR="$(pwd)/../../mocks"
|
||||
export PATH=".:${PATH}"
|
||||
|
||||
# On Alpine (where /etc/alpine-release exists), both x64 and arm64 should get
|
||||
# the -musl suffix, since unofficial-builds publishes musl binaries for both.
|
||||
# On Alpine (where /etc/alpine-release exists), x64 should get -musl suffix
|
||||
# and arm64 should NOT get -musl suffix.
|
||||
# On non-Alpine, neither should get -musl.
|
||||
|
||||
if [ -f "/etc/alpine-release" ]; then
|
||||
@@ -26,11 +26,11 @@ if [ -f "/etc/alpine-release" ]; then
|
||||
rm -f ./uname
|
||||
[ "_${OUTPUT}" = "_x64-musl" ] || die "x64 on Alpine should be x64-musl, got ${OUTPUT}"
|
||||
|
||||
# aarch64 on Alpine should produce arm64-musl
|
||||
# aarch64 on Alpine should produce arm64, NOT arm64-musl
|
||||
ln -sf "${MOCKS_DIR}/uname_linux_aarch64" ./uname
|
||||
OUTPUT="$(nvm_get_arch)"
|
||||
rm -f ./uname
|
||||
[ "_${OUTPUT}" = "_arm64-musl" ] || die "aarch64 on Alpine should be arm64-musl, got ${OUTPUT}"
|
||||
[ "_${OUTPUT}" = "_arm64" ] || die "aarch64 on Alpine should be arm64 (no musl suffix), got ${OUTPUT}"
|
||||
else
|
||||
# x64 on non-Alpine should produce x64 (no musl suffix)
|
||||
ln -sf "${MOCKS_DIR}/uname_linux_x86_64" ./uname
|
||||
|
||||
@@ -57,23 +57,15 @@ setup_chroot() {
|
||||
sudo mknod "${chroot_dir}/dev/null" c 1 3
|
||||
}
|
||||
|
||||
# The chroot fixtures assume a glibc layout (fixed dynamic-linker path under
|
||||
# /lib64, coreutils binaries). On musl Alpine that setup does not apply, and
|
||||
# nvm_get_arch's musl mapping is already covered by the "nvm_get_arch alpine"
|
||||
# test, so skip the chroot checks there and still run the ls-remote checks.
|
||||
if [ -f "/etc/alpine-release" ]; then
|
||||
echo "on Alpine; skipping chroot arch checks (covered by 'nvm_get_arch alpine')"
|
||||
else
|
||||
setup_chroot "${CHROOT_WITH_ALPINE}"
|
||||
setup_chroot "${CHROOT_WITHOUT_ALPINE}"
|
||||
setup_chroot "${CHROOT_WITH_ALPINE}"
|
||||
setup_chroot "${CHROOT_WITHOUT_ALPINE}"
|
||||
|
||||
# Run tests in chroot environments
|
||||
ARCH_WITH_ALPINE=$(sudo chroot "${CHROOT_WITH_ALPINE}" /bin/sh -c ". ./nvm.sh && nvm_get_arch")
|
||||
[ "${ARCH_WITH_ALPINE}" = "x64-musl" ] || die "Expected x64-musl for alpine environment but got ${ARCH_WITH_ALPINE}"
|
||||
# Run tests in chroot environments
|
||||
ARCH_WITH_ALPINE=$(sudo chroot "${CHROOT_WITH_ALPINE}" /bin/sh -c ". ./nvm.sh && nvm_get_arch")
|
||||
[ "${ARCH_WITH_ALPINE}" = "x64-musl" ] || die "Expected x64-musl for alpine environment but got ${ARCH_WITH_ALPINE}"
|
||||
|
||||
ARCH_WITHOUT_ALPINE=$(sudo chroot "${CHROOT_WITHOUT_ALPINE}" /bin/sh -c ". ./nvm.sh && nvm_get_arch")
|
||||
[ "${ARCH_WITHOUT_ALPINE}" != "x64-musl" ] || die "Did not expect x64-musl for non-alpine environment"
|
||||
fi
|
||||
ARCH_WITHOUT_ALPINE=$(sudo chroot "${CHROOT_WITHOUT_ALPINE}" /bin/sh -c ". ./nvm.sh && nvm_get_arch")
|
||||
[ "${ARCH_WITHOUT_ALPINE}" != "x64-musl" ] || die "Did not expect x64-musl for non-alpine environment"
|
||||
|
||||
# Run tests for nvm ls-remote
|
||||
test_default_ls_remote() {
|
||||
|
||||
@@ -110,26 +110,3 @@ ACTUAL="$(nvm_get_download_slug iojs source 15.99.99)"
|
||||
EXPECTED="iojs-15.99.99"
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
|
||||
|
||||
REAL_OS="$(command uname -s 2>/dev/null || echo '')"
|
||||
REAL_ARCH="$(command uname -m 2>/dev/null || echo '')"
|
||||
if [ "${REAL_OS}" = "Darwin" ] && [ "${REAL_ARCH}" = "arm64" ]; then
|
||||
# Node < 16 uses x64 on darwin-arm64
|
||||
ACTUAL="$(nvm_get_download_slug node binary 14.21.3)"
|
||||
EXPECTED='node-14.21.3-darwin-x64'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
ACTUAL="$(nvm_get_download_slug node binary 15.99.99)"
|
||||
EXPECTED='node-15.99.99-darwin-x64'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
ACTUAL="$(nvm_get_download_slug iojs binary 15.99.99)"
|
||||
EXPECTED='iojs-15.99.99-darwin-x64'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
|
||||
# Test Node >= 16 uses arm64 on darwin-arm64
|
||||
ACTUAL="$(nvm_get_download_slug node binary 16.0.0)"
|
||||
EXPECTED='node-16.0.0-darwin-arm64'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
ACTUAL="$(nvm_get_download_slug node binary 18.0.0)"
|
||||
EXPECTED='node-18.0.0-darwin-arm64'
|
||||
[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<"
|
||||
fi
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
cleanup() {
|
||||
unset -f nvm_has nvm_has_executable
|
||||
unset -f nvm_has
|
||||
}
|
||||
|
||||
: nvm.sh
|
||||
@@ -12,7 +12,6 @@ cleanup() {
|
||||
\. ../../common.sh
|
||||
|
||||
nvm_has() { return 1 ; }
|
||||
nvm_has_executable() { return 1 ; }
|
||||
|
||||
try_err nvm_get_latest
|
||||
[ "_$CAPTURED_STDERR" = "_nvm needs curl or wget to proceed." ] \
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup() {
|
||||
[ -n "${tmp_dir}" ] && [ -d "${tmp_dir}" ] && rm -rf "${tmp_dir}"
|
||||
[ -n "${NVM_DIR}" ] && [ -d "${NVM_DIR}" ] && rm -rf "${NVM_DIR}"
|
||||
unset -f die cleanup nvm_supports_xz
|
||||
unset NVM_DIR tmp_dir version archi node_dir version_path
|
||||
}
|
||||
|
||||
die() { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
# Use gzip so the test does not depend on xz being installed.
|
||||
nvm_supports_xz() { return 1; }
|
||||
|
||||
set -ex
|
||||
|
||||
type nvm_install_binary_extract > /dev/null 2>&1 || die 'nvm_install_binary_extract is not available'
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
tmp_dir="$(mktemp -d)"
|
||||
[ -n "${NVM_DIR}" ] && [ -n "${tmp_dir}" ] || die 'Unable to create temporary folders'
|
||||
|
||||
version='v14.15.4'
|
||||
archi='linux-x64'
|
||||
node_dir="${tmp_dir}/node-${version}-${archi}"
|
||||
|
||||
# Build a fake binary tarball: a runnable node, an npm symlink, and a lib tree.
|
||||
mkdir -p "${node_dir}/bin" "${node_dir}/lib/node_modules/npm/bin" || die 'setup mkdir failed'
|
||||
printf '#!/bin/sh\necho "%s"\n' "${version}" > "${node_dir}/bin/node"
|
||||
chmod +x "${node_dir}/bin/node"
|
||||
echo 'npm-cli' > "${node_dir}/lib/node_modules/npm/bin/npm-cli.js"
|
||||
( cd "${node_dir}/bin" && ln -s ../lib/node_modules/npm/bin/npm-cli.js npm )
|
||||
echo 'fresh' > "${node_dir}/lib/FRESHFILE"
|
||||
( cd "${tmp_dir}" && tar -czf "${node_dir}.tar.gz" "node-${version}-${archi}" ) || die 'unable to create fake tarball'
|
||||
|
||||
# Pre-create a BROKEN, non-empty version directory: npm and a stale lib file
|
||||
# are present, but bin/node is missing - exactly the shape that wedges a
|
||||
# per-entry move onto existing non-empty subdirectories.
|
||||
version_path="${NVM_DIR}/versions/node/${version}"
|
||||
mkdir -p "${version_path}/bin" "${version_path}/lib" || die 'unable to stage broken dir'
|
||||
echo 'stale-npm' > "${version_path}/bin/npm"
|
||||
echo 'stale' > "${version_path}/lib/STALEFILE"
|
||||
[ -e "${version_path}/bin/node" ] && die 'precondition: broken dir should have no bin/node'
|
||||
|
||||
# Extract over the broken directory.
|
||||
nvm_install_binary_extract 'linux' "${version}" "$(expr "${version}" : '.\(.*\)')" "${node_dir}.tar.gz" "${tmp_dir}/files" || die 'nvm_install_binary_extract failed over a broken version dir'
|
||||
|
||||
# bin/node is restored and runnable.
|
||||
[ -x "${version_path}/bin/node" ] || die 'bin/node was not restored'
|
||||
[ "$("${version_path}/bin/node")" = "${version}" ] || die 'restored bin/node has wrong contents'
|
||||
|
||||
# The stale file is gone: the whole directory was replaced, not merged into.
|
||||
[ ! -e "${version_path}/lib/STALEFILE" ] || die 'stale lib file survived: install was not atomic'
|
||||
[ -e "${version_path}/lib/FRESHFILE" ] || die 'fresh lib content missing after install'
|
||||
|
||||
# npm resolves to the freshly installed target.
|
||||
[ -e "${version_path}/bin/npm" ] || die 'npm missing after install'
|
||||
|
||||
cleanup
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup() {
|
||||
[ -n "${NVM_DIR}" ] && [ -d "${NVM_DIR}" ] && rm -rf "${NVM_DIR}"
|
||||
unset -f die cleanup
|
||||
unset NVM_DIR
|
||||
}
|
||||
|
||||
die() { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
type nvm_install_lock_name > /dev/null 2>&1 || die 'nvm_install_lock_name is not available'
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
[ -n "${NVM_DIR}" ] || die 'unable to create temp NVM_DIR'
|
||||
|
||||
# A normal version passes through unchanged.
|
||||
[ "$(nvm_install_lock_name 'v20.0.0')" = 'v20.0.0' ] || die "v20.0.0 => >$(nvm_install_lock_name 'v20.0.0')<"
|
||||
|
||||
# Dots, hyphens, plus, and underscores are all preserved.
|
||||
[ "$(nvm_install_lock_name 'iojs-v1.0.0')" = 'iojs-v1.0.0' ] || die "iojs-v1.0.0 => >$(nvm_install_lock_name 'iojs-v1.0.0')<"
|
||||
|
||||
# Path separators and other unsafe characters become underscores.
|
||||
[ "$(nvm_install_lock_name 'lts/*')" = 'lts__' ] || die "lts/* => >$(nvm_install_lock_name 'lts/*')<"
|
||||
[ "$(nvm_install_lock_name 'a b/c')" = 'a_b_c' ] || die "a b/c => >$(nvm_install_lock_name 'a b/c')<"
|
||||
|
||||
# The empty string maps to the empty string (no trailing-newline artifact).
|
||||
[ "$(nvm_install_lock_name '')" = '' ] || die "empty => >$(nvm_install_lock_name '')<"
|
||||
|
||||
cleanup
|
||||
@@ -14,14 +14,6 @@ die () { >&2 echo "$@" ; cleanup ; exit 1; }
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
# v0.12.18 predates musl binaries and the expected output hardcodes the glibc
|
||||
# x64 tarball URL, so on Alpine nvm would request a nonexistent -musl build.
|
||||
# The progress-bar behavior is not OS-specific, so skip below the musl floor.
|
||||
if [ -f "/etc/alpine-release" ]; then
|
||||
echo 'on Alpine; skipping (v0.12.18 has no musl binary)'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
OUTPUT="$(TERM=dumb 2>&1 nvm install --no-progress v0.12.18)"
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
cleanup() {
|
||||
rm -rf "${TEST_DIR-}"
|
||||
unset NVMRC_CONTENT OUTPUT EXIT_CODE TEST_DIR NVM_DIR
|
||||
}
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
# an isolated NVM_DIR with a single known version, so that ambient versions
|
||||
# and aliases can not affect any assertions
|
||||
TEST_DIR="${PWD}/nvm_ls_hash_pattern_tmp"
|
||||
mkdir -p "${TEST_DIR}/versions/node/v24.13.0/bin" || die 'failed to create test version dir'
|
||||
make_echo "${TEST_DIR}/versions/node/v24.13.0/bin/node" 'v24.13.0' || die 'failed to create test node binary'
|
||||
NVM_DIR="${TEST_DIR}"
|
||||
|
||||
# Test: nvm_ls with pattern containing # should not cause sed error
|
||||
# This is a regression test for https://github.com/nvm-sh/nvm/issues/3761
|
||||
|
||||
@@ -36,33 +22,3 @@ echo "$OUTPUT" | grep -q "invalid command code" && \
|
||||
# Should return N/A with exit code 3 (not found)
|
||||
[ "$EXIT_CODE" = "3" ] || die "nvm_ls 'foo#bar' should exit with code 3, got $EXIT_CODE"
|
||||
echo "$OUTPUT" | grep -q "N/A" || die "nvm_ls 'foo#bar' should output N/A, got: $OUTPUT"
|
||||
|
||||
NVMRC_CONTENT='v24.13.0
|
||||
# krypton is the codename for Node.js v24.x'
|
||||
|
||||
OUTPUT="$(nvm_ls "${NVMRC_CONTENT}" 2>&1)"
|
||||
EXIT_CODE=$?
|
||||
|
||||
[ "$EXIT_CODE" = "0" ] || die "nvm_ls with .nvmrc comments should exit with code 0, got $EXIT_CODE"
|
||||
[ "$OUTPUT" = "v24.13.0" ] || die "nvm_ls with .nvmrc comments should output v24.13.0, got: $OUTPUT"
|
||||
|
||||
OUTPUT="$(nvm_version "${NVMRC_CONTENT}" 2>&1)"
|
||||
[ "$OUTPUT" = "v24.13.0" ] || die "nvm_version with .nvmrc comments should output v24.13.0, got: $OUTPUT"
|
||||
|
||||
# a partial version reaches nvm_ls's find/sed pipeline, which is where multiline
|
||||
# patterns used to break sed ("unterminated regular expression" on BSD sed,
|
||||
# "unterminated address regex" on GNU sed); a full x.y.z version takes the
|
||||
# explicit-version fast path and never reaches it
|
||||
NVMRC_CONTENT='24
|
||||
# krypton is the codename for Node.js v24.x'
|
||||
|
||||
OUTPUT="$(nvm_ls "${NVMRC_CONTENT}" 2>&1)"
|
||||
EXIT_CODE=$?
|
||||
|
||||
echo "$OUTPUT" | grep -q "unterminated" && \
|
||||
die "nvm_ls with partial version + comments caused a sed error: $OUTPUT"
|
||||
|
||||
[ "$EXIT_CODE" = "0" ] || die "nvm_ls with partial version + comments should exit with code 0, got $EXIT_CODE"
|
||||
[ "$OUTPUT" = "v24.13.0" ] || die "nvm_ls with partial version + comments should output v24.13.0, got: $OUTPUT"
|
||||
|
||||
cleanup
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="$PWD/ls_remote-traversal-work.$$"
|
||||
export HOME="$WORK/home"
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
BASHRC="$HOME/.bashrc"
|
||||
|
||||
cleanup() {
|
||||
unset -f die cleanup nvm_download
|
||||
rm -rf "$WORK"
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
mkdir -p "$NVM_DIR/alias"
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
# a malicious/compromised mirror: the LTS codename field ($10) carries path
|
||||
# traversal, and the version field ($1) carries a command-substitution payload;
|
||||
# a valid codename (Iron) is included to prove real aliases still get written
|
||||
nvm_download() {
|
||||
printf 'version\tdate\tfiles\tnpm\tv8\tuv\tzlib\topenssl\tmodules\tlts\tsecurity\n'
|
||||
printf '$(>%s/pwned)\t2026-01-01\tlinux-x64\t-\t-\t-\t-\t-\t-\t../../../.bashrc\t-\n' "$WORK"
|
||||
printf 'v20.0.0\t2026-01-01\tlinux-x64\t-\t-\t-\t-\t-\t-\tIron\t-\n'
|
||||
}
|
||||
|
||||
nvm_ls_remote >/dev/null 2>&1 || true
|
||||
|
||||
[ ! -e "$BASHRC" ] || die "path traversal wrote outside the alias dir: $BASHRC was created"
|
||||
[ ! -e "$WORK/pwned" ] || die "mirror-supplied payload landed: $WORK/pwned was created"
|
||||
[ -f "$NVM_DIR/alias/lts/iron" ] || die "valid LTS alias lts/iron was not created"
|
||||
|
||||
nvm_make_alias 'lts/../../../escape' 'v1.0.0' 2>/dev/null && die 'nvm_make_alias accepted a traversing alias name'
|
||||
[ ! -e "$NVM_DIR/../escape" ] || die 'nvm_make_alias wrote outside the alias dir'
|
||||
nvm_make_alias 'lts/carbon' 'v8.0.0' >/dev/null 2>&1 || die 'nvm_make_alias rejected a valid alias name'
|
||||
[ -f "$NVM_DIR/alias/lts/carbon" ] || die 'nvm_make_alias did not create a valid alias'
|
||||
|
||||
cleanup
|
||||
echo "nvm_ls_remote LTS codename traversal: passed"
|
||||
Executable → Regular
-33
@@ -10,10 +10,6 @@ TEST_DIR="$(pwd)/nvm_offline_version_tmp"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "${TEST_DIR}"
|
||||
CACHE_DIR="$(nvm_cache_dir)"
|
||||
SUFFIX="$(nvm_get_os)-$(nvm_get_arch)"
|
||||
rm -rf "${CACHE_DIR}/bin/node-v0.99.98-${SUFFIX}" "${CACHE_DIR}/bin/iojs-v0.99.97-${SUFFIX}" \
|
||||
"${CACHE_DIR}/src/node-v0.99.96" "${CACHE_DIR}/src/iojs-v0.99.95"
|
||||
}
|
||||
|
||||
[ ! -e "${TEST_DIR}" ] && mkdir -p "${TEST_DIR}"
|
||||
@@ -40,33 +36,4 @@ try nvm_ls_cached "999.999"
|
||||
[ -z "$CAPTURED_STDOUT" ] \
|
||||
|| die "nvm_ls_cached '999.999' should return empty; got '$CAPTURED_STDOUT'"
|
||||
|
||||
# nvm_ls_cached should list bin- and src-cached artifacts, for node and io.js
|
||||
CACHE_DIR="$(nvm_cache_dir)"
|
||||
SUFFIX="$(nvm_get_os)-$(nvm_get_arch)"
|
||||
mkdir -p "${CACHE_DIR}/bin/node-v0.99.98-${SUFFIX}" "${CACHE_DIR}/bin/iojs-v0.99.97-${SUFFIX}" \
|
||||
"${CACHE_DIR}/src/node-v0.99.96" "${CACHE_DIR}/src/iojs-v0.99.95"
|
||||
|
||||
try nvm_ls_cached "v0.99.98"
|
||||
[ "_$CAPTURED_STDOUT" = "_v0.99.98" ] \
|
||||
|| die "nvm_ls_cached 'v0.99.98' should find the bin-cached node version; got '$CAPTURED_STDOUT'"
|
||||
|
||||
try nvm_ls_cached "iojs-v0.99.97"
|
||||
[ "_$CAPTURED_STDOUT" = "_iojs-v0.99.97" ] \
|
||||
|| die "nvm_ls_cached 'iojs-v0.99.97' should find the bin-cached io.js version; got '$CAPTURED_STDOUT'"
|
||||
|
||||
try nvm_ls_cached "v0.99.96"
|
||||
[ "_$CAPTURED_STDOUT" = "_v0.99.96" ] \
|
||||
|| die "nvm_ls_cached 'v0.99.96' should find the src-cached node version; got '$CAPTURED_STDOUT'"
|
||||
|
||||
try nvm_ls_cached "iojs-v0.99.95"
|
||||
[ "_$CAPTURED_STDOUT" = "_iojs-v0.99.95" ] \
|
||||
|| die "nvm_ls_cached 'iojs-v0.99.95' should find the src-cached io.js version; got '$CAPTURED_STDOUT'"
|
||||
|
||||
# ... and nvm_offline_version should resolve them
|
||||
try nvm_offline_version "iojs-v0.99.95"
|
||||
[ "_$CAPTURED_STDOUT" = "_iojs-v0.99.95" ] \
|
||||
|| die "nvm_offline_version 'iojs-v0.99.95' should resolve the src-cached io.js version; got '$CAPTURED_STDOUT'"
|
||||
[ "_$CAPTURED_EXIT_CODE" = "_0" ] \
|
||||
|| die "nvm_offline_version 'iojs-v0.99.95' should exit 0; got '$CAPTURED_EXIT_CODE'"
|
||||
|
||||
cleanup
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup() {
|
||||
[ -n "${NVM_DIR}" ] && [ -d "${NVM_DIR}" ] && rm -rf "${NVM_DIR}"
|
||||
unset -f die cleanup
|
||||
unset NVM_DIR NVM_INSTALL_LOCK version lock
|
||||
}
|
||||
|
||||
die() { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
type nvm_release_install_lock > /dev/null 2>&1 || die 'nvm_release_install_lock is not available'
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
[ -n "${NVM_DIR}" ] || die 'unable to create temp NVM_DIR'
|
||||
version='v20.0.0'
|
||||
lock="$(nvm_cache_dir)/locks/$(nvm_install_lock_name "${version}")"
|
||||
|
||||
# Releasing when nothing is held is a no-op that succeeds.
|
||||
unset NVM_INSTALL_LOCK
|
||||
nvm_release_install_lock || die 'releasing with no lock held should succeed'
|
||||
|
||||
# After acquiring, releasing removes the lock directory and clears the marker.
|
||||
nvm_acquire_install_lock "${version}" || die 'setup: acquire failed'
|
||||
[ -d "${lock}" ] || die 'setup: lock dir should exist after acquire'
|
||||
nvm_release_install_lock || die 'release should succeed'
|
||||
[ ! -d "${lock}" ] || die 'release should remove the lock directory'
|
||||
[ -z "${NVM_INSTALL_LOCK-}" ] || die 'release should clear NVM_INSTALL_LOCK'
|
||||
|
||||
# Releasing again is a harmless no-op.
|
||||
nvm_release_install_lock || die 'a second release should be a no-op'
|
||||
|
||||
cleanup
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
set -ex
|
||||
|
||||
# Test 1: all standard base64 characters (RFC 4648) are preserved
|
||||
STANDARD_B64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
|
||||
RESULT=$(nvm_sanitize_auth_header "${STANDARD_B64}")
|
||||
[ "${RESULT}" = "${STANDARD_B64}" ] || die "FAIL: standard base64 chars were stripped. Got: '${RESULT}'"
|
||||
|
||||
# Test 2: all base64url characters (RFC 4648 §5) are preserved
|
||||
B64URL="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
|
||||
RESULT=$(nvm_sanitize_auth_header "${B64URL}")
|
||||
[ "${RESULT}" = "${B64URL}" ] || die "FAIL: base64url chars were stripped. Got: '${RESULT}'"
|
||||
|
||||
# Test 3: a real JWT Bearer token (base64url-encoded header.payload.signature) is preserved
|
||||
JWT_TOKEN="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
RESULT=$(nvm_sanitize_auth_header "${JWT_TOKEN}")
|
||||
[ "${RESULT}" = "${JWT_TOKEN}" ] || die "FAIL: JWT Bearer token chars were stripped. Got: '${RESULT}'"
|
||||
|
||||
# Test 4: Basic auth (base64-encoded user:pass) is preserved
|
||||
BASIC_TOKEN="Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
||||
RESULT=$(nvm_sanitize_auth_header "${BASIC_TOKEN}")
|
||||
[ "${RESULT}" = "${BASIC_TOKEN}" ] || die "FAIL: Basic auth base64 token chars were stripped. Got: '${RESULT}'"
|
||||
|
||||
# Test 5: dangerous shell metacharacters are removed
|
||||
DANGEROUS="Bearer token;\`evil\`-cmd \$(inject)"
|
||||
RESULT=$(nvm_sanitize_auth_header "${DANGEROUS}")
|
||||
case "${RESULT}" in
|
||||
*";"*|*"\`"*|*'$'*|*"("*|*")"*)
|
||||
die "FAIL: dangerous shell metacharacters survived sanitization. Got: '${RESULT}'"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "All nvm_sanitize_auth_header tests passed"
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup() {
|
||||
[ -n "${NVM_DIR}" ] && [ -d "${NVM_DIR}" ] && rm -rf "${NVM_DIR}"
|
||||
unset -f die cleanup
|
||||
unset NVM_DIR version version_path
|
||||
}
|
||||
|
||||
die() { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
type nvm_validate_install > /dev/null 2>&1 || die 'nvm_validate_install is not available'
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
[ -n "${NVM_DIR}" ] || die 'Unable to create temporary folder'
|
||||
|
||||
version='v20.0.0'
|
||||
version_path="${NVM_DIR}/versions/node/${version}"
|
||||
|
||||
# No version directory at all: not valid.
|
||||
nvm_validate_install "${version}" 2>/dev/null && die 'a missing version dir should not validate'
|
||||
|
||||
# A healthy install: a non-empty node plus an npm symlink that resolves.
|
||||
# (validation checks layout, not execution, so bin/node need not be a real
|
||||
# runnable binary here - just non-empty and executable.)
|
||||
mkdir -p "${version_path}/bin" "${version_path}/lib/node_modules/npm/bin" || die 'setup mkdir failed'
|
||||
printf '#!/bin/sh\necho "%s"\n' "${version}" > "${version_path}/bin/node"
|
||||
chmod +x "${version_path}/bin/node"
|
||||
echo 'npm-cli' > "${version_path}/lib/node_modules/npm/bin/npm-cli.js"
|
||||
( cd "${version_path}/bin" && ln -s ../lib/node_modules/npm/bin/npm-cli.js npm )
|
||||
nvm_validate_install "${version}" 2>/dev/null || die 'a healthy install should validate'
|
||||
|
||||
# A binary that is correctly installed but cannot run on this host (e.g. a
|
||||
# newer node on an older glibc) must STILL validate: whether it runs is the
|
||||
# host's concern, not a broken install.
|
||||
printf '#!/bin/sh\nexit 1\n' > "${version_path}/bin/node"
|
||||
chmod +x "${version_path}/bin/node"
|
||||
nvm_validate_install "${version}" 2>/dev/null || die 'a non-runnable but present binary should still validate'
|
||||
|
||||
# A zero-byte but executable node: passes `[ -x ]` but must not validate.
|
||||
: > "${version_path}/bin/node"
|
||||
chmod +x "${version_path}/bin/node"
|
||||
nvm_validate_install "${version}" 2>/dev/null && die 'a zero-byte node should not validate'
|
||||
|
||||
# A non-empty node again, but with a dangling npm symlink: must not validate.
|
||||
printf '#!/bin/sh\necho "%s"\n' "${version}" > "${version_path}/bin/node"
|
||||
chmod +x "${version_path}/bin/node"
|
||||
rm -f "${version_path}/lib/node_modules/npm/bin/npm-cli.js"
|
||||
nvm_validate_install "${version}" 2>/dev/null && die 'a dangling npm symlink should not validate'
|
||||
|
||||
cleanup
|
||||
Executable → Regular
+2
-2
@@ -32,9 +32,9 @@ fi
|
||||
# Test 2: Verify that sanitized header still works for legitimate requests
|
||||
# The sanitized header should only contain safe characters
|
||||
SANITIZED=$(nvm_sanitize_auth_header "${MALICIOUS_HEADER}")
|
||||
# Verify that dangerous shell metacharacters were removed
|
||||
# Verify that dangerous characters were removed
|
||||
case "${SANITIZED}" in
|
||||
*";"*|*'$'*|*"\`"*)
|
||||
*";"*|*"touch"*|*"/tmp"*)
|
||||
die "SECURITY FAILURE: Sanitization did not remove dangerous characters properly"
|
||||
;;
|
||||
esac
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
include config.mk
|
||||
|
||||
all: out/fake-bin
|
||||
|
||||
out/fake-bin: VERSION
|
||||
mkdir -p out
|
||||
printf '#!/bin/sh\necho %s\n' "$$(cat VERSION)" > out/fake-bin
|
||||
chmod +x out/fake-bin
|
||||
|
||||
install: all
|
||||
mkdir -p "$(PREFIX)/bin"
|
||||
cp out/fake-bin "$(PREFIX)/bin/node"
|
||||
cp out/fake-bin "$(PREFIX)/bin/iojs"
|
||||
|
||||
.PHONY: all install
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# a fake node/io.js `configure`, for source-install tests: real io.js (and
|
||||
# old node) can no longer be compiled on modern toolchains (python 2
|
||||
# `configure`, ancient V8), so source-install tests exercise nvm's entire
|
||||
# source pipeline against this tiny stand-in instead.
|
||||
|
||||
PREFIX=''
|
||||
while [ $# -gt 0 ]; do
|
||||
case "${1}" in
|
||||
--prefix=*) PREFIX="${1#--prefix=}" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "${PREFIX}" ]; then
|
||||
echo 'configure: a --prefix is required' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'PREFIX = %s\n' "${PREFIX}" > config.mk
|
||||
echo "configured with PREFIX=${PREFIX}"
|
||||
@@ -63,15 +63,9 @@ test_install_data() {
|
||||
echo "$current_ref" | grep -q "$ref" || die "install_nvm_from_git ${message} did not clone with ref ${ref}"
|
||||
fi
|
||||
|
||||
if [ -n "${avoid_ref}" ] && echo "${current_ref}" | grep -q "${avoid_ref}"; then
|
||||
# the avoided tag decorating HEAD is only legitimate if HEAD really is that
|
||||
# tag's commit in the installed clone (eg master released at that tag); query
|
||||
# the clone itself, not the caller's checkout, so the trigger ref cannot skew it
|
||||
local avoid_changeset
|
||||
avoid_changeset="$(git --git-dir "$NVM_DIR"/.git rev-parse --verify --quiet "refs/tags/${avoid_ref}^{commit}" 2>/dev/null)"
|
||||
if [ "${avoid_changeset}" != "${current_changeset}" ]; then
|
||||
die "install_nvm_from_git ${message} did clone with unwanted ref ${avoid_ref}"
|
||||
fi
|
||||
local head_ref="$(git for-each-ref --points-at HEAD --format='%(refname:short)' 'refs/tags/')"
|
||||
if [ -n "${avoid_ref}" ] && [ "${head_ref}" != "${avoid_ref}" ]; then
|
||||
echo "${current_ref}" | grep -q "$avoid_ref" && die "install_nvm_from_git ${message} did clone with unwanted ref ${avoid_ref}"
|
||||
fi
|
||||
|
||||
if [ -n "$changeset" ]; then
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
WORK="$PWD/nvm_download-noeval-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
ARGV_LOG="$WORK/argv.log"
|
||||
PROOF="$WORK/nvm_injection_proof"
|
||||
|
||||
cleanup() {
|
||||
unset -f die cleanup
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
NVM_ENV=testing \. ../../install.sh
|
||||
|
||||
mkdir -p "$TEST_BIN"
|
||||
|
||||
# fake curl/wget: record each received argument verbatim, then succeed
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo ': > "$ARGV_LOG"'
|
||||
echo 'for a in "$@"; do printf "%s\n" "$a" >> "$ARGV_LOG"; done'
|
||||
echo 'exit 0'
|
||||
} > "$TEST_BIN/curl"
|
||||
chmod +x "$TEST_BIN/curl"
|
||||
cp "$TEST_BIN/curl" "$TEST_BIN/wget"
|
||||
|
||||
# the wget-path calls below restrict PATH to $TEST_BIN so that no real curl is
|
||||
# found; link sed and touch in so that a regression to the old sed|eval
|
||||
# implementation fails this test by actually injecting, rather than by failing
|
||||
# to find the binaries the injection needs
|
||||
ln -s "$(command -v sed)" "$TEST_BIN/sed"
|
||||
ln -s "$(command -v touch)" "$TEST_BIN/touch"
|
||||
|
||||
export ARGV_LOG
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
|
||||
URL="https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/nvm.sh"
|
||||
|
||||
# given a url containing command-substitution syntax
|
||||
INJECT_URL="http://example.test/v1\$(touch ${PROOF})/x"
|
||||
|
||||
# when nvm_download is invoked (curl path)
|
||||
rm -f "$PROOF"
|
||||
nvm_download "$INJECT_URL" -o - || die 'nvm_download (curl) returned nonzero on injection url'
|
||||
# then the substitution must not have executed
|
||||
[ ! -e "$PROOF" ] || die "command injection fired via curl path: proof file was created"
|
||||
# and curl must have received the url as one literal argument
|
||||
grep -Fxq "$INJECT_URL" "$ARGV_LOG" || die "curl did not receive the url as a single literal argument; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# given curl is unavailable (the wget-path calls run with PATH limited to our
|
||||
# fake wget, so neither the fake nor the system curl is found)
|
||||
rm -f "$TEST_BIN/curl"
|
||||
|
||||
# when nvm_download is invoked with the injection url (wget path)
|
||||
rm -f "$PROOF"
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download "$INJECT_URL" -o - ) || die 'nvm_download (wget) returned nonzero on injection url'
|
||||
# then the substitution must not have executed
|
||||
[ ! -e "$PROOF" ] || die "command injection fired via wget path: proof file was created"
|
||||
grep -Fxq "$INJECT_URL" "$ARGV_LOG" || die "wget did not receive the url as a single literal argument; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# given an output path containing a space, as a real $NVM_DIR may
|
||||
SPACED_DIR="$WORK/dir with space"
|
||||
SPACED="$SPACED_DIR/nvm.sh"
|
||||
mkdir -p "$SPACED_DIR"
|
||||
|
||||
# when nvm_download is invoked with it (wget path)
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download -s "$URL" -o "$SPACED" ) || die 'nvm_download (wget) returned nonzero on spaced output path'
|
||||
# then wget receives it as one argument rather than word-split
|
||||
grep -Fxq "$SPACED" "$ARGV_LOG" || die "wget did not receive the spaced output path as a single argument; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# given a url with an ampersand in its query string, as a real mirror may
|
||||
AMP_URL="http://example.test/nvm.sh?a=1&b=2"
|
||||
|
||||
# when nvm_download is invoked with it (wget path)
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download -s "$AMP_URL" -o - ) || die 'nvm_download (wget) returned nonzero on ampersand url'
|
||||
# then the url is passed intact rather than backgrounding the command
|
||||
grep -Fxq "$AMP_URL" "$ARGV_LOG" || die "wget did not receive the ampersand url intact; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# when invoked with -L -C - --progress-bar URL -o FILE (wget path)
|
||||
FILE="$WORK/target"
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download -L -C - --progress-bar "$URL" -o "$FILE" ) || die 'nvm_download (wget) returned nonzero on normal url'
|
||||
# then flags are translated to wget equivalents
|
||||
grep -Fxqe "-c" "$ARGV_LOG" || die "wget did not translate -C - to -c; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "--progress=bar" "$ARGV_LOG" || die "wget did not translate --progress-bar; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-O" "$ARGV_LOG" || die "wget did not translate -o to -O; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-L" "$ARGV_LOG" && die "wget should drop -L; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-C" "$ARGV_LOG" && die "wget should not pass -C through; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-" "$ARGV_LOG" && die "wget should drop the lone - after -C; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# when invoked with -s (wget path)
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download -s "$URL" -o "$FILE" ) || die 'nvm_download (wget) returned nonzero on -s url'
|
||||
# then -s becomes -q
|
||||
grep -Fxqe "-q" "$ARGV_LOG" || die "wget did not translate -s to -q; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
cleanup
|
||||
echo "nvm_download no eval injection: passed"
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
\. ../common.sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
\. ../../nvm.sh
|
||||
|
||||
set -ex
|
||||
|
||||
NVM_TEST_VERSION='v3.99.9'
|
||||
NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}"
|
||||
|
||||
# Remove the stuff we're clobbering.
|
||||
nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed'
|
||||
|
||||
# Exercise nvm's entire source-install pipeline - cache, extract, configure,
|
||||
# make, make install, activation - against a tiny fake source tree, seeded
|
||||
# into the download cache and installed with --offline. This runs everywhere,
|
||||
# including on toolchains where real io.js can not compile; `install from
|
||||
# source` covers the real compile, in CI's gcc:4.9 container job.
|
||||
SLUG="$(nvm_get_download_slug iojs source "${NVM_TEST_VERSION}")"
|
||||
COMPRESSION="$(nvm_get_artifact_compression "${NVM_TEST_VERSION}")"
|
||||
CACHE_DIR="$(nvm_cache_dir)/src/${SLUG}"
|
||||
STAGING="$(mktemp -d)"
|
||||
mkdir -p "${STAGING}/${SLUG}" "${CACHE_DIR}"
|
||||
cp ../fixtures/fake-source-tarball/configure ../fixtures/fake-source-tarball/Makefile "${STAGING}/${SLUG}/"
|
||||
chmod +x "${STAGING}/${SLUG}/configure"
|
||||
echo "${NVM_TEST_VERSION}" > "${STAGING}/${SLUG}/VERSION"
|
||||
if [ "${COMPRESSION}" = 'tar.xz' ]; then
|
||||
(cd "${STAGING}" && tar -cJf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}")
|
||||
else
|
||||
(cd "${STAGING}" && tar -czf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}")
|
||||
fi
|
||||
rm -rf "${STAGING}"
|
||||
|
||||
# Install from source
|
||||
nvm install -s --offline "${NVM_PREFIXED_TEST_VERSION}" || die "'nvm install -s --offline ${NVM_PREFIXED_TEST_VERSION}' failed"
|
||||
|
||||
# Check
|
||||
nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version not installed'
|
||||
nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" || die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed"
|
||||
|
||||
rm -rf "${CACHE_DIR}"
|
||||
Executable → Regular
+1
-18
@@ -8,23 +8,6 @@ die () { echo "$@" ; exit 1; }
|
||||
|
||||
set -ex
|
||||
|
||||
# Compiling io.js requires a period toolchain: python 2 (its `configure`),
|
||||
# and gcc <= 5 (its bundled V8). CI compiles it for real in a gcc:4.9
|
||||
# container job; on modern toolchains, this can only be skipped
|
||||
# (`install from fake source` covers nvm's source pipeline everywhere).
|
||||
GCC_MAJOR="$(gcc -dumpversion 2>/dev/null | command cut -d. -f1)"
|
||||
if ! command -v python2 >/dev/null 2>&1 && ! python -V 2>&1 | command grep -q '^Python 2'; then
|
||||
echo 'python 2 is not available; skipping the io.js source install'
|
||||
exit 0
|
||||
fi
|
||||
case "${GCC_MAJOR}" in
|
||||
'' | *[!0-9]*) echo 'gcc is not available; skipping the io.js source install'; exit 0 ;;
|
||||
esac
|
||||
if [ "${GCC_MAJOR}" -gt 5 ]; then
|
||||
echo "gcc <= 5 is required to compile io.js, got ${GCC_MAJOR}; skipping the io.js source install"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
NVM_TEST_VERSION='v3.3.1'
|
||||
NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}"
|
||||
|
||||
@@ -36,4 +19,4 @@ nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed'
|
||||
|
||||
# Check
|
||||
nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version not installed'
|
||||
nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" || die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed"
|
||||
nvm run "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" || "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed"
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
\. ../../nvm.sh
|
||||
|
||||
set -ex
|
||||
|
||||
NVM_TEST_VERSION='v3.99.8'
|
||||
NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}"
|
||||
|
||||
# Remove the stuff we're clobbering.
|
||||
nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed'
|
||||
|
||||
# Exercise nvm's source-install pipeline against a tiny fake source tree,
|
||||
# seeded into the download cache and installed with --offline (see `install
|
||||
# from fake source`). This runs everywhere, including on toolchains where
|
||||
# real io.js can not compile; `install version specified in .nvmrc from
|
||||
# source` covers the real compile, in CI's gcc:4.9 container job.
|
||||
SLUG="$(nvm_get_download_slug iojs source "${NVM_TEST_VERSION}")"
|
||||
COMPRESSION="$(nvm_get_artifact_compression "${NVM_TEST_VERSION}")"
|
||||
CACHE_DIR="$(nvm_cache_dir)/src/${SLUG}"
|
||||
STAGING="$(mktemp -d)"
|
||||
mkdir -p "${STAGING}/${SLUG}" "${CACHE_DIR}"
|
||||
cp ../fixtures/fake-source-tarball/configure ../fixtures/fake-source-tarball/Makefile "${STAGING}/${SLUG}/"
|
||||
chmod +x "${STAGING}/${SLUG}/configure"
|
||||
echo "${NVM_TEST_VERSION}" > "${STAGING}/${SLUG}/VERSION"
|
||||
if [ "${COMPRESSION}" = 'tar.xz' ]; then
|
||||
(cd "${STAGING}" && tar -cJf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}")
|
||||
else
|
||||
(cd "${STAGING}" && tar -czf "${CACHE_DIR}/${SLUG}.${COMPRESSION}" "${SLUG}")
|
||||
fi
|
||||
rm -rf "${STAGING}"
|
||||
|
||||
# Install from source, with the version specified in .nvmrc
|
||||
echo "${NVM_PREFIXED_TEST_VERSION}" > .nvmrc
|
||||
|
||||
nvm install -s --offline || die "'nvm install -s --offline' failed"
|
||||
|
||||
# Check
|
||||
nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version is not installed'
|
||||
nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" \
|
||||
|| die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed"
|
||||
|
||||
rm -rf "${CACHE_DIR}"
|
||||
Executable → Regular
+3
-21
@@ -6,36 +6,18 @@ die () { echo "$@" ; exit 1; }
|
||||
|
||||
set -ex
|
||||
|
||||
# Compiling io.js requires a period toolchain: python 2 (its `configure`),
|
||||
# and gcc <= 5 (its bundled V8). CI compiles it for real in a gcc:4.9
|
||||
# container job; on modern toolchains, this can only be skipped
|
||||
# (`install version specified in .nvmrc from fake source` covers nvm's
|
||||
# source pipeline everywhere).
|
||||
GCC_MAJOR="$(gcc -dumpversion 2>/dev/null | command cut -d. -f1)"
|
||||
if ! command -v python2 >/dev/null 2>&1 && ! python -V 2>&1 | command grep -q '^Python 2'; then
|
||||
echo 'python 2 is not available; skipping the io.js source install'
|
||||
exit 0
|
||||
fi
|
||||
case "${GCC_MAJOR}" in
|
||||
'' | *[!0-9]*) echo 'gcc is not available; skipping the io.js source install'; exit 0 ;;
|
||||
esac
|
||||
if [ "${GCC_MAJOR}" -gt 5 ]; then
|
||||
echo "gcc <= 5 is required to compile io.js, got ${GCC_MAJOR}; skipping the io.js source install"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
NVM_TEST_VERSION='v3.3.0'
|
||||
NVM_PREFIXED_TEST_VERSION="iojs-${NVM_TEST_VERSION}"
|
||||
|
||||
# Remove the stuff we're clobbering.
|
||||
nvm uninstall "${NVM_TEST_VERSION}" || echo 'not installed'
|
||||
|
||||
# Install from source, with the version specified in .nvmrc
|
||||
# Install from binary
|
||||
echo "${NVM_PREFIXED_TEST_VERSION}" > .nvmrc
|
||||
|
||||
nvm install -s || die "'nvm install -s' failed"
|
||||
nvm install -s || "'nvm install -s' failed"
|
||||
|
||||
# Check
|
||||
nvm_is_version_installed "${NVM_PREFIXED_TEST_VERSION}" || die 'version is not installed'
|
||||
nvm run --silent "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" \
|
||||
nvm run "${NVM_PREFIXED_TEST_VERSION}" --version | grep "${NVM_TEST_VERSION}" \
|
||||
|| die "'nvm run ${NVM_PREFIXED_TEST_VERSION} --version | grep ${NVM_TEST_VERSION}' failed"
|
||||
|
||||
@@ -6,8 +6,6 @@ nvm uninstall iojs-v1.0.0
|
||||
nvm uninstall iojs-v1.0.3
|
||||
nvm uninstall iojs-v3.3.0
|
||||
nvm uninstall iojs-v3.3.1
|
||||
nvm uninstall iojs-v3.99.8
|
||||
nvm uninstall iojs-v3.99.9
|
||||
|
||||
if [ -f ".nvmrc" ]; then
|
||||
rm .nvmrc
|
||||
|
||||
@@ -72,19 +72,19 @@ npm list --global | grep object-is > /dev/null || die "object-is isn't installed
|
||||
|
||||
# install a fresh global package on the source (v9.7.0) only
|
||||
nvm use 9.7.0
|
||||
npm install -g is-nan@1.0.1 || die "npm install -g is-nan failed"
|
||||
npm list --global --depth=0 | grep is-nan > /dev/null || die "is-nan isn't installed on v9.7.0"
|
||||
npm install -g is-nan@1.0.0 || die "npm install -g is-nan failed"
|
||||
npm list --global | grep is-nan > /dev/null || die "is-nan isn't installed on v9.7.0"
|
||||
|
||||
# precondition: the already-installed target (v9.10.0) must not have it yet
|
||||
nvm use 9.10.0
|
||||
npm list --global --depth=0 | grep is-nan > /dev/null && die "is-nan should not be installed on v9.10.0 before reinstall"
|
||||
npm list --global | grep is-nan > /dev/null && die "is-nan should not be installed on v9.10.0 before reinstall"
|
||||
|
||||
# target is already installed, so this should report so AND still migrate packages
|
||||
OUTPUT="$(nvm install --reinstall-packages-from=9.7.0 9.10.0 2>&1)" || die "nvm install --reinstall-packages-from=9.7.0 9.10.0 failed: ${OUTPUT}"
|
||||
nvm_echo "${OUTPUT}" | grep "is already installed" > /dev/null || die "expected 'already installed' message, got: ${OUTPUT}"
|
||||
|
||||
nvm use 9.10.0
|
||||
npm list --global --depth=0 | grep is-nan > /dev/null || die "is-nan was not migrated to already-installed v9.10.0"
|
||||
npm list --global | grep is-nan > /dev/null || die "is-nan was not migrated to already-installed v9.10.0"
|
||||
|
||||
|
||||
# ...the same is true for default packages: they must install on the already-installed path too.
|
||||
@@ -99,11 +99,11 @@ nvm_echo 'object-inspect@1.0.2' > "${DEFAULT_PACKAGES_FILE}"
|
||||
|
||||
# precondition: the already-installed target must not have the default package yet
|
||||
nvm use 9.10.0
|
||||
npm list --global --depth=0 | grep object-inspect > /dev/null && die "object-inspect should not be installed on v9.10.0 before default-packages test"
|
||||
npm list --global | grep object-inspect > /dev/null && die "object-inspect should not be installed on v9.10.0 before default-packages test"
|
||||
|
||||
nvm install 9.10.0 > /dev/null 2>&1 || die "nvm install 9.10.0 (already installed, default-packages) failed"
|
||||
nvm use 9.10.0
|
||||
npm list --global --depth=0 | grep object-inspect > /dev/null || die "default packages were not installed on already-installed v9.10.0"
|
||||
npm list --global | grep object-inspect > /dev/null || die "default packages were not installed on already-installed v9.10.0"
|
||||
|
||||
# restore the default-packages file before the next assertion
|
||||
cleanup
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
if [ "_$1" = "_-m" ]; then
|
||||
echo "loongarch64"
|
||||
else
|
||||
echo "Linux foo 6.12.54-16k #0 SMP PREEMPT_DYNAMIC Fri Dec 5 12:35:43 UTC loongarch64 GNU/Linux"
|
||||
fi
|
||||
Executable → Regular
+1
-3
@@ -6,6 +6,4 @@ die () { echo "$@" ; exit 1; }
|
||||
|
||||
nvm use 0.10
|
||||
|
||||
# note: in a BRE, `[options]` is a bracket expression, so the literal `[options]`
|
||||
# in node's help output could never match; match the unbracketed prefix instead
|
||||
nvm exec stable -- node --help | grep 'Usage: node' || die "Help menu should have been displayed for node and not nvm"
|
||||
nvm exec stable -- node --help | grep 'Usage: node [options]' || die "Help menu should have been displayed for node and not nvm"
|
||||
|
||||
Executable → Regular
+16
-37
@@ -1,51 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
# Source nvm
|
||||
\. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
# Version to install/uninstall
|
||||
NVM_TEST_VERSION=5.10.1
|
||||
|
||||
NVM_TEST_VERSION=v5.99.99
|
||||
# Make sure it's not already here
|
||||
[ -e ../../../$NVM_TEST_VERSION ] && rm -R ../../../$NVM_TEST_VERSION
|
||||
|
||||
cleanup() {
|
||||
VERSION_PATH="${VERSION_PATH-}"
|
||||
if [ -n "${VERSION_PATH}" ] && [ -d "${VERSION_PATH}" ]; then
|
||||
sudo -n rm -f "${VERSION_PATH}/lib/root-owned-file" 2>/dev/null
|
||||
rm -rf "${VERSION_PATH}"
|
||||
fi
|
||||
}
|
||||
# Install it
|
||||
nvm install $NVM_TEST_VERSION
|
||||
|
||||
# The permissions check only trips on files that are neither writable nor
|
||||
# self-owned, and creating one requires another user: skip where passwordless
|
||||
# sudo is unavailable; CI always has it.
|
||||
if ! sudo -n true 2>/dev/null; then
|
||||
echo 'passwordless sudo is not available, skipping'
|
||||
exit 0
|
||||
fi
|
||||
# Make sure it installed
|
||||
nvm ls | grep "$NVM_TEST_VERSION" || die "Failed to install node"
|
||||
|
||||
# Install a fake version to uninstall
|
||||
make_fake_node "${NVM_TEST_VERSION}" || die 'unable to make fake node'
|
||||
VERSION_PATH="$(nvm_version_path "${NVM_TEST_VERSION}")"
|
||||
# Install global module as root
|
||||
npm_path=$(which npm)
|
||||
sudo -n "$npm_path" install jspm@'<2' -g || die 'either sudo failed, or `npm install jspm -g` failed`'
|
||||
|
||||
# Make part of the installation folder unwritable, and not self-owned
|
||||
mkdir -p "${VERSION_PATH}/lib"
|
||||
sudo -n touch "${VERSION_PATH}/lib/root-owned-file" || die 'sudo touch failed'
|
||||
sudo -n chmod 444 "${VERSION_PATH}/lib/root-owned-file" || die 'sudo chmod failed'
|
||||
|
||||
# Make sure the version to uninstall is not the active one
|
||||
nvm deactivate >/dev/null 2>&1 || true
|
||||
# Switch to another version so we can uninstall
|
||||
nvm use 0.12.7
|
||||
|
||||
# Attempt to uninstall it
|
||||
RETURN_MESSAGE="$(nvm uninstall "${NVM_TEST_VERSION}" 2>&1)"
|
||||
RETURN_MESSAGE="$(nvm uninstall $NVM_TEST_VERSION 2>&1)"
|
||||
CHECK_FOR="Cannot uninstall, incorrect permissions on installation folder"
|
||||
case "${RETURN_MESSAGE}" in
|
||||
*"${CHECK_FOR}"*) ;;
|
||||
*) die "Failed to show error message; got: ${RETURN_MESSAGE}" ;;
|
||||
esac
|
||||
|
||||
# ... and the version must not have been removed
|
||||
nvm_is_version_installed "${NVM_TEST_VERSION}" || die 'version was uninstalled despite the permissions error'
|
||||
|
||||
cleanup
|
||||
test "${RETURN_MESSAGE#*$CHECK_FOR}" != "$RETURN_MESSAGE" || die "Failed to show error message"
|
||||
|
||||
Executable → Regular
@@ -2,46 +2,37 @@
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
WORK="$PWD/nvm_get_latest-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
|
||||
cleanup() {
|
||||
unset -f curl wget
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
unset -f curl wget nvm_has
|
||||
}
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
EXPECTED_VERSION="v12.3.456"
|
||||
URL="https://github.com/nvm-sh/nvm/releases/tag/$EXPECTED_VERSION"
|
||||
EXPECTED_CURL_ARGS="--compressed -q -w %{url_effective}\n -L -s -S https://latest.nvm.sh -o /dev/null"
|
||||
EXPECTED_WGET_ARGS="-q https://latest.nvm.sh --server-response -O /dev/null"
|
||||
export URL EXPECTED_CURL_ARGS EXPECTED_WGET_ARGS
|
||||
|
||||
VERSION_MESSAGE="
|
||||
curl() {
|
||||
if [ $# -eq 1 ] && [ "$1" = "-V" ]; then
|
||||
echo "
|
||||
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3
|
||||
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
|
||||
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets"
|
||||
|
||||
# fake curl/wget: `command curl` bypasses shell functions, so the mocks must be executables on PATH
|
||||
make_fake_curl "$TEST_BIN" 'if [ "_$*" != "_$EXPECTED_CURL_ARGS" ]; then
|
||||
echo >&2 "expected args ($EXPECTED_CURL_ARGS), got ($*)"
|
||||
exit 1
|
||||
fi
|
||||
echo "$URL"'
|
||||
|
||||
cat > "$TEST_BIN/wget" <<'OUTER'
|
||||
#!/bin/sh
|
||||
if [ "_$*" != "_$EXPECTED_WGET_ARGS" ]; then
|
||||
echo >&2 "expected args ($EXPECTED_WGET_ARGS), got ($*)"
|
||||
exit 1
|
||||
fi
|
||||
cat >&2 <<CONTENTS
|
||||
elif [ "_$*" != "_$EXPECTED_CURL_ARGS" ]; then
|
||||
echo >&2 "expected args ($EXPECTED_CURL_ARGS), got ($*)"
|
||||
return 1
|
||||
else
|
||||
echo $URL
|
||||
fi
|
||||
}
|
||||
wget() {
|
||||
if [ "_$*" != "_$EXPECTED_WGET_ARGS" ]; then
|
||||
echo >&2 "expected args ($EXPECTED_WGET_ARGS), got ($*)"
|
||||
return 1
|
||||
else
|
||||
local WGET_CONTENTS
|
||||
WGET_CONTENTS="
|
||||
HTTP/1.1 301 Moved Permanently
|
||||
Location: https://github.com/nvm-sh/nvm/releases/latest
|
||||
Content-Type: text/html; charset=utf-8
|
||||
@@ -91,23 +82,16 @@ cat >&2 <<CONTENTS
|
||||
X-Content-Type-Options: nosniff
|
||||
Vary: Accept-Encoding
|
||||
X-Served-By: 926b734ea1992f8ee1f88ab967a93dac
|
||||
CONTENTS
|
||||
OUTER
|
||||
chmod +x "$TEST_BIN/wget"
|
||||
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
|
||||
# shadowing shell functions, like aliases baked into a user's shell, must be
|
||||
# bypassed in favor of the executables on PATH (https://github.com/nvm-sh/nvm/issues/2923)
|
||||
curl() {
|
||||
echo >&2 'the shadowing curl function was called'
|
||||
return 1
|
||||
}
|
||||
wget() {
|
||||
echo >&2 'the shadowing wget function was called'
|
||||
return 1
|
||||
"
|
||||
"$WGET_CONTENTS" | while read line
|
||||
do
|
||||
>&2 echo "$line"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
try nvm_get_latest
|
||||
[ "_$CAPTURED_STDOUT" = "_$EXPECTED_VERSION" ] \
|
||||
|| die "success path did not return version '$EXPECTED_VERSION', got '$CAPTURED_STDOUT'"
|
||||
|
||||
@@ -2,26 +2,20 @@
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
WORK="$PWD/nvm_get_latest_failed_redirect-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
unset -f curl wget
|
||||
}
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
\. ../../common.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
# fake curl/wget: `command curl` bypasses shell functions, so the mocks must be executables on PATH
|
||||
make_fake_curl "$TEST_BIN" 'exit 1'
|
||||
printf '#!/bin/sh\nexit 1\n' > "$TEST_BIN/wget"
|
||||
chmod +x "$TEST_BIN/wget"
|
||||
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
curl() {
|
||||
return 1
|
||||
}
|
||||
wget() {
|
||||
return 1
|
||||
}
|
||||
|
||||
try_err nvm_get_latest
|
||||
[ "_$CAPTURED_STDERR" = "_https://latest.nvm.sh did not redirect to the latest release on GitHub" ] \
|
||||
|
||||
Reference in New Issue
Block a user