mirror of
https://github.com/nvm-sh/nvm.git
synced 2026-10-11 00:00:13 +08:00
Compare commits
31
Commits
09822033d2
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1b3a0038c | ||
|
|
6dc3765f6b | ||
|
|
5c05fb659c | ||
|
|
37be73f11d | ||
|
|
906fb605bc | ||
|
|
e45c2df7c0 | ||
|
|
d55a55c387 | ||
|
|
9c8cac4a2e | ||
|
|
66640e6000 | ||
|
|
1dd1d8c82e | ||
|
|
c28f2b70db | ||
|
|
f296b8d210 | ||
|
|
c7ba8d2106 | ||
|
|
d211d5fdb8 | ||
|
|
913b8cb625 | ||
|
|
f1f3d463ab | ||
|
|
077156e88f | ||
|
|
50e73c4cd5 | ||
|
|
ed1b96f5db | ||
|
|
5326eaab9d | ||
|
|
a27fb0bd78 | ||
|
|
cb96d2a56a | ||
|
|
46ca42f939 | ||
|
|
52a1e55574 | ||
|
|
79bc9b90dd | ||
|
|
e2f57b733f | ||
|
|
0add6b8555 | ||
|
|
3bfc2b4a9a | ||
|
|
04ac3f2a83 | ||
|
|
a8bb497400 | ||
|
|
04fbd9570c |
@@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Install apt packages, riding out Ubuntu/Debian mirror outages: apt itself
|
||||
# retries each request, and the whole update+install is retried with backoff
|
||||
# for about 10 minutes, since outages have lasted longer than apt's own retries.
|
||||
#
|
||||
# usage: sh .github/scripts/apt-install.sh <package>...
|
||||
# Runs apt-get through sudo when not root.
|
||||
|
||||
set -u
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo 'usage: apt-install.sh <package>...' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
SUDO=''
|
||||
if [ "$(id -u)" != '0' ]; then
|
||||
SUDO='sudo'
|
||||
fi
|
||||
|
||||
apt_get() {
|
||||
# never prompt: an upgrade (e.g. libssl asking to restart services) would wait forever
|
||||
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get \
|
||||
-o Acquire::Retries=5 \
|
||||
-o Acquire::http::Timeout=30 \
|
||||
-o Acquire::https::Timeout=30 \
|
||||
-o Dpkg::Options::=--force-confdef \
|
||||
-o Dpkg::Options::=--force-confold \
|
||||
"$@" < /dev/null
|
||||
}
|
||||
|
||||
ATTEMPT=1
|
||||
MAX_ATTEMPTS=8
|
||||
DELAY=15
|
||||
while :; do
|
||||
apt_get update || echo "apt-get update failed (attempt ${ATTEMPT}/${MAX_ATTEMPTS})" >&2
|
||||
if apt_get install -y "$@"; then
|
||||
exit 0
|
||||
fi
|
||||
if [ "${ATTEMPT}" -ge "${MAX_ATTEMPTS}" ]; then
|
||||
echo "apt-get install failed after ${MAX_ATTEMPTS} attempts: $*" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "apt-get install failed (attempt ${ATTEMPT}/${MAX_ATTEMPTS}); retrying in ${DELAY}s" >&2
|
||||
sleep "${DELAY}"
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
DELAY=$((DELAY * 2))
|
||||
if [ "${DELAY}" -gt 120 ]; then
|
||||
DELAY=120
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Cache a container image as a tarball, keyed by digests looked up live from the registry: a cached
|
||||
# image is only used if it is exactly what the registry serves now, so a poisoned cache entry is never used.
|
||||
#
|
||||
# usage:
|
||||
# sh .github/scripts/docker-image.sh resolve <image>
|
||||
# prints `key=<config digest>` and `digests=<index, manifest, and config digests>`, for $GITHUB_OUTPUT
|
||||
# DIGESTS='<digests>' sh .github/scripts/docker-image.sh ensure <image> <tarball>
|
||||
# loads <tarball> if it holds one of $DIGESTS; otherwise pulls <image>, checks it, and saves it to <tarball>
|
||||
|
||||
set -eu
|
||||
|
||||
ACCEPT='application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json'
|
||||
|
||||
sha256_of() {
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$1"
|
||||
else
|
||||
shasum -a 256 "$1"
|
||||
fi | cut -d ' ' -f 1
|
||||
}
|
||||
|
||||
# fetches a manifest into a file; digests are computed from its exact bytes
|
||||
registry_get() {
|
||||
curl -fsSL --retry 5 --retry-all-errors -H "Accept: ${ACCEPT}" -o "$2" "$1"
|
||||
}
|
||||
|
||||
resolve() {
|
||||
IMAGE="$1"
|
||||
HOST="${IMAGE%%/*}"
|
||||
REST="${IMAGE#*/}"
|
||||
REPO="${REST%:*}"
|
||||
TAG="${REST##*:}"
|
||||
OS="$(docker version --format '{{.Server.Os}}')"
|
||||
ARCH="$(docker version --format '{{.Server.Arch}}')"
|
||||
|
||||
WORK="$(mktemp -d)"
|
||||
trap 'rm -rf "${WORK}"' EXIT
|
||||
|
||||
registry_get "https://${HOST}/v2/${REPO}/manifests/${TAG}" "${WORK}/index.json"
|
||||
INDEX_DIGEST="sha256:$(sha256_of "${WORK}/index.json")"
|
||||
|
||||
if jq -e '.manifests' "${WORK}/index.json" >/dev/null; then
|
||||
MANIFEST_DIGEST="$(jq -r --arg os "${OS}" --arg arch "${ARCH}" '[.manifests[] | select(.platform.os == $os and .platform.architecture == $arch)][0].digest // empty' "${WORK}/index.json")"
|
||||
if [ -z "${MANIFEST_DIGEST}" ]; then
|
||||
echo "${IMAGE} has no ${OS}/${ARCH} image" >&2
|
||||
exit 1
|
||||
fi
|
||||
registry_get "https://${HOST}/v2/${REPO}/manifests/${MANIFEST_DIGEST}" "${WORK}/manifest.json"
|
||||
if [ "sha256:$(sha256_of "${WORK}/manifest.json")" != "${MANIFEST_DIGEST}" ]; then
|
||||
echo "the ${OS}/${ARCH} manifest of ${IMAGE} does not match its digest" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
MANIFEST_DIGEST="${INDEX_DIGEST}"
|
||||
cp "${WORK}/index.json" "${WORK}/manifest.json"
|
||||
fi
|
||||
|
||||
CONFIG_DIGEST="$(jq -r '.config.digest' "${WORK}/manifest.json")"
|
||||
echo "key=${CONFIG_DIGEST}"
|
||||
echo "digests=${INDEX_DIGEST} ${MANIFEST_DIGEST} ${CONFIG_DIGEST}"
|
||||
}
|
||||
|
||||
# an image's ID is its config digest (or, with the containerd image store, its manifest or index digest)
|
||||
image_matches() {
|
||||
ID="$(docker image inspect --format '{{.Id}}' "$1" 2>/dev/null)" || return 1
|
||||
case " ${DIGESTS} " in
|
||||
*" ${ID} "*) return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
ensure() {
|
||||
IMAGE="$1"
|
||||
TARBALL="$2"
|
||||
|
||||
if [ -f "${TARBALL}" ]; then
|
||||
if docker load -i "${TARBALL}" && image_matches "${IMAGE}"; then
|
||||
echo "loaded ${IMAGE} from the cache"
|
||||
return 0
|
||||
fi
|
||||
echo "::warning::the cached ${IMAGE} does not match the registry's digests; pulling it instead"
|
||||
docker image rm -f "${IMAGE}" >/dev/null 2>&1 || true
|
||||
rm -f "${TARBALL}"
|
||||
fi
|
||||
|
||||
ATTEMPT=1
|
||||
until docker pull "${IMAGE}"; do
|
||||
if [ "${ATTEMPT}" -ge 5 ]; then
|
||||
echo "docker pull ${IMAGE} failed after ${ATTEMPT} attempts" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "docker pull failed, attempt ${ATTEMPT}/5"
|
||||
sleep $((ATTEMPT * 5))
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
done
|
||||
|
||||
# the tag can move between `resolve` and the pull; then this run uses the image, but does not cache it
|
||||
if image_matches "${IMAGE}"; then
|
||||
docker save -o "${TARBALL}" "${IMAGE}"
|
||||
else
|
||||
echo "::warning::the pulled ${IMAGE} no longer matches the digests looked up earlier; not caching it"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1-}" in
|
||||
resolve) resolve "${2-}" ;;
|
||||
ensure) ensure "${2-}" "${3-}" ;;
|
||||
*)
|
||||
echo 'usage: docker-image.sh resolve <image> | ensure <image> <tarball>' >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -46,6 +46,7 @@ jobs:
|
||||
dl-cdn.alpinelinux.org:80
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
mirror.gcr.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
- uses: actions/checkout@v6
|
||||
@@ -66,19 +67,29 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
skip-ls-check: true
|
||||
- run: npm ls urchin
|
||||
# Pull through Google's Docker Hub mirror, which is not subject to Docker Hub's
|
||||
# anonymous pull rate limits, and cache the image; a cached image is only used
|
||||
# if it matches the digests the registry serves now
|
||||
- name: 'Resolve the alpine:${{ matrix.alpine }} image digests'
|
||||
id: image
|
||||
run: sh .github/scripts/docker-image.sh resolve mirror.gcr.io/library/alpine:${{ matrix.alpine }} >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ runner.temp }}/docker-image.tar
|
||||
key: docker-image-${{ runner.arch }}-${{ steps.image.outputs.key }}
|
||||
- name: 'Load or pull the alpine:${{ matrix.alpine }} image'
|
||||
env:
|
||||
DIGESTS: ${{ steps.image.outputs.digests }}
|
||||
run: sh .github/scripts/docker-image.sh ensure mirror.gcr.io/library/alpine:${{ matrix.alpine }} "${RUNNER_TEMP}/docker-image.tar"
|
||||
- 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 }} \
|
||||
mirror.gcr.io/library/alpine:${{ matrix.alpine }} \
|
||||
sh -c '
|
||||
set -ex
|
||||
cat /etc/alpine-release; uname -m
|
||||
@@ -142,23 +153,34 @@ jobs:
|
||||
dl-cdn.alpinelinux.org:80
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
mirror.gcr.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: false
|
||||
# Pull through Google's Docker Hub mirror, which is not subject to Docker Hub's
|
||||
# anonymous pull rate limits, and cache the image; a cached image is only used
|
||||
# if it matches the digests the registry serves now
|
||||
- name: 'Resolve the alpine:${{ matrix.alpine }} image digests'
|
||||
id: image
|
||||
run: sh .github/scripts/docker-image.sh resolve mirror.gcr.io/library/alpine:${{ matrix.alpine }} >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ runner.temp }}/docker-image.tar
|
||||
key: docker-image-${{ runner.arch }}-${{ steps.image.outputs.key }}
|
||||
- name: 'Load or pull the alpine:${{ matrix.alpine }} image'
|
||||
env:
|
||||
DIGESTS: ${{ steps.image.outputs.digests }}
|
||||
run: sh .github/scripts/docker-image.sh ensure mirror.gcr.io/library/alpine:${{ matrix.alpine }} "${RUNNER_TEMP}/docker-image.tar"
|
||||
- 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 }} \
|
||||
mirror.gcr.io/library/alpine:${{ matrix.alpine }} \
|
||||
sh -c '
|
||||
set -ex
|
||||
cat /etc/alpine-release; uname -m
|
||||
|
||||
@@ -6,7 +6,7 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
fast:
|
||||
fast-linux:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -60,11 +60,11 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
- name: Install zsh, additional shells, and awk variant
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y zsh ${{ matrix.awk }}
|
||||
PACKAGES="zsh ${{ matrix.awk }}"
|
||||
if [ "${{ matrix.shell }}" != "sh" ] && [ "${{ matrix.shell }}" != "bash" ] && [ "${{ matrix.shell }}" != "zsh" ]; then
|
||||
sudo apt-get install -y ${{ matrix.shell }}
|
||||
PACKAGES="${PACKAGES} ${{ matrix.shell }}"
|
||||
fi
|
||||
sh .github/scripts/apt-install.sh ${PACKAGES}
|
||||
# Set the selected awk as the default
|
||||
sudo update-alternatives --set awk /usr/bin/${{ matrix.awk }}
|
||||
shell: bash
|
||||
@@ -101,11 +101,140 @@ jobs:
|
||||
if [ -f /usr/local/bin/npx.bak ]; then sudo mv /usr/local/bin/npx.bak /usr/local/bin/npx; fi
|
||||
shell: bash
|
||||
|
||||
fast-macos:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# APFS is case-insensitive, and /bin/sh and /bin/bash are bash 3.2
|
||||
name: 'fast (macOS, ${{ matrix.shell }})'
|
||||
runs-on: macos-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shell:
|
||||
- sh
|
||||
- bash
|
||||
- dash
|
||||
- zsh
|
||||
|
||||
steps:
|
||||
- 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
|
||||
- run: sw_vers; uname -m; which ${{ matrix.shell }}
|
||||
- name: Ensure wget is installed
|
||||
run: command -v wget || brew install wget
|
||||
- run: wget --version
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'npm install && version checks'
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
skip-ls-check: true
|
||||
- run: npm ls urchin
|
||||
- name: Hide system node
|
||||
shell: bash
|
||||
run: |
|
||||
export PATH="$(echo "$PATH" | tr ':' '\n' | grep -v '\.nvm' | grep -v 'toolcache' | tr '\n' ':')"
|
||||
for bin in node npm npx; do
|
||||
while found="$(command -v "${bin}")"; do
|
||||
sudo mv "${found}" "${found}.bak" || break
|
||||
echo "${found}" >> "${RUNNER_TEMP}/hidden-node"
|
||||
hash -r
|
||||
done
|
||||
done
|
||||
- name: Run fast tests
|
||||
shell: bash
|
||||
run: |
|
||||
unset NVM_CD_FLAGS NVM_BIN NVM_INC
|
||||
export NVM_DIR="${{ github.workspace }}"
|
||||
export PATH="$(echo "$PATH" | tr ':' '\n' | grep -v '\.nvm' | grep -v 'toolcache' | tr '\n' ':')"
|
||||
script -q /dev/null make TERM=xterm-256color TEST_SUITE="fast" SHELL="${{ matrix.shell }}" URCHIN="${{ github.workspace }}/node_modules/.bin/urchin" test-${{ matrix.shell }} < /dev/null
|
||||
- name: Restore system node
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -f "${RUNNER_TEMP}/hidden-node" ]; then
|
||||
while IFS= read -r found; do sudo mv "${found}.bak" "${found}"; done < "${RUNNER_TEMP}/hidden-node"
|
||||
fi
|
||||
|
||||
fast-windows:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# GitHub's Windows runners provide WSL1
|
||||
name: 'fast (WSL ${{ matrix.wsl-distrib }}, ${{ matrix.shell }})'
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: wsl-bash {0}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
wsl-distrib:
|
||||
- Ubuntu-24.04
|
||||
shell:
|
||||
- sh
|
||||
- bash
|
||||
- dash
|
||||
- zsh
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: Vampire/setup-wsl@v7
|
||||
with:
|
||||
distribution: ${{ matrix.wsl-distrib }}
|
||||
- name: Install packages with retries
|
||||
# the Windows checkout has CRLF line endings
|
||||
run: |
|
||||
tr -d '\r' < "$(wslpath "${{ github.workspace }}")/.github/scripts/apt-install.sh" > /tmp/apt-install.sh
|
||||
sh /tmp/apt-install.sh git make zsh dash sudo curl wget ca-certificates nodejs npm
|
||||
- name: 'Clone into the WSL filesystem as a non-root user'
|
||||
# the Windows checkout shows every file as executable, which urchin would run as tests
|
||||
run: |
|
||||
set -ex
|
||||
useradd -m tester
|
||||
echo 'tester ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/tester
|
||||
chmod 0440 /etc/sudoers.d/tester
|
||||
su tester -c '
|
||||
set -ex
|
||||
git config --global --add safe.directory "*"
|
||||
git clone "$(wslpath "${{ github.workspace }}")" ~/nvm
|
||||
cd ~/nvm
|
||||
git submodule set-url test/fixtures/nvmrc https://github.com/nvm-sh/nvmrc.git
|
||||
git submodule update --init --recursive
|
||||
npm install
|
||||
npm ls urchin
|
||||
'
|
||||
- name: Hide system node
|
||||
run: |
|
||||
for bin in node npm npx; do
|
||||
if [ -e "/usr/bin/${bin}" ]; then mv "/usr/bin/${bin}" "/usr/bin/${bin}.bak"; fi
|
||||
done
|
||||
- name: Run fast tests
|
||||
run: |
|
||||
su tester -c '
|
||||
cd ~/nvm
|
||||
unset NVM_CD_FLAGS NVM_BIN NVM_INC
|
||||
export NVM_DIR="${HOME}/nvm" GITHUB_ACTIONS=true TERM=xterm-256color
|
||||
script -q -e -c "make TEST_SUITE=fast SHELL=${{ matrix.shell }} URCHIN=${HOME}/nvm/node_modules/.bin/urchin test-${{ matrix.shell }}" /dev/null
|
||||
'
|
||||
|
||||
all:
|
||||
permissions:
|
||||
contents: none
|
||||
name: 'all fast tests'
|
||||
needs: [fast]
|
||||
needs: [fast-linux, fast-macos, fast-windows]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: true
|
||||
|
||||
@@ -52,11 +52,11 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
- name: Install zsh and additional shells
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y zsh
|
||||
PACKAGES='zsh'
|
||||
if [ "${{ matrix.shell }}" != "sh" ] && [ "${{ matrix.shell }}" != "bash" ] && [ "${{ matrix.shell }}" != "zsh" ]; then
|
||||
sudo apt-get install -y ${{ matrix.shell }}
|
||||
PACKAGES="${PACKAGES} ${{ matrix.shell }}"
|
||||
fi
|
||||
sh .github/scripts/apt-install.sh ${PACKAGES}
|
||||
shell: bash
|
||||
- run: sudo ${{ matrix.shell }} --version 2> /dev/null || dpkg -s ${{ matrix.shell }} 2> /dev/null || which ${{ matrix.shell }}
|
||||
- run: wget --version
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
shell: bash
|
||||
- name: Restore curl
|
||||
if: always()
|
||||
run: sudo apt-get install curl -y
|
||||
run: sh .github/scripts/apt-install.sh curl
|
||||
shell: bash
|
||||
|
||||
installation_iojs_source_compile:
|
||||
|
||||
@@ -44,6 +44,7 @@ jobs:
|
||||
production.cloudfront.docker.com:443
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
mirror.gcr.io:443
|
||||
- uses: actions/checkout@v6
|
||||
id: checkout
|
||||
continue-on-error: true
|
||||
@@ -63,14 +64,22 @@ jobs:
|
||||
skip-ls-check: true
|
||||
- run: npm ls urchin
|
||||
- run: npx which urchin
|
||||
# Pull through Google's Docker Hub mirror, which is not subject to Docker Hub's
|
||||
# anonymous pull rate limits, and cache the image; a cached image is only used
|
||||
# if it matches the digests the registry serves now
|
||||
- name: Resolve the ubuntu:16.04 image digests
|
||||
id: image
|
||||
run: sh .github/scripts/docker-image.sh resolve mirror.gcr.io/library/ubuntu:16.04 >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ runner.temp }}/docker-image.tar
|
||||
key: docker-image-${{ runner.arch }}-${{ steps.image.outputs.key }}
|
||||
- name: Load or pull the ubuntu:16.04 image
|
||||
env:
|
||||
DIGESTS: ${{ steps.image.outputs.digests }}
|
||||
run: sh .github/scripts/docker-image.sh ensure mirror.gcr.io/library/ubuntu:16.04 "${RUNNER_TEMP}/docker-image.tar"
|
||||
- name: Run installation_node tests in container
|
||||
run: |
|
||||
# Retry the image pull to tolerate transient Docker registry failures
|
||||
for i in 1 2 3 4 5; do
|
||||
docker pull ubuntu:16.04 && break
|
||||
echo "docker pull failed, attempt $i/5"
|
||||
sleep $((i * 5))
|
||||
done
|
||||
docker run --rm \
|
||||
-v "${{ github.workspace }}:/workspace" \
|
||||
-w /workspace \
|
||||
@@ -79,25 +88,13 @@ jobs:
|
||||
-e "DEBIAN_FRONTEND=noninteractive" \
|
||||
-e "GITHUB_ACTIONS=true" \
|
||||
-e "WITHOUT_CURL=${{ matrix.without_curl }}" \
|
||||
ubuntu:16.04 \
|
||||
mirror.gcr.io/library/ubuntu:16.04 \
|
||||
bash -c '
|
||||
set -ex
|
||||
|
||||
# Retry apt-get update up to 5 times due to flaky Ubuntu mirrors
|
||||
# apt-get update can return 0 even with partial failures, so check for warnings
|
||||
for i in 1 2 3 4 5; do
|
||||
if apt-get update 2>&1 | tee /tmp/apt-update.log | grep -qE "^(W:|E:|Err:)"; then
|
||||
echo "apt-get update had warnings/errors, attempt $i/5"
|
||||
cat /tmp/apt-update.log
|
||||
sleep $((i * 5))
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
apt-get install -y git curl wget make build-essential python zsh libssl-dev
|
||||
sh /workspace/.github/scripts/apt-install.sh git curl wget make build-essential python zsh libssl-dev
|
||||
if [ "$TEST_SHELL" != "sh" ] && [ "$TEST_SHELL" != "bash" ]; then
|
||||
apt-get install -y $TEST_SHELL || true
|
||||
sh /workspace/.github/scripts/apt-install.sh $TEST_SHELL || true
|
||||
fi
|
||||
|
||||
# Use nvm to install Node.js for running urchin
|
||||
|
||||
@@ -41,6 +41,7 @@ jobs:
|
||||
production.cloudfront.docker.com:443
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
mirror.gcr.io:443
|
||||
- uses: actions/checkout@v6
|
||||
id: checkout
|
||||
continue-on-error: true
|
||||
@@ -60,14 +61,22 @@ jobs:
|
||||
skip-ls-check: true
|
||||
- run: npm ls urchin
|
||||
- run: npx which urchin
|
||||
# Pull through Google's Docker Hub mirror, which is not subject to Docker Hub's
|
||||
# anonymous pull rate limits, and cache the image; a cached image is only used
|
||||
# if it matches the digests the registry serves now
|
||||
- name: Resolve the ubuntu:16.04 image digests
|
||||
id: image
|
||||
run: sh .github/scripts/docker-image.sh resolve mirror.gcr.io/library/ubuntu:16.04 >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ runner.temp }}/docker-image.tar
|
||||
key: docker-image-${{ runner.arch }}-${{ steps.image.outputs.key }}
|
||||
- name: Load or pull the ubuntu:16.04 image
|
||||
env:
|
||||
DIGESTS: ${{ steps.image.outputs.digests }}
|
||||
run: sh .github/scripts/docker-image.sh ensure mirror.gcr.io/library/ubuntu:16.04 "${RUNNER_TEMP}/docker-image.tar"
|
||||
- name: Run xenial tests in container
|
||||
run: |
|
||||
# Retry the image pull to tolerate transient Docker registry failures
|
||||
for i in 1 2 3 4 5; do
|
||||
docker pull ubuntu:16.04 && break
|
||||
echo "docker pull failed, attempt $i/5"
|
||||
sleep $((i * 5))
|
||||
done
|
||||
docker run --rm \
|
||||
-v "${{ github.workspace }}:/workspace" \
|
||||
-w /workspace \
|
||||
@@ -75,25 +84,13 @@ jobs:
|
||||
-e "TERM=xterm-256color" \
|
||||
-e "DEBIAN_FRONTEND=noninteractive" \
|
||||
-e "GITHUB_ACTIONS=true" \
|
||||
ubuntu:16.04 \
|
||||
mirror.gcr.io/library/ubuntu:16.04 \
|
||||
bash -c '
|
||||
set -ex
|
||||
|
||||
# Retry apt-get update up to 5 times due to flaky Ubuntu mirrors
|
||||
# apt-get update can return 0 even with partial failures, so check for warnings
|
||||
for i in 1 2 3 4 5; do
|
||||
if apt-get update 2>&1 | tee /tmp/apt-update.log | grep -qE "^(W:|E:|Err:)"; then
|
||||
echo "apt-get update had warnings/errors, attempt $i/5"
|
||||
cat /tmp/apt-update.log
|
||||
sleep $((i * 5))
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
apt-get install -y git curl wget make build-essential python zsh libssl-dev
|
||||
sh /workspace/.github/scripts/apt-install.sh git curl wget make build-essential python zsh libssl-dev
|
||||
if [ "$TEST_SHELL" != "sh" ] && [ "$TEST_SHELL" != "bash" ]; then
|
||||
apt-get install -y $TEST_SHELL || true
|
||||
sh /workspace/.github/scripts/apt-install.sh $TEST_SHELL || true
|
||||
fi
|
||||
|
||||
# Use nvm to install Node.js for running urchin
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
azure.archive.ubuntu.com:80
|
||||
packages.microsoft.com:443
|
||||
- uses: actions/checkout@v6
|
||||
- run: sudo apt-get update; sudo apt-get install ${{ matrix.shell }}
|
||||
- run: sh .github/scripts/apt-install.sh ${{ matrix.shell }}
|
||||
if: matrix.shell == 'zsh' || matrix.shell == 'ksh'
|
||||
# zsh (https://github.com/actions/runner-images/issues/264) and ksh are not in the ubuntu image
|
||||
shell: bash
|
||||
|
||||
@@ -145,12 +145,37 @@ jobs:
|
||||
- ''
|
||||
- 'script'
|
||||
steps:
|
||||
# For Ubuntu: install with packages directly
|
||||
# For Ubuntu: `additional-packages` can't retry, so install in a separate step
|
||||
- if: ${{ !startsWith(matrix.wsl-distrib, 'Debian') }}
|
||||
uses: Vampire/setup-wsl@v7
|
||||
with:
|
||||
distribution: ${{ matrix.wsl-distrib }}
|
||||
additional-packages: bash git curl ca-certificates wget
|
||||
# mirror outages can outlast apt's own retries, so retry update+install with
|
||||
# backoff for about 10 minutes (as .github/scripts/apt-install.sh does; this
|
||||
# job has no checkout)
|
||||
- if: ${{ !startsWith(matrix.wsl-distrib, 'Debian') }}
|
||||
name: 'Install packages with retries'
|
||||
shell: 'wsl-bash {0}'
|
||||
run: |
|
||||
# never prompt: an upgrade (e.g. libssl asking to restart services) would wait forever
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
attempt=1
|
||||
delay=15
|
||||
while true; do
|
||||
apt-get -o Acquire::Retries=5 update < /dev/null || true
|
||||
if apt-get -o Acquire::Retries=5 -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold install --yes bash git curl ca-certificates wget < /dev/null; then
|
||||
break
|
||||
fi
|
||||
if [ "${attempt}" -ge 8 ]; then
|
||||
echo "apt-get install failed after ${attempt} attempts" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "apt-get install failed (attempt ${attempt}/8); retrying in ${delay}s" >&2
|
||||
sleep "${delay}"
|
||||
attempt=$((attempt + 1))
|
||||
delay=$((delay * 2))
|
||||
if [ "${delay}" -gt 120 ]; then delay=120; fi
|
||||
done
|
||||
|
||||
# For Debian, install without packages: `additional-packages` would run
|
||||
# `apt-get update` before we get to fix up sources.list
|
||||
@@ -204,14 +229,17 @@ jobs:
|
||||
}
|
||||
done
|
||||
}
|
||||
# never prompt: an upgrade (e.g. libssl asking to restart services) would wait forever
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
packages=(bash git curl ca-certificates wget)
|
||||
retry apt-get update || true
|
||||
if ! retry apt-get install --yes "${packages[@]}"; then
|
||||
install_options=(-o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold install --yes)
|
||||
retry apt-get update < /dev/null || true
|
||||
if ! retry apt-get "${install_options[@]}" "${packages[@]}" < /dev/null; then
|
||||
# a security index can outlive the pool objects it advertises, which
|
||||
# no retry resolves; main is complete once a release is archived
|
||||
sed -i '/debian-security/d' /etc/apt/sources.list
|
||||
retry apt-get update || true
|
||||
retry apt-get install --yes "${packages[@]}"
|
||||
retry apt-get update < /dev/null || true
|
||||
retry apt-get "${install_options[@]}" "${packages[@]}" < /dev/null
|
||||
fi
|
||||
|
||||
- name: Retrieve nvm on WSL
|
||||
+3
-3
@@ -15,14 +15,14 @@ LABEL version="latest"
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
# Prevent dialog during apt install
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# ShellCheck version
|
||||
ENV SHELLCHECK_VERSION=0.7.0
|
||||
|
||||
# Pick a Ubuntu apt mirror site for better speed
|
||||
# ref: https://launchpad.net/ubuntu/+archivemirrors
|
||||
ENV UBUNTU_APT_SITE ubuntu.cs.utah.edu
|
||||
ENV UBUNTU_APT_SITE=ubuntu.cs.utah.edu
|
||||
|
||||
# Replace origin apt package site with the mirror site
|
||||
RUN sed -E -i "s/([a-z]+.)?archive.ubuntu.com/$UBUNTU_APT_SITE/g" /etc/apt/sources.list
|
||||
@@ -92,7 +92,7 @@ RUN echo 'nvm ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
|
||||
USER nvm
|
||||
|
||||
# Create a script file sourced by both interactive and non-interactive bash shells
|
||||
ENV BASH_ENV /home/nvm/.bash_env
|
||||
ENV BASH_ENV=/home/nvm/.bash_env
|
||||
RUN touch "$BASH_ENV"
|
||||
RUN echo '. "$BASH_ENV"' >> "$HOME/.bashrc"
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ When invoking bash as a non-interactive shell, like in a [Docker](https://www.do
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
# Create a script file sourced by both interactive and non-interactive bash shells
|
||||
ENV BASH_ENV "${HOME}/.bash_env"
|
||||
ENV BASH_ENV="${HOME:-/root}/.bash_env"
|
||||
RUN touch "${BASH_ENV}"
|
||||
RUN echo '. "${BASH_ENV}"' >> ~/.bashrc
|
||||
|
||||
@@ -174,7 +174,7 @@ RUN apt update && apt install curl -y
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash
|
||||
|
||||
# set env
|
||||
ENV NVM_DIR=/root/.nvm
|
||||
ENV NVM_DIR="${HOME:-/root}/.nvm"
|
||||
|
||||
# install node
|
||||
RUN bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION"
|
||||
@@ -401,6 +401,15 @@ You can list available versions using `ls-remote`:
|
||||
nvm ls-remote
|
||||
```
|
||||
|
||||
When no version or `--lts` filter is given, the list also marks the version the `node` alias would install, and every version that one of your own aliases would resolve to. Each kind of annotation always starts in the same column, so the output can be sliced with `cut -c` or similar tools:
|
||||
|
||||
```
|
||||
v20.9.0 (Latest LTS: Iron) (Aliases: work)
|
||||
v21.0.0
|
||||
v21.1.0
|
||||
v21.2.0 (Latest: node) (Aliases: default)
|
||||
```
|
||||
|
||||
And then in any new shell just use the installed version:
|
||||
|
||||
```sh
|
||||
@@ -886,6 +895,12 @@ Run all of the tests like this:
|
||||
|
||||
npm test
|
||||
|
||||
`npm run test/fast` runs `make` inside a clean environment (`env -i ... bash -lc`), so `node` must be on the `PATH` of a fresh login `bash` shell.
|
||||
If `node` is only available through `nvm` in your current shell, it fails with "Did you forget to run `npm install` after cloning the repo?", even after a successful `npm install`.
|
||||
In that case, run `make` directly from the root of the nvm git repository, choosing the test suite and shell:
|
||||
|
||||
make TEST_SUITE=fast test-zsh
|
||||
|
||||
Nota bene: Avoid running nvm while the tests are running.
|
||||
|
||||
## Environment variables
|
||||
|
||||
@@ -490,6 +490,9 @@ nvm_do_install() {
|
||||
# shellcheck disable=SC2016
|
||||
if ${BASH_OR_ZSH} && ! command grep -qc '$NVM_DIR/bash_completion' "$NVM_PROFILE"; then
|
||||
nvm_echo "=> Appending bash_completion source string to $NVM_PROFILE"
|
||||
if [ -n "$(command tail -c 1 "$NVM_PROFILE")" ]; then
|
||||
nvm_echo >> "$NVM_PROFILE"
|
||||
fi
|
||||
command printf '%b' "$COMPLETION_STR" >> "$NVM_PROFILE"
|
||||
else
|
||||
nvm_echo "=> bash_completion source string already in ${NVM_PROFILE}"
|
||||
|
||||
@@ -22,15 +22,32 @@ nvm_stdout_is_terminal() {
|
||||
}
|
||||
|
||||
nvm_echo() {
|
||||
command printf %s\\n "$*" 2>/dev/null
|
||||
# in zsh, `command printf` skips the builtin and runs `/usr/bin/printf`
|
||||
if [ -n "${ZSH_VERSION-}" ]; then
|
||||
# shellcheck disable=SC3044
|
||||
builtin printf %s\\n "$*" 2>/dev/null
|
||||
else
|
||||
command printf %s\\n "$*" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
nvm_echo_with_colors() {
|
||||
command printf %b\\n "$*" 2>/dev/null
|
||||
if [ -n "${ZSH_VERSION-}" ]; then
|
||||
# shellcheck disable=SC3044
|
||||
builtin printf %b\\n "$*" 2>/dev/null
|
||||
else
|
||||
command printf %b\\n "$*" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
nvm_cd() {
|
||||
\cd "$@"
|
||||
# `\cd` still runs a user-defined `cd` function; in zsh, `command cd` runs `/usr/bin/cd` instead of the builtin
|
||||
if [ -n "${ZSH_VERSION-}" ]; then
|
||||
# shellcheck disable=SC3044
|
||||
builtin cd "$@"
|
||||
else
|
||||
command cd "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# a caller that closed stderr, rather than redirecting it to /dev/null, makes
|
||||
@@ -101,6 +118,11 @@ nvm_has_colors() {
|
||||
[ -t 1 ] && [ "${NVM_NUM_COLORS:--1}" -ge 8 ] && [ "${NVM_NO_COLORS-}" != '--no-colors' ]
|
||||
}
|
||||
|
||||
# terminals without italics may render the italic escape as reverse video instead, so check terminfo's `sitm` capability
|
||||
nvm_has_italics() {
|
||||
nvm_has_colors && command tput -T "${TERM:-vt100}" sitm >/dev/null 2>&1
|
||||
}
|
||||
|
||||
nvm_curl_libz_support() {
|
||||
command curl -V 2>/dev/null | nvm_grep "^Features:" | nvm_grep -q "libz"
|
||||
}
|
||||
@@ -207,10 +229,43 @@ nvm_has_system_iojs() {
|
||||
[ "$(nvm deactivate >/dev/null 2>&1 && command -v iojs)" != '' ]
|
||||
}
|
||||
|
||||
nvm_is_installed_exact_version() {
|
||||
case "${1-}" in
|
||||
v*) ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
case "${1#v}" in
|
||||
*[!0-9.]* | .* | *. | *..* | *.*.*.*) return 1 ;;
|
||||
*.*.*) ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
# an alias with this exact name takes precedence, as it does in `nvm_version`
|
||||
if [ -e "$(nvm_alias_path)/${1}" ]; then
|
||||
return 1
|
||||
fi
|
||||
nvm_is_version_installed "${1}"
|
||||
}
|
||||
|
||||
nvm_is_version_installed() {
|
||||
if [ -z "${1-}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Fast path: versions in the modern layout live at a directly-computable
|
||||
# path. Checking it first avoids forking `nvm_version_path` (and the `awk`
|
||||
# version comparison inside it) and `nvm_get_os` on shell startup. Versions
|
||||
# below `v0.12` are skipped, since they must not count from the modern
|
||||
# layout; those, io.js, and Windows binaries fall through to the full lookup
|
||||
# below, which also rejects path traversal.
|
||||
case "/${1}/" in
|
||||
*/../* | /v0.[0-9].* | /v0.1[01].* | /0.[0-9].* | /0.1[01].*) ;;
|
||||
*)
|
||||
if [ -x "${NVM_DIR}/versions/node/${1}/bin/node" ]; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
local NVM_NODE_BINARY
|
||||
NVM_NODE_BINARY='node'
|
||||
if [ "_$(nvm_get_os)" = '_win' ]; then
|
||||
@@ -547,9 +602,8 @@ else
|
||||
fi
|
||||
unset NVM_SCRIPT_SOURCE 2>/dev/null
|
||||
|
||||
# Performs pure in-memory POSIX path containment checking without subshell process forks.
|
||||
# Uses case-guarded ${pathdir%/*} for parent-walk and exact string equality (=) to ensure literal matching
|
||||
# for directory names containing glob metacharacters (*, ?, []) across all shells including zsh.
|
||||
# Walks parent paths without subshells, comparing names literally before checking
|
||||
# filesystem identity to account for case-insensitive paths and directory symlinks.
|
||||
nvm_tree_contains_path() {
|
||||
local tree
|
||||
tree="${1-}"
|
||||
@@ -572,12 +626,11 @@ nvm_tree_contains_path() {
|
||||
clean_tree='/'
|
||||
fi
|
||||
|
||||
# Pure in-memory POSIX parent-walk using parameter expansion instead of subshell dirname forks.
|
||||
# Uses literal string equality [ "${pathdir}" = "${clean_tree}" ] to prevent glob expansion bugs.
|
||||
# Keep literal matching for paths that do not exist, including glob characters.
|
||||
local pathdir
|
||||
pathdir="${node_path}"
|
||||
while [ "${pathdir}" != '' ] && [ "${pathdir}" != '.' ] && [ "${pathdir}" != '/' ] &&
|
||||
[ "${pathdir}" != "${clean_tree}" ]; do
|
||||
[ "${pathdir}" != "${clean_tree}" ] && ! [ "${pathdir}" -ef "${clean_tree}" ]; do
|
||||
case "${pathdir}" in
|
||||
*/*)
|
||||
pathdir="${pathdir%/*}"
|
||||
@@ -590,7 +643,7 @@ nvm_tree_contains_path() {
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[ "${pathdir}" = "${clean_tree}" ]
|
||||
[ "${pathdir}" = "${clean_tree}" ] || [ "${pathdir}" -ef "${clean_tree}" ]
|
||||
}
|
||||
|
||||
nvm_find_project_dir() {
|
||||
@@ -749,33 +802,81 @@ nvm_curl_version() {
|
||||
command curl -V | command awk '{ if ($1 == "curl") print $2 }' | command sed 's/-.*$//g'
|
||||
}
|
||||
|
||||
# Try to do some shell magic to do semver greater-than maths.
|
||||
# Fall through to the expensive test by calling `awk` if we can't be sure.
|
||||
nvm_version_greater() {
|
||||
command awk 'BEGIN {
|
||||
local NVM_LEFT
|
||||
NVM_LEFT="${1#v}"
|
||||
local NVM_RIGHT
|
||||
NVM_RIGHT="${2#v}"
|
||||
local NVM_PLAIN
|
||||
# a third, more complex, argument of `1` (a flag) compares equal versions as true; `0` or
|
||||
# no argument compares them as false.
|
||||
local NVM_OR_EQUAL
|
||||
NVM_OR_EQUAL="${3:-0}"
|
||||
NVM_PLAIN='true'
|
||||
|
||||
# `x.y.z` operands are compared in shell arithmetic, which avoids a fork,
|
||||
# except when a group is long enough to overflow it; anything else - partial
|
||||
# versions, prereleases, and malformed operands - keeps the comparison this
|
||||
# function has always made
|
||||
case "${NVM_LEFT}" in
|
||||
'' | *[!0-9.]* | *..* | .* | *. | *.*.*.*) NVM_PLAIN='false' ;;
|
||||
*.*.*) ;;
|
||||
*) NVM_PLAIN='false' ;;
|
||||
esac
|
||||
case "${NVM_RIGHT}" in
|
||||
'' | *[!0-9.]* | *..* | .* | *. | *.*.*.*) NVM_PLAIN='false' ;;
|
||||
*.*.*) ;;
|
||||
*) NVM_PLAIN='false' ;;
|
||||
esac
|
||||
|
||||
if [ "${NVM_PLAIN}" = 'true' ] && [ "${#NVM_LEFT}" -lt 19 ] && [ "${#NVM_RIGHT}" -lt 19 ]; then
|
||||
local NVM_GROUP
|
||||
local NVM_LEFT_GROUP
|
||||
local NVM_RIGHT_GROUP
|
||||
NVM_GROUP=1
|
||||
while [ "${NVM_GROUP}" -le 3 ]; do
|
||||
NVM_LEFT_GROUP="${NVM_LEFT%%.*}"
|
||||
NVM_LEFT="${NVM_LEFT#*.}"
|
||||
NVM_RIGHT_GROUP="${NVM_RIGHT%%.*}"
|
||||
NVM_RIGHT="${NVM_RIGHT#*.}"
|
||||
if [ "${NVM_LEFT_GROUP}" -lt "${NVM_RIGHT_GROUP}" ]; then
|
||||
return 1
|
||||
elif [ "${NVM_LEFT_GROUP}" -gt "${NVM_RIGHT_GROUP}" ]; then
|
||||
return 0
|
||||
fi
|
||||
NVM_GROUP=$((NVM_GROUP + 1))
|
||||
done
|
||||
if [ "${NVM_OR_EQUAL}" = 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
command awk -v or_equal="${NVM_OR_EQUAL}" 'BEGIN {
|
||||
if (ARGV[1] == "" || ARGV[2] == "") exit(1)
|
||||
split(ARGV[1], a, /\./);
|
||||
split(ARGV[2], b, /\./);
|
||||
for (i=1; i<=3; i++) {
|
||||
if (a[i] && a[i] !~ /^[0-9]+$/) exit(2);
|
||||
if (b[i] && b[i] !~ /^[0-9]+$/) { exit(0); }
|
||||
if (b[i] && b[i] !~ /^[0-9]+$/) {
|
||||
# a non-numeric right-hand group (a prerelease): the left is greater,
|
||||
# unless equality counts, in which case the comparison below decides
|
||||
if (or_equal != 1) { exit(0); }
|
||||
}
|
||||
if (a[i] < b[i]) exit(3);
|
||||
else if (a[i] > b[i]) exit(0);
|
||||
}
|
||||
if (or_equal == 1) { exit(0); }
|
||||
exit(4)
|
||||
}' "${1#v}" "${2#v}"
|
||||
}' "${NVM_LEFT}" "${NVM_RIGHT}"
|
||||
}
|
||||
|
||||
# Try to do some shell magic to do semver greater-than or equal-to maths.
|
||||
# Fall through to the expensive test by calling `awk` if we can't be sure.
|
||||
# see `nvm_version_greater`
|
||||
nvm_version_greater_than_or_equal_to() {
|
||||
command awk 'BEGIN {
|
||||
if (ARGV[1] == "" || ARGV[2] == "") exit(1)
|
||||
split(ARGV[1], a, /\./);
|
||||
split(ARGV[2], b, /\./);
|
||||
for (i=1; i<=3; i++) {
|
||||
if (a[i] && a[i] !~ /^[0-9]+$/) exit(2);
|
||||
if (a[i] < b[i]) exit(3);
|
||||
else if (a[i] > b[i]) exit(0);
|
||||
}
|
||||
exit(0)
|
||||
}' "${1#v}" "${2#v}"
|
||||
nvm_version_greater "${1-}" "${2-}" 1
|
||||
}
|
||||
|
||||
nvm_version_dir() {
|
||||
@@ -877,6 +978,16 @@ nvm_version() {
|
||||
PATTERN="stable"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Fast path: an exact, fully-qualified version that is installed resolves to
|
||||
# itself, unless an alias of the same name takes precedence. Short-circuiting
|
||||
# it here avoids the `nvm_ls` subprocess pipeline on shell startup; every
|
||||
# other pattern falls through to `nvm_ls` unchanged.
|
||||
if nvm_is_installed_exact_version "${PATTERN}"; then
|
||||
nvm_echo "${PATTERN}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
VERSION="$(nvm_ls "${PATTERN}" | command tail -1)"
|
||||
case "${VERSION}" in
|
||||
system[[:blank:]]*)
|
||||
@@ -1004,17 +1115,14 @@ ${NVM_LS_REMOTE_POST_MERGED_OUTPUT}" | nvm_grep -v "N/A" | command sed '/^ *$/d'
|
||||
}
|
||||
|
||||
nvm_is_valid_version() {
|
||||
if nvm_validate_implicit_alias "${1-}" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
case "${1-}" in
|
||||
"$(nvm_iojs_prefix)" | \
|
||||
"$(nvm_node_prefix)")
|
||||
"stable" | "unstable" | "iojs" | "node")
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
local VERSION
|
||||
VERSION="$(nvm_strip_iojs_prefix "${1-}")"
|
||||
VERSION="${1-}"
|
||||
VERSION="${VERSION#iojs-}"
|
||||
local NVM_VERSION_CORE
|
||||
NVM_VERSION_CORE="${VERSION#v}"
|
||||
case "${NVM_VERSION_CORE}" in
|
||||
@@ -1028,6 +1136,9 @@ nvm_is_valid_version() {
|
||||
*.*.*) ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
# a prerelease's `-` makes it unreachable for the pure-shell checks
|
||||
# below, so do the full check now.
|
||||
nvm_version_greater_than_or_equal_to "${VERSION}" 0 || return 1
|
||||
;;
|
||||
*.)
|
||||
NVM_VERSION_CORE="${NVM_VERSION_CORE%.}"
|
||||
@@ -1036,7 +1147,7 @@ nvm_is_valid_version() {
|
||||
case "${NVM_VERSION_CORE}" in
|
||||
'' | .* | *. | *..* | *.*.*.* | *[!0-9.]*) return 1 ;;
|
||||
esac
|
||||
nvm_version_greater_than_or_equal_to "${VERSION}" 0
|
||||
# every version that reaches this point is a number, and so `>= 0`
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -1149,24 +1260,33 @@ nvm_change_path() {
|
||||
# if there’s no initial path, just return the supplementary path
|
||||
if [ -z "${1-}" ]; then
|
||||
nvm_echo "${3-}${2-}"
|
||||
return
|
||||
fi
|
||||
# `${NVM_DIR}` is matched literally, so escape characters that are special in regexes
|
||||
local NVM_DIR_RE
|
||||
NVM_DIR_RE="$(nvm_echo "${NVM_DIR}" | command sed 's/[][\.*^$+?(){}|]/\\&/g')"
|
||||
# if the initial path doesn’t contain an nvm path, prepend the supplementary
|
||||
# path
|
||||
elif ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/[^/]*${2-}" \
|
||||
&& ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/versions/[^/]*/[^/]*${2-}"; then
|
||||
if ! nvm_echo "${1-}" | nvm_grep -Eq "${NVM_DIR_RE}/[^/]*${2-}" \
|
||||
&& ! nvm_echo "${1-}" | nvm_grep -Eq "${NVM_DIR_RE}/versions/[^/]*/[^/]*${2-}"; then
|
||||
nvm_echo "${3-}${2-}:${1-}"
|
||||
# if the initial path contains BOTH an nvm path (checked for above) and
|
||||
# that nvm path is preceded by a system binary path, just prepend the
|
||||
# supplementary path instead of replacing it.
|
||||
# https://github.com/nvm-sh/nvm/issues/1652#issuecomment-342571223
|
||||
elif nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR}/[^/]*${2-}" \
|
||||
|| nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR}/versions/[^/]*/[^/]*${2-}"; then
|
||||
elif nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR_RE}/[^/]*${2-}" \
|
||||
|| nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR_RE}/versions/[^/]*/[^/]*${2-}"; then
|
||||
nvm_echo "${3-}${2-}:${1-}"
|
||||
# use sed to replace the existing nvm path with the supplementary path. This
|
||||
# preserves the order of the path.
|
||||
else
|
||||
nvm_echo "${1-}" | command sed \
|
||||
-e "s#${NVM_DIR}/[^/]*${2-}[^:]*#${3-}${2-}#" \
|
||||
-e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*#${3-}${2-}#"
|
||||
local NVM_DIR_SED
|
||||
NVM_DIR_SED="$(nvm_echo "${NVM_DIR_RE}" | command sed 's/#/\\#/g')"
|
||||
local NEW_DIR_SED
|
||||
NEW_DIR_SED="$(nvm_echo "${3-}" | command sed 's/[\&#]/\\&/g')"
|
||||
nvm_echo "${1-}" | command sed -E \
|
||||
-e "s#${NVM_DIR_SED}/[^/]*${2-}[^:]*#${NEW_DIR_SED}${2-}#" \
|
||||
-e "s#${NVM_DIR_SED}/versions/[^/]*/[^/]*${2-}[^:]*#${NEW_DIR_SED}${2-}#"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1458,8 +1578,9 @@ nvm_list_aliases() {
|
||||
nvm_is_zsh && setopt local_options nonomatch
|
||||
(
|
||||
local ALIAS_PATH
|
||||
# background jobs are explicit subshells: bash 3.2 otherwise drops output from nested `command` calls in them
|
||||
for ALIAS_PATH in "${NVM_ALIAS_DIR}/${ALIAS}"*; do
|
||||
NVM_NO_COLORS="${NVM_NO_COLORS-}" NVM_HAS_COLORS="${NVM_HAS_COLORS}" NVM_CURRENT="${NVM_CURRENT}" nvm_print_alias_path "${NVM_ALIAS_DIR}" "${ALIAS_PATH}" &
|
||||
(NVM_NO_COLORS="${NVM_NO_COLORS-}" NVM_HAS_COLORS="${NVM_HAS_COLORS}" NVM_CURRENT="${NVM_CURRENT}" nvm_print_alias_path "${NVM_ALIAS_DIR}" "${ALIAS_PATH}") &
|
||||
done
|
||||
wait
|
||||
) | command sort
|
||||
@@ -1467,12 +1588,12 @@ nvm_list_aliases() {
|
||||
(
|
||||
local ALIAS_NAME
|
||||
for ALIAS_NAME in "$(nvm_node_prefix)" "stable" "unstable" "$(nvm_iojs_prefix)"; do
|
||||
{
|
||||
(
|
||||
# shellcheck disable=SC2030,SC2031 # (https://github.com/koalaman/shellcheck/issues/2217)
|
||||
if [ ! -f "${NVM_ALIAS_DIR}/${ALIAS_NAME}" ] && { [ -z "${ALIAS}" ] || [ "${ALIAS_NAME}" = "${ALIAS}" ]; }; then
|
||||
NVM_NO_COLORS="${NVM_NO_COLORS-}" NVM_HAS_COLORS="${NVM_HAS_COLORS}" NVM_CURRENT="${NVM_CURRENT}" nvm_print_default_alias "${ALIAS_NAME}"
|
||||
fi
|
||||
} &
|
||||
) &
|
||||
done
|
||||
wait
|
||||
) | command sort
|
||||
@@ -1481,12 +1602,12 @@ nvm_list_aliases() {
|
||||
local LTS_ALIAS
|
||||
# shellcheck disable=SC2030,SC2031 # (https://github.com/koalaman/shellcheck/issues/2217)
|
||||
for ALIAS_PATH in "${NVM_ALIAS_DIR}/lts/${ALIAS}"*; do
|
||||
{
|
||||
(
|
||||
LTS_ALIAS="$(NVM_NO_COLORS="${NVM_NO_COLORS-}" NVM_HAS_COLORS="${NVM_HAS_COLORS}" NVM_LTS=true nvm_print_alias_path "${NVM_ALIAS_DIR}" "${ALIAS_PATH}")"
|
||||
if [ -n "${LTS_ALIAS}" ]; then
|
||||
nvm_echo "${LTS_ALIAS}"
|
||||
fi
|
||||
} &
|
||||
) &
|
||||
done
|
||||
wait
|
||||
) | command sort
|
||||
@@ -1652,11 +1773,18 @@ nvm_resolve_local_alias() {
|
||||
if [ -z "${VERSION}" ]; then
|
||||
return $EXIT_CODE
|
||||
fi
|
||||
if [ "_${VERSION}" != '_∞' ]; then
|
||||
nvm_version "${VERSION}"
|
||||
else
|
||||
if [ "_${VERSION}" = '_∞' ]; then
|
||||
nvm_echo "${VERSION}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Fast path: an exact, installed vX.Y.Z needs no further resolution.
|
||||
if nvm_is_installed_exact_version "${VERSION}"; then
|
||||
nvm_echo "${VERSION}"
|
||||
return
|
||||
fi
|
||||
|
||||
nvm_version "${VERSION}"
|
||||
}
|
||||
|
||||
nvm_iojs_prefix() {
|
||||
@@ -1676,12 +1804,9 @@ nvm_add_iojs_prefix() {
|
||||
}
|
||||
|
||||
nvm_strip_iojs_prefix() {
|
||||
local NVM_IOJS_PREFIX
|
||||
NVM_IOJS_PREFIX="$(nvm_iojs_prefix)"
|
||||
|
||||
case "${1-}" in
|
||||
"${NVM_IOJS_PREFIX}") nvm_echo ;;
|
||||
*) nvm_echo "${1#"${NVM_IOJS_PREFIX}"-}" ;;
|
||||
"iojs") nvm_echo ;;
|
||||
*) nvm_echo "${1#iojs-}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -2156,10 +2281,53 @@ nvm_get_checksum() {
|
||||
nvm_download -L -s "${SHASUMS_URL}" -o - | command awk -v tarball="${4}.${5}" '{ if (tarball == $2) print $1 }'
|
||||
}
|
||||
|
||||
nvm_get_remote_aliases() {
|
||||
local NVM_ALIAS_DIR
|
||||
NVM_ALIAS_DIR="$(nvm_alias_path)"
|
||||
if [ ! -d "${NVM_ALIAS_DIR}" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local NVM_NODE_PREFIX
|
||||
NVM_NODE_PREFIX="$(nvm_node_prefix)"
|
||||
local NVM_IOJS_PREFIX
|
||||
NVM_IOJS_PREFIX="$(nvm_iojs_prefix)"
|
||||
local NVM_ALIAS_PATH
|
||||
local NVM_ALIAS_NAME
|
||||
local NVM_ALIAS_TARGET
|
||||
|
||||
nvm_is_zsh && setopt local_options nonomatch
|
||||
# the same glob as `nvm_list_aliases`, so this never shows an alias that `nvm alias` omits
|
||||
for NVM_ALIAS_PATH in "${NVM_ALIAS_DIR}/"*; do
|
||||
if [ ! -f "${NVM_ALIAS_PATH}" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
NVM_ALIAS_NAME="${NVM_ALIAS_PATH##*/}"
|
||||
case "${NVM_ALIAS_NAME}" in
|
||||
"${NVM_NODE_PREFIX}" | "${NVM_IOJS_PREFIX}" | stable | unstable) continue ;;
|
||||
# control characters would corrupt the terminal, or the tab-separated output
|
||||
*[[:cntrl:]]*) continue ;;
|
||||
esac
|
||||
|
||||
NVM_ALIAS_TARGET="$(nvm_resolve_alias "${NVM_ALIAS_NAME}" 2>/dev/null)" || continue
|
||||
case "${NVM_ALIAS_TARGET}" in
|
||||
'' | *[[:cntrl:]]*) continue ;;
|
||||
esac
|
||||
|
||||
command printf '%s\t%s\n' "${NVM_ALIAS_TARGET}" "${NVM_ALIAS_NAME}"
|
||||
done
|
||||
}
|
||||
|
||||
nvm_print_versions() {
|
||||
local NVM_CURRENT
|
||||
NVM_CURRENT=$(nvm_ls_current)
|
||||
|
||||
local NVM_LATEST_ALIAS
|
||||
NVM_LATEST_ALIAS="${2-}"
|
||||
local NVM_NAMED_ALIASES
|
||||
NVM_NAMED_ALIASES="${3-}"
|
||||
|
||||
local INSTALLED_COLOR
|
||||
local SYSTEM_COLOR
|
||||
local CURRENT_COLOR
|
||||
@@ -2180,20 +2348,36 @@ nvm_print_versions() {
|
||||
NVM_HAS_COLORS=1
|
||||
fi
|
||||
|
||||
local NVM_HAS_ITALICS
|
||||
NVM_HAS_ITALICS=0
|
||||
if [ "${NVM_HAS_COLORS}" = 1 ] && [ -n "${NVM_LATEST_ALIAS}${NVM_NAMED_ALIASES}" ] && nvm_has_italics; then
|
||||
NVM_HAS_ITALICS=1
|
||||
fi
|
||||
|
||||
NVM_NAMED_ALIASES="${NVM_NAMED_ALIASES}" \
|
||||
command awk \
|
||||
-v remote_versions="$(printf '%s' "${1-}" | tr '\n' '|')" \
|
||||
-v latest_alias="${NVM_LATEST_ALIAS}" \
|
||||
-v node_prefix="$(nvm_node_prefix)" -v iojs_prefix="$(nvm_iojs_prefix)" \
|
||||
-v installed_versions="$(nvm_ls | tr '\n' '|')" -v current="$NVM_CURRENT" \
|
||||
-v installed_color="$INSTALLED_COLOR" -v system_color="$SYSTEM_COLOR" \
|
||||
-v current_color="$CURRENT_COLOR" -v default_color="$DEFAULT_COLOR" \
|
||||
-v old_lts_color="$DEFAULT_COLOR" -v has_colors="$NVM_HAS_COLORS" '
|
||||
-v old_lts_color="$DEFAULT_COLOR" -v has_colors="$NVM_HAS_COLORS" \
|
||||
-v has_italics="$NVM_HAS_ITALICS" '
|
||||
function alen(arr, i, len) { len=0; for(i in arr) len++; return len; }
|
||||
function paint(color, text) { return (has_colors && color) ? ("\033[" color text "\033[0m") : text; }
|
||||
function spaces(count, result) { result = ""; while (count-- > 0) result = result " "; return result; }
|
||||
function italicize(text) { return has_italics ? ("\033[3m" text "\033[23m") : text; }
|
||||
BEGIN {
|
||||
named_alias_list = ENVIRON["NVM_NAMED_ALIASES"];
|
||||
fmt_installed = has_colors ? (installed_color ? "\033[" installed_color "%15s\033[0m" : "%15s") : "%15s *";
|
||||
fmt_system = has_colors ? (system_color ? "\033[" system_color "%15s\033[0m" : "%15s") : "%15s *";
|
||||
fmt_current = has_colors ? (current_color ? "\033[" current_color "->%13s\033[0m" : "%15s") : "->%13s *";
|
||||
|
||||
latest_lts_color = current_color;
|
||||
sub(/0;/, "1;", latest_lts_color);
|
||||
alias_color = current_color;
|
||||
sub(/^1;/, "0;", alias_color);
|
||||
|
||||
fmt_latest_lts = has_colors && latest_lts_color ? ("\033[" latest_lts_color " (Latest LTS: %s)\033[0m") : " (Latest LTS: %s)";
|
||||
fmt_old_lts = has_colors && old_lts_color ? ("\033[" old_lts_color " (LTS: %s)\033[0m") : " (LTS: %s)";
|
||||
@@ -2203,6 +2387,57 @@ BEGIN {
|
||||
split(installed_versions, installed, "|");
|
||||
rows = alen(lines);
|
||||
|
||||
for (n = 1; n <= rows; n++) {
|
||||
split(lines[n], fields, "[[:blank:]]+");
|
||||
if (index(fields[1], iojs_prefix "-") == 1) {
|
||||
latest_iojs = fields[1];
|
||||
} else if (fields[1] ~ /^v0\.[0-9]*[13579]\./) {
|
||||
latest_unstable = fields[1];
|
||||
} else if (fields[1] ~ /^v[0-9]/) {
|
||||
latest_stable = fields[1];
|
||||
}
|
||||
}
|
||||
|
||||
named_alias_rows = split(named_alias_list, named_alias_lines, "\n");
|
||||
for (a = 1; a <= named_alias_rows; a++) {
|
||||
tab_position = index(named_alias_lines[a], "\t");
|
||||
if (tab_position < 2) {
|
||||
continue;
|
||||
}
|
||||
alias_target = substr(named_alias_lines[a], 1, tab_position - 1);
|
||||
alias_name = substr(named_alias_lines[a], tab_position + 1);
|
||||
if (alias_name == "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
alias_version = "";
|
||||
if (alias_target == node_prefix || alias_target == "stable") {
|
||||
alias_version = latest_stable;
|
||||
} else if (alias_target == "unstable") {
|
||||
alias_version = latest_unstable;
|
||||
} else if (alias_target == iojs_prefix || alias_target == (iojs_prefix "-")) {
|
||||
alias_version = latest_iojs;
|
||||
} else {
|
||||
for (n = 1; n <= rows; n++) {
|
||||
split(lines[n], fields, "[[:blank:]]+");
|
||||
if (fields[1] == alias_target || index(fields[1], alias_target ".") == 1) {
|
||||
alias_version = fields[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (alias_version != "") {
|
||||
# mawk creates the assigned element before evaluating the right side, so `in` must be checked first
|
||||
if (alias_version in named_aliases) {
|
||||
named_aliases[alias_version] = named_aliases[alias_version] ", " alias_name;
|
||||
italic_aliases[alias_version] = italic_aliases[alias_version] ", " italicize(alias_name);
|
||||
} else {
|
||||
named_aliases[alias_version] = alias_name;
|
||||
italic_aliases[alias_version] = italicize(alias_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (n = 1; n <= rows; n++) {
|
||||
split(lines[n], fields, "[[:blank:]]+");
|
||||
cols = alen(fields);
|
||||
@@ -2225,23 +2460,69 @@ BEGIN {
|
||||
fmt_version = fmt_installed;
|
||||
}
|
||||
|
||||
padding = (!has_colors && is_installed) ? "" : " ";
|
||||
padding[n] = (!has_colors && is_installed) ? "" : " ";
|
||||
|
||||
if (cols == 1) {
|
||||
formatted = sprintf(fmt_version, version);
|
||||
} else if (version == "system" && cols >= 2) {
|
||||
formatted = sprintf((fmt_version fmt_system_target), version, fields[2]);
|
||||
output[n] = sprintf(fmt_version, version);
|
||||
version_width[n] = output[n] padding[n];
|
||||
gsub(/\033\[[0-9;]*m/, "", version_width[n]);
|
||||
version_width[n] = length(version_width[n]);
|
||||
if (version_width[n] > max_version_width) {
|
||||
max_version_width = version_width[n];
|
||||
}
|
||||
for (c = 1; c <= 3; c++) {
|
||||
text[n, c] = "";
|
||||
cell[n, c] = "";
|
||||
}
|
||||
if (version == "system" && cols >= 2) {
|
||||
output[n] = output[n] sprintf(fmt_system_target, fields[2]);
|
||||
} else if (cols == 2) {
|
||||
formatted = sprintf((fmt_version padding fmt_old_lts), version, fields[2]);
|
||||
text[n, 1] = sprintf(" (LTS: %s)", fields[2]);
|
||||
cell[n, 1] = sprintf(fmt_old_lts, fields[2]);
|
||||
} else if (cols == 3 && fields[3] == "*") {
|
||||
formatted = sprintf((fmt_version padding fmt_latest_lts), version, fields[2]);
|
||||
text[n, 1] = sprintf(" (Latest LTS: %s)", fields[2]);
|
||||
cell[n, 1] = sprintf(fmt_latest_lts, fields[2]);
|
||||
}
|
||||
|
||||
output[n] = formatted;
|
||||
if (latest_alias != "" && version == latest_stable) {
|
||||
text[n, 2] = " (Latest: " latest_alias ")";
|
||||
cell[n, 2] = paint(alias_color, " (Latest: " italicize(latest_alias) ")");
|
||||
}
|
||||
|
||||
if (version in named_aliases) {
|
||||
text[n, 3] = " (Aliases: " named_aliases[version] ")";
|
||||
cell[n, 3] = paint(alias_color, " (Aliases: " italic_aliases[version] ")");
|
||||
}
|
||||
|
||||
for (c = 1; c <= 3; c++) {
|
||||
if (length(text[n, c]) > width[c]) {
|
||||
width[c] = length(text[n, c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# each kind of annotation starts in the same column on every row, so the output stays `cut`-able
|
||||
for (n = 1; n <= rows; n++) {
|
||||
print output[n]
|
||||
last = 0;
|
||||
for (c = 1; c <= 3; c++) {
|
||||
if (text[n, c] != "") {
|
||||
last = c;
|
||||
}
|
||||
}
|
||||
formatted = output[n];
|
||||
if (last) {
|
||||
formatted = formatted padding[n];
|
||||
if (width[2] || width[3]) {
|
||||
formatted = formatted spaces(max_version_width - version_width[n]);
|
||||
}
|
||||
gap = "";
|
||||
for (c = 1; c <= last; c++) {
|
||||
if (width[c]) {
|
||||
formatted = formatted gap cell[n, c];
|
||||
gap = spaces(width[c] - length(text[n, c])) " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
print formatted
|
||||
}
|
||||
|
||||
exit
|
||||
@@ -2249,17 +2530,12 @@ BEGIN {
|
||||
}
|
||||
|
||||
nvm_validate_implicit_alias() {
|
||||
local NVM_IOJS_PREFIX
|
||||
NVM_IOJS_PREFIX="$(nvm_iojs_prefix)"
|
||||
local NVM_NODE_PREFIX
|
||||
NVM_NODE_PREFIX="$(nvm_node_prefix)"
|
||||
|
||||
case "$1" in
|
||||
"stable" | "unstable" | "${NVM_IOJS_PREFIX}" | "${NVM_NODE_PREFIX}")
|
||||
"stable" | "unstable" | "iojs" | "node")
|
||||
return
|
||||
;;
|
||||
*)
|
||||
nvm_err "Only implicit aliases 'stable', 'unstable', '${NVM_IOJS_PREFIX}', and '${NVM_NODE_PREFIX}' are supported."
|
||||
nvm_err "Only implicit aliases 'stable', 'unstable', 'iojs', and 'node' are supported."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
@@ -2353,18 +2629,74 @@ nvm_print_implicit_alias() {
|
||||
}
|
||||
|
||||
nvm_get_os() {
|
||||
local NVM_UNAME
|
||||
NVM_UNAME="$(command uname -a)"
|
||||
# dash's `local` keeps the caller's value, so these must start empty
|
||||
local NVM_OS
|
||||
case "${NVM_UNAME}" in
|
||||
Linux\ *) NVM_OS=linux ;;
|
||||
Darwin\ *) NVM_OS=darwin ;;
|
||||
SunOS\ *) NVM_OS=sunos ;;
|
||||
FreeBSD\ *) NVM_OS=freebsd ;;
|
||||
OpenBSD\ *) NVM_OS=openbsd ;;
|
||||
AIX\ *) NVM_OS=aix ;;
|
||||
CYGWIN* | MSYS* | MINGW*) NVM_OS=win ;;
|
||||
esac
|
||||
NVM_OS=''
|
||||
local NVM_UNAME
|
||||
local NVM_OS_RELEASE_PATH
|
||||
local NVM_OS_RELEASE_LINE
|
||||
local NVM_OS_RELEASE_ID
|
||||
NVM_OS_RELEASE_ID=''
|
||||
local NVM_OS_KERNEL_PATH
|
||||
local NVM_OS_KERNEL_TYPE
|
||||
NVM_OS_KERNEL_TYPE=''
|
||||
|
||||
# `/etc/os-release` is available on Linux, FreeBSD 13.2+, and the MSYS2 and
|
||||
# Cygwin runtimes; where it is readable, its `ID` names the OS without
|
||||
# spawning `uname`. The file is read, not sourced, so nothing in it runs.
|
||||
# systems without the file (macOS, OpenBSD, ...) and files without an `ID`
|
||||
# fall through to `uname -a` below.
|
||||
NVM_OS_RELEASE_PATH="${NVM_OS_RELEASE:-/etc/os-release}"
|
||||
if [ -r "${NVM_OS_RELEASE_PATH}" ]; then
|
||||
while IFS= read -r NVM_OS_RELEASE_LINE || [ -n "${NVM_OS_RELEASE_LINE}" ]; do
|
||||
case "${NVM_OS_RELEASE_LINE}" in
|
||||
ID=*)
|
||||
NVM_OS_RELEASE_ID="${NVM_OS_RELEASE_LINE#ID=}"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done < "${NVM_OS_RELEASE_PATH}"
|
||||
# an `ID` is a single word, which may be quoted and followed by
|
||||
# whitespace (including a CR) or a comment
|
||||
NVM_OS_RELEASE_ID="${NVM_OS_RELEASE_ID%%[[:space:]#]*}"
|
||||
NVM_OS_RELEASE_ID="${NVM_OS_RELEASE_ID#[\"\']}"
|
||||
NVM_OS_RELEASE_ID="${NVM_OS_RELEASE_ID%[\"\']}"
|
||||
case "${NVM_OS_RELEASE_ID}" in
|
||||
freebsd) NVM_OS=freebsd ;;
|
||||
cygwin* | mingw* | msys*) NVM_OS=win ;;
|
||||
# illumos distributions and Oracle Solaris report `SunOS` from
|
||||
# `uname -a`, and GhostBSD reports `FreeBSD`; map these directly instead
|
||||
# of forking `uname`
|
||||
solaris | omnios | openindiana | smartos | illumos) NVM_OS=sunos ;;
|
||||
ghostbsd) NVM_OS=freebsd ;;
|
||||
# no `ID`, or one with no `nvm` OS of its own (DragonFly BSD): let
|
||||
# `uname -a` decide
|
||||
'' | dragonfly) NVM_OS='' ;;
|
||||
# any other `ID` is a Linux distribution's only if the kernel says so;
|
||||
# unlisted BSD and illumos derivatives (e.g. HardenedBSD, Helios) let
|
||||
# `uname -a` decide
|
||||
*)
|
||||
NVM_OS_KERNEL_PATH="${NVM_OS_KERNEL_OSTYPE:-/proc/sys/kernel/ostype}"
|
||||
if [ -r "${NVM_OS_KERNEL_PATH}" ] && IFS= read -r NVM_OS_KERNEL_TYPE < "${NVM_OS_KERNEL_PATH}" && [ "${NVM_OS_KERNEL_TYPE}" = 'Linux' ]; then
|
||||
NVM_OS=linux
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ -z "${NVM_OS-}" ]; then
|
||||
NVM_UNAME="$(command uname -a)"
|
||||
case "${NVM_UNAME}" in
|
||||
Linux\ *) NVM_OS=linux ;;
|
||||
Darwin\ *) NVM_OS=darwin ;;
|
||||
SunOS\ *) NVM_OS=sunos ;;
|
||||
FreeBSD\ *) NVM_OS=freebsd ;;
|
||||
OpenBSD\ *) NVM_OS=openbsd ;;
|
||||
AIX\ *) NVM_OS=aix ;;
|
||||
CYGWIN* | MSYS* | MINGW*) NVM_OS=win ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
nvm_echo "${NVM_OS-}"
|
||||
}
|
||||
|
||||
@@ -2614,8 +2946,12 @@ nvm_install_binary() {
|
||||
return 2
|
||||
fi
|
||||
|
||||
# dash's `local` keeps the caller's value, and TMPDIR is usually set: it is
|
||||
# later removed with `rm -rf`, so it must not start as the system temp dir
|
||||
local TARBALL
|
||||
TARBALL=''
|
||||
local TMPDIR
|
||||
TMPDIR=''
|
||||
|
||||
local PROGRESS_BAR
|
||||
local NODE_OR_IOJS
|
||||
@@ -3015,9 +3351,13 @@ nvm_install_source() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# see nvm_install_binary: these must not inherit TMPDIR, which is removed on failure
|
||||
local TARBALL
|
||||
TARBALL=''
|
||||
local TMPDIR
|
||||
TMPDIR=''
|
||||
local VERSION_PATH
|
||||
VERSION_PATH=''
|
||||
|
||||
if [ "${NVM_NO_PROGRESS-}" = "1" ]; then
|
||||
# --silent, --show-error, use short option as @samrocketman mentions the compatibility issue.
|
||||
@@ -3111,9 +3451,28 @@ nvm_npm_global_modules() {
|
||||
|
||||
nvm_npmrc_bad_news_bears() {
|
||||
local NVM_NPMRC
|
||||
local NVM_NPMRC_LINE
|
||||
local NVM_NPMRC_KEY
|
||||
NVM_NPMRC="${1-}"
|
||||
if [ -n "${NVM_NPMRC}" ] && [ -f "${NVM_NPMRC}" ] && nvm_grep -Ee '^(prefix|globalconfig) *=' <"${NVM_NPMRC}" >/dev/null; then
|
||||
return 0
|
||||
if [ -n "${NVM_NPMRC}" ] && [ -f "${NVM_NPMRC}" ]; then
|
||||
# this is fork-free on purpose: it runs up to four times on every `nvm use`/`nvm install`
|
||||
# avoid grepping or sub-shells as much as possible here.
|
||||
while IFS= read -r NVM_NPMRC_LINE || [ -n "${NVM_NPMRC_LINE}" ]; do
|
||||
case "${NVM_NPMRC_LINE}" in
|
||||
*=*)
|
||||
NVM_NPMRC_KEY="${NVM_NPMRC_LINE%%=*}"
|
||||
;;
|
||||
*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
NVM_NPMRC_KEY="${NVM_NPMRC_KEY%"${NVM_NPMRC_KEY##*[! ]}"}"
|
||||
case "${NVM_NPMRC_KEY}" in
|
||||
prefix | globalconfig)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
done < "${NVM_NPMRC}"
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
@@ -3147,9 +3506,6 @@ nvm_die_on_prefix() {
|
||||
return 3
|
||||
fi
|
||||
|
||||
local NVM_OS
|
||||
NVM_OS="$(nvm_get_os)"
|
||||
|
||||
# npm normalizes NPM_CONFIG_-prefixed env vars
|
||||
# https://github.com/npm/npmconf/blob/22827e4038d6eebaafeb5c13ed2b92cf97b8fb82/npmconf.js#L331-L348
|
||||
# https://github.com/npm/npm/blob/5e426a78ca02d0044f8dd26e0c5f881217081cbd/lib/config/core.js#L343-L359
|
||||
@@ -3159,10 +3515,14 @@ nvm_die_on_prefix() {
|
||||
local NVM_NPM_CONFIG_x_PREFIX_ENV
|
||||
NVM_NPM_CONFIG_x_PREFIX_ENV="$(command awk 'BEGIN { for (name in ENVIRON) if (toupper(name) == "NPM_CONFIG_PREFIX") { print name; break } }')"
|
||||
if [ -n "${NVM_NPM_CONFIG_x_PREFIX_ENV-}" ]; then
|
||||
# `$NVM_OS` is only used to translate Windows paths, but detecting the OS
|
||||
# shells out to `uname`, so it is deferred until it is actually needed
|
||||
local NVM_OS
|
||||
NVM_OS="$(nvm_get_os)"
|
||||
local NVM_CONFIG_VALUE
|
||||
eval "NVM_CONFIG_VALUE=\"\$${NVM_NPM_CONFIG_x_PREFIX_ENV}\""
|
||||
if [ -n "${NVM_CONFIG_VALUE-}" ] && [ "_${NVM_OS}" = "_win" ]; then
|
||||
NVM_CONFIG_VALUE="$(cd "$NVM_CONFIG_VALUE" 2>/dev/null && pwd)"
|
||||
NVM_CONFIG_VALUE="$(nvm_cd "$NVM_CONFIG_VALUE" 2>/dev/null && pwd)"
|
||||
fi
|
||||
if [ -n "${NVM_CONFIG_VALUE-}" ] && ! nvm_tree_contains_path "${NVM_DIR}" "${NVM_CONFIG_VALUE}"; then
|
||||
nvm deactivate >/dev/null 2>&1
|
||||
@@ -3293,14 +3653,29 @@ nvm_has_solaris_binary() {
|
||||
fi
|
||||
}
|
||||
|
||||
# replaces every literal occurrence of $2 in $1 with $3; values go through the
|
||||
# environment so awk does not interpret backslash escapes or regex characters
|
||||
nvm_replace_literal() {
|
||||
nvm_echo "${1-}" | NVM_FROM="${2-}" NVM_TO="${3-}" command awk '
|
||||
BEGIN { from = ENVIRON["NVM_FROM"]; to = ENVIRON["NVM_TO"]; n = length(from) }
|
||||
{
|
||||
out = ""; s = $0
|
||||
while (n > 0 && (i = index(s, from)) > 0) {
|
||||
out = out substr(s, 1, i - 1) to
|
||||
s = substr(s, i + n)
|
||||
}
|
||||
print out s
|
||||
}'
|
||||
}
|
||||
|
||||
nvm_sanitize_path() {
|
||||
local SANITIZED_PATH
|
||||
SANITIZED_PATH="${1-}"
|
||||
if [ "_${SANITIZED_PATH}" != "_${NVM_DIR}" ]; then
|
||||
SANITIZED_PATH="$(nvm_echo "${SANITIZED_PATH}" | command sed -e "s#${NVM_DIR}#\${NVM_DIR}#g")"
|
||||
SANITIZED_PATH="$(nvm_replace_literal "${SANITIZED_PATH}" "${NVM_DIR}" '${NVM_DIR}')"
|
||||
fi
|
||||
if [ "_${SANITIZED_PATH}" != "_${HOME}" ]; then
|
||||
SANITIZED_PATH="$(nvm_echo "${SANITIZED_PATH}" | command sed -e "s#${HOME}#\${HOME}#g")"
|
||||
SANITIZED_PATH="$(nvm_replace_literal "${SANITIZED_PATH}" "${HOME}" '${HOME}')"
|
||||
fi
|
||||
nvm_echo "${SANITIZED_PATH}"
|
||||
}
|
||||
@@ -3503,14 +3878,18 @@ nvm() {
|
||||
if [ "${-#*e}" != "$-" ]; then
|
||||
set +e
|
||||
local EXIT_CODE
|
||||
IFS="${DEFAULT_IFS}" nvm "$@"
|
||||
local IFS
|
||||
IFS="${DEFAULT_IFS}"
|
||||
nvm "$@"
|
||||
EXIT_CODE="$?"
|
||||
set -e
|
||||
return "$EXIT_CODE"
|
||||
elif [ "${-#*a}" != "$-" ]; then
|
||||
set +a
|
||||
local EXIT_CODE
|
||||
IFS="${DEFAULT_IFS}" nvm "$@"
|
||||
local IFS
|
||||
IFS="${DEFAULT_IFS}"
|
||||
nvm "$@"
|
||||
EXIT_CODE="$?"
|
||||
set -a
|
||||
return "$EXIT_CODE"
|
||||
@@ -3518,13 +3897,17 @@ nvm() {
|
||||
# shellcheck disable=SC3041
|
||||
set +E
|
||||
local EXIT_CODE
|
||||
IFS="${DEFAULT_IFS}" nvm "$@"
|
||||
local IFS
|
||||
IFS="${DEFAULT_IFS}"
|
||||
nvm "$@"
|
||||
EXIT_CODE="$?"
|
||||
# shellcheck disable=SC3041
|
||||
set -E
|
||||
return "$EXIT_CODE"
|
||||
elif [ "${IFS}" != "${DEFAULT_IFS}" ]; then
|
||||
IFS="${DEFAULT_IFS}" nvm "$@"
|
||||
local IFS
|
||||
IFS="${DEFAULT_IFS}"
|
||||
nvm "$@"
|
||||
return "$?"
|
||||
fi
|
||||
|
||||
@@ -4360,6 +4743,8 @@ nvm() {
|
||||
IS_VERSION_FROM_NVMRC=0
|
||||
local NVM_WRITE_TO_NVMRC
|
||||
NVM_WRITE_TO_NVMRC=0
|
||||
local NVM_USE_EXACT_INSTALLED
|
||||
NVM_USE_EXACT_INSTALLED=0
|
||||
|
||||
while [ $# -ne 0 ]; do
|
||||
case "$1" in
|
||||
@@ -4400,6 +4785,9 @@ nvm() {
|
||||
nvm_err 'Please see `nvm --help` or https://github.com/nvm-sh/nvm#nvmrc for more information.'
|
||||
return 127
|
||||
fi
|
||||
elif nvm_is_installed_exact_version "${PROVIDED_VERSION}"; then
|
||||
VERSION="${PROVIDED_VERSION}"
|
||||
NVM_USE_EXACT_INSTALLED=1
|
||||
else
|
||||
VERSION="$(nvm_match_version "${PROVIDED_VERSION}")"
|
||||
fi
|
||||
@@ -4441,9 +4829,8 @@ nvm() {
|
||||
nvm_ensure_version_installed "${PROVIDED_VERSION}" "${IS_VERSION_FROM_NVMRC}"
|
||||
fi
|
||||
return 3
|
||||
# This nvm_ensure_version_installed call can be a performance bottleneck
|
||||
# on shell startup. Perhaps we can optimize it away or make it faster.
|
||||
elif ! nvm_ensure_version_installed "${VERSION}" "${IS_VERSION_FROM_NVMRC}"; then
|
||||
# an exact, already-installed version needs no further resolution or checks
|
||||
elif [ "${NVM_USE_EXACT_INSTALLED}" -ne 1 ] && ! nvm_ensure_version_installed "${VERSION}" "${IS_VERSION_FROM_NVMRC}"; then
|
||||
return $?
|
||||
fi
|
||||
|
||||
@@ -4746,7 +5133,15 @@ nvm() {
|
||||
NVM_OUTPUT="$(NVM_LTS="${NVM_LTS-}" nvm_remote_versions "${PATTERN-}" &&:)"
|
||||
EXIT_CODE=$?
|
||||
if [ -n "${NVM_OUTPUT}" ]; then
|
||||
NVM_NO_COLORS="${NVM_NO_COLORS-}" nvm_print_versions "${NVM_OUTPUT}"
|
||||
local NVM_REMOTE_LATEST_ALIAS
|
||||
NVM_REMOTE_LATEST_ALIAS=''
|
||||
local NVM_REMOTE_NAMED_ALIASES
|
||||
NVM_REMOTE_NAMED_ALIASES=''
|
||||
if [ "${EXIT_CODE}" -eq 0 ] && [ -z "${NVM_LTS-}" ] && [ -z "${PATTERN-}" ]; then
|
||||
NVM_REMOTE_LATEST_ALIAS="$(nvm_node_prefix)"
|
||||
NVM_REMOTE_NAMED_ALIASES="$(nvm_get_remote_aliases)"
|
||||
fi
|
||||
NVM_NO_COLORS="${NVM_NO_COLORS-}" nvm_print_versions "${NVM_OUTPUT}" "${NVM_REMOTE_LATEST_ALIAS}" "${NVM_REMOTE_NAMED_ALIASES}"
|
||||
return $EXIT_CODE
|
||||
fi
|
||||
NVM_NO_COLORS="${NVM_NO_COLORS-}" nvm_print_versions "N/A"
|
||||
@@ -5030,7 +5425,7 @@ nvm() {
|
||||
nvm_iojs_prefix nvm_node_prefix \
|
||||
nvm_add_iojs_prefix nvm_strip_iojs_prefix \
|
||||
nvm_is_iojs_version nvm_is_alias nvm_has_non_aliased \
|
||||
nvm_ls_remote nvm_ls_remote_iojs nvm_ls_remote_index_tab \
|
||||
nvm_ls_remote nvm_ls_remote_iojs nvm_ls_remote_index_tab nvm_get_remote_aliases \
|
||||
nvm_ls nvm_remote_version nvm_remote_versions \
|
||||
nvm_install_binary nvm_install_source nvm_clang_version \
|
||||
nvm_get_mirror nvm_get_download_slug nvm_download_artifact \
|
||||
@@ -5057,11 +5452,11 @@ nvm() {
|
||||
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_is_natural_num nvm_is_installed_exact_version nvm_is_version_installed nvm_validate_install \
|
||||
nvm_install_lock_name nvm_acquire_install_lock nvm_release_install_lock \
|
||||
nvm_list_aliases nvm_make_alias nvm_print_alias_file nvm_print_alias_path \
|
||||
nvm_print_default_alias nvm_print_formatted_alias nvm_resolve_local_alias \
|
||||
nvm_sanitize_path nvm_has_colors nvm_process_parameters \
|
||||
nvm_replace_literal nvm_sanitize_path nvm_has_colors nvm_has_italics nvm_process_parameters \
|
||||
nvm_node_version_has_solaris_binary nvm_iojs_version_has_solaris_binary \
|
||||
nvm_curl_libz_support nvm_command_info nvm_is_zsh nvm_stdout_is_terminal \
|
||||
nvm_npmrc_bad_news_bears nvm_sanitize_auth_header \
|
||||
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
nvm_version() {
|
||||
echo 'nvm_version was called' >&2
|
||||
return 42
|
||||
}
|
||||
|
||||
EXACT="$(nvm_resolve_local_alias test-stable-1)"
|
||||
[ "_$EXACT" = "_v0.0.1" ] || die "'nvm_resolve_local_alias test-stable-1' was not v0.0.1; got $EXACT"
|
||||
|
||||
echo v0.0.99 > ../../../alias/test-not-installed
|
||||
NOT_INSTALLED="$(nvm_resolve_local_alias test-not-installed 2>/dev/null)"
|
||||
EXIT_CODE=$?
|
||||
rm -f ../../../alias/test-not-installed
|
||||
[ "_$EXIT_CODE" = "_42" ] || die "an exact version that is not installed should still go through nvm_version; got exit code $EXIT_CODE"
|
||||
+3
@@ -5,6 +5,7 @@ set -ex
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
cleanup () {
|
||||
rm -rf "${NVM_DIR}/v0.10.4"
|
||||
unset -f nvm_remote_version
|
||||
}
|
||||
|
||||
export NVM_DIR="$(cd ../.. && pwd)"
|
||||
@@ -13,6 +14,8 @@ export NVM_DIR="$(cd ../.. && pwd)"
|
||||
\. ../../nvm.sh
|
||||
\. ../common.sh
|
||||
|
||||
nvm_remote_version() { nvm_echo "${1-}"; }
|
||||
|
||||
make_fake_node v0.10.4
|
||||
|
||||
nvm deactivate >/dev/null 2>&1
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { IFS=' '; echo "$@" ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../nvm.sh
|
||||
|
||||
IFS=':'
|
||||
nvm --version >/dev/null
|
||||
[ "${IFS}" = ':' ] || die "nvm changed the caller's IFS; got >${IFS}<"
|
||||
|
||||
set -e
|
||||
nvm --version >/dev/null
|
||||
set +e
|
||||
[ "${IFS}" = ':' ] || die "nvm changed the caller's IFS under set -e; got >${IFS}<"
|
||||
|
||||
! env | grep -q '^IFS=' || die 'nvm exported IFS'
|
||||
|
||||
IFS=' '
|
||||
@@ -797,4 +797,4 @@
|
||||
v22.10.0
|
||||
v22.11.0 [1;32m (Latest LTS: Jod)[0m
|
||||
v23.0.0
|
||||
v23.1.0
|
||||
v23.1.0 [0;32m (Latest: [3mnode[23m)[0m
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
cleanup() {
|
||||
unset -f nvm_download nvm_ls_remote nvm_ls_remote_iojs
|
||||
unset -f nvm_download nvm_ls_remote nvm_ls_remote_iojs nvm_get_remote_aliases nvm_has_italics
|
||||
if [ -n "${TEMP_NVM_COLORS-}" ]; then
|
||||
export NVM_COLORS=TEMP_NVM_COLORS
|
||||
fi
|
||||
@@ -87,6 +87,9 @@ REMOTE_IOJS="${PWD}/mocks/nvm_ls_remote_iojs.txt"
|
||||
nvm_ls_remote_iojs() {
|
||||
cat "${REMOTE_IOJS}"
|
||||
}
|
||||
# the mock must not depend on whatever local aliases exist in $NVM_DIR
|
||||
nvm_get_remote_aliases() { :; }
|
||||
nvm_has_italics() { return 0; }
|
||||
|
||||
EXPECTED_OUTPUT_PATH="${MOCKS_DIR}/nvm ls-remote.txt"
|
||||
OUTPUT="$(nvm ls-remote | sed 's/[ \t]*$//')"
|
||||
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="${PWD}/nvm-ls-remote-aliases-work.$$"
|
||||
export HOME="${WORK}/home"
|
||||
export NVM_DIR="${HOME}/.nvm"
|
||||
|
||||
cleanup() {
|
||||
command rm -rf "${WORK}"
|
||||
unset -f cleanup die nvm_has_colors nvm_has_italics nvm_remote_versions expected_row
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
command mkdir -p "${NVM_DIR}/alias/lts" "${NVM_DIR}/versions/node/v1.5.0"
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
nvm_has_colors() { return 1; }
|
||||
nvm_remote_versions() {
|
||||
if [ -n "${NVM_LTS-}" ] || [ -n "${1-}" ]; then
|
||||
command printf '%s\n' 'v2.0.0'
|
||||
else
|
||||
# io.js sorts last here, so the latest node release is not the last row
|
||||
command printf '%s\n' 'v0.11.16' 'v0.12.18' 'v1.0.0 Argon' 'v1.5.0' 'v2.0.0 Iron *' 'iojs-v3.3.1'
|
||||
fi
|
||||
return "${NVM_REMOTE_STATUS-0}"
|
||||
}
|
||||
|
||||
nvm_make_alias 'project' '1' >/dev/null
|
||||
nvm_make_alias 'argon-project' '1.0' >/dev/null
|
||||
nvm_make_alias 'lts/iron' 'v2.0.0' >/dev/null
|
||||
nvm_make_alias 'iron-project' 'lts/iron' >/dev/null
|
||||
nvm_make_alias 'latest-project' 'node' >/dev/null
|
||||
nvm_make_alias 'stable-project' 'stable' >/dev/null
|
||||
nvm_make_alias 'unstable-project' 'unstable' >/dev/null
|
||||
nvm_make_alias 'io-project' 'iojs' >/dev/null
|
||||
nvm_make_alias '.hidden-project' '1' >/dev/null
|
||||
command printf '%s\n' '[' > "${NVM_DIR}/alias/invalid-pattern"
|
||||
command printf '%s\n' '1' > "${NVM_DIR}/alias/$(command printf 'control\rname')"
|
||||
|
||||
# the version cell is 17 wide (`%15s` plus the ` *` installed marker or two spaces), then LTS, Latest, and Aliases columns
|
||||
expected_row() {
|
||||
command printf '%-17s%-19s %-15s %s\n' "$1" "$2" "$3" "$4" | command sed 's/[[:space:]]*$//'
|
||||
}
|
||||
EXPECTED_OUTPUT="$(
|
||||
expected_row ' v0.11.16' '' '' ' (Aliases: unstable-project)'
|
||||
expected_row ' v0.12.18'
|
||||
expected_row ' v1.0.0' ' (LTS: Argon)' '' ' (Aliases: argon-project)'
|
||||
expected_row ' v1.5.0 *' '' '' ' (Aliases: project)'
|
||||
expected_row ' v2.0.0' ' (Latest LTS: Iron)' ' (Latest: node)' ' (Aliases: iron-project, latest-project, stable-project)'
|
||||
expected_row ' iojs-v3.3.1' '' '' ' (Aliases: io-project)'
|
||||
)"
|
||||
|
||||
REMOTE_ERROR="${WORK}/remote-error"
|
||||
OUTPUT="$(NVM_REMOTE_LATEST_ALIAS='injected' NVM_REMOTE_NAMED_ALIASES="$(command printf 'v0.12.18\tinjected')" nvm ls-remote --no-colors 2>"${REMOTE_ERROR}")"
|
||||
|
||||
[ ! -s "${REMOTE_ERROR}" ] \
|
||||
|| die "bare nvm ls-remote emitted an error; got >$(command cat "${REMOTE_ERROR}")<"
|
||||
[ "_${OUTPUT}" = "_${EXPECTED_OUTPUT}" ] \
|
||||
|| die "bare nvm ls-remote did not annotate aliases in stable columns; got >${OUTPUT}< expected >${EXPECTED_OUTPUT}<"
|
||||
|
||||
ALIASES_COLUMN="$(command printf '%s\n' "${OUTPUT}" | command cut -c56-65 | command sort -u)"
|
||||
[ "_${ALIASES_COLUMN}" = "_$(command printf '\n (Aliases:')" ] \
|
||||
|| die "bare nvm ls-remote did not start every Aliases annotation in the same column; got >${ALIASES_COLUMN}<"
|
||||
|
||||
case "${OUTPUT}" in
|
||||
*'node, stable'* | *unstable,* | *hidden* | *invalid* | *control* | *injected*)
|
||||
die "bare nvm ls-remote showed an alias it should omit; got >${OUTPUT}<"
|
||||
;;
|
||||
esac
|
||||
|
||||
EXPECTED_FILTERED_OUTPUT='v2.0.0'
|
||||
OUTPUT="$(nvm ls-remote 2 --no-colors | command sed 's/^[[:space:]]*//')"
|
||||
[ "_${OUTPUT}" = "_${EXPECTED_FILTERED_OUTPUT}" ] \
|
||||
|| die "filtered nvm ls-remote annotated aliases; got >${OUTPUT}< expected >${EXPECTED_FILTERED_OUTPUT}<"
|
||||
|
||||
OUTPUT="$(nvm ls-remote --lts --no-colors | command sed 's/^[[:space:]]*//')"
|
||||
[ "_${OUTPUT}" = "_${EXPECTED_FILTERED_OUTPUT}" ] \
|
||||
|| die "LTS nvm ls-remote annotated aliases; got >${OUTPUT}< expected >${EXPECTED_FILTERED_OUTPUT}<"
|
||||
|
||||
NVM_REMOTE_STATUS=1
|
||||
OUTPUT="$(nvm ls-remote --no-colors 2>/dev/null)"
|
||||
COMMAND_STATUS=$?
|
||||
NVM_REMOTE_STATUS=0
|
||||
[ "${COMMAND_STATUS}" -eq 1 ] \
|
||||
|| die "failed nvm ls-remote returned ${COMMAND_STATUS}; expected 1"
|
||||
case "${OUTPUT}" in
|
||||
*Latest:* | *Aliases:*) die "failed nvm ls-remote annotated aliases; got >${OUTPUT}<" ;;
|
||||
esac
|
||||
|
||||
nvm_has_colors() { return 0; }
|
||||
nvm_has_italics() { return 0; }
|
||||
|
||||
ESC="$(command printf '\033')"
|
||||
OUTPUT="$(nvm ls-remote)"
|
||||
EXPECTED_LATEST_ROW="$(command printf ' v2.0.0 \033[1;32m (Latest LTS: Iron)\033[0m \033[0;32m (Latest: \033[3mnode\033[23m)\033[0m \033[0;32m (Aliases: \033[3miron-project\033[23m, \033[3mlatest-project\033[23m, \033[3mstable-project\033[23m)\033[0m')"
|
||||
case "${OUTPUT}" in
|
||||
*"${EXPECTED_LATEST_ROW}"*) ;;
|
||||
*) die "colored nvm ls-remote did not use an unbolded color and italic names for aliases; got >${OUTPUT}<" ;;
|
||||
esac
|
||||
|
||||
nvm_has_italics() { return 1; }
|
||||
NO_ITALICS_OUTPUT="$(nvm ls-remote)"
|
||||
nvm_has_italics() { return 0; }
|
||||
EXPECTED_LATEST_ROW="$(command printf ' v2.0.0 \033[1;32m (Latest LTS: Iron)\033[0m \033[0;32m (Latest: node)\033[0m \033[0;32m (Aliases: iron-project, latest-project, stable-project)\033[0m')"
|
||||
case "${NO_ITALICS_OUTPUT}" in
|
||||
*"${EXPECTED_LATEST_ROW}"*) ;;
|
||||
*) die "colored nvm ls-remote used italics on a terminal without italics support; got >${NO_ITALICS_OUTPUT}<" ;;
|
||||
esac
|
||||
|
||||
OUTPUT="$(command printf '%s\n' "${OUTPUT}" | command sed "s/${ESC}\[[0-9;]*m//g")"
|
||||
EXPECTED_OUTPUT="$(command printf '%s\n' "${EXPECTED_OUTPUT}" | command sed 's/^\( v1\.5\.0\) \*/\1 /')"
|
||||
[ "_${OUTPUT}" = "_${EXPECTED_OUTPUT}" ] \
|
||||
|| die "colored nvm ls-remote did not keep annotation columns aligned; got >${OUTPUT}< expected >${EXPECTED_OUTPUT}<"
|
||||
|
||||
nvm_has_colors() { return 1; }
|
||||
nvm_remote_versions() {
|
||||
command printf '%s\n' 'v2.0.0-nightly20240101abcdef0123 Iron *' 'v3.0.0-nightly2024020201234567890abc'
|
||||
}
|
||||
nvm_make_alias 'nightly-project' 'v2.0.0-nightly20240101abcdef0123' >/dev/null
|
||||
OUTPUT="$(nvm ls-remote --no-colors)"
|
||||
ALIASES_COLUMNS="$(command printf '%s\n' "${OUTPUT}" | command awk 'index($0, "(Aliases:") { print index($0, "(Aliases:") }' | command sort -u)"
|
||||
[ "$(command printf '%s\n' "${ALIASES_COLUMNS}" | command wc -l | command tr -d ' ')" = 1 ] \
|
||||
|| die "versions wider than the version column did not keep Aliases annotations in one column; got >${OUTPUT}<"
|
||||
|
||||
cleanup
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup () {
|
||||
rm -rf "$NVM_DIR"
|
||||
unset -f die cleanup nvm_version
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
NVM_DIR="$(mktemp -d)"
|
||||
NODE_BIN="$NVM_DIR/versions/node/v12.0.0/bin"
|
||||
mkdir -p "$NODE_BIN"
|
||||
printf '#!/bin/sh\necho v12.0.0\n' > "$NODE_BIN/node"
|
||||
chmod +x "$NODE_BIN/node"
|
||||
|
||||
# the slow path resolves through nvm_version; make it fail so only the fast path can succeed
|
||||
nvm_version() {
|
||||
return 42
|
||||
}
|
||||
|
||||
nvm use --silent v12.0.0 || die 'nvm use with an exact, installed version should not need nvm_version'
|
||||
case ":${PATH}:" in
|
||||
*":${NODE_BIN}:"*) ;;
|
||||
*) die "nvm use did not put ${NODE_BIN} on PATH; got ${PATH}" ;;
|
||||
esac
|
||||
|
||||
! nvm use --silent v12 2>/dev/null || die 'nvm use with a partial version should still resolve it through nvm_version'
|
||||
|
||||
cleanup
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
EXPECTED="$(\cd .. && pwd)"
|
||||
|
||||
# a user-defined `cd` function that rejects nvm's arguments, like cdhist's (#3835)
|
||||
cd() {
|
||||
echo 'user-defined cd function was called' >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
OUTPUT="$(nvm_cd .. 2>&1 && pwd)"
|
||||
[ "${OUTPUT}" = "${EXPECTED}" ] || die "expected '${EXPECTED}', got '${OUTPUT}'"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
OUTPUT="$(nvm_cd ${NVM_CD_FLAGS} .. 2>&1 && pwd)"
|
||||
[ "${OUTPUT}" = "${EXPECTED}" ] || die "with NVM_CD_FLAGS '${NVM_CD_FLAGS}', expected '${EXPECTED}', got '${OUTPUT}'"
|
||||
|
||||
unset -f cd
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
for DIR in '/tmp/a&b/.nvm' '/tmp/a#b/.nvm' '/tmp/a[1]/.nvm' '/tmp/a*b/.nvm' '/tmp/a+(b)/.nvm' '/tmp/a.b/.nvm' '/tmp/a\b/.nvm' '/tmp/a$b/.nvm' '/tmp/a|b/.nvm'; do
|
||||
NVM_DIR="${DIR}"
|
||||
TEST_PATH="/opt/bin:${NVM_DIR}/versions/node/v4.5.0/bin:/usr/bin"
|
||||
|
||||
# replace branch
|
||||
NEW_PATH="$(nvm_change_path "${TEST_PATH}" "/bin" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||
EXPECTED="/opt/bin:${NVM_DIR}/versions/node/v7.1.0/bin:/usr/bin"
|
||||
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR}: expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||
|
||||
# system path precedes the nvm path: prepend instead of replacing
|
||||
SYSTEM_FIRST_PATH="/usr/bin:${NVM_DIR}/versions/node/v4.5.0/bin"
|
||||
NEW_PATH="$(nvm_change_path "${SYSTEM_FIRST_PATH}" "/bin" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||
EXPECTED="${NVM_DIR}/versions/node/v7.1.0/bin:${SYSTEM_FIRST_PATH}"
|
||||
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR} (system path first): expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||
|
||||
# MANPATH call
|
||||
TEST_MANPATH="/opt/share/man:${NVM_DIR}/versions/node/v4.5.0/share/man:/usr/share/man"
|
||||
NEW_PATH="$(nvm_change_path "${TEST_MANPATH}" "/share/man" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||
EXPECTED="/opt/share/man:${NVM_DIR}/versions/node/v7.1.0/share/man:/usr/share/man"
|
||||
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR} (MANPATH): expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||
done
|
||||
|
||||
# `.` in NVM_DIR must be escaped so it matches itself only, not "any character":
|
||||
# an unrelated path entry that merely looks similar (some other char where the
|
||||
# `.` is) must be left alone, not mistaken for an nvm path and rewritten.
|
||||
NVM_DIR="/tmp/a.b/.nvm"
|
||||
UNRELATED_PATH="/opt/bin:/tmp/aXb/.nvm/versions/node/v4.5.0/bin:/usr/bin"
|
||||
NEW_PATH="$(nvm_change_path "${UNRELATED_PATH}" "/bin" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||
EXPECTED="${NVM_DIR}/versions/node/v7.1.0/bin:${UNRELATED_PATH}"
|
||||
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "unrelated PATH entry /tmp/aXb/.nvm/... should not be touched by NVM_DIR=${NVM_DIR}: expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||
@@ -49,6 +49,20 @@ nvm_has() { return 0; }
|
||||
OUTPUT="$(nvm_die_on_prefix 0 foo "$(nvm_version_dir new)" 2>&1)"
|
||||
[ -z "$OUTPUT" ] || die "'nvm_die_on_prefix' was not a noop when prefix is good; got '$OUTPUT'"
|
||||
|
||||
# `nvm_get_os` runs `uname`; it is only needed when an `NPM_CONFIG_PREFIX`
|
||||
# variable is set, so with none set it must not run at all
|
||||
MOCK_BIN="${TEST_DIR}/bin"
|
||||
mkdir -p "${MOCK_BIN}"
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo 'echo "uname was called" >&2'
|
||||
echo 'exit 1'
|
||||
} > "${MOCK_BIN}/uname"
|
||||
chmod +x "${MOCK_BIN}/uname"
|
||||
unset NPM_CONFIG_PREFIX npm_config_prefix npm_CONFIG_PREFIX
|
||||
OUTPUT="$(PATH="${MOCK_BIN}:${PATH}" nvm_die_on_prefix 0 foo "$(nvm_version_dir new)" 2>&1)"
|
||||
[ -z "$OUTPUT" ] || die "'nvm_die_on_prefix' detected the OS (ran uname) when no NPM_CONFIG_PREFIX was set; got '$OUTPUT'"
|
||||
|
||||
mkdir -p "$(nvm_version_dir new)"
|
||||
ln -s "$(nvm_version_dir new)" "$TEST_DIR/node"
|
||||
|
||||
@@ -187,4 +201,55 @@ Run \`foo\` to unset it."
|
||||
[ "_$CAPTURED_EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with user .npmrc that has globalconfig did not exit with 10; got '$CAPTURED_EXIT_CODE'"
|
||||
)
|
||||
|
||||
# nvm_npmrc_bad_news_bears matches `^(prefix|globalconfig) *=`
|
||||
NPMRC="${TEST_DIR}/bad-news.npmrc"
|
||||
printf 'prefix=x\n' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'prefix=x should be bad news'
|
||||
printf 'prefix =x\n' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'prefix =x should be bad news'
|
||||
printf 'prefix =x\n' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'prefix =x should be bad news'
|
||||
printf 'prefix =\n' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'prefix = should be bad news'
|
||||
printf 'foo=1\nprefix = y\n' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'a matching line after a non-matching line should be bad news'
|
||||
printf 'prefix=x' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'prefix=x without a trailing newline should be bad news'
|
||||
printf 'globalconfig=x\n' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'globalconfig=x should be bad news'
|
||||
printf 'globalconfig = x\n' > "${NPMRC}"
|
||||
nvm_npmrc_bad_news_bears "${NPMRC}" || die 'globalconfig = x should be bad news'
|
||||
|
||||
printf 'prefixfoo=x\n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'prefixfoo=x should not be bad news'
|
||||
printf 'globalconfigfoo=x\n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'globalconfigfoo=x should not be bad news'
|
||||
printf 'prefix x=x\n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'prefix x=x should not be bad news'
|
||||
printf 'prefix\t=x\n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'a tab before = should not be bad news'
|
||||
printf ' prefix=x\n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'a leading space should not be bad news'
|
||||
printf 'Prefix=x\n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'an uppercase key should not be bad news'
|
||||
printf 'prefix\n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'a bare prefix should not be bad news'
|
||||
printf 'prefix \n' > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'trailing spaces without = should not be bad news'
|
||||
: > "${NPMRC}"
|
||||
! nvm_npmrc_bad_news_bears "${NPMRC}" || die 'an empty npmrc should not be bad news'
|
||||
! nvm_npmrc_bad_news_bears || die 'a missing argument should not be bad news'
|
||||
! nvm_npmrc_bad_news_bears "${TEST_DIR}/nonexistent.npmrc" || die 'a missing file should not be bad news'
|
||||
|
||||
# the npmrc scan must not fork `grep`
|
||||
(
|
||||
rm -f "${TEST_DIR}/grep-was-called"
|
||||
nvm_grep() { : > "${TEST_DIR}/grep-was-called"; return 1; }
|
||||
printf 'globalconfig = nope\n' > "${TEST_DIR}/probe.npmrc"
|
||||
NVM_PROBE_EXIT=0
|
||||
nvm_npmrc_bad_news_bears "${TEST_DIR}/probe.npmrc" || NVM_PROBE_EXIT=$?
|
||||
[ ! -e "${TEST_DIR}/grep-was-called" ] || exit 10
|
||||
[ "${NVM_PROBE_EXIT}" = 0 ] || exit 11
|
||||
) || die 'nvm_npmrc_bad_news_bears must read npmrc files without calling nvm_grep'
|
||||
|
||||
cleanup
|
||||
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
cleanup() {
|
||||
rm -rf "${TEST_DIR}"
|
||||
}
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
TEST_DIR="$(pwd)/nvm_echo_tmp"
|
||||
[ ! -e "${TEST_DIR}" ] && mkdir "${TEST_DIR}"
|
||||
|
||||
# a `printf` that reports that it ran: `nvm_echo` must use a shell builtin
|
||||
# rather than resolving `printf` through `PATH` (zsh's `command` skips the
|
||||
# builtin and runs `/usr/bin/printf` on every call)
|
||||
MOCK_BIN="${TEST_DIR}/bin"
|
||||
mkdir -p "${MOCK_BIN}"
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo "echo 'MOCKED-PRINTF'"
|
||||
} > "${MOCK_BIN}/printf"
|
||||
chmod +x "${MOCK_BIN}/printf"
|
||||
|
||||
OUTPUT="$(nvm_echo 'hello world')"
|
||||
[ "${OUTPUT}" = 'hello world' ] || die "expected 'hello world', got '${OUTPUT}'"
|
||||
|
||||
OUTPUT="$(nvm_echo)"
|
||||
[ -z "${OUTPUT}" ] || die "expected no output, got '${OUTPUT}'"
|
||||
|
||||
OUTPUT="$(PATH="${MOCK_BIN}:${PATH}" nvm_echo 'no fork, please' 2>&1)"
|
||||
[ "${OUTPUT}" = 'no fork, please' ] || die "for nvm_echo, expected 'no fork, please', got '${OUTPUT}'"
|
||||
|
||||
OUTPUT="$(PATH="${MOCK_BIN}:${PATH}" nvm_echo_with_colors 'no fork, please' 2>&1)"
|
||||
[ "${OUTPUT}" = 'no fork, please' ] || die "for nvm_echo_with_colors, expected 'no fork, please', got '${OUTPUT}'"
|
||||
|
||||
cleanup
|
||||
@@ -21,6 +21,10 @@ MOCKS_DIR="$(pwd)/../../mocks"
|
||||
# binaries
|
||||
export PATH=".:${PATH}"
|
||||
|
||||
# `nvm_get_os` reads `/etc/os-release` where it is available; this test drives
|
||||
# the OS through the mocked `uname`, so point it at a file that does not exist
|
||||
export NVM_OS_RELEASE="${MOCKS_DIR}/nonexistent-os-release"
|
||||
|
||||
# Setups mock binaries for a given OS and arch that mimic
|
||||
# the output of the real binaries used by nvm_get_arch to guess
|
||||
# the architecture of a given system.
|
||||
@@ -90,10 +94,10 @@ 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
|
||||
# These smartos cases have no arch-specific uname mock, so they fall through to
|
||||
# the real host: they only hold on an x86_64 host, and not on Alpine, where the
|
||||
# host picks up the musl suffix these non-musl assertions do not expect.
|
||||
if [ ! -f "/etc/alpine-release" ] && [ "$(command uname -m)" = 'x86_64' ]; then
|
||||
run_test arm64 smartos x64
|
||||
run_test armv8l smartos x64
|
||||
fi
|
||||
|
||||
@@ -15,6 +15,10 @@ die () { cleanup; echo "$@" ; exit 1; }
|
||||
MOCKS_DIR="$(pwd)/../../mocks"
|
||||
export PATH=".:${PATH}"
|
||||
|
||||
# `nvm_get_os` reads `/etc/os-release` where it is available; this test drives
|
||||
# the OS through the mocked `uname`, so point it at a file that does not exist
|
||||
export NVM_OS_RELEASE="${MOCKS_DIR}/nonexistent-os-release"
|
||||
|
||||
# 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 non-Alpine, neither should get -musl.
|
||||
|
||||
@@ -63,6 +63,8 @@ setup_chroot() {
|
||||
# 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')"
|
||||
elif [ "$(command uname -s)" != 'Linux' ] || [ "$(command uname -m)" != 'x86_64' ] || [ ! -e /lib64/ld-linux-x86-64.so.2 ]; then
|
||||
echo "not on x86_64 glibc Linux; skipping chroot arch checks"
|
||||
else
|
||||
setup_chroot "${CHROOT_WITH_ALPINE}"
|
||||
setup_chroot "${CHROOT_WITHOUT_ALPINE}"
|
||||
|
||||
Executable
+149
@@ -0,0 +1,149 @@
|
||||
#!/bin/sh
|
||||
|
||||
TEST_PWD=$(pwd)
|
||||
TEST_DIR="${TEST_PWD}/nvm_get_os_tmp"
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
cleanup() {
|
||||
rm -rf "${TEST_DIR}"
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "$@"
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ ! -e "${TEST_DIR}" ] && mkdir "${TEST_DIR}"
|
||||
|
||||
OS_RELEASE="${TEST_DIR}/os-release"
|
||||
MISSING_OS_RELEASE="${TEST_DIR}/nonexistent-os-release"
|
||||
|
||||
# a `uname` that reports that it ran (and fails), so that any test can prove
|
||||
# whether `nvm_get_os` forked it
|
||||
MOCK_BIN="${TEST_DIR}/bin"
|
||||
mkdir -p "${MOCK_BIN}"
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo 'echo "uname was called" >&2'
|
||||
echo 'exit 1'
|
||||
} > "${MOCK_BIN}/uname"
|
||||
chmod +x "${MOCK_BIN}/uname"
|
||||
|
||||
# asserts that `nvm_get_os` maps the os-release file to $EXPECTED, without
|
||||
# forking `uname`; the kernel is mocked as Linux, so Linux distro `ID`s pass on any host
|
||||
KERNEL_LINUX="${TEST_DIR}/ostype-linux"
|
||||
echo 'Linux' > "${KERNEL_LINUX}"
|
||||
assert_os_release() {
|
||||
local DESCRIPTION
|
||||
DESCRIPTION="${1}"
|
||||
local EXPECTED
|
||||
EXPECTED="${2}"
|
||||
local OUTPUT
|
||||
NVM_OS_RELEASE="${OS_RELEASE}"
|
||||
NVM_OS_KERNEL_OSTYPE="${KERNEL_LINUX}"
|
||||
export NVM_OS_RELEASE NVM_OS_KERNEL_OSTYPE
|
||||
OUTPUT="$(PATH="${MOCK_BIN}:${PATH}" nvm_get_os 2>&1)"
|
||||
unset NVM_OS_RELEASE NVM_OS_KERNEL_OSTYPE
|
||||
case "${OUTPUT}" in
|
||||
*'uname was called'*) die "for ${DESCRIPTION}, \`nvm_get_os\` forked \`uname\` despite \`${OS_RELEASE}\` being readable" ;;
|
||||
esac
|
||||
[ "${OUTPUT}" = "${EXPECTED}" ] || die "for ${DESCRIPTION}, expected '${EXPECTED}', got '${OUTPUT}'"
|
||||
}
|
||||
|
||||
printf 'NAME=Ubuntu\nID=ubuntu\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'a distro-style os-release' 'linux'
|
||||
|
||||
printf 'NAME="Arch Linux"\nID=arch\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'an os-release with a quoted NAME' 'linux'
|
||||
|
||||
printf 'NAME=FreeBSD\nVERSION=14.0-RELEASE\nID=freebsd\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'the FreeBSD 13.2+ os-release' 'freebsd'
|
||||
|
||||
printf 'ID=freebsd' > "${OS_RELEASE}"
|
||||
assert_os_release 'an os-release whose ID has no trailing newline' 'freebsd'
|
||||
|
||||
printf 'NAME=MSYS2\nID=msys2\nPRETTY_NAME="MSYS2"\nID_LIKE="cygwin arch"\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'the MSYS2 os-release' 'win'
|
||||
|
||||
printf 'NAME="Oracle Solaris"\nPRETTY_NAME="Oracle Solaris 11.4"\nCPE_NAME="cpe:/o:oracle:solaris:11:4"\nID=solaris\nVERSION=11.4\nVERSION_ID=11.4\nBUILD_ID=11.4.0.0.1.15.0\nHOME_URL="https://www.oracle.com/solaris/"\nSUPPORT_URL="https://support.oracle.com/"\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'the Oracle Solaris 11.4 os-release' 'sunos'
|
||||
|
||||
printf 'NAME="OmniOS"\nPRETTY_NAME="OmniOS Community Edition v11 r151048"\nCPE_NAME="cpe:/o:omniosce:omnios:11:151048:0"\nID=omnios\nVERSION=r151048\nVERSION_ID=r151048\nBUILD_ID=151048.0.2023.11.04\nHOME_URL="https://omnios.org/"\nSUPPORT_URL="https://omnios.org/"\nBUG_REPORT_URL="https://github.com/omniosorg/omnios-build/issues/new"\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'the OmniOS r151048 os-release' 'sunos'
|
||||
|
||||
printf 'ID=openindiana\nID_LIKE="illumos solaris"\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'an OpenIndiana os-release' 'sunos'
|
||||
|
||||
printf 'ID=smartos\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'a SmartOS os-release' 'sunos'
|
||||
|
||||
printf 'ID=illumos\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'an illumos os-release' 'sunos'
|
||||
|
||||
printf 'ID=ghostbsd\nID_LIKE=freebsd\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'a GhostBSD os-release' 'freebsd'
|
||||
|
||||
printf 'ID=freebsd # a comment\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'an ID followed by whitespace and a comment' 'freebsd'
|
||||
|
||||
# the file is read, not sourced: nothing in it runs
|
||||
printf 'exit 3\nID=ubuntu\n' > "${OS_RELEASE}"
|
||||
assert_os_release 'an os-release containing shell code' 'linux'
|
||||
|
||||
# a file without an `ID`, or no file at all, falls back to parsing `uname -a`
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo 'echo "${NVM_MOCK_UNAME}"'
|
||||
} > "${MOCK_BIN}/uname"
|
||||
chmod +x "${MOCK_BIN}/uname"
|
||||
|
||||
assert_uname_os() {
|
||||
local UNAME_OUTPUT
|
||||
UNAME_OUTPUT="${1}"
|
||||
local OS_RELEASE_FILE
|
||||
OS_RELEASE_FILE="${2}"
|
||||
local EXPECTED
|
||||
EXPECTED="${3}"
|
||||
local OUTPUT
|
||||
NVM_OS_RELEASE="${OS_RELEASE_FILE}"
|
||||
NVM_OS_KERNEL_OSTYPE="${MISSING_OS_RELEASE}"
|
||||
export NVM_OS_RELEASE NVM_OS_KERNEL_OSTYPE
|
||||
NVM_MOCK_UNAME="${UNAME_OUTPUT}"
|
||||
export NVM_MOCK_UNAME
|
||||
OUTPUT="$(PATH="${MOCK_BIN}:${PATH}" nvm_get_os)"
|
||||
unset NVM_OS_RELEASE NVM_OS_KERNEL_OSTYPE NVM_MOCK_UNAME
|
||||
[ "${OUTPUT}" = "${EXPECTED}" ] || die "for uname -a '${UNAME_OUTPUT}', expected '${EXPECTED}', got '${OUTPUT}'"
|
||||
}
|
||||
|
||||
printf 'NAME=OnlyAName\n' > "${OS_RELEASE}"
|
||||
assert_uname_os 'Linux host 6.1.0 #1 SMP x86_64 GNU/Linux' "${OS_RELEASE}" 'linux'
|
||||
assert_uname_os 'Darwin host 23.1.0 arm64' "${OS_RELEASE}" 'darwin'
|
||||
assert_uname_os 'SunOS host 5.11' "${OS_RELEASE}" 'sunos'
|
||||
assert_uname_os 'FreeBSD host 14.0-RELEASE' "${OS_RELEASE}" 'freebsd'
|
||||
assert_uname_os 'OpenBSD host 7.4' "${OS_RELEASE}" 'openbsd'
|
||||
assert_uname_os 'AIX host 3 7' "${OS_RELEASE}" 'aix'
|
||||
assert_uname_os 'CYGWIN_NT-10.0 host' "${OS_RELEASE}" 'win'
|
||||
assert_uname_os 'MINGW64_NT-10.0 host' "${OS_RELEASE}" 'win'
|
||||
assert_uname_os 'MSYS_NT-10.0 host' "${OS_RELEASE}" 'win'
|
||||
assert_uname_os 'Darwin host 23.1.0 arm64' "${MISSING_OS_RELEASE}" 'darwin'
|
||||
|
||||
# a pre-set `ID` in the environment must not be mistaken for the file's `ID`
|
||||
ID=1000
|
||||
export ID
|
||||
assert_uname_os 'Darwin host 23.1.0 arm64' "${OS_RELEASE}" 'darwin'
|
||||
unset ID
|
||||
|
||||
# DragonFly BSD has no `nvm` OS of its own: fall back to `uname -a` as before
|
||||
printf 'NAME="DragonFly"\nID=dragonfly\n' > "${OS_RELEASE}"
|
||||
assert_uname_os 'DragonFly host 6.4-RELEASE' "${OS_RELEASE}" ''
|
||||
|
||||
# an unlisted `ID` on a non-Linux kernel is not assumed to be Linux
|
||||
printf 'NAME=HardenedBSD\nID=hardenedbsd\n' > "${OS_RELEASE}"
|
||||
assert_uname_os 'FreeBSD host 14.0-STABLE' "${OS_RELEASE}" 'freebsd'
|
||||
printf 'NAME=Helios\nID=helios\n' > "${OS_RELEASE}"
|
||||
assert_uname_os 'SunOS host 5.11' "${OS_RELEASE}" 'sunos'
|
||||
|
||||
cleanup
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="${PWD}/nvm-has-italics-work.$$"
|
||||
FAKE_BIN="${WORK}/bin"
|
||||
TPUT_LOG="${WORK}/tput.log"
|
||||
|
||||
cleanup() {
|
||||
command rm -rf "${WORK}"
|
||||
unset -f cleanup die nvm_has_colors
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
nvm_has_colors() { return 0; }
|
||||
|
||||
command mkdir -p "${FAKE_BIN}"
|
||||
command cat > "${FAKE_BIN}/tput" <<EOF
|
||||
#!/bin/sh
|
||||
echo "\$*" > "${TPUT_LOG}"
|
||||
command printf '\033[3m'
|
||||
exit "\${FAKE_TPUT_STATUS-0}"
|
||||
EOF
|
||||
command chmod +x "${FAKE_BIN}/tput"
|
||||
|
||||
(
|
||||
export PATH="${FAKE_BIN}:${PATH}" TERM=xterm-256color
|
||||
nvm_has_italics
|
||||
) || die 'nvm_has_italics should be true when terminfo has the sitm capability'
|
||||
[ "$(command cat "${TPUT_LOG}")" = '-T xterm-256color sitm' ] \
|
||||
|| die "nvm_has_italics should query sitm for the current TERM; got >$(command cat "${TPUT_LOG}")<"
|
||||
|
||||
OUTPUT="$(
|
||||
export PATH="${FAKE_BIN}:${PATH}" TERM=xterm-256color
|
||||
nvm_has_italics
|
||||
)"
|
||||
[ -z "${OUTPUT}" ] \
|
||||
|| die "nvm_has_italics should not print the escape sequence; got >${OUTPUT}<"
|
||||
|
||||
(
|
||||
export PATH="${FAKE_BIN}:${PATH}" TERM=screen FAKE_TPUT_STATUS=1
|
||||
! nvm_has_italics
|
||||
) || die 'nvm_has_italics should be false when terminfo lacks the sitm capability'
|
||||
|
||||
(
|
||||
export PATH="${FAKE_BIN}:${PATH}" TERM=not-a-terminal FAKE_TPUT_STATUS=3
|
||||
! nvm_has_italics
|
||||
) || die 'nvm_has_italics should be false when the terminal is unknown'
|
||||
|
||||
(
|
||||
export PATH="${FAKE_BIN}:${PATH}"
|
||||
unset TERM
|
||||
nvm_has_italics
|
||||
) || die 'nvm_has_italics should query vt100 when TERM is unset'
|
||||
[ "$(command cat "${TPUT_LOG}")" = '-T vt100 sitm' ] \
|
||||
|| die "nvm_has_italics should fall back to vt100 when TERM is unset; got >$(command cat "${TPUT_LOG}")<"
|
||||
|
||||
(
|
||||
PATH="${WORK}"
|
||||
! nvm_has_italics
|
||||
) || die 'nvm_has_italics should be false when tput is unavailable'
|
||||
|
||||
command rm -f "${TPUT_LOG}"
|
||||
(
|
||||
export PATH="${FAKE_BIN}:${PATH}" TERM=xterm-256color
|
||||
nvm_has_colors() { return 1; }
|
||||
! nvm_has_italics
|
||||
) || die 'nvm_has_italics should be false when colors are disabled'
|
||||
[ ! -f "${TPUT_LOG}" ] \
|
||||
|| die 'nvm_has_italics should not query terminfo when colors are disabled'
|
||||
|
||||
if nvm_has tput; then
|
||||
(
|
||||
export TERM=dumb
|
||||
! nvm_has_italics
|
||||
) || die 'nvm_has_italics should be false for the dumb terminal'
|
||||
fi
|
||||
|
||||
cleanup
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="${PWD}/nvm-inherited-tmpdir-work.$$"
|
||||
|
||||
cleanup() {
|
||||
command rm -rf "${WORK}"
|
||||
unset -f cleanup die nvm_download_artifact
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
nvm_download_artifact() { return 1; }
|
||||
|
||||
make_canary() {
|
||||
command rm -rf "${WORK}/tmp"
|
||||
command mkdir -p "${WORK}/tmp"
|
||||
echo canary > "${WORK}/tmp/canary"
|
||||
}
|
||||
|
||||
make_canary
|
||||
TMPDIR="${WORK}/tmp" nvm_install_binary node std 8.0.0 0 < /dev/null > /dev/null 2>&1
|
||||
[ -f "${WORK}/tmp/canary" ] || die 'nvm_install_binary removed the TMPDIR it inherited after a failed download'
|
||||
|
||||
make_canary
|
||||
TMPDIR="${WORK}/tmp" nvm_install_source node std 8.0.0 < /dev/null > /dev/null 2>&1
|
||||
[ -f "${WORK}/tmp/canary" ] || die 'nvm_install_source removed the TMPDIR it inherited after a failed download'
|
||||
|
||||
cleanup
|
||||
@@ -24,10 +24,13 @@ fi
|
||||
|
||||
cleanup
|
||||
|
||||
SLUG="$(nvm_get_download_slug node binary v0.12.18)"
|
||||
CHECKSUM_LINE="$(nvm_compute_checksum ../../../nvm.sh 2>&1 >/dev/null)"
|
||||
|
||||
OUTPUT="$(TERM=dumb 2>&1 nvm install --no-progress v0.12.18)"
|
||||
EXPECTED_OUTPUT="Downloading and installing node v0.12.18...
|
||||
Downloading https://nodejs.org/dist/v0.12.18/node-v0.12.18-linux-x64.tar.xz...
|
||||
Computing checksum with sha256sum
|
||||
Downloading https://nodejs.org/dist/v0.12.18/${SLUG}.tar.xz...
|
||||
${CHECKSUM_LINE}
|
||||
Checksums matched!
|
||||
Now using node v0.12.18 (npm v2.15.11)
|
||||
Creating default alias: default -> v0.12.18 *"
|
||||
@@ -40,9 +43,9 @@ cleanup
|
||||
|
||||
OUTPUT="$(TERM=dumb 2>&1 nvm install v0.12.18)"
|
||||
EXPECTED_OUTPUT="Downloading and installing node v0.12.18...
|
||||
Downloading https://nodejs.org/dist/v0.12.18/node-v0.12.18-linux-x64.tar.xz...
|
||||
Downloading https://nodejs.org/dist/v0.12.18/${SLUG}.tar.xz...
|
||||
######################################################################### 100.0%
|
||||
Computing checksum with sha256sum
|
||||
${CHECKSUM_LINE}
|
||||
Checksums matched!
|
||||
Now using node v0.12.18 (npm v2.15.11)
|
||||
Creating default alias: default -> v0.12.18 *"
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup () {
|
||||
rm -rf "$NVM_DIR"
|
||||
unset -f die cleanup
|
||||
unset NVM_DIR NODE_PATH VERSION
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
set -ex
|
||||
|
||||
NVM_DIR=$(mktemp -d)
|
||||
NODE_PATH="$NVM_DIR/versions/node"
|
||||
mkdir -p "$NODE_PATH"
|
||||
if [ -z "$NODE_PATH" ]; then
|
||||
die 'Unable to create temporary folder'
|
||||
fi
|
||||
|
||||
# nvm_is_installed_exact_version is available
|
||||
type nvm_is_installed_exact_version > /dev/null 2>&1 || die 'nvm_is_installed_exact_version is not available'
|
||||
|
||||
# nvm_is_installed_exact_version with no parameter fails
|
||||
! nvm_is_installed_exact_version || die 'nvm_is_installed_exact_version without parameter should fail'
|
||||
|
||||
# an exact version that is not installed fails
|
||||
! nvm_is_installed_exact_version 'v12.0.0' || die 'v12.0.0 is not installed and should fail'
|
||||
|
||||
# an exact version whose node binary is not executable fails
|
||||
mkdir -p "$NODE_PATH/v12.0.0/bin"
|
||||
touch "$NODE_PATH/v12.0.0/bin/node"
|
||||
! nvm_is_installed_exact_version 'v12.0.0' || die 'v12.0.0 with a non-executable node should fail'
|
||||
|
||||
# an exact, installed version succeeds
|
||||
chmod +x "$NODE_PATH/v12.0.0/bin/node"
|
||||
nvm_is_installed_exact_version 'v12.0.0' || die 'v12.0.0 is installed and should succeed'
|
||||
|
||||
# something shaped almost like vX.Y.Z fails, even when a directory by that name is installed
|
||||
for VERSION in 'v12.0.0.0' 'v12.0.0.' 'v12..0' 'v.12.0' 'v12.0'; do
|
||||
mkdir -p "$NODE_PATH/$VERSION/bin"
|
||||
touch "$NODE_PATH/$VERSION/bin/node"
|
||||
chmod +x "$NODE_PATH/$VERSION/bin/node"
|
||||
! nvm_is_installed_exact_version "$VERSION" || die "$VERSION is not an exact version and should fail, even when installed"
|
||||
done
|
||||
|
||||
# anything that is not an exact vX.Y.Z fails, even if it would resolve to an installed version
|
||||
for VERSION in '12.0.0' '12' 'v12' 'v12.0' 'v12.0.0-rc.1' 'node' 'stable' 'lts/*' 'iojs-v1.0.0' 'system' 'default'; do
|
||||
! nvm_is_installed_exact_version "$VERSION" || die "$VERSION is not an exact version and should fail"
|
||||
done
|
||||
|
||||
# an alias with the exact same name takes precedence, so the fast path must not apply
|
||||
mkdir -p "$(nvm_alias_path)"
|
||||
echo 'v12.0.0' > "$(nvm_alias_path)/v12.0.0"
|
||||
! nvm_is_installed_exact_version 'v12.0.0' || die 'an alias named v12.0.0 should take precedence'
|
||||
|
||||
cleanup
|
||||
@@ -41,3 +41,22 @@ nvm_is_valid_version v9.9.1-nightly201803256591d9f761 || die "nvm_is_valid_versi
|
||||
nvm_is_valid_version v9.0.0-v8-canary201710301f8f3dcf36 || die "nvm_is_valid_version v9.0.0-v8-canary201710301f8f3dcf36 did not return 0"
|
||||
nvm_is_valid_version v8.16.2-test201910293843c56739f || die "nvm_is_valid_version v8.16.2-test201910293843c56739f did not return 0"
|
||||
nvm_is_valid_version iojs-v3.2.1-nightly201508291a531b4e44 || die "nvm_is_valid_version iojs-v3.2.1-nightly201508291a531b4e44 did not return 0"
|
||||
|
||||
# `nvm_is_valid_version` must not fork a command for a plain version: mock
|
||||
# `awk`, which the version comparisons used, and fail if it runs. (Prereleases
|
||||
# still reach the comparison, which is why this checks a release version.)
|
||||
TEST_DIR="$(pwd)/nvm_is_valid_version_tmp"
|
||||
[ ! -e "${TEST_DIR}" ] && mkdir "${TEST_DIR}"
|
||||
MOCK_BIN="${TEST_DIR}/bin"
|
||||
mkdir -p "${MOCK_BIN}"
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo 'echo "awk was called" >&2'
|
||||
echo 'exit 1'
|
||||
} > "${MOCK_BIN}/awk"
|
||||
chmod +x "${MOCK_BIN}/awk"
|
||||
|
||||
PATH="${MOCK_BIN}:${PATH}" nvm_is_valid_version v18.20.0 \
|
||||
|| die 'nvm_is_valid_version v18.20.0 forked a command'
|
||||
|
||||
rm -rf "${TEST_DIR}"
|
||||
|
||||
@@ -45,6 +45,47 @@ type nvm_is_version_installed > /dev/null 2>&1 || die 'nvm_is_version_installed
|
||||
|
||||
check_version '12.0.0' 'node'
|
||||
|
||||
# the pre-0.12 layout is still found via the fallback
|
||||
OLD_VERSION='0.11.15'
|
||||
mkdir -p "$NVM_DIR/$OLD_VERSION/bin"
|
||||
touch "$NVM_DIR/$OLD_VERSION/bin/node"
|
||||
chmod +x "$NVM_DIR/$OLD_VERSION/bin/node"
|
||||
nvm_is_version_installed "$OLD_VERSION" || die "nvm_is_version_installed $OLD_VERSION should work with the pre-0.12 layout"
|
||||
|
||||
# a pre-0.12 version does not count as installed from the modern layout
|
||||
for STRAY_VERSION in '0.10.48' 'v0.10.48'; do
|
||||
mkdir -p "$NODE_PATH/$STRAY_VERSION/bin"
|
||||
touch "$NODE_PATH/$STRAY_VERSION/bin/node"
|
||||
chmod +x "$NODE_PATH/$STRAY_VERSION/bin/node"
|
||||
! nvm_is_version_installed "$STRAY_VERSION" || die "nvm_is_version_installed $STRAY_VERSION should not find a pre-0.12 version in the modern layout"
|
||||
done
|
||||
|
||||
# io.js versions live in a separate tree and are still found via the fallback
|
||||
IOJS_VERSION='v2.0.0'
|
||||
mkdir -p "$NVM_DIR/versions/io.js/$IOJS_VERSION/bin"
|
||||
touch "$NVM_DIR/versions/io.js/$IOJS_VERSION/bin/node"
|
||||
chmod +x "$NVM_DIR/versions/io.js/$IOJS_VERSION/bin/node"
|
||||
nvm_is_version_installed "iojs-$IOJS_VERSION" || die "nvm_is_version_installed iojs-$IOJS_VERSION should work via the fallback"
|
||||
|
||||
# the modern layout is found without shelling out to detect the OS
|
||||
FAST_VERSION='14.0.0'
|
||||
! nvm_is_version_installed "$FAST_VERSION" || die "nvm_is_version_installed $FAST_VERSION should fail before it is installed"
|
||||
nvm_get_os() {
|
||||
die 'nvm_is_version_installed must not call nvm_get_os when the modern-layout binary exists'
|
||||
}
|
||||
mkdir -p "$NODE_PATH/$FAST_VERSION/bin"
|
||||
touch "$NODE_PATH/$FAST_VERSION/bin/node"
|
||||
chmod +x "$NODE_PATH/$FAST_VERSION/bin/node"
|
||||
nvm_is_version_installed "$FAST_VERSION" || die "nvm_is_version_installed $FAST_VERSION should succeed via the fast path"
|
||||
# 0.12.x is the first version in the modern layout
|
||||
for FAST_VERSION in '0.12.18' 'v0.12.18'; do
|
||||
mkdir -p "$NODE_PATH/$FAST_VERSION/bin"
|
||||
touch "$NODE_PATH/$FAST_VERSION/bin/node"
|
||||
chmod +x "$NODE_PATH/$FAST_VERSION/bin/node"
|
||||
nvm_is_version_installed "$FAST_VERSION" || die "nvm_is_version_installed $FAST_VERSION should succeed via the fast path"
|
||||
done
|
||||
unset -f nvm_get_os
|
||||
|
||||
# Checking for Windows
|
||||
nvm_get_os() {
|
||||
echo "win"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
HOME='/home/user'
|
||||
|
||||
for DIR in '/tmp/a&b/.nvm' '/tmp/a#b/.nvm' '/tmp/a[1]/.nvm' '/tmp/a*b/.nvm' '/tmp/a+(b)/.nvm' '/tmp/a.b/.nvm' '/tmp/a\b/.nvm' '/tmp/a$b/.nvm' '/tmp/a|b/.nvm'; do
|
||||
NVM_DIR="${DIR}"
|
||||
|
||||
OUTPUT="$(nvm_sanitize_path "${NVM_DIR}/versions/node/v4.5.0")"
|
||||
EXPECTED='${NVM_DIR}/versions/node/v4.5.0'
|
||||
[ "${OUTPUT}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR}: expected >${EXPECTED}<, got >${OUTPUT}<"
|
||||
|
||||
OUTPUT="$(nvm_sanitize_path "${NVM_DIR}")"
|
||||
[ "${OUTPUT}" = "${NVM_DIR}" ] || die "NVM_DIR=${DIR}: path equal to NVM_DIR should be left alone, got >${OUTPUT}<"
|
||||
done
|
||||
|
||||
# `.` in NVM_DIR must match itself only, not any character
|
||||
NVM_DIR='/tmp/a.b/.nvm'
|
||||
OUTPUT="$(nvm_sanitize_path '/tmp/aXb/.nvm/foo')"
|
||||
[ "${OUTPUT}" = '/tmp/aXb/.nvm/foo' ] || die "unrelated path was rewritten: >${OUTPUT}<"
|
||||
|
||||
# HOME is replaced too
|
||||
NVM_DIR='/tmp/a#b/.nvm'
|
||||
OUTPUT="$(nvm_sanitize_path "${HOME}/.npmrc")"
|
||||
[ "${OUTPUT}" = '${HOME}/.npmrc' ] || die "expected >\${HOME}/.npmrc<, got >${OUTPUT}<"
|
||||
@@ -1,6 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
cleanup () {
|
||||
rm tmp-alias
|
||||
if [ "${CASE_SENSITIVE-}" = '1' ]; then
|
||||
rm tmp/TREE/node
|
||||
rmdir tmp/TREE
|
||||
fi
|
||||
rm tmp/tree/node
|
||||
rmdir tmp/tree
|
||||
rm tmp/node
|
||||
rmdir tmp
|
||||
rm tmp2/node
|
||||
@@ -15,6 +22,15 @@ mkdir -p tmp
|
||||
touch tmp/node
|
||||
mkdir -p tmp2
|
||||
touch tmp2/node
|
||||
mkdir -p tmp/tree
|
||||
touch tmp/tree/node
|
||||
ln -s tmp tmp-alias
|
||||
CASE_SENSITIVE=0
|
||||
if [ ! -d tmp/TREE ]; then
|
||||
CASE_SENSITIVE=1
|
||||
mkdir tmp/TREE
|
||||
touch tmp/TREE/node
|
||||
fi
|
||||
|
||||
[ "$(nvm_tree_contains_path 2>&1)" = "both the tree and the node path are required" ] || die 'incorrect error message with no args'
|
||||
[ "$(nvm_tree_contains_path > /dev/null 2>&1 ; echo $?)" = "2" ] || die 'incorrect error code with no args'
|
||||
@@ -36,4 +52,18 @@ nvm_tree_contains_path / /tmp/node || die '"/" should contain "/tmp/node"'
|
||||
nvm_tree_contains_path "tmp[glob]" "tmp[glob]/node" || die '"tmp[glob]" should contain "tmp[glob]/node"'
|
||||
nvm_tree_contains_path "tmp" "tmp[glob]/node" && die '"tmp" should not contain "tmp[glob]/node"'
|
||||
|
||||
if [ "${CASE_SENSITIVE}" = '1' ]; then
|
||||
nvm_tree_contains_path tmp/tree tmp/TREE/node && die 'case-sensitive directories should stay distinct'
|
||||
nvm_tree_contains_path tmp/TREE tmp/tree/node && die 'case-sensitive directories should stay distinct'
|
||||
else
|
||||
nvm_tree_contains_path tmp/tree tmp/TREE/node || die 'case-insensitive directories should match'
|
||||
nvm_tree_contains_path tmp/TREE tmp/tree/node || die 'case-insensitive directories should match'
|
||||
fi
|
||||
|
||||
nvm_tree_contains_path tmp-alias tmp/node || die 'a symlink to the tree should contain its node'
|
||||
nvm_tree_contains_path tmp tmp-alias/node || die 'the tree should contain a node reached through a symlink'
|
||||
nvm_tree_contains_path tmp-alias tmp2/node && die 'a symlink should not contain a sibling tree'
|
||||
|
||||
nvm_tree_contains_path nonexistent NONEXISTENT/node && die 'missing directories should not be case folded'
|
||||
|
||||
cleanup
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
cleanup () {
|
||||
unset -f nvm_ls_current nvm_ls
|
||||
unset -f nvm_ls_current nvm_ls nvm_is_version_installed
|
||||
}
|
||||
|
||||
: nvm.sh
|
||||
@@ -48,3 +48,30 @@ try nvm_version foo
|
||||
EXPECTED_OUTPUT="N/A"
|
||||
[ "_$CAPTURED_STDOUT" = "_$EXPECTED_OUTPUT" ] || die '"nvm_version" did not return N/A when nvm_ls returns nothing'
|
||||
[ "_$CAPTURED_EXIT_CODE" = "_3" ] || die '"nvm_version" returning N/A did not exit code with code 3'
|
||||
|
||||
# an exact installed version resolves without forking "nvm_ls"
|
||||
nvm_is_version_installed() { return 0; }
|
||||
nvm_ls() { die '"nvm_version" must not call "nvm_ls" for an exact installed version'; }
|
||||
try nvm_version v1.2.3
|
||||
EXPECTED_OUTPUT="v1.2.3"
|
||||
[ "_$CAPTURED_STDOUT" = "_$EXPECTED_OUTPUT" ] || die '"nvm_version v1.2.3" did not return the version itself; got '"$CAPTURED_STDOUT"
|
||||
[ "_$CAPTURED_EXIT_CODE" = "_0" ] || die '"nvm_version v1.2.3" did not exit 0; got '"$CAPTURED_EXIT_CODE"
|
||||
|
||||
# an exact version that is not installed still falls back to "nvm_ls"
|
||||
nvm_is_version_installed() { return 1; }
|
||||
nvm_ls() { echo "pattern: $1"; }
|
||||
try nvm_version v1.2.3
|
||||
EXPECTED_OUTPUT="pattern: v1.2.3"
|
||||
[ "_$CAPTURED_STDOUT" = "_$EXPECTED_OUTPUT" ] || die '"nvm_version v1.2.3" did not fall back to "nvm_ls"; got '"$CAPTURED_STDOUT"
|
||||
[ "_$CAPTURED_EXIT_CODE" = "_0" ] || die '"nvm_version v1.2.3" did not exit 0; got '"$CAPTURED_EXIT_CODE"
|
||||
|
||||
# only exact, fully-qualified versions take the fast path
|
||||
nvm_is_version_installed() { die '"nvm_version" must not call "nvm_is_version_installed" for non-exact versions'; }
|
||||
nvm_ls() { echo "pattern: $1"; }
|
||||
for PATTERN in v1.2 v1 1.2.3 v1.2.3-rc.1 v1.2.3.4 v1.2.3.; do
|
||||
try nvm_version "$PATTERN"
|
||||
EXPECTED_OUTPUT="pattern: $PATTERN"
|
||||
[ "_$CAPTURED_STDOUT" = "_$EXPECTED_OUTPUT" ] || die "nvm_version $PATTERN did not fall back to nvm_ls; got $CAPTURED_STDOUT"
|
||||
[ "_$CAPTURED_EXIT_CODE" = "_0" ] || die "nvm_version $PATTERN did not exit 0; got $CAPTURED_EXIT_CODE"
|
||||
done
|
||||
unset -f nvm_is_version_installed
|
||||
|
||||
@@ -6,6 +6,8 @@ die () { echo "$@" ; exit 1; }
|
||||
\. ../../../nvm.sh
|
||||
|
||||
nvm_version_greater 0.10.0 0.2.12 || die '"nvm_version_greater 0.10.0 0.2.12" did not return true'
|
||||
nvm_version_greater 0.20.12 0.10.0 || die '"nvm_version_greater 0.20.12 0.10.0" did not return true'
|
||||
nvm_version_greater v18.20.0 v18.19.9 || die '"nvm_version_greater v18.20.0 v18.19.9" did not return true'
|
||||
|
||||
if nvm_version_greater 0.10.0 0.20.12; then
|
||||
die '"nvm_version_greater 0.10.0 0.20.12" returned true'
|
||||
@@ -14,3 +16,40 @@ fi
|
||||
if nvm_version_greater 0.10.0 0.10.0; then
|
||||
die '"nvm_version_greater" returned false for the same two versions'
|
||||
fi
|
||||
|
||||
if nvm_version_greater v18.20.0 v18.20.0; then
|
||||
die '"nvm_version_greater v18.20.0 v18.20.0" returned true for the same two versions'
|
||||
fi
|
||||
|
||||
if nvm_version_greater v18.19.9 v18.20.0; then
|
||||
die '"nvm_version_greater v18.19.9 v18.20.0" returned true'
|
||||
fi
|
||||
|
||||
# a partial version is not a plain `x.y.z`, so it keeps the awk comparison
|
||||
nvm_version_greater 0.10 0.2.12 || die '"nvm_version_greater 0.10 0.2.12" did not return true'
|
||||
|
||||
# the comparison must not write anything to stdout (zsh's `typeset` prints a
|
||||
# local variable when it is redeclared)
|
||||
OUTPUT="$(nvm_version_greater 0.10.0 0.2.12 2>&1)"
|
||||
[ -z "${OUTPUT}" ] || die "nvm_version_greater 0.10.0 0.2.12 wrote output: '${OUTPUT}'"
|
||||
|
||||
# `nvm_version_greater` must not fork a command for a plain `x.y.z` pair:
|
||||
# mock `awk`, which the comparison used, and fail if it runs
|
||||
TEST_DIR="$(pwd)/nvm_version_greater_tmp"
|
||||
[ ! -e "${TEST_DIR}" ] && mkdir "${TEST_DIR}"
|
||||
MOCK_BIN="${TEST_DIR}/bin"
|
||||
mkdir -p "${MOCK_BIN}"
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo 'echo "awk was called" >&2'
|
||||
echo 'exit 1'
|
||||
} > "${MOCK_BIN}/awk"
|
||||
chmod +x "${MOCK_BIN}/awk"
|
||||
|
||||
PATH="${MOCK_BIN}:${PATH}" nvm_version_greater 0.10.0 0.2.12 \
|
||||
|| die 'nvm_version_greater 0.10.0 0.2.12 forked a command'
|
||||
|
||||
PATH="${MOCK_BIN}:${PATH}" nvm_version_greater v18.20.0 v18.19.9 \
|
||||
|| die 'nvm_version_greater v18.20.0 v18.19.9 forked a command'
|
||||
|
||||
rm -rf "${TEST_DIR}"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
: nvm.sh
|
||||
\. ../../../nvm.sh
|
||||
|
||||
nvm_version_greater_than_or_equal_to 0.10.0 0.2.12 || die '"nvm_version_greater_than_or_equal_to 0.10.0 0.2.12" did not return true'
|
||||
|
||||
nvm_version_greater_than_or_equal_to 0.10.0 0.10.0 || die '"nvm_version_greater_than_or_equal_to" did not return true for the same two versions'
|
||||
|
||||
nvm_version_greater_than_or_equal_to v0.10.0 0.10.0 || die '"nvm_version_greater_than_or_equal_to v0.10.0 0.10.0" did not return true'
|
||||
|
||||
if nvm_version_greater_than_or_equal_to 0.10.0 0.20.12; then
|
||||
die '"nvm_version_greater_than_or_equal_to 0.10.0 0.20.12" returned true'
|
||||
fi
|
||||
|
||||
if nvm_version_greater_than_or_equal_to 0.2.12 0.10.0; then
|
||||
die '"nvm_version_greater_than_or_equal_to 0.2.12 0.10.0" returned true'
|
||||
fi
|
||||
|
||||
if nvm_version_greater_than_or_equal_to 0.10.0 ''; then
|
||||
die '"nvm_version_greater_than_or_equal_to" returned true with an empty argument'
|
||||
fi
|
||||
|
||||
if nvm_version_greater_than_or_equal_to '' 0.10.0; then
|
||||
die '"nvm_version_greater_than_or_equal_to" returned true with an empty first argument'
|
||||
fi
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
cleanup() {
|
||||
unset -f install_nvm_from_git install_nvm_as_script nvm_detect_profile nvm_has
|
||||
unset -f nvm_check_global_modules nvm_install_node
|
||||
unset -f setup cleanup die assert_profile
|
||||
unset NVM_ENV METHOD PROFILE
|
||||
rm -rf "${TMPDIR_FOR_TEST-}" 2>/dev/null
|
||||
}
|
||||
|
||||
setup() {
|
||||
NVM_ENV=testing \. ../../install.sh
|
||||
|
||||
# Mock installation functions to do nothing
|
||||
install_nvm_from_git() { :; }
|
||||
install_nvm_as_script() { :; }
|
||||
nvm_check_global_modules() { :; }
|
||||
nvm_install_node() { :; }
|
||||
|
||||
# Mock nvm_has to return true for git
|
||||
nvm_has() {
|
||||
case "$1" in
|
||||
git) return 0 ;;
|
||||
xcode-select) return 1 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
}
|
||||
|
||||
setup
|
||||
|
||||
TMPDIR_FOR_TEST="$(mktemp -d)"
|
||||
TEST_PROFILE="${TMPDIR_FOR_TEST}/.bashrc"
|
||||
EXPECTED_PROFILE="${TMPDIR_FOR_TEST}/expected"
|
||||
COMPLETION_LINE='[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion'
|
||||
|
||||
# runs the installer against $TEST_PROFILE, and compares the whole file, byte for byte, with $EXPECTED_PROFILE.
|
||||
# `nvm_do_install` unsets every `nvm_` function when it finishes, so each run sources the installer and mocks afresh.
|
||||
assert_profile() {
|
||||
setup
|
||||
nvm_detect_profile() {
|
||||
echo "${TEST_PROFILE}"
|
||||
}
|
||||
PROFILE='' METHOD='' NVM_DIR="$(cd ../.. && pwd)" nvm_do_install > /dev/null 2>&1
|
||||
command cmp -s "${TEST_PROFILE}" "${EXPECTED_PROFILE}" || die "$1; got: >$(command cat "${TEST_PROFILE}")<"
|
||||
}
|
||||
|
||||
#
|
||||
# Test: when the profile already sources nvm.sh but does not end with a newline,
|
||||
# the bash_completion line must be appended on its own line, and the profile must end with a newline
|
||||
#
|
||||
command printf '%s\n%s' '. "$HOME/.nvm/nvm.sh"' "alias ll='ls -l'" > "${TEST_PROFILE}"
|
||||
command printf '%s\n%s\n%s\n' '. "$HOME/.nvm/nvm.sh"' "alias ll='ls -l'" "${COMPLETION_LINE}" > "${EXPECTED_PROFILE}"
|
||||
assert_profile 'bash_completion line was not appended on its own line, ending with a newline'
|
||||
|
||||
#
|
||||
# Test: when the profile already ends with a newline, no blank line is added before the bash_completion line
|
||||
#
|
||||
command printf '%s\n%s\n' '. "$HOME/.nvm/nvm.sh"' "alias ll='ls -l'" > "${TEST_PROFILE}"
|
||||
command printf '%s\n%s\n%s\n' '. "$HOME/.nvm/nvm.sh"' "alias ll='ls -l'" "${COMPLETION_LINE}" > "${EXPECTED_PROFILE}"
|
||||
assert_profile 'a profile ending with a newline got an extra blank line before the bash_completion line'
|
||||
|
||||
cleanup
|
||||
@@ -20,6 +20,16 @@ nvm_make_alias() {
|
||||
return 0
|
||||
}
|
||||
|
||||
nvm_get_remote_aliases() {
|
||||
# prevent local aliases from leaking into `nvm ls-remote` mocks
|
||||
return 0
|
||||
}
|
||||
|
||||
nvm_has_italics() {
|
||||
# keep mocks independent of the local terminal's italics support
|
||||
return 0
|
||||
}
|
||||
|
||||
nvm_ls_remote > "$MOCKS_DIR/nvm_ls_remote.txt"
|
||||
nvm_ls_remote_iojs > "$MOCKS_DIR/nvm_ls_remote_iojs.txt"
|
||||
NVM_LTS=* nvm_ls_remote > "$MOCKS_DIR/nvm_ls_remote LTS.txt"
|
||||
|
||||
Reference in New Issue
Block a user