0
0
mirror of https://github.com/tj/n.git synced 2024-11-29 13:52:34 +01:00
n/bin/n
2015-05-08 10:02:00 -04:00

524 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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