mirror of
https://github.com/tj/n.git
synced 2024-11-29 13:52:34 +01:00
524 lines
10 KiB
Bash
Executable File
524 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
#
|
||
# Setup.
|
||
#
|
||
|
||
VERSION="2.0.0"
|
||
UP=$'\033[A'
|
||
DOWN=$'\033[B'
|
||
N_PREFIX=${N_PREFIX-/usr/local}
|
||
BASE_VERSIONS_DIR=$N_PREFIX/n/versions
|
||
|
||
#
|
||
# All Bin(node/io) configurations
|
||
#
|
||
|
||
BINS=("node"
|
||
"io")
|
||
MIRROR=(${NODE_MIRROR-https://nodejs.org/dist/}
|
||
${IO_MIRROR-https://iojs.org/dist/})
|
||
BIN_NAME=("node"
|
||
"iojs")
|
||
VERSIONS_DIR=($BASE_VERSIONS_DIR/node
|
||
$BASE_VERSIONS_DIR/io)
|
||
|
||
#
|
||
# State
|
||
#
|
||
|
||
DEFAULT=0
|
||
|
||
#
|
||
# set_default <bin_name>
|
||
#
|
||
|
||
set_default() {
|
||
for (( i=0 ; i<${#BINS[@]} ; i++ )); do
|
||
if test ${BINS[$i]} = $1; then
|
||
DEFAULT=$i
|
||
fi
|
||
done
|
||
}
|
||
|
||
for dir in ${VERSIONS_DIR[@]}; do
|
||
test -d $dir || mkdir -p $dir
|
||
done
|
||
|
||
#
|
||
# Log <type> <msg>
|
||
#
|
||
|
||
log() {
|
||
printf " \033[36m%10s\033[0m : \033[90m%s\033[0m\n" $1 $2
|
||
}
|
||
|
||
#
|
||
# Exit with the given <msg ...>
|
||
#
|
||
|
||
abort() {
|
||
printf "\n \033[31mError: $@\033[0m\n\n" && exit 1
|
||
}
|
||
|
||
#
|
||
# Ensure we have curl or wget support.
|
||
#
|
||
|
||
GET=
|
||
|
||
# wget support (Added --no-check-certificate for Github downloads)
|
||
command -v wget > /dev/null && GET="wget --no-check-certificate -q -O-"
|
||
|
||
command -v curl > /dev/null && GET="curl -# -L"
|
||
|
||
test -z "$GET" && abort "curl or wget required"
|
||
|
||
#
|
||
# Functions used when showing versions installed
|
||
#
|
||
|
||
enter_fullscreen() {
|
||
tput smcup
|
||
stty -echo
|
||
}
|
||
|
||
leave_fullscreen() {
|
||
tput rmcup
|
||
stty echo
|
||
}
|
||
|
||
handle_sigint() {
|
||
leave_fullscreen
|
||
exit $?
|
||
}
|
||
|
||
handle_sigtstp() {
|
||
leave_fullscreen
|
||
kill -s SIGSTOP $$
|
||
}
|
||
|
||
#
|
||
# Output usage information.
|
||
#
|
||
|
||
display_help() {
|
||
cat <<-EOF
|
||
|
||
Usage: n [options/env] [COMMAND] [args]
|
||
|
||
Environments:
|
||
n [COMMAND] [args] Uses default env (node)
|
||
n node [COMMAND] [args] Sets env as node
|
||
n io [COMMAND] Sets env as io
|
||
|
||
Commands:
|
||
|
||
n Output versions installed
|
||
n latest Install or activate the latest node release
|
||
n stable Install or activate the latest stable node release
|
||
n <version> Install node <version>
|
||
n use <version> [args ...] Execute node <version> with [args ...]
|
||
n bin <version> Output bin path for <version>
|
||
n rm <version ...> Remove the given version(s)
|
||
n --latest Output the latest node version available
|
||
n --stable Output the latest stable node version available
|
||
n ls Output the versions of node available
|
||
|
||
(iojs):
|
||
n io latest Install or activate the latest iojs release
|
||
n io stable Install or activate the latest stable iojs release
|
||
n io <version> Install iojs <version>
|
||
n io use <version> [args ...] Execute iojs <version> with [args ...]
|
||
n io bin <version> Output bin path for <version>
|
||
n io rm <version ...> Remove the given version(s)
|
||
n io --latest Output the latest iojs version available
|
||
n io --stable Output the latest stable iojs version available
|
||
n io ls Output the versions of iojs available
|
||
|
||
Options:
|
||
|
||
-V, --version Output current version of n
|
||
-h, --help Display help information
|
||
|
||
Aliases:
|
||
|
||
iojs io
|
||
which bin
|
||
use as
|
||
list ls
|
||
- rm
|
||
|
||
EOF
|
||
exit 0
|
||
}
|
||
|
||
#
|
||
# Hide cursor.
|
||
#
|
||
|
||
hide_cursor() {
|
||
printf "\e[?25l"
|
||
}
|
||
|
||
#
|
||
# Show cursor.
|
||
#
|
||
|
||
show_cursor() {
|
||
printf "\e[?25h"
|
||
}
|
||
|
||
#
|
||
# Output version after selected.
|
||
#
|
||
|
||
next_version_installed() {
|
||
list_versions_installed | grep $selected -A 1 | tail -n 1
|
||
}
|
||
|
||
#
|
||
# Output version before selected.
|
||
#
|
||
|
||
prev_version_installed() {
|
||
list_versions_installed | grep $selected -B 1 | head -n 1
|
||
}
|
||
|
||
#
|
||
# Output n version.
|
||
#
|
||
|
||
display_n_version() {
|
||
echo $VERSION && exit 0
|
||
}
|
||
|
||
#
|
||
# Check for installed version, and populate $active
|
||
#
|
||
|
||
check_current_version() {
|
||
command -v node &> /dev/null
|
||
if test $? -eq 0; then
|
||
local current=$(node --version)
|
||
current=${current#v}
|
||
for bin in ${BINS[@]}; do
|
||
if diff &> /dev/null \
|
||
$BASE_VERSIONS_DIR/$bin/$current/bin/node \
|
||
$(which node) ; then
|
||
active=$bin/$current
|
||
fi
|
||
done
|
||
fi
|
||
}
|
||
|
||
#
|
||
# Display sorted versions directories paths.
|
||
#
|
||
|
||
versions_paths() {
|
||
find $BASE_VERSIONS_DIR -maxdepth 2 -type d \
|
||
| sed 's|'$BASE_VERSIONS_DIR'/||g' \
|
||
| egrep "/[0-9]+\.[0-9]+\.[0-9]+$" \
|
||
| sort -k 1,1n -k 2,2n -k 3,3n -t .
|
||
}
|
||
|
||
#
|
||
# Display installed versions with <selected>
|
||
#
|
||
|
||
display_versions_with_selected() {
|
||
selected=$1
|
||
echo
|
||
for version in $(versions_paths); do
|
||
if test "$version" = "$selected"; then
|
||
printf " \033[36mο\033[0m $version\033[0m\n"
|
||
else
|
||
printf " \033[90m$version\033[0m\n"
|
||
fi
|
||
done
|
||
echo
|
||
}
|
||
|
||
#
|
||
# List installed versions.
|
||
#
|
||
|
||
list_versions_installed() {
|
||
for version in $(versions_paths); do
|
||
echo $version
|
||
done
|
||
}
|
||
|
||
#
|
||
# Display current node --version and others installed.
|
||
#
|
||
|
||
display_versions() {
|
||
enter_fullscreen
|
||
check_current_version
|
||
display_versions_with_selected $active
|
||
|
||
trap handle_sigint INT
|
||
trap handle_sigtstp SIGTSTP
|
||
|
||
while true; do
|
||
read -n 3 c
|
||
case "$c" in
|
||
$UP)
|
||
clear
|
||
display_versions_with_selected $(prev_version_installed)
|
||
;;
|
||
$DOWN)
|
||
clear
|
||
display_versions_with_selected $(next_version_installed)
|
||
;;
|
||
*)
|
||
activate $selected
|
||
leave_fullscreen
|
||
exit
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
#
|
||
# Move up a line and erase.
|
||
#
|
||
|
||
erase_line() {
|
||
printf "\033[1A\033[2K"
|
||
}
|
||
|
||
#
|
||
# Check if the HEAD response of <url> is 200.
|
||
#
|
||
|
||
is_ok() {
|
||
curl -Is $1 | head -n 1 | grep 200 > /dev/null
|
||
}
|
||
|
||
#
|
||
# Determine tarball url for <version>
|
||
#
|
||
|
||
tarball_url() {
|
||
local version=$1
|
||
local uname="$(uname -a)"
|
||
local arch=x86
|
||
local os=
|
||
|
||
# from nave(1)
|
||
case "$uname" in
|
||
Linux*) os=linux ;;
|
||
Darwin*) os=darwin ;;
|
||
SunOS*) os=sunos ;;
|
||
esac
|
||
|
||
case "$uname" in
|
||
*x86_64*) arch=x64 ;;
|
||
*armv6l*) arch=armv6l ;;
|
||
*armv7l*) arch=armv7l ;;
|
||
esac
|
||
|
||
if [ ${arch} = "armv6l" -a ${BIN_NAME[$DEFAULT]} = node ]; then
|
||
arch=arm-pi
|
||
fi
|
||
|
||
echo "${MIRROR[$DEFAULT]}v${version}/${BIN_NAME[$DEFAULT]}-v${version}-${os}-${arch}.tar.gz"
|
||
|
||
}
|
||
|
||
#
|
||
# Activate <version>
|
||
#
|
||
|
||
activate() {
|
||
local version=$1
|
||
check_current_version
|
||
if test "$version" != "$active"; then
|
||
local dir=$BASE_VERSIONS_DIR/$version
|
||
cp -fR $dir/bin $dir/lib $dir/share $N_PREFIX
|
||
[[ -d "$dir/include" ]] && cp -fR $dir/include $N_PREFIX
|
||
fi
|
||
}
|
||
|
||
#
|
||
# Install <version>
|
||
#
|
||
|
||
install() {
|
||
local version=${1#v}
|
||
|
||
local dots=$(echo $version | sed 's/[^.]*//g')
|
||
if test ${#dots} -eq 1; then
|
||
version=$($GET 2> /dev/null ${MIRROR[DEFAULT]} \
|
||
| egrep -o '[0-9]+\.[0-9]+\.[0-9]+' \
|
||
| egrep -v '^0\.[0-7]\.' \
|
||
| egrep -v '^0\.8\.[0-5]$' \
|
||
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
||
| egrep ^$version \
|
||
| tail -n1)
|
||
|
||
test $version || abort "invalid version ${1#v}"
|
||
fi
|
||
|
||
local dir=${VERSIONS_DIR[$DEFAULT]}/$version
|
||
local url=$(tarball_url $version)
|
||
|
||
if test -d $dir; then
|
||
if [[ ! -e $dir/n.lock ]] ; then
|
||
activate ${BINS[$DEFAULT]}/$version
|
||
exit
|
||
fi
|
||
fi
|
||
|
||
echo
|
||
log install ${BINS[$DEFAULT]}-v$version
|
||
|
||
is_ok $url || abort "invalid version $version"
|
||
|
||
log mkdir $dir
|
||
mkdir -p $dir
|
||
if [ $? -ne 0 ] ; then
|
||
abort "sudo required"
|
||
else
|
||
touch $dir/n.lock
|
||
fi
|
||
|
||
cd $dir
|
||
|
||
log fetch $url
|
||
curl -L# $url | tar -zx --strip 1
|
||
erase_line
|
||
rm -f $dir/n.lock
|
||
|
||
activate ${BINS[$DEFAULT]}/$version
|
||
log installed $(node --version)
|
||
echo
|
||
}
|
||
|
||
#
|
||
# Remove <version ...>
|
||
#
|
||
|
||
remove_versions() {
|
||
test -z $1 && abort "version(s) required"
|
||
while test $# -ne 0; do
|
||
rm -rf ${VERSIONS_DIR[$DEFAULT]}/${1#v}
|
||
shift
|
||
done
|
||
}
|
||
|
||
#
|
||
# Output bin path for <version>
|
||
#
|
||
|
||
display_bin_path_for_version() {
|
||
test -z $1 && abort "version required"
|
||
local version=${1#v}
|
||
local bin=${VERSIONS_DIR[$DEFAULT]}/$version/bin/node
|
||
if test -f $bin; then
|
||
printf "$bin \n"
|
||
else
|
||
abort "$1 is not installed"
|
||
fi
|
||
}
|
||
|
||
#
|
||
# Execute the given <version> of node with [args ...]
|
||
#
|
||
|
||
execute_with_version() {
|
||
test -z $1 && abort "version required"
|
||
local version=${1#v}
|
||
local bin=${VERSIONS_DIR[$DEFAULT]}/$version/bin/node
|
||
|
||
shift # remove version
|
||
|
||
if test -f $bin; then
|
||
$bin "$@"
|
||
else
|
||
abort "$version is not installed"
|
||
fi
|
||
}
|
||
|
||
#
|
||
# Display the latest release version.
|
||
#
|
||
|
||
display_latest_version() {
|
||
$GET 2> /dev/null ${MIRROR[$DEFAULT]} \
|
||
| egrep -o '[0-9]+\.[0-9]+\.[0-9]+' \
|
||
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
||
| tail -n1
|
||
}
|
||
|
||
#
|
||
# Display the latest stable release version.
|
||
#
|
||
|
||
display_latest_stable_version() {
|
||
$GET 2> /dev/null ${MIRROR[$DEFAULT]} \
|
||
| egrep -o '[0-9]+\.[0-9]*[02468]\.[0-9]+' \
|
||
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
||
| tail -n1
|
||
}
|
||
|
||
#
|
||
# Display the versions available.
|
||
#
|
||
|
||
display_remote_versions() {
|
||
check_current_version
|
||
local versions=""
|
||
versions=$($GET 2> /dev/null ${MIRROR[$DEFAULT]} \
|
||
| egrep -o '[0-9]+\.[0-9]+\.[0-9]+' \
|
||
| egrep -v '^0\.[0-7]\.' \
|
||
| egrep -v '^0\.8\.[0-5]$' \
|
||
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
||
| awk '{ print " " $1 }')
|
||
|
||
echo
|
||
local bin=${BINS[$DEFAULT]}
|
||
for v in $versions; do
|
||
if test "$active" = "$bin/$v"; then
|
||
printf " \033[36mο\033[0m $v \033[0m\n"
|
||
else
|
||
if test -d $BASE_VERSIONS_DIR/$bin/$v; then
|
||
printf " $v \033[0m\n"
|
||
else
|
||
printf " \033[90m$v\033[0m\n"
|
||
fi
|
||
fi
|
||
done
|
||
echo
|
||
}
|
||
|
||
#
|
||
# Handle arguments.
|
||
#
|
||
|
||
if test $# -eq 0; then
|
||
test -z "$(versions_paths)" && abort "no installed version"
|
||
display_versions
|
||
else
|
||
while test $# -ne 0; do
|
||
case $1 in
|
||
-V|--version) display_n_version ;;
|
||
-h|--help|help) display_help ;;
|
||
--latest) display_latest_version; exit ;;
|
||
--stable) display_latest_stable_version; exit ;;
|
||
io|iojs|node) set_default $1;; # set bin and continue
|
||
bin|which) display_bin_path_for_version $2; exit ;;
|
||
as|use) shift; execute_with_version $@; exit ;;
|
||
rm|-) shift; remove_versions $@; exit ;;
|
||
latest) install $($0 ${BINS[$DEFAULT]} --latest); exit ;;
|
||
stable) install $($0 ${BINS[$DEFAULT]} --stable); exit ;;
|
||
ls|list) display_remote_versions; exit ;;
|
||
*) install $1; exit ;;
|
||
esac
|
||
shift
|
||
done
|
||
fi
|