code-server/install.sh

619 lines
16 KiB
Bash
Raw Normal View History

2020-05-20 16:50:16 +08:00
#!/bin/sh
set -eu
2020-05-23 03:38:03 +08:00
# code-server's automatic install script.
# See https://coder.com/docs/code-server/latest/install
2020-05-23 03:38:03 +08:00
2020-05-20 16:50:16 +08:00
usage() {
2020-05-23 03:38:03 +08:00
arg0="$0"
if [ "$0" = sh ]; then
2020-05-23 03:38:03 +08:00
arg0="curl -fsSL https://code-server.dev/install.sh | sh -s --"
else
2020-05-28 05:46:55 +08:00
not_curl_usage="The latest script is available at https://code-server.dev/install.sh
2020-05-23 03:38:03 +08:00
"
fi
2020-05-23 03:38:03 +08:00
cath << EOF
2021-07-14 01:21:22 +08:00
Installs code-server.
2020-05-22 10:16:16 +08:00
It tries to use the system package manager if possible.
After successful installation it explains how to start using code-server.
Pass in user@host to install code-server on user@host over ssh.
The remote host must have internet access.
2020-05-28 05:46:55 +08:00
${not_curl_usage-}
Usage:
2020-05-20 16:50:16 +08:00
$arg0 [--dry-run] [--version X.X.X] [--edge] [--method detect] \
[--prefix ~/.local] [--rsh ssh] [user@host]
2020-05-23 03:38:03 +08:00
--dry-run
Echo the commands for the install process without running them.
2020-05-23 03:38:03 +08:00
--version X.X.X
Install a specific version instead of the latest.
--edge
Install the latest edge version instead of the latest stable version.
2020-05-28 04:39:17 +08:00
--method [detect | standalone]
2020-05-23 03:38:03 +08:00
Choose the installation method. Defaults to detect.
- detect detects the system package manager and tries to use it.
Full reference on the process is further below.
2020-05-28 04:39:17 +08:00
- standalone installs a standalone release archive into ~/.local
2020-05-23 03:38:03 +08:00
Add ~/.local/bin to your \$PATH to use it.
2020-05-23 03:38:03 +08:00
--prefix <dir>
2020-05-28 04:39:17 +08:00
Sets the prefix used by standalone release archives. Defaults to ~/.local
2020-05-23 03:38:03 +08:00
The release is unarchived into ~/.local/lib/code-server-X.X.X
and the binary symlinked into ~/.local/bin/code-server
To install system wide pass ---prefix=/usr/local
2020-05-22 11:53:02 +08:00
--rsh <bin>
Specifies the remote shell for remote installation. Defaults to ssh.
2021-07-14 01:21:22 +08:00
The detection method works as follows:
- Debian, Ubuntu, Raspbian: install the deb package from GitHub.
- Fedora, CentOS, RHEL, openSUSE: install the rpm package from GitHub.
- Arch Linux: install from the AUR (which pulls releases from GitHub).
- FreeBSD, Alpine: install from yarn/npm.
- macOS: install using Homebrew if installed otherwise install from GitHub.
- All others: install the release from GitHub.
2020-05-20 16:50:16 +08:00
2021-07-14 01:21:22 +08:00
We only build releases on GitHub for amd64 and arm64 on Linux and amd64 for
macOS. When the detection method tries to pull a release from GitHub it will
fall back to installing from npm when there is no matching release for the
system's operating system and architecture.
2020-05-20 16:50:16 +08:00
2021-07-14 01:21:22 +08:00
The standalone method will force installion using GitHub releases. It will not
fall back to npm so on architectures without pre-built releases this will error.
2020-06-13 22:56:50 +08:00
2021-07-14 01:21:22 +08:00
The installer will cache all downloaded assets into ~/.cache/code-server
2020-05-23 03:38:03 +08:00
More installation docs are at https://coder.com/docs/code-server/latest/install
2020-05-20 16:50:16 +08:00
EOF
}
echo_latest_version() {
if [ "${EDGE-}" ]; then
version="$(curl -fsSL https://api.github.com/repos/coder/code-server/releases | awk 'match($0,/.*"html_url": "(.*\/releases\/tag\/.*)".*/)' | head -n 1 | awk -F '"' '{print $4}')"
else
# https://gist.github.com/lukechilds/a83e1d7127b78fef38c2914c4ececc3c#gistcomment-2758860
version="$(curl -fsSLI -o /dev/null -w "%{url_effective}" https://github.com/coder/code-server/releases/latest)"
fi
version="${version#https://github.com/coder/code-server/releases/tag/}"
version="${version#v}"
echo "$version"
2020-05-20 16:50:16 +08:00
}
echo_npm_postinstall() {
echoh
cath << EOF
2021-07-14 01:21:22 +08:00
npm package has been installed.
Extend your path to use code-server:
PATH="$NPM_BIN_DIR:\$PATH"
2021-07-14 01:21:22 +08:00
Then run with:
code-server
EOF
}
2020-05-28 04:39:17 +08:00
echo_standalone_postinstall() {
2020-05-28 05:01:33 +08:00
echoh
cath << EOF
2020-05-28 04:39:17 +08:00
Standalone release has been installed into $STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION
2021-07-14 01:21:22 +08:00
Extend your path to use code-server:
2020-05-28 04:39:17 +08:00
PATH="$STANDALONE_INSTALL_PREFIX/bin:\$PATH"
2021-07-14 01:21:22 +08:00
Then run with:
code-server
EOF
}
echo_brew_postinstall() {
echoh
cath << EOF
Brew release has been installed.
Run with:
2020-05-20 16:50:16 +08:00
code-server
EOF
}
echo_systemd_postinstall() {
2020-05-28 05:01:33 +08:00
echoh
cath << EOF
2021-07-14 01:21:22 +08:00
$1 package has been installed.
2020-05-20 16:50:16 +08:00
To have systemd start code-server now and restart on boot:
sudo systemctl enable --now code-server@\$USER
2020-05-20 16:50:16 +08:00
Or, if you don't want/need a background service you can run:
code-server
EOF
}
main() {
if [ "${TRACE-}" ]; then
set -x
fi
unset \
DRY_RUN \
2020-05-23 03:38:03 +08:00
METHOD \
OPTIONAL \
ALL_FLAGS \
RSH_ARGS \
EDGE \
RSH
ALL_FLAGS=""
while [ "$#" -gt 0 ]; do
case "$1" in
-*)
ALL_FLAGS="${ALL_FLAGS} $1"
;;
esac
case "$1" in
--dry-run)
DRY_RUN=1
;;
--method)
METHOD="$(parse_arg "$@")"
shift
;;
--method=*)
METHOD="$(parse_arg "$@")"
;;
--prefix)
STANDALONE_INSTALL_PREFIX="$(parse_arg "$@")"
shift
;;
--prefix=*)
STANDALONE_INSTALL_PREFIX="$(parse_arg "$@")"
;;
--version)
VERSION="$(parse_arg "$@")"
shift
;;
--version=*)
VERSION="$(parse_arg "$@")"
;;
--edge)
EDGE=1
;;
--rsh)
RSH="$(parse_arg "$@")"
shift
;;
--rsh=*)
RSH="$(parse_arg "$@")"
;;
-h | --h | -help | --help)
usage
exit 0
;;
--)
shift
# We remove the -- added above.
ALL_FLAGS="${ALL_FLAGS% --}"
RSH_ARGS="$*"
break
;;
-*)
echoerr "Unknown flag $1"
echoerr "Run with --help to see usage."
exit 1
;;
*)
RSH_ARGS="$*"
break
;;
2020-05-20 16:50:16 +08:00
esac
shift
2020-05-20 16:50:16 +08:00
done
if [ "${RSH_ARGS-}" ]; then
2020-10-22 13:43:22 +08:00
RSH="${RSH-ssh}"
echoh "Installing remotely with $RSH $RSH_ARGS"
curl -fsSL https://code-server.dev/install.sh | prefix "$RSH_ARGS" "$RSH" "$RSH_ARGS" sh -s -- "$ALL_FLAGS"
return
fi
2020-05-23 03:38:03 +08:00
METHOD="${METHOD-detect}"
2020-05-28 04:39:17 +08:00
if [ "$METHOD" != detect ] && [ "$METHOD" != standalone ]; then
2020-05-23 03:38:03 +08:00
echoerr "Unknown install method \"$METHOD\""
echoerr "Run with --help to see usage."
exit 1
fi
2020-05-20 16:50:16 +08:00
2021-07-14 01:21:22 +08:00
# These are used by the various install_* functions that make use of GitHub
# releases in order to download and unpack the right release.
CACHE_DIR=$(echo_cache_dir)
STANDALONE_INSTALL_PREFIX=${STANDALONE_INSTALL_PREFIX:-$HOME/.local}
VERSION=${VERSION:-$(echo_latest_version)}
# These can be overridden for testing but shouldn't normally be used as it can
# result in a broken code-server.
OS=${OS:-$(os)}
ARCH=${ARCH:-$(arch)}
2020-05-20 16:50:16 +08:00
distro_name
2021-07-14 01:21:22 +08:00
# Standalone installs by pulling pre-built releases from GitHub.
if [ "$METHOD" = standalone ]; then
if has_standalone; then
install_standalone
exit 0
else
echoerr "There are no standalone releases for $ARCH"
echoerr "Please try again without '--method standalone'"
2021-07-02 06:49:13 +08:00
exit 1
fi
fi
2021-07-14 01:21:22 +08:00
# DISTRO can be overridden for testing but shouldn't normally be used as it
# can result in a broken code-server.
DISTRO=${DISTRO:-$(distro)}
2020-05-20 16:50:16 +08:00
2021-07-14 01:21:22 +08:00
case $DISTRO in
# macOS uses brew when available and falls back to standalone. We only have
# amd64 for macOS so for anything else use npm.
macos)
2021-07-14 01:21:22 +08:00
BREW_PATH="${BREW_PATH-brew}"
if command_exists "$BREW_PATH"; then
install_brew
else
echoh "Homebrew not installed."
echoh "Falling back to standalone installation."
npm_fallback install_standalone
fi
;;
2021-07-14 01:21:22 +08:00
# The .deb and .rpm files are pulled from GitHub and we only have amd64 and
# arm64 there and need to fall back to npm otherwise.
debian) npm_fallback install_deb ;;
fedora | opensuse) npm_fallback install_rpm ;;
# Arch uses the AUR package which only supports amd64 and arm64 since it
# pulls releases from GitHub so we need to fall back to npm.
arch) npm_fallback install_aur ;;
# We don't have GitHub releases that work on Alpine or FreeBSD so we have no
# choice but to use npm here.
alpine | freebsd) install_npm ;;
# For anything else we'll try to install standalone but fall back to npm if
# we don't have releases for the architecture.
*)
echoh "Unsupported package manager."
2021-07-14 01:21:22 +08:00
echoh "Falling back to standalone installation."
npm_fallback install_standalone
;;
2020-05-20 16:50:16 +08:00
esac
}
parse_arg() {
case "$1" in
*=*)
# Remove everything after first equal sign.
opt="${1%%=*}"
# Remove everything before first equal sign.
optarg="${1#*=}"
if [ ! "$optarg" ] && [ ! "${OPTIONAL-}" ]; then
echoerr "$opt requires an argument"
echoerr "Run with --help to see usage."
exit 1
fi
echo "$optarg"
return
;;
esac
2020-05-20 16:50:16 +08:00
case "${2-}" in
"" | -*)
if [ ! "${OPTIONAL-}" ]; then
echoerr "$1 requires an argument"
echoerr "Run with --help to see usage."
exit 1
fi
;;
*)
echo "$2"
return
;;
esac
2020-05-20 16:50:16 +08:00
}
fetch() {
URL="$1"
FILE="$2"
if [ -e "$FILE" ]; then
echoh "+ Reusing $FILE"
2020-05-20 16:50:16 +08:00
return
fi
sh_c mkdir -p "$CACHE_DIR"
2020-05-20 16:50:16 +08:00
sh_c curl \
-#fL \
2020-05-23 03:38:03 +08:00
-o "$FILE.incomplete" \
2020-05-20 16:50:16 +08:00
-C - \
"$URL"
2020-05-23 03:38:03 +08:00
sh_c mv "$FILE.incomplete" "$FILE"
2020-05-20 16:50:16 +08:00
}
2021-07-14 01:21:22 +08:00
install_brew() {
echoh "Installing latest from Homebrew."
echoh
2021-07-14 01:21:22 +08:00
sh_c "$BREW_PATH" install code-server
2021-07-14 01:21:22 +08:00
echo_brew_postinstall
}
2020-05-20 16:50:16 +08:00
install_deb() {
2021-07-14 01:21:22 +08:00
echoh "Installing v$VERSION of the $ARCH deb package from GitHub."
2020-05-28 05:01:33 +08:00
echoh
2020-05-20 16:50:16 +08:00
fetch "https://github.com/coder/code-server/releases/download/v$VERSION/code-server_${VERSION}_$ARCH.deb" \
2020-05-27 11:15:44 +08:00
"$CACHE_DIR/code-server_${VERSION}_$ARCH.deb"
2020-05-20 16:50:16 +08:00
sudo_sh_c dpkg -i "$CACHE_DIR/code-server_${VERSION}_$ARCH.deb"
2021-07-14 01:21:22 +08:00
echo_systemd_postinstall deb
2020-05-20 16:50:16 +08:00
}
install_rpm() {
2021-07-14 01:21:22 +08:00
echoh "Installing v$VERSION of the $ARCH rpm package from GitHub."
2020-05-28 05:01:33 +08:00
echoh
2020-05-20 16:50:16 +08:00
fetch "https://github.com/coder/code-server/releases/download/v$VERSION/code-server-$VERSION-$ARCH.rpm" \
2020-05-27 11:15:44 +08:00
"$CACHE_DIR/code-server-$VERSION-$ARCH.rpm"
2020-05-20 16:50:16 +08:00
sudo_sh_c rpm -i "$CACHE_DIR/code-server-$VERSION-$ARCH.rpm"
2021-07-14 01:21:22 +08:00
echo_systemd_postinstall rpm
2020-05-20 16:50:16 +08:00
}
install_aur() {
2021-07-14 01:21:22 +08:00
echoh "Installing latest from the AUR."
2020-05-28 05:01:33 +08:00
echoh
2020-05-20 16:50:16 +08:00
2020-05-28 05:46:55 +08:00
sh_c mkdir -p "$CACHE_DIR/code-server-aur"
sh_c "curl -#fsSL https://aur.archlinux.org/cgit/aur.git/snapshot/code-server.tar.gz | tar -xzC $CACHE_DIR/code-server-aur --strip-components 1"
echo "+ cd $CACHE_DIR/code-server-aur"
if [ ! "${DRY_RUN-}" ]; then
cd "$CACHE_DIR/code-server-aur"
fi
2020-05-28 03:48:03 +08:00
sh_c makepkg -si
2020-05-20 16:50:16 +08:00
2021-07-14 01:21:22 +08:00
echo_systemd_postinstall AUR
2020-05-20 16:50:16 +08:00
}
2020-05-28 04:39:17 +08:00
install_standalone() {
2021-07-14 01:21:22 +08:00
echoh "Installing v$VERSION of the $ARCH release from GitHub."
2020-05-28 05:01:33 +08:00
echoh
2020-05-20 16:50:16 +08:00
fetch "https://github.com/coder/code-server/releases/download/v$VERSION/code-server-$VERSION-$OS-$ARCH.tar.gz" \
2020-05-23 03:38:03 +08:00
"$CACHE_DIR/code-server-$VERSION-$OS-$ARCH.tar.gz"
2020-05-20 16:50:16 +08:00
# -w only works if the directory exists so try creating it first. If this
# fails we can ignore the error as the -w check will then swap us to sudo.
sh_c mkdir -p "$STANDALONE_INSTALL_PREFIX" 2> /dev/null || true
2020-05-20 16:50:16 +08:00
sh_c="sh_c"
2020-05-28 04:39:17 +08:00
if [ ! -w "$STANDALONE_INSTALL_PREFIX" ]; then
2020-05-20 16:50:16 +08:00
sh_c="sudo_sh_c"
fi
2020-05-28 04:39:17 +08:00
if [ -e "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION" ]; then
2020-05-28 05:01:33 +08:00
echoh
echoh "code-server-$VERSION is already installed at $STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION"
echoh "Remove it to reinstall."
2020-05-23 03:38:03 +08:00
exit 0
fi
2020-05-28 03:48:03 +08:00
2020-05-28 04:39:17 +08:00
"$sh_c" mkdir -p "$STANDALONE_INSTALL_PREFIX/lib" "$STANDALONE_INSTALL_PREFIX/bin"
"$sh_c" tar -C "$STANDALONE_INSTALL_PREFIX/lib" -xzf "$CACHE_DIR/code-server-$VERSION-$OS-$ARCH.tar.gz"
"$sh_c" mv -f "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION-$OS-$ARCH" "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION"
"$sh_c" ln -fs "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION/bin/code-server" "$STANDALONE_INSTALL_PREFIX/bin/code-server"
2020-05-20 16:50:16 +08:00
2020-05-28 04:39:17 +08:00
echo_standalone_postinstall
2020-05-20 16:50:16 +08:00
}
install_npm() {
2021-07-14 01:21:22 +08:00
echoh "Installing latest from npm."
echoh
YARN_PATH="${YARN_PATH-yarn}"
NPM_PATH="${YARN_PATH-npm}"
if command_exists "$YARN_PATH"; then
2020-05-28 03:48:03 +08:00
sh_c="sh_c"
2021-07-14 01:21:22 +08:00
if [ ! "${DRY_RUN-}" ] && [ ! -w "$($YARN_PATH global bin)" ]; then
2020-05-28 03:48:03 +08:00
sh_c="sudo_sh_c"
fi
2020-05-28 05:01:33 +08:00
echoh "Installing with yarn."
echoh
2021-07-14 01:21:22 +08:00
"$sh_c" "$YARN_PATH" global add code-server --unsafe-perm
NPM_BIN_DIR="\$($YARN_PATH global bin)" echo_npm_postinstall
2020-05-20 16:50:16 +08:00
return
2021-07-14 01:21:22 +08:00
elif command_exists "$NPM_PATH"; then
2020-05-28 03:48:03 +08:00
sh_c="sh_c"
2021-07-14 01:21:22 +08:00
if [ ! "${DRY_RUN-}" ] && [ ! -w "$(NPM_PATH config get prefix)" ]; then
2020-05-28 03:48:03 +08:00
sh_c="sudo_sh_c"
fi
2020-05-28 05:01:33 +08:00
echoh "Installing with npm."
echoh
2021-07-14 01:21:22 +08:00
"$sh_c" "$NPM_PATH" install -g code-server --unsafe-perm
NPM_BIN_DIR="\$($NPM_PATH bin -g)" echo_npm_postinstall
2020-05-20 16:50:16 +08:00
return
fi
echoerr "Please install npm or yarn to install code-server!"
2020-05-23 03:38:03 +08:00
echoerr "You will need at least node v12 and a few C dependencies."
echoerr "See the docs https://coder.com/docs/code-server/latest/install#yarn-npm"
2021-07-14 01:21:22 +08:00
2020-05-20 16:50:16 +08:00
exit 1
}
2021-07-14 01:21:22 +08:00
# Run $1 if we have a standalone otherwise run install_npm.
npm_fallback() {
if has_standalone; then
$1
else
echoh "No standalone releases for $ARCH."
echoh "Falling back to installation from npm."
install_npm
fi
}
# Determine if we have standalone releases on GitHub for the system's arch.
has_standalone() {
case $ARCH in
amd64) return 0 ;;
# We only have amd64 for macOS.
arm64)
[ "$(distro)" != macos ]
return
;;
2021-07-14 01:21:22 +08:00
*) return 1 ;;
esac
}
os() {
uname="$(uname)"
case $uname in
Linux) echo linux ;;
Darwin) echo macos ;;
FreeBSD) echo freebsd ;;
*) echo "$uname" ;;
2020-05-20 16:50:16 +08:00
esac
}
2021-07-14 01:21:22 +08:00
# Print the detected Linux distro, otherwise print the OS name.
2020-05-20 16:50:16 +08:00
#
# Example outputs:
# - macos -> macos
# - freebsd -> freebsd
# - ubuntu, raspbian, debian ... -> debian
# - amzn, centos, rhel, fedora, ... -> fedora
# - opensuse-{leap,tumbleweed} -> opensuse
# - alpine -> alpine
# - arch -> arch
2020-05-20 16:50:16 +08:00
#
# Inspired by https://github.com/docker/docker-install/blob/26ff363bcf3b3f5a00498ac43694bf1c7d9ce16c/install.sh#L111-L120.
distro() {
2020-06-13 22:56:50 +08:00
if [ "$OS" = "macos" ] || [ "$OS" = "freebsd" ]; then
echo "$OS"
2020-05-20 16:50:16 +08:00
return
fi
if [ -f /etc/os-release ]; then
(
. /etc/os-release
if [ "${ID_LIKE-}" ]; then
for id_like in $ID_LIKE; do
case "$id_like" in debian | fedora | opensuse)
echo "$id_like"
return
;;
esac
done
fi
echo "$ID"
)
2020-05-20 16:50:16 +08:00
return
fi
}
2021-07-14 01:21:22 +08:00
# Print a human-readable name for the OS/distro.
2020-05-20 16:50:16 +08:00
distro_name() {
if [ "$(uname)" = "Darwin" ]; then
echo "macOS v$(sw_vers -productVersion)"
return
fi
if [ -f /etc/os-release ]; then
2020-05-20 16:50:16 +08:00
(
. /etc/os-release
echo "$PRETTY_NAME"
)
return
2020-05-20 16:50:16 +08:00
fi
# Prints something like: Linux 4.19.0-9-amd64
uname -sr
}
arch() {
2021-07-14 01:21:22 +08:00
uname_m=$(uname -m)
case $uname_m in
aarch64) echo arm64 ;;
x86_64) echo amd64 ;;
*) echo "$uname_m" ;;
2020-05-20 16:50:16 +08:00
esac
}
command_exists() {
2021-07-14 01:21:22 +08:00
if [ ! "$1" ]; then return 1; fi
command -v "$@" > /dev/null
2020-05-20 16:50:16 +08:00
}
sh_c() {
2020-05-28 05:01:33 +08:00
echoh "+ $*"
2020-05-20 16:50:16 +08:00
if [ ! "${DRY_RUN-}" ]; then
sh -c "$*"
fi
}
sudo_sh_c() {
if [ "$(id -u)" = 0 ]; then
sh_c "$@"
elif command_exists sudo; then
sh_c "sudo $*"
elif command_exists su; then
sh_c "su - -c '$*'"
2020-05-20 16:50:16 +08:00
else
2020-05-28 05:01:33 +08:00
echoh
echoerr "This script needs to run the following command as root."
echoerr " $*"
2020-05-23 03:38:03 +08:00
echoerr "Please install sudo or su."
2020-05-20 16:50:16 +08:00
exit 1
fi
}
echo_cache_dir() {
2020-05-20 16:50:16 +08:00
if [ "${XDG_CACHE_HOME-}" ]; then
echo "$XDG_CACHE_HOME/code-server"
elif [ "${HOME-}" ]; then
echo "$HOME/.cache/code-server"
else
echo "/tmp/code-server-cache"
fi
}
2020-05-28 05:01:33 +08:00
echoh() {
echo "$@" | humanpath
2020-05-28 03:48:03 +08:00
}
2020-05-28 05:01:33 +08:00
cath() {
2020-05-28 03:48:03 +08:00
humanpath
}
echoerr() {
2020-05-28 05:01:33 +08:00
echoh "$@" >&2
}
# humanpath replaces all occurrences of " $HOME" with " ~"
# and all occurrences of '"$HOME' with the literal '"$HOME'.
2020-05-28 03:48:03 +08:00
humanpath() {
2020-05-28 05:46:55 +08:00
sed "s# $HOME# ~#g; s#\"$HOME#\"\$HOME#g"
2020-05-28 03:48:03 +08:00
}
# We need to make sure we exit with a non zero exit if the command fails.
# /bin/sh does not support -o pipefail unfortunately.
prefix() {
PREFIX="$1"
shift
fifo="$(mktemp -d)/fifo"
mkfifo "$fifo"
sed -e "s#^#$PREFIX: #" "$fifo" &
"$@" > "$fifo" 2>&1
}
2020-05-20 16:50:16 +08:00
main "$@"