Skip to content

Commit c6497c6

Browse files
committed
Make it work on Windows with WSL, MSYS, Cygwin
1 parent 10399ed commit c6497c6

File tree

4 files changed

+199
-38
lines changed

4 files changed

+199
-38
lines changed

.github/workflows/windows-npm.yml

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: 'Tests on Windows: `nvm install`'
2+
3+
on: [pull_request, push]
4+
5+
jobs:
6+
node:
7+
name: 'MSYS fail prefix nvm install'
8+
runs-on: windows-latest
9+
steps:
10+
- name: Retrieve nvm
11+
shell: bash
12+
run: |
13+
mkdir vp "$HOME/.nvm"
14+
curl -sSLo "$HOME/.nvm/nvm.sh" "https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/${GITHUB_SHA}/nvm.sh"
15+
source "$HOME/.nvm/nvm.sh"
16+
! nvm install --lts
17+
18+
nodes:
19+
name: 'MSYS nvm install'
20+
runs-on: windows-latest
21+
strategy:
22+
matrix:
23+
npm-node-version:
24+
- '--lts'
25+
- '--default 12'
26+
- '--no-progress 10'
27+
28+
steps:
29+
- name: Retrieve nvm
30+
shell: bash
31+
run: |
32+
mkdir vp "$HOME/.nvm"
33+
curl -sSLo "$HOME/.nvm/nvm.sh" "https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/${GITHUB_SHA}/nvm.sh"
34+
source "$HOME/.nvm/nvm.sh"
35+
unset npm_config_prefix
36+
nvm install ${{ matrix.npm-node-version }}
37+
38+
wsl_nodes:
39+
name: 'WSL nvm install'
40+
runs-on: windows-latest
41+
strategy:
42+
matrix:
43+
wsl-distrib:
44+
- Debian
45+
- Alpine
46+
- Ubuntu-18.04
47+
npm-node-version:
48+
- '--lts'
49+
- '11'
50+
steps:
51+
- uses: Vampire/setup-wsl@v1
52+
with:
53+
distribution: ${{ matrix.wsl-distrib }}
54+
additional-packages: bash curl ca-certificates
55+
- name: Retrieve nvm on WSL
56+
shell: wsl-bash {0}
57+
env:
58+
GITHUB_REPOSITORY: ${{ github.repository }}
59+
GITHUB_SHA: github.sha
60+
run: |
61+
mkdir -p "$HOME/.nvm"
62+
curl -sSLo "$HOME/.nvm/nvm.sh" "https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/nvm.sh"
63+
source "$HOME/.nvm/nvm.sh"
64+
nvm install ${{ matrix.npm-node-version }}

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,17 @@ which should output `nvm` if the installation was successful. Please note that `
136136

137137
If you're running a system without prepackaged binary available, which means you're going to install nodejs or io.js from its source code, you need to make sure your system has a C++ compiler. For OS X, Xcode will work, for Debian/Ubuntu based GNU/Linux, the `build-essential` and `libssl-dev` packages work.
138138

139-
**Note:** `nvm` does not support Windows (see [#284](https://github.com/nvm-sh/nvm/issues/284)), but may work in WSL (Windows Subsystem for Linux) depending on the version of WSL. For Windows, two alternatives exist, which are neither supported nor developed by us:
139+
**Note:** `nvm` does not support Windows (see [#284](https://github.com/nvm-sh/nvm/issues/284)), but may work in WSL (Windows Subsystem for Linux) depending on the version of WSL. It may work also with GitBash or Cygwin. For Windows, two alternatives exist, which are neither supported nor developed by us:
140140

141141
- [nvm-windows](https://github.com/coreybutler/nvm-windows)
142142
- [nodist](https://github.com/marcelklehr/nodist)
143143

144+
**Note:** `nvm` may work with [GitBash](https://gitforwindows.org/) if installed with script method (the git repository contains filenames with characters not supported on Windows):
145+
146+
```sh
147+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | METHOD=script bash
148+
```
149+
144150
**Note:** `nvm` does not support [Fish] either (see [#303](https://github.com/nvm-sh/nvm/issues/303)). Alternatives exist, which are neither supported nor developed by us:
145151

146152
- [bass](https://github.com/edc/bass) allows you to use utilities written for Bash in fish shell

nvm.sh

+126-35
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,12 @@ nvm_has_system_iojs() {
146146
}
147147

148148
nvm_is_version_installed() {
149-
[ -n "${1-}" ] && [ -x "$(nvm_version_path "$1" 2>/dev/null)"/bin/node ]
149+
local NVM_OS
150+
NVM_OS="$(nvm_get_os)"
151+
local NODE
152+
NODE='node'
153+
[ "_${NVM_OS}" = "_win" ] && NODE='node.exe'
154+
[ -n "${1-}" ] && [ -x "$(nvm_version_path "$1" 2>/dev/null)"/bin/"${NODE}" ]
150155
}
151156

152157
nvm_print_npm_version() {
@@ -326,10 +331,14 @@ nvm_tree_contains_path() {
326331
return 2
327332
fi
328333

334+
local previous_pathdir
335+
previous_pathdir="${node_path}"
329336
local pathdir
330-
pathdir=$(dirname "${node_path}")
331-
while [ "${pathdir}" != "" ] && [ "${pathdir}" != "." ] && [ "${pathdir}" != "/" ] && [ "${pathdir}" != "${tree}" ]; do
332-
pathdir=$(dirname "${pathdir}")
337+
pathdir=$(dirname "${previous_pathdir}")
338+
while [ "${pathdir}" != "" ] && [ "${pathdir}" != "." ] && [ "${pathdir}" != "/" ] &&
339+
[ "${pathdir}" != "${tree}" ] && [ "${pathdir}" != "${previous_pathdir}" ]; do
340+
previous_pathdir="${pathdir}"
341+
pathdir=$(dirname "${previous_pathdir}")
333342
done
334343
[ "${pathdir}" = "${tree}" ]
335344
}
@@ -1559,7 +1568,7 @@ nvm_get_checksum() {
15591568
SHASUMS_URL="${MIRROR}/${3}/SHASUMS.txt"
15601569
fi
15611570

1562-
nvm_download -L -s "${SHASUMS_URL}" -o - | command awk "{ if (\"${4}.tar.${5}\" == \$2) print \$1}"
1571+
nvm_download -L -s "${SHASUMS_URL}" -o - | command awk "{ if (\"${4}.${5}\" == \$2) print \$1}"
15631572
}
15641573

15651574
nvm_print_versions() {
@@ -1761,6 +1770,7 @@ nvm_get_os() {
17611770
SunOS\ *) NVM_OS=sunos ;;
17621771
FreeBSD\ *) NVM_OS=freebsd ;;
17631772
AIX\ *) NVM_OS=aix ;;
1773+
CYGWIN* | MSYS* | MINGW*) NVM_OS=win ;;
17641774
esac
17651775
nvm_echo "${NVM_OS-}"
17661776
}
@@ -1858,6 +1868,57 @@ nvm_get_mirror() {
18581868
esac
18591869
}
18601870

1871+
# args: os, prefixed version, tarball, extract directory
1872+
nvm_install_binary_extract() {
1873+
local NVM_OS
1874+
local PREFIXED_VERSION
1875+
local TARBALL
1876+
local TMPDIR
1877+
NVM_OS="${1}"
1878+
PREFIXED_VERSION="${2}"
1879+
TARBALL="${3}"
1880+
TMPDIR="${4}"
1881+
1882+
local VERSION_PATH
1883+
1884+
[ -n "${TMPDIR-}" ] && \
1885+
command mkdir -p "${TMPDIR}" && \
1886+
VERSION_PATH="$(nvm_version_path "${PREFIXED_VERSION}")" || return 1
1887+
1888+
# For Windows system (GitBash with MSYS, Cygwin)
1889+
if [ "${NVM_OS}" = 'win' ]; then
1890+
VERSION_PATH="${VERSION_PATH}/bin"
1891+
command unzip -q "${TARBALL}" -d "${TMPDIR}" || return 1
1892+
# For non Windows system (including WSL running on Windows)
1893+
else
1894+
local tar_compression_flag
1895+
tar_compression_flag='z'
1896+
if nvm_supports_xz "${VERSION}"; then
1897+
tar_compression_flag='J'
1898+
fi
1899+
1900+
local tar
1901+
if [ "${NVM_OS}" = 'aix' ]; then
1902+
tar='gtar'
1903+
else
1904+
tar='tar'
1905+
fi
1906+
command "${tar}" -x${tar_compression_flag}f "${TARBALL}" -C "${TMPDIR}" --strip-components 1 || return 1
1907+
fi
1908+
1909+
command mkdir -p "${VERSION_PATH}" || return 1
1910+
1911+
if [ "${NVM_OS}" = 'win' ]; then
1912+
command mv "${TMPDIR}/"*/* "${VERSION_PATH}" || return 1
1913+
else
1914+
command mv "${TMPDIR}/"* "${VERSION_PATH}" || return 1
1915+
fi
1916+
1917+
command rm -rf "${TMPDIR}" || return 1
1918+
1919+
return 0
1920+
}
1921+
18611922
# args: flavor, type, version, reinstall
18621923
nvm_install_binary() {
18631924
local FLAVOR
@@ -1882,19 +1943,15 @@ nvm_install_binary() {
18821943
local VERSION
18831944
VERSION="$(nvm_strip_iojs_prefix "${PREFIXED_VERSION}")"
18841945

1885-
if [ -z "$(nvm_get_os)" ]; then
1886-
return 2
1887-
fi
1946+
local NVM_OS
1947+
NVM_OS="$(nvm_get_os)"
18881948

1889-
local tar_compression_flag
1890-
tar_compression_flag='z'
1891-
if nvm_supports_xz "${VERSION}"; then
1892-
tar_compression_flag='J'
1949+
if [ -z "${NVM_OS}" ]; then
1950+
return 2
18931951
fi
18941952

18951953
local TARBALL
18961954
local TMPDIR
1897-
local VERSION_PATH
18981955

18991956
local PROGRESS_BAR
19001957
local NODE_OR_IOJS
@@ -1914,21 +1971,8 @@ nvm_install_binary() {
19141971
if [ -f "${TARBALL}" ]; then
19151972
TMPDIR="$(dirname "${TARBALL}")/files"
19161973
fi
1917-
local tar
1918-
tar='tar'
1919-
if [ "${NVM_OS}" = 'aix' ]; then
1920-
tar='gtar'
1921-
fi
1922-
if (
1923-
[ -n "${TMPDIR-}" ] && \
1924-
command mkdir -p "${TMPDIR}" && \
1925-
command "${tar}" -x${tar_compression_flag}f "${TARBALL}" -C "${TMPDIR}" --strip-components 1 && \
1926-
VERSION_PATH="$(nvm_version_path "${PREFIXED_VERSION}")" && \
1927-
command mkdir -p "${VERSION_PATH}" && \
1928-
command mv "${TMPDIR}/"* "${VERSION_PATH}" && \
1929-
command rm -rf "${TMPDIR}"
1930-
); then
19311974

1975+
if nvm_install_binary_extract "${NVM_OS}" "${PREFIXED_VERSION}" "${TARBALL}" "${TMPDIR}"; then
19321976
if [ -n "${ALIAS-}" ]; then
19331977
nvm alias "${ALIAS}" "${provided_version}"
19341978
fi
@@ -2028,10 +2072,15 @@ nvm_download_artifact() {
20282072
local SLUG
20292073
SLUG="$(nvm_get_download_slug "${FLAVOR}" "${KIND}" "${VERSION}")"
20302074

2075+
local NVM_OS
2076+
NVM_OS="$(nvm_get_os)"
2077+
20312078
local COMPRESSION
2032-
COMPRESSION='gz'
2033-
if nvm_supports_xz "${VERSION}"; then
2034-
COMPRESSION='xz'
2079+
COMPRESSION='tar.gz'
2080+
if [ "${NVM_OS}" = "win" ]; then
2081+
COMPRESSION='zip'
2082+
elif nvm_supports_xz "${VERSION}"; then
2083+
COMPRESSION='tar.xz'
20352084
fi
20362085

20372086
local CHECKSUM
@@ -2049,13 +2098,13 @@ nvm_download_artifact() {
20492098
)
20502099

20512100
local TARBALL
2052-
TARBALL="${tmpdir}/${SLUG}.tar.${COMPRESSION}"
2101+
TARBALL="${tmpdir}/${SLUG}.${COMPRESSION}"
20532102
local TARBALL_URL
20542103
if nvm_version_greater_than_or_equal_to "${VERSION}" 0.1.14; then
2055-
TARBALL_URL="${MIRROR}/${VERSION}/${SLUG}.tar.${COMPRESSION}"
2104+
TARBALL_URL="${MIRROR}/${VERSION}/${SLUG}.${COMPRESSION}"
20562105
else
20572106
# node <= 0.1.13 does not have a directory
2058-
TARBALL_URL="${MIRROR}/${SLUG}.tar.${COMPRESSION}"
2107+
TARBALL_URL="${MIRROR}/${SLUG}.${COMPRESSION}"
20592108
fi
20602109

20612110
if [ -r "${TARBALL}" ]; then
@@ -2317,6 +2366,36 @@ nvm_npmrc_bad_news_bears() {
23172366
return 1
23182367
}
23192368

2369+
# args: path to check and transform if needed
2370+
nvm_ensure_posix_path() {
2371+
[ -z "$1" ] && nvm_err "Need the location as first parameter" && return 1
2372+
local location
2373+
location="$1"
2374+
2375+
# If already posix path, just return it
2376+
[ "$(printf '%s' "$location" | command cut -b 1)" = "/" ] && nvm_echo "$location" && return 0
2377+
2378+
local letter
2379+
letter=$(printf '%s' "$location" | command cut -b 1 | command tr '[:upper:]' '[:lower:]')
2380+
local letter_upper
2381+
letter_upper=$(printf '%s' "$letter" | command tr '[:lower:]' '[:upper:]')
2382+
local letter_prefix
2383+
[ -d '/mnt/' ] && letter_prefix='/mnt'
2384+
# prefix like /mnt/c or /c
2385+
if [ -d "$letter_prefix/$letter" ]; then
2386+
letter_prefix="$letter_prefix/$letter"
2387+
# prefix like /mnt/C or /C
2388+
elif [ -d "$letter_prefix/$letter_upper" ]; then
2389+
letter_prefix="$letter_prefix/$letter_upper"
2390+
else
2391+
nvm_err "Unable to convert the path to posix"
2392+
return 1
2393+
fi
2394+
# Remove Windows prefix (eg. C:) and replace \ by /
2395+
location=$(printf '%s' "$location" | command cut -b 3- | command sed 's#\\#/#g')
2396+
nvm_echo "${letter_prefix}${location}"
2397+
}
2398+
23202399
nvm_die_on_prefix() {
23212400
local NVM_DELETE_PREFIX
23222401
NVM_DELETE_PREFIX="${1-}"
@@ -2346,6 +2425,9 @@ nvm_die_on_prefix() {
23462425
return 3
23472426
fi
23482427

2428+
local NVM_OS
2429+
NVM_OS="$(nvm_get_os)"
2430+
23492431
# npm normalizes NPM_CONFIG_-prefixed env vars
23502432
# https://github.com/npm/npmconf/blob/22827e4038d6eebaafeb5c13ed2b92cf97b8fb82/npmconf.js#L331-L348
23512433
# https://github.com/npm/npm/blob/5e426a78ca02d0044f8dd26e0c5f881217081cbd/lib/config/core.js#L343-L359
@@ -2357,6 +2439,9 @@ nvm_die_on_prefix() {
23572439
if [ -n "${NVM_NPM_CONFIG_PREFIX_ENV-}" ]; then
23582440
local NVM_CONFIG_VALUE
23592441
eval "NVM_CONFIG_VALUE=\"\$${NVM_NPM_CONFIG_PREFIX_ENV}\""
2442+
if [ -n "$NVM_CONFIG_VALUE" ] && [ "_${NVM_OS}" = "_win" ]; then
2443+
NVM_CONFIG_VALUE="$(nvm_ensure_posix_path "$NVM_CONFIG_VALUE")"
2444+
fi
23602445
if [ -n "${NVM_CONFIG_VALUE-}" ] && ! nvm_tree_contains_path "${NVM_DIR}" "${NVM_CONFIG_VALUE}"; then
23612446
nvm deactivate >/dev/null 2>&1
23622447
nvm_err "nvm is not compatible with the \"${NVM_NPM_CONFIG_PREFIX_ENV}\" environment variable: currently set to \"${NVM_CONFIG_VALUE}\""
@@ -3130,8 +3215,13 @@ nvm() {
31303215
nvm_get_make_jobs
31313216
fi
31323217

3133-
NVM_NO_PROGRESS="${NVM_NO_PROGRESS:-${noprogress}}" nvm_install_source "${FLAVOR}" std "${VERSION}" "${NVM_MAKE_JOBS}" "${ADDITIONAL_PARAMETERS}"
3134-
EXIT_CODE=$?
3218+
if [ "_${NVM_OS}" = "_win" ]; then
3219+
nvm_err "Unable to install from source for Windows"
3220+
EXIT_CODE=1
3221+
else
3222+
NVM_NO_PROGRESS="${NVM_NO_PROGRESS:-${noprogress}}" nvm_install_source "${FLAVOR}" std "${VERSION}" "${NVM_MAKE_JOBS}" "${ADDITIONAL_PARAMETERS}"
3223+
EXIT_CODE=$?
3224+
fi
31353225
fi
31363226

31373227
fi
@@ -3944,6 +4034,7 @@ nvm() {
39444034
nvm_npmrc_bad_news_bears \
39454035
nvm_get_colors nvm_set_colors nvm_print_color_code nvm_format_help_message_colors \
39464036
nvm_echo_with_colors nvm_err_with_colors \
4037+
nvm_ensure_posix_path \
39474038
>/dev/null 2>&1
39484039
unset NVM_RC_VERSION NVM_NODEJS_ORG_MIRROR NVM_IOJS_ORG_MIRROR NVM_DIR \
39494040
NVM_CD_FLAGS NVM_BIN NVM_INC NVM_MAKE_JOBS \

test/fast/Unit tests/nvm_get_checksum

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ nvm_download() {
3131
nvm_get_checksum_alg() {
3232
echo 'sha-256'
3333
}
34-
OUTPUT="$(nvm_get_checksum node std foo bar baz)"
34+
OUTPUT="$(nvm_get_checksum node std foo bar tar.baz)"
3535
EXPECTED_OUTPUT="mirror-node-std/foo/SHASUMS256.txt"
3636
[ "${OUTPUT}" = "${EXPECTED_OUTPUT}" ] || die "expected >${EXPECTED_OUTPUT}<, got >${OUTPUT}<"
3737

3838
nvm_get_checksum_alg() {
3939
echo 'sha-1'
4040
}
41-
OUTPUT="$(nvm_get_checksum iojs std foo bar baz)"
41+
OUTPUT="$(nvm_get_checksum iojs std foo bar tar.baz)"
4242
EXPECTED_OUTPUT="mirror-iojs-std/foo/SHASUMS.txt"
4343
[ "${OUTPUT}" = "${EXPECTED_OUTPUT}" ] || die "expected >${EXPECTED_OUTPUT}<, got >${OUTPUT}<"
4444

0 commit comments

Comments
 (0)