diff --git a/install.sh b/install.sh index 0b768def308e..4bf4cc3d8da5 100755 --- a/install.sh +++ b/install.sh @@ -17,27 +17,45 @@ usage() { Installs code-server for Linux, macOS and FreeBSD. It tries to use the system package manager if possible. After successful installation it explains how to start using code-server. + +Pass --start to startup code-server immediately, print the URL it can be +accessed at and the initial password. Then the script will tail code-server's logs. + +Pass in user@host to install code-server on user@host over ssh. +Pass --start to forward the code-server port and start it so that +you can immediately access it. + +If you rerun the script, code-server will be updated only if necessary. ${not_curl_usage-} Usage: - $arg0 [--dry-run] [--version X.X.X] [--method detect] [--prefix ~/.local] + $arg0 [--dry-run] [--version X.X.X] [--method detect] [--prefix ~/.local] [--start] [user@host] --dry-run Echo the commands for the install process without running them. + --version X.X.X Install a specific version instead of the latest. + --method [detect | standalone] 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. - standalone installs a standalone release archive into ~/.local Add ~/.local/bin to your \$PATH to use it. + --prefix Sets the prefix used by standalone release archives. Defaults to ~/.local 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 + --start + Ensures code-server is running and prints the URL at which it can be accessed. + Also will print code-server's password and when installing over ssh, will forward + the code-server port so that it can be easily accessed locally. + Will block on tailing code-server's logs. + - For Debian, Ubuntu and Raspbian it will install the latest deb package. - For Fedora, CentOS, RHEL and openSUSE it will install the latest rpm package. - For Arch Linux it will install the AUR package. @@ -56,6 +74,8 @@ Usage: - The npm package builds the native modules on postinstall. It will cache all downloaded assets into ~/.cache/code-server +With ssh installation, assets will be transferred over via ssh +as needed instead of being downloaded directly on the ssh host. More installation docs are at https://github.com/cdr/code-server/blob/master/doc/install.md EOF @@ -77,6 +97,7 @@ Please extend your path to use code-server: PATH="$STANDALONE_INSTALL_PREFIX/bin:\$PATH" Then you can run: code-server +Or pass the --start flag to the install script to have it start code-server for you. EOF } @@ -87,6 +108,7 @@ To have systemd start code-server now and restart on boot: sudo systemctl enable --now code-server@\$USER Or, if you don't want/need a background service you can run: code-server +Or pass the --start flag to the install script to have it start code-server for you. EOF } @@ -128,11 +150,18 @@ main() { --version=*) VERSION="$(parse_arg "$@")" ;; + --start) + START=1 + ;; + --) + shift + break + ;; -h | --h | -help | --help) usage exit 0 ;; - *) + -*) echoerr "Unknown flag $1" echoerr "Run with --help to see usage." exit 1 @@ -153,7 +182,7 @@ main() { OS="$(os)" if [ ! "$OS" ]; then - echoerr "Unsupported OS $(uname)." + echoerr "Unsupported OS $(sh_f uname)." exit 1 fi @@ -162,11 +191,11 @@ main() { ARCH="$(arch)" if [ ! "$ARCH" ]; then if [ "$METHOD" = standalone ]; then - echoerr "No precompiled releases for $(uname -m)." + echoerr "No precompiled releases for $(sh_f uname -m)." echoerr 'Please rerun without the "--method standalone" flag to install from npm.' exit 1 fi - echoh "No precompiled releases for $(uname -m)." + echoh "No precompiled releases for $(sh_f uname -m)." install_npm return fi @@ -242,20 +271,25 @@ parse_arg() { } fetch() { - URL="$1" - FILE="$2" + RHOME="$(sh_f printenv HOME)" + + URL="$(echo "$1" | sed "s#$HOME#$RHOME#g")" + FILE="$(echo "$2" | sed "s#$HOME#$RHOME#g")" - if [ -e "$FILE" ]; then + if sh_f [ -e "$FILE" ]; then echoh "+ Reusing $FILE" return fi sh_c mkdir -p "$CACHE_DIR" - sh_c curl \ + SSH_ARGS= sh_c curl \ -#fL \ -o "$FILE.incomplete" \ -C - \ "$URL" + if [ "${SSH_ARGS}" ]; then + sh_c cat '>' "$FILE.incomplete" + fi sh_c mv "$FILE.incomplete" "$FILE" } @@ -319,11 +353,11 @@ install_standalone() { "$CACHE_DIR/code-server-$VERSION-$OS-$ARCH.tar.gz" sh_c="sh_c" - if [ ! -w "$STANDALONE_INSTALL_PREFIX" ]; then + if sh_f [ ! -w "$STANDALONE_INSTALL_PREFIX" ]; then sh_c="sudo_sh_c" fi - if [ -e "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION" ]; then + if sh_f [ -e "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION" ]; then echoh echoh "code-server-$VERSION is already installed at $STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION" echoh "Remove it to reinstall." @@ -341,7 +375,7 @@ install_standalone() { install_npm() { if command_exists yarn; then sh_c="sh_c" - if [ ! -w "$(yarn global bin)" ]; then + if sh_f [ ! -w "$(sh_f yarn global bin)" ]; then sh_c="sudo_sh_c" fi echoh "Installing with yarn." @@ -350,7 +384,7 @@ install_npm() { return elif command_exists npm; then sh_c="sh_c" - if [ ! -w "$(npm config get prefix)" ]; then + if sh_f [ ! -w "$(sh_f npm config get prefix)" ]; then sh_c="sudo_sh_c" fi echoh "Installing with npm." @@ -366,7 +400,7 @@ install_npm() { } os() { - case "$(uname)" in + case "$(sh_f uname)" in Linux) echo linux ;; @@ -396,9 +430,9 @@ distro() { return fi - if [ -f /etc/os-release ]; then + if sh_f [ -f /etc/os-release ]; then ( - . /etc/os-release + ID="$(sh_f '. /etc/os-release && echo "$ID"')" case "$ID" in opensuse-*) # opensuse's ID's look like opensuse-leap and opensuse-tumbleweed. echo "opensuse" @@ -414,25 +448,22 @@ distro() { # os_name prints a pretty human readable name for the OS/Distro. distro_name() { - if [ "$(uname)" = "Darwin" ]; then - echo "macOS v$(sw_vers -productVersion)" + if [ "$(sh_f uname)" = "Darwin" ]; then + echo "macOS v$(sh_f sw_vers -productVersion)" return fi - if [ -f /etc/os-release ]; then - ( - . /etc/os-release - echo "$PRETTY_NAME" - ) + if sh_f [ -f /etc/os-release ]; then + sh_f '. /etc/os-release && echo "$PRETTY_NAME"' return fi # Prints something like: Linux 4.19.0-9-amd64 - uname -sr + sh_f uname -sr } arch() { - case "$(uname -m)" in + case "$(sh_f uname -m)" in aarch64) echo arm64 ;; @@ -446,18 +477,58 @@ arch() { } command_exists() { - command -v "$@" > /dev/null 2>&1 + sh_f command -v "$@" > /dev/null } sh_c() { echoh "+ $*" if [ ! "${DRY_RUN-}" ]; then + sh_f "$@" + fi +} + +sshs() { + cmdline="$*" + + # We want connection sharing between invocations, a connection timeout, + # heartbeat and ssh to exit if port forwarding fails. + mkdir -p ~/.ssh/sockets + chmod 700 ~/.ssh + set -- \ + -o ControlPath=~/.ssh/sockets/%r@%n.sock \ + -o ControlMaster=auto \ + -o ControlPersist=yes \ + -o ConnectTimeout=10 \ + -o ServerAliveInterval=5 \ + -o ExitOnForwardFailure=yes \ + $SSH_ARGS \ + "$@" + + set +e + ssh "$@"; code="$?" + set -e + # Exit code of 255 means ssh itself failed. + if [ "$code" -ne 255 ]; then + return "$code" + fi + + echoerr "Failed to SSH into remote machine:" + echoerr "+ ssh $SSH_ARGS $cmdline" + echoerr "+ \$? = $code" + exit 1 +} + +# Always runs. +sh_f() { + if [ "${SSH_ARGS-}" ]; then + sshs "$*" + else sh -c "$*" fi } sudo_sh_c() { - if [ "$(id -u)" = 0 ]; then + if [ "$(sh_f id -u)" = 0 ]; then sh_c "$@" elif command_exists sudo; then sh_c "sudo $*" @@ -473,8 +544,8 @@ sudo_sh_c() { } echo_cache_dir() { - if [ "${XDG_CACHE_HOME-}" ]; then - echo "$XDG_CACHE_HOME/code-server" + if [ "$(sh_f printenv XDG_CACHE_HOME)" ]; then + echo "$(sh_f printenv XDG_CACHE_HOME)/code-server" elif [ "${HOME-}" ]; then echo "$HOME/.cache/code-server" else