Last active
May 4, 2019 09:38
-
-
Save abhisekp/760f6e5f162e10ca007bd26b0a52ca5d to your computer and use it in GitHub Desktop.
Node Version Checker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
const path = require("path"); | |
const { spawnSync } = require("child_process"); | |
const { engines } = require(path.resolve(process.cwd(), "package.json")); | |
const requiredVersionRule = engines.node; | |
const version = process.version.replace("v", ""); | |
try { | |
const checkNodeVersionProcess = spawnSync( | |
path.resolve(__dirname, "./semver.sh"), | |
["-r", requiredVersionRule, version] | |
); | |
const versionOut = checkNodeVersionProcess.stdout.toString().trim(); | |
if (versionOut !== version) { | |
throw new Error(); | |
} | |
} catch (error) { | |
throw new Error( | |
`The current node version ${version} does not satisfy the required version ${requiredVersionRule} .` | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
_num_part='([0-9]|[1-9][0-9]*)' | |
_lab_part='([0-9]|[1-9][0-9]*|[0-9]*[a-zA-Z-][a-zA-Z0-9-]*)' | |
_met_part='([0-9A-Za-z-]+)' | |
RE_NUM="$_num_part(\.$_num_part)*" | |
RE_LAB="$_lab_part(\.$_lab_part)*" | |
RE_MET="$_met_part(\.$_met_part)*" | |
RE_VER="[ \t]*$RE_NUM(-$RE_LAB)?(\+$RE_MET)?" | |
BRE_DIGIT='[0-9]\{1,\}' | |
BRE_ALNUM='[0-9a-zA-Z-]\{1,\}' | |
BRE_IDENT="$BRE_ALNUM\(\.$BRE_ALNUM\)*" | |
BRE_MAJOR="$BRE_DIGIT" | |
BRE_MINOR="\(\.$BRE_DIGIT\)\{0,1\}" | |
BRE_PATCH="\(\.$BRE_DIGIT\)\{0,1\}" | |
BRE_PRERE="\(-$BRE_IDENT\)\{0,1\}" | |
BRE_BUILD="\(+$BRE_IDENT\)\{0,1\}" | |
BRE_VERSION="${BRE_MAJOR}${BRE_MINOR}${BRE_PATCH}${BRE_PRERE}${BRE_BUILD}" | |
filter() | |
{ | |
local text="$1" | |
local regex="$2" | |
shift 2 | |
echo "$text" | grep -E "$@" "$regex" | |
} | |
# Gets number part from normalized version | |
get_number() | |
{ | |
echo "${1%%-*}" | |
} | |
# Gets prerelase part from normalized version | |
get_prerelease() | |
{ | |
local pre_and_meta=${1%+*} | |
local pre=${pre_and_meta#*-} | |
if [ "$pre" = "$1" ]; then | |
echo | |
else | |
echo "$pre" | |
fi | |
} | |
# Gets major number from normalized version | |
get_major() | |
{ | |
echo "${1%%.*}" | |
} | |
# Gets minor number from normalized version | |
get_minor() | |
{ | |
local minor_major_bug=${1%%-*} | |
local minor_major=${minor_major_bug%.*} | |
local minor=${minor_major#*.} | |
if [ "$minor" = "$minor_major" ]; then | |
echo | |
else | |
echo "$minor" | |
fi | |
} | |
get_bugfix() | |
{ | |
local minor_major_bug=${1%%-*} | |
local bugfix=${minor_major_bug##*.*.} | |
if [ "$bugfix" = "$minor_major_bug" ]; then | |
echo | |
else | |
echo "$bugfix" | |
fi | |
} | |
strip_metadata() | |
{ | |
echo "${1%+*}" | |
} | |
semver_eq() | |
{ | |
local ver1 ver2 part1 part2 | |
ver1=$(get_number "$1") | |
ver2=$(get_number "$2") | |
local count=1 | |
while true; do | |
part1=$(echo "$ver1"'.' | cut -d '.' -f $count) | |
part2=$(echo "$ver2"'.' | cut -d '.' -f $count) | |
if [ -z "$part1" ] || [ -z "$part2" ]; then | |
break | |
fi | |
if [ "$part1" != "$part2" ]; then | |
return 1 | |
fi | |
local count=$(( count + 1 )) | |
done | |
if [ "$(get_prerelease "$1")" = "$(get_prerelease "$2")" ]; then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
semver_lt() | |
{ | |
local number_a number_b prerelease_a prerelease_b | |
number_a=$(get_number "$1") | |
number_b=$(get_number "$2") | |
prerelease_a=$(get_prerelease "$1") | |
prerelease_b=$(get_prerelease "$2") | |
local head_a='' | |
local head_b='' | |
local rest_a=$number_a. | |
local rest_b=$number_b. | |
while [ -n "$rest_a" ] || [ -n "$rest_b" ]; do | |
head_a=${rest_a%%.*} | |
head_b=${rest_b%%.*} | |
rest_a=${rest_a#*.} | |
rest_b=${rest_b#*.} | |
if [ -z "$head_a" ] || [ -z "$head_b" ]; then | |
return 1 | |
fi | |
if [ "$head_a" -eq "$head_b" ]; then | |
continue | |
fi | |
if [ "$head_a" -lt "$head_b" ]; then | |
return 0 | |
else | |
return 1 | |
fi | |
done | |
if [ -n "$prerelease_a" ] && [ -z "$prerelease_b" ]; then | |
return 0 | |
elif [ -z "$prerelease_a" ] && [ -n "$prerelease_b" ]; then | |
return 1 | |
fi | |
local head_a='' | |
local head_b='' | |
local rest_a=$prerelease_a. | |
local rest_b=$prerelease_b. | |
while [ -n "$rest_a" ] || [ -n "$rest_b" ]; do | |
head_a=${rest_a%%.*} | |
head_b=${rest_b%%.*} | |
rest_a=${rest_a#*.} | |
rest_b=${rest_b#*.} | |
if [ -z "$head_a" ] && [ -n "$head_b" ]; then | |
return 0 | |
elif [ -n "$head_a" ] && [ -z "$head_b" ]; then | |
return 1 | |
fi | |
if [ "$head_a" = "$head_b" ]; then | |
continue | |
fi | |
# If both are numbers then compare numerically | |
if [ "$head_a" = "${head_a%[!0-9]*}" ] && [ "$head_b" = "${head_b%[!0-9]*}" ]; then | |
[ "$head_a" -lt "$head_b" ] && return 0 || return 1 | |
# If only a is a number then return true (number has lower precedence than strings) | |
elif [ "$head_a" = "${head_a%[!0-9]*}" ]; then | |
return 0 | |
# If only b is a number then return false | |
elif [ "$head_b" = "${head_b%[!0-9]*}" ]; then | |
return 1 | |
# Finally if of identifiers is a number compare them lexically | |
else | |
test "$head_a" \< "$head_b" && return 0 || return 1 | |
fi | |
done | |
return 1 | |
} | |
semver_gt() | |
{ | |
if semver_lt "$1" "$2" || semver_eq "$1" "$2"; then | |
return 1 | |
else | |
return 0 | |
fi | |
} | |
semver_le() | |
{ | |
semver_gt "$1" "$2" && return 1 || return 0 | |
} | |
semver_ge() | |
{ | |
semver_lt "$1" "$2" && return 1 || return 0 | |
} | |
semver_sort() | |
{ | |
if [ $# -le 1 ]; then | |
echo "$1" | |
return | |
fi | |
local pivot=$1 | |
local args_a=() | |
local args_b=() | |
shift 1 | |
for ver in "$@"; do | |
if semver_le "$ver" "$pivot"; then | |
args_a=( "${args_a[@]}" "$ver" ) | |
else | |
args_b=( "$ver" "${args_b[@]}" ) | |
fi | |
done | |
args_a=( $(semver_sort "${args_a[@]}") ) | |
args_b=( $(semver_sort "${args_b[@]}") ) | |
echo "${args_a[@]}" "$pivot" "${args_b[@]}" | |
} | |
regex_match() | |
{ | |
local string="$1 " | |
local regexp="$2" | |
local match | |
match="$(eval "echo '$string' | grep -E -o '^[ \t]*($regexp)[ \t]+'")"; | |
for i in $(seq 0 9); do | |
unset "MATCHED_VER_$i" | |
unset "MATCHED_NUM_$i" | |
done | |
unset REST | |
if [ -z "$match" ]; then | |
return 1 | |
fi | |
local match_len=${#match} | |
REST="${string:$match_len}" | |
local part | |
local i=1 | |
for part in $string; do | |
local ver num | |
ver="$(eval "echo '$part' | grep -E -o '$RE_VER' | head -n 1 | sed 's/ \t//g'")"; | |
num=$(get_number "$ver") | |
if [ -n "$ver" ]; then | |
eval "MATCHED_VER_$i='$ver'" | |
eval "MATCHED_NUM_$i='$num'" | |
i=$(( i + 1 )) | |
fi | |
done | |
return 0 | |
} | |
# Normalizes rules string | |
# | |
# * replaces chains of whitespaces with single spaces | |
# * replaces whitespaces around hyphen operator with "_" | |
# * removes wildcards from version numbers (1.2.* -> 1.2) | |
# * replaces "x" with "*" | |
# * removes whitespace between operators and version numbers | |
# * removes leading "v" from version numbers | |
# * removes leading and trailing spaces | |
normalize_rules() | |
{ | |
echo " $1" \ | |
| sed 's/\\t/ /g' \ | |
| sed 's/ / /g' \ | |
| sed 's/ \{2,\}/ /g' \ | |
| sed 's/ - /_-_/g' \ | |
| sed 's/\([~^<>=]\) /\1/g' \ | |
| sed 's/\([ _~^<>=]\)v/\1/g' \ | |
| sed 's/\.[xX*]//g' \ | |
| sed 's/[xX]/*/g' \ | |
| sed 's/^ //g' \ | |
| sed 's/ $//g' | |
} | |
# Reads rule from provided string | |
resolve_rule() | |
{ | |
local rule operator operands | |
rule="$1" | |
operator="$( echo "$rule" | sed "s/$BRE_VERSION/#/g" )" | |
operands=( $( echo "$rule" | grep -o "$BRE_VERSION") ) | |
case "$operator" in | |
'*') echo "all" ;; | |
'#') echo "eq ${operands[0]}" ;; | |
'=#') echo "eq ${operands[0]}" ;; | |
'<#') echo "lt ${operands[0]}" ;; | |
'>#') echo "gt ${operands[0]}" ;; | |
'<=#') echo "le ${operands[0]}" ;; | |
'>=#') echo "ge ${operands[0]}" ;; | |
'#_-_#') echo "ge ${operands[0]}" | |
echo "le ${operands[1]}" ;; | |
'~#') echo "tilde ${operands[0]}" ;; | |
'^#') echo "caret ${operands[0]}" ;; | |
*) return 1 | |
esac | |
} | |
resolve_rules() | |
{ | |
local rules | |
rules="$(normalize_rules "$1")" | |
IFS=' ' read -ra rules <<< "${rules:-all}" | |
for rule in "${rules[@]}"; do | |
resolve_rule "$rule" | |
done | |
} | |
rule_eq() | |
{ | |
local rule_ver="$1" | |
local tested_ver="$2" | |
semver_eq "$tested_ver" "$rule_ver" && return 0 || return 1; | |
} | |
rule_le() | |
{ | |
local rule_ver="$1" | |
local tested_ver="$2" | |
semver_le "$tested_ver" "$rule_ver" && return 0 || return 1; | |
} | |
rule_lt() | |
{ | |
local rule_ver="$1" | |
local tested_ver="$2" | |
semver_lt "$tested_ver" "$rule_ver" && return 0 || return 1; | |
} | |
rule_ge() | |
{ | |
local rule_ver="$1" | |
local tested_ver="$2" | |
semver_ge "$tested_ver" "$rule_ver" && return 0 || return 1; | |
} | |
rule_gt() | |
{ | |
local rule_ver="$1" | |
local tested_ver="$2" | |
semver_gt "$tested_ver" "$rule_ver" && return 0 || return 1; | |
} | |
rule_tilde() | |
{ | |
local rule_ver="$1" | |
local tested_ver="$2" | |
if rule_ge "$rule_ver" "$tested_ver"; then | |
local rule_major rule_minor | |
rule_major=$(get_major "$rule_ver") | |
rule_minor=$(get_minor "$rule_ver") | |
if [ -n "$rule_minor" ] && rule_eq "$rule_major.$rule_minor" "$(get_number "$tested_ver")"; then | |
return 0 | |
fi | |
if [ -z "$rule_minor" ] && rule_eq "$rule_major" "$(get_number "$tested_ver")"; then | |
return 0 | |
fi | |
fi | |
return 1 | |
} | |
rule_caret() | |
{ | |
local rule_ver="$1" | |
local tested_ver="$2" | |
if rule_ge "$rule_ver" "$tested_ver"; then | |
local rule_major | |
rule_major="$(get_major "$rule_ver")" | |
if [ "$rule_major" != "0" ] && rule_eq "$rule_major" "$(get_number "$tested_ver")"; then | |
return 0 | |
fi | |
if [ "$rule_major" = "0" ] && rule_eq "$rule_ver" "$(get_number "$tested_ver")"; then | |
return 0 | |
fi | |
fi | |
return 1 | |
} | |
rule_all() | |
{ | |
return 0 | |
} | |
apply_rules() | |
{ | |
local rules_string="$1" | |
shift | |
local versions=( "$@" ) | |
# Loop over sets of rules (sets of rules are separated with ||) | |
for ver in "${versions[@]}"; do | |
rules_tail="$rules_string"; | |
while [ -n "$rules_tail" ]; do | |
head="${rules_tail%%||*}" | |
if [ "$head" = "$rules_tail" ]; then | |
rules_string="" | |
else | |
rules_tail="${rules_tail#*||}" | |
fi | |
#if [ -z "$head" ] || [ -n "$(echo "$head" | grep -E -x '[ \t]*')" ]; then | |
#group=$(( $group + 1 )) | |
#continue | |
#fi | |
rules="$(resolve_rules "$head")" | |
# If specified rule cannot be recognised - end with error | |
if [ $? -eq 1 ]; then | |
exit 1 | |
fi | |
if ! echo "$ver" | grep -q -E -x "[v=]?[ \t]*$RE_VER"; then | |
continue | |
fi | |
ver=$(echo "$ver" | grep -E -x "$RE_VER") | |
success=true | |
allow_prerel=false | |
if $FORCE_ALLOW_PREREL; then | |
allow_prerel=true | |
fi | |
while read -r rule; do | |
comparator="${rule%% *}" | |
operand="${rule#* }" | |
if [ -n "$(get_prerelease "$operand")" ] && semver_eq "$(get_number "$operand")" "$(get_number "$ver")" || [ "$rule" = "all" ]; then | |
allow_prerel=true | |
fi | |
"rule_$comparator" "$operand" "$ver" | |
if [ $? -eq 1 ]; then | |
success=false | |
break | |
fi | |
done <<< "$rules" | |
if $success; then | |
if [ -z "$(get_prerelease "$ver")" ] || $allow_prerel; then | |
echo "$ver" | |
break; | |
fi | |
fi | |
done | |
group=$(( group + 1 )) | |
done | |
} | |
FORCE_ALLOW_PREREL=false | |
USAGE="Usage: $0 [-r <rule>] [<version>... ] | |
Omitting <version>s reads them from STDIN. | |
Omitting -r <rule> simply sorts the versions according to semver ordering." | |
while getopts ar:h o; do | |
case "$o" in | |
a) FORCE_ALLOW_PREREL=true ;; | |
r) RULES_STRING="$OPTARG||";; | |
h) echo "$USAGE" && exit ;; | |
?) echo "$USAGE" && exit 1;; | |
esac | |
done | |
shift $(( OPTIND-1 )) | |
VERSIONS=( ${@:-$(cat -)} ) | |
# Sort versions | |
VERSIONS=( $(semver_sort "${VERSIONS[@]}") ) | |
if [ -z "$RULES_STRING" ]; then | |
printf '%s\n' "${VERSIONS[@]}" | |
else | |
apply_rules "$RULES_STRING" "${VERSIONS[@]}" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment