From 46ffb67d60bb198b1db8fad10bba51c47751c235 Mon Sep 17 00:00:00 2001 From: Ben Potter Date: Wed, 1 Jun 2022 14:15:09 -0500 Subject: [PATCH] feat: one-line install script (#1924) * feat: one-line install script * remove homebrew support * remove arch linux * use proper filename for packages * fix variable format * fix systemd instructions * fixes to standalone script * fix missing var bugs * fix standalone install * fix for MacOS * format * fix armv7 assets and zips * remove windows * update install docs * support external sources with shellcheck * shfmt * add external sources to GitHub action & unfold * change wording * first template docs * default to /usr/local instead * add option for binary name --- .github/workflows/coder.yaml | 2 + Makefile | 2 +- README.md | 48 +-- docs/install.md | 116 +++++--- install.sh | 555 +++++++++++++++++++++++++++++++++++ 5 files changed, 641 insertions(+), 82 deletions(-) create mode 100755 install.sh diff --git a/.github/workflows/coder.yaml b/.github/workflows/coder.yaml index 6a32d6babc..398c7e6c82 100644 --- a/.github/workflows/coder.yaml +++ b/.github/workflows/coder.yaml @@ -52,6 +52,8 @@ jobs: - uses: actions/checkout@v3 - name: Run ShellCheck uses: ludeeus/action-shellcheck@1.1.0 + env: + SHELLCHECK_OPTS: --external-sources with: ignore: node_modules diff --git a/Makefile b/Makefile index 61fb4a5537..a75a9cb288 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ lint/go: # Use shfmt to determine the shell files, takes editorconfig into consideration. lint/shellcheck: $(shell shfmt -f .) @echo "--- shellcheck" - shellcheck $(shell shfmt -f .) + shellcheck --external-sources $(shell shfmt -f .) peerbroker/proto/peerbroker.pb.go: peerbroker/proto/peerbroker.proto protoc \ diff --git a/README.md b/README.md index 17cc962d84..2b6ab2ad62 100644 --- a/README.md +++ b/README.md @@ -56,47 +56,25 @@ You can use any Web IDE ([code-server](https://github.com/coder/code-server), [p ## Installing Coder -We recommend installing [the latest -release](https://github.com/coder/coder/releases) on a system with at least 1 -CPU core and 2 GB RAM: +There are a few ways to install Coder: [install script](./docs/install.md#installsh) (macOS, Linux), [docker-compose](./docs/install.md#docker-compose), or [manually](./docs/install.md#manual) via the latest release (macOS, Windows, and Linux). -1. Download the [release asset](https://github.com/coder/coder/releases) appropriate for your operating system -1. Unzip the folder you just downloaded, and move the `coder` executable to a location that's on your `PATH` +If you use the install script, you can preview what occurs during the install process: - ```sh - # ex. MacOS and Linux - mv coder /usr/local/bin - ``` +```sh +curl -fsSL https://coder.com/install.sh | sh -s -- --dry-run +``` - Windows: see [this guide](https://answers.microsoft.com/en-us/windows/forum/all/adding-path-variable/97300613-20cb-4d85-8d0e-cc9d3549ba23) on adding a folder to `PATH` +To install, run: -There are a few ways to run Coder: +```sh +curl -fsSL https://coder.com/install.sh | sh +``` -- To run a **temporary deployment**, start with dev mode (all data is in-memory and destroyed on exit): +Once installed, you can run a temporary deployment in dev mode (all data is in-memory and destroyed on exit): - ```bash - coder server --dev - ``` - -- To run a **production deployment** with PostgreSQL: - - ```bash - CODER_PG_CONNECTION_URL="postgres://@/?password=" \ - coder server - ``` - -- To run as a **system service**, install with `.deb` (Debian, Ubuntu) or `.rpm` (Fedora, CentOS, RHEL, SUSE): - - ```bash - # Edit the configuration! - sudo vim /etc/coder.d/coder.env - sudo service coder restart - ``` - - > macOS and Windows users: You'll need to write your own - > configuration to run Coder as a system service. - -- See the [installation guide](./docs/install.md) for additional ways to run Coder (e.g., docker-compose) +```sh +coder server --dev +``` Use `coder --help` to get a complete list of flags and environment variables. diff --git a/docs/install.md b/docs/install.md index 04e680448f..5f055f7224 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,6 +2,58 @@ This article walks you through the various ways of installing and deploying Coder. +## install.sh + +The easiest way to install Coder is to use our [install script](https://github.com/coder/coder/main/install.sh) for Linux and macOS. The install script +attempts to use the system package manager detection-reference if possible. + +You can preview what occurs during the install process: + +```bash +curl -L https://coder.com/install.sh | sh -s -- --dry-run +``` + +To install, run: + +```bash +curl -L https://coder.com/install.sh | sh +``` + +> If you're concerned about the install script's use of `curl | sh` and the +> security implications, please see [this blog +> post](https://sandstorm.io/news/2015-09-24-is-curl-bash-insecure-pgp-verified-install) +> by [sandstorm.io](https://sandstorm.io). + +You can modify the installation process by including flags. Run the help command for reference: + +```bash +curl -L https://coder.com/install.sh | sh -s -- --help +``` + +## System packages + +Coder publishes the following system packages [in GitHub releases](https://github.com/coder/coder/releases): + +- .deb (Debian, Ubuntu) +- .rpm (Fedora, CentOS, RHEL, SUSE) +- .apk (Alpine) + +Once installed, you can run Coder as a system service: + +```sh +# Specify a PostgreSQL database +# in the configuration first: +sudo vim /etc/coder.d/coder.env +sudo service coder restart +``` + +Or run a **temporary deployment** with dev mode (all data is in-memory and destroyed on exit): + + +```sh +coder server --dev +``` + ## docker-compose Before proceeding, please ensure that you have both Docker and the [latest version of @@ -40,62 +92,34 @@ Coder](https://github.com/coder/coder/releases) installed. ghcr.io/coder/coder:v0.5.10 ``` -1. Open a new terminal window, and run `coder login ` to create - your first user (once you've done so, you can navigate to `yourAccessURL` and - log in with these credentials). +1. Follow the on-screen instructions to create your first template and workspace -1. Next, copy a sample template into a new directory so that you can create a custom template in a - subsequent step (be sure that you're working in the directory where you want - your templates stored): +## Manual - ```console - coder templates init - ``` +We publish self-contained .zip and .tar.gz archives in [GitHub releases](https://github.com/coder/coder/releases). The archives bundle `coder` binary. - Choose the "Develop in Docker" example to generate a sample template in the - `docker` subdirectory. +1. Download the [release archive](https://github.com/coder/coder/releases) appropriate for your operating system -1. Navigate into the new directory and create a new template: +1. Unzip the folder you just downloaded, and move the `coder` executable to a location that's on your `PATH` - ```console - cd docker - coder templates create + ```sh + # ex. MacOS and Linux + mv coder /usr/local/bin ``` - Follow the prompts displayed to proceed. When done, you'll see the following - message: + > Windows users: see [this guide](https://answers.microsoft.com/en-us/windows/forum/all/adding-path-variable/97300613-20cb-4d85-8d0e-cc9d3549ba23) for adding folders to `PATH`. - ```console - The docker template has been created! Developers can - provision a workspace with this template using: +1. Start a Coder server - coder create --template="docker" [workspace name] + To run a **temporary deployment**, start with dev mode (all data is in-memory and destroyed on exit): + + ```bash + coder server --dev ``` -1. At this point, you're ready to provision your first workspace: + To run a **production deployment** with PostgreSQL: - ```console - coder create --template="docker" [workspace name] + ```bash + CODER_PG_CONNECTION_URL="postgres://@/?password=" \ + coder server ``` - - Follow the on-screen prompts to set the parameters for your workspace. If - the process is successful, you'll get information regarding your workspace: - - ```console - ┌─────────────────────────────────────────────────────────────────┐ - │ RESOURCE STATUS ACCESS │ - ├─────────────────────────────────────────────────────────────────┤ - │ docker_container.workspace ephemeral │ - │ └─ dev (linux, amd64) ⦾ connecting [0s] coder ssh main │ - ├─────────────────────────────────────────────────────────────────┤ - │ docker_volume.coder_volume ephemeral │ - └─────────────────────────────────────────────────────────────────┘ - The main workspace has been created! - ``` - -You can now access your workspace via your web browser by navigating to your -access URL, or you can connect to it via SSH by running: - -```console -coder ssh [workspace name] -``` diff --git a/install.sh b/install.sh new file mode 100755 index 0000000000..795c1af367 --- /dev/null +++ b/install.sh @@ -0,0 +1,555 @@ +#!/bin/sh +set -eu + +# Coder's automatic install script. +# See https://github.com/coder/coder#installing-coder + +usage() { + arg0="$0" + if [ "$0" = sh ]; then + arg0="curl -fsSL https://coder.com/install.sh | sh -s --" + else + not_curl_usage="The latest script is available at https://coder.com/install.sh +" + fi + + cath < + Sets the prefix used by standalone release archives. Defaults to /usr/local + and the binary is copied into /usr/local/bin + To install in \$HOME, pass ---prefix=\$HOME/.local + + --binary-name + Sets the name for the CLI in standalone release archives. Defaults to "coder" + To use the CLI as coder2, pass --binary-name=coder2 + Note: in-product documentation will always refer to the CLI as "coder" + + --rsh + Specifies the remote shell for remote installation. Defaults to ssh. + +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. + - Alpine: install the apk package from GitHub. + - macOS: install the release from GitHub. + - All others: install the release from GitHub. + +We build releases on GitHub for amd64, armv7, and arm64 on Windows, Linux, and macOS. + +When the detection method tries to pull a release from GitHub it will +fall back to installing standalone when there is no matching release for +the system's operating system and architecture. + +The installer will cache all downloaded assets into ~/.cache/coder +EOF +} + +echo_latest_version() { + if [ "${EDGE-}" ]; then + version="$(curl -fsSL https://api.github.com/repos/coder/coder/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/coder/releases/latest)" + fi + version="${version#https://github.com/coder/coder/releases/tag/}" + version="${version#v}" + echo "$version" +} + +echo_standalone_postinstall() { + cath </dev/null || true + + sh_c="sh_c" + if [ ! -w "$STANDALONE_INSTALL_PREFIX" ]; then + sh_c="sudo_sh_c" + fi + + "$sh_c" mkdir -p "$STANDALONE_INSTALL_PREFIX/bin" + if [ "$STANDALONE_ARCHIVE_FORMAT" = tar.gz ]; then + "$sh_c" tar -C "$CACHE_DIR" -xzf "$CACHE_DIR/coder_${VERSION}_${OS}_${ARCH}.tar.gz" + else + "$sh_c" unzip -d "$CACHE_DIR" -o "$CACHE_DIR/coder_${VERSION}_${OS}_${ARCH}.zip" + fi + + "$sh_c" cp "$CACHE_DIR/coder" "$STANDALONE_INSTALL_PREFIX/bin/$STANDALONE_BINARY_NAME" + + echo_standalone_postinstall +} + +# Determine if we have standalone releases on GitHub for the system's arch. +has_standalone() { + case $ARCH in + amd64) return 0 ;; + ard64) return 0 ;; + armv7) + [ "$(distro)" != darwin ] + return + ;; + *) return 1 ;; + esac +} + +os() { + uname="$(uname)" + case $uname in + Linux) echo linux ;; + Darwin) echo darwin ;; + FreeBSD) echo freebsd ;; + *) echo "$uname" ;; + esac +} + +# Print the detected Linux distro, otherwise print the OS name. +# +# Example outputs: +# - darwin -> darwin +# - freebsd -> freebsd +# - ubuntu, raspbian, debian ... -> debian +# - amzn, centos, rhel, fedora, ... -> fedora +# - opensuse-{leap,tumbleweed} -> opensuse +# - alpine -> alpine +# - arch -> arch +# +# Inspired by https://github.com/docker/docker-install/blob/26ff363bcf3b3f5a00498ac43694bf1c7d9ce16c/install.sh#L111-L120. +distro() { + if [ "$OS" = "darwin" ] || [ "$OS" = "freebsd" ]; then + echo "$OS" + 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" + ) + return + fi +} + +# Print a human-readable name for the OS/distro. +distro_name() { + if [ "$(uname)" = "Darwin" ]; then + echo "macOS v$(sw_vers -productVersion)" + return + fi + + if [ -f /etc/os-release ]; then + ( + . /etc/os-release + echo "$PRETTY_NAME" + ) + return + fi + + # Prints something like: Linux 4.19.0-9-amd64 + uname -sr +} + +arch() { + uname_m=$(uname -m) + case $uname_m in + aarch64) echo arm64 ;; + x86_64) echo amd64 ;; + armv7l) echo armv7 ;; + *) echo "$uname_m" ;; + esac +} + +command_exists() { + if [ ! "$1" ]; then return 1; fi + command -v "$@" >/dev/null +} + +sh_c() { + echoh "+ $*" + 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 '$*'" + else + echoh + echoerr "This script needs to run the following command as root." + echoerr " $*" + echoerr "Please install sudo or su." + exit 1 + fi +} + +echo_cache_dir() { + if [ "${XDG_CACHE_HOME-}" ]; then + echo "$XDG_CACHE_HOME/coder" + elif [ "${HOME-}" ]; then + echo "$HOME/.cache/coder" + else + echo "/tmp/coder-cache" + fi +} + +echoh() { + echo "$@" | humanpath +} + +cath() { + humanpath +} + +echoerr() { + echoh "$@" >&2 +} + +# humanpath replaces all occurrences of " $HOME" with " ~" +# and all occurrences of '"$HOME' with the literal '"$HOME'. +humanpath() { + sed "s# $HOME# ~#g; s#\"$HOME#\"\$HOME#g" +} + +# 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 +} + +main "$@"