Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend auto support to include .node-version, .nvmrc, and engines field of package.json #628

Merged
merged 2 commits into from
Jul 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- markdownlint-disable MD024 -->

## [Unreleased] (date goes here)
## [6.7.0] (2020-07-25)

### Added

- `auto` support for:
- `.node-version`
- `.nvmrc`
- `engines` field of `package.json`

## [6.6.0] (2020-07-04)

Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ There are labels for two especially useful versions:
- `lts`: newest Long Term Support official release
- `latest`, `current`: newest official release

There is a label to read the target version from a file, on the first line:
There is an `auto` label to read the target version from a file in the current directory, or any parent directory. `n` looks for in order:

- `auto`: read version from `.n-node-version` file
- `.n-node-version`: version on single line. Custom to `n`.
- `.node-version`: version on single line. Used by multiple tools: [node-version-usage](https://github.com/shadowspawn/node-version-usage)
- `.nvmrc`: version on single line. Used by `nvm`.
- `package.json`: use `engines` field to determine compatible `node`. Requires an installed version of `node`, and uses `npx semver` to resolve complex ranges.

There is support for the named release streams:

Expand Down
104 changes: 96 additions & 8 deletions bin/n
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
#

log() {
printf " ${SGR_CYAN}%10s${SGR_RESET} : ${SGR_FAINT}%s${SGR_RESET}${SGR_RESET}\n" "$1" "$2"
printf " ${SGR_CYAN}%10s${SGR_RESET} : ${SGR_FAINT}%s${SGR_RESET}\n" "$1" "$2"
}
trace_log() {
>&2 log "$1" "$2"
}

#
Expand Down Expand Up @@ -40,7 +43,7 @@ function echo_red() {
# Setup and state
#

VERSION="6.6.1-0"
VERSION="6.7.0"

N_PREFIX="${N_PREFIX-/usr/local}"
N_PREFIX=${N_PREFIX%/}
Expand Down Expand Up @@ -360,7 +363,7 @@ Versions:
4.9.1, 8, v6.1 Numeric versions
lts Newest Long Term Support official release
latest, current Newest official release
auto Read version from .n-node-version
auto Read version from file: .n-node-version, .node-version, .nvmrc, or package.json
boron, carbon Codenames for release streams
lts_latest node support aliases

Expand Down Expand Up @@ -925,19 +928,104 @@ function tarball_url() {
}

#
# Synopsis: display_auto_version
# Synopsis: display_file_node_version filename
#

function display_auto_version() {
local filename=".n-node-version"
[[ -e "${filename}" ]] || abort "auto version file not found (${filename})"
function display_file_node_version() {
local filepath="$1"
trace_log "found" "${filepath}"
# read returns a non-zero status but does still work if there is no line ending
<"${filename}" read -r version
local version
<"${filepath}" read -r version
# trim possible trailing \d from a Windows created file
version="${version%%[[:space:]]}"
trace_log "read" "${version}"
echo "${version}"
}

#
# Synopsis: display_package_engine_version
#

function display_package_engine_version() {
local filepath="$1"
trace_log "found" "${filepath}"
command -v node &> /dev/null || abort "an active version of node is required to read 'engines' from package.json"
local range
range="$(node -e "package = require('${filepath}'); if (package && package.engines && package.engines.node) console.log(package.engines.node)")"
trace_log "read" "${range}"
if [[ -z "${range}" || "*" == "${range}" ]]; then
echo "current"
return
fi

local version
if [[ "${range}" =~ ^([>~^=]|\>\=)?v?([0-9]+(\.[0-9]+){0,2})(.[xX*])?$ ]]; then
local operator="${BASH_REMATCH[1]}"
version="${BASH_REMATCH[2]}"
case "${operator}" in
'' | =) ;;
\> | \>=) version="current" ;;
\~) [[ "${version}" =~ ^([0-9]+\.[0-9]+)\.[0-9]+$ ]] && version="${BASH_REMATCH[1]}" ;;
^) [[ "${version}" =~ ^([0-9]+) ]] && version="${BASH_REMATCH[1]}" ;;
esac
else
command -v npx &> /dev/null || abort "an active version of npx is required to use complex 'engine' ranges from package.json"
trace_log "resolving" "${range}"
local version_per_line="$(n lsr --all)"
local versions_one_line=$(echo "${version_per_line}" | tr '\n' ' ')
# Using semver@7 so works with older versions of node.
# shellcheck disable=SC2086
version=$(npx --quiet semver@7 -r "${range}" ${versions_one_line} | tail -n 1)
fi
echo "${version}"
}

#
# Synopsis: display_nvmrc_version
#

function display_nvmrc_version() {
local filepath="$1"
trace_log "found" "${filepath}"
local version
<"${filepath}" read -r version
trace_log "read" "${version}"
# Translate from nvm aliases
case "${version}" in
lts/\*) version="lts" ;;
lts/*) version="${version:4}" ;;
node) version="current" ;;
*) ;;
esac
echo "${version}"
}

#
# Synopsis: display_auto_version
#

function display_auto_version() {
local parent
parent="${PWD}"
while [[ -n "${parent}" ]]; do
if [[ -e "${parent}/.n-node-version" ]]; then
display_file_node_version "${parent}/.n-node-version"
elif [[ -e "${parent}/.node-version" ]]; then
display_file_node_version "${parent}/.node-version"
elif [[ -e "${parent}/.nvmrc" ]]; then
display_nvmrc_version "${parent}/.nvmrc"
elif [[ -e "${parent}/package.json" ]]; then
display_package_engine_version "${parent}/package.json"
else
parent=${parent%/*}
continue
fi
break
done
[[ -n "${parent}" ]] || abort "no file found for auto version"
}

#
# Synopsis: display_latest_resolved_version version
#
Expand Down
77 changes: 77 additions & 0 deletions test/tests/version-auto-priority.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bats

load shared-functions


# auto

function setup() {
unset_n_env
tmpdir="${TMPDIR:-/tmp}"
export MY_DIR="${tmpdir}/n/test/version-resolve-auto-priority"
mkdir -p "${MY_DIR}"
rm -f "${MY_DIR}/package.json"
rm -f "${MY_DIR}/.n-node-version"
rm -f "${MY_DIR}/.node-version"
rm -f "${MY_DIR}/.nvmrc"

PAYLOAD_LINE=2

# Need a version of node available for reading package.json
export N_PREFIX="${MY_DIR}"
export PATH="${MY_DIR}/bin:${PATH}"
if [[ "${BATS_TEST_NUMBER}" -eq 1 ]] ; then
# beforeAll
n install lts
fi
}

function teardown() {
# afterAll
if [[ "${#BATS_TEST_NAMES[@]}" -eq "${BATS_TEST_NUMBER}" ]] ; then
rm -rf "${MY_DIR}"
fi
}

@test ".n-node-version first" {
cd "${MY_DIR}"
echo "401.0.1" > .n-node-version
echo "401.0.2" > .node-version
echo "401.0.3" > .nvmrc
echo '{ "engines" : { "node" : "v401.0.4" } }' > package.json

run n N_TEST_DISPLAY_LATEST_RESOLVED_VERSION auto
[ "$status" -eq 0 ]
[ "${lines[${PAYLOAD_LINE}]}" = "401.0.1" ]
}

@test ".node-version second" {
cd "${MY_DIR}"
echo "401.0.2" > .node-version
echo "401.0.3" > .nvmrc
echo '{ "engines" : { "node" : "v401.0.4" } }' > package.json

run n N_TEST_DISPLAY_LATEST_RESOLVED_VERSION auto
[ "$status" -eq 0 ]
[ "${lines[${PAYLOAD_LINE}]}" = "401.0.2" ]
}

@test ".nvmrc third" {
cd "${MY_DIR}"
echo "401.0.3" > .nvmrc
echo '{ "engines" : { "node" : "v401.0.4" } }' > package.json

run n N_TEST_DISPLAY_LATEST_RESOLVED_VERSION auto
[ "$status" -eq 0 ]
[ "${lines[${PAYLOAD_LINE}]}" = "401.0.3" ]
}

@test ".package.json last" {
cd "${MY_DIR}"
echo '{ "engines" : { "node" : "v401.0.4" } }' > package.json

run n N_TEST_DISPLAY_LATEST_RESOLVED_VERSION auto
[ "$status" -eq 0 ]
[ "${lines[${PAYLOAD_LINE}]}" = "401.0.4" ]
}

Loading