Add base Jerboa installer
ober
9c1cbce8093f4e0160bee6bee260b8243309f4ea
--- a/docs/release-artifacts.md +++ b/docs/release-artifacts.md @@ -22,6 +22,40 @@ jerboa-vX.Y.Z-<target>/bin/jmcp -> jerboa jerboa-vX.Y.Z-<target>/bin/jlsp -> jerboa ``` +## End-User Install + +Install the base Jerboa toolchain with: + +```sh +curl -fsSL https://git.sr.ht/~lisp/jerboa/blob/master/support/install.sh | bash +``` + +That installs `jerboa`, `jmcp`, `jlsp`, `jerbuild`, and `jpkg` into +`$HOME/.local/bin` by default. After that, use `jpkg` to install additional +binary packages from package repos. + +The installer defaults to the newest `vX.Y.Z` SourceHut tag. Pin a release +with: + +```sh +curl -fsSL https://git.sr.ht/~lisp/jerboa/blob/master/support/install.sh \ + | JERBOA_VERSION=v0.2.3 bash +``` + +Install somewhere else with: + +```sh +curl -fsSL https://git.sr.ht/~lisp/jerboa/blob/master/support/install.sh \ + | PREFIX=/usr/local bash +``` + +or: + +```sh +curl -fsSL https://git.sr.ht/~lisp/jerboa/blob/master/support/install.sh \ + | JERBOA_INSTALL_DIR="$HOME/bin" bash +``` + ## Build Locally ```sh new file mode 100755 --- /dev/null +++ b/support/install.sh @@ -0,0 +1,262 @@ +#!/bin/sh +# Install the base Jerboa toolchain from SourceHut release artifacts. +# +# End-user install: +# curl -fsSL https://git.sr.ht/~lisp/jerboa/blob/master/support/install.sh | bash +# +# This installs the Jerboa multicall binary plus the jmcp, jlsp, jerbuild, and +# jpkg command links. Use jpkg after this to install additional binary packages. + +set -eu + +usage() { + cat >&2 <<'EOF' +Usage: install.sh [--version VERSION] [--bindir DIR] [--prefix DIR] + +Installs the base Jerboa toolchain from SourceHut release artifacts. + +Options: + --version VERSION Install a tag such as v0.2.3, or latest + --bindir DIR Install commands directly into DIR + --prefix DIR Install commands into DIR/bin + -h, --help Show this help + +Environment: + JERBOA_VERSION Version tag to install, or latest (default: latest) + JERBOA_INSTALL_DIR Bin directory to install into (default: $HOME/.local/bin) + PREFIX Prefix used when JERBOA_INSTALL_DIR is unset + JERBOA_RELEASE_REPO SourceHut repo path (default: ~lisp/jerboa) + JERBOA_RELEASE_ORIGIN SourceHut origin (default: https://git.sr.ht) + JERBOA_RELEASE_BASE Complete release base URL override + JERBOA_RELEASE_TARGET Target override, e.g. linux-amd64 +EOF +} + +error() { + echo "ERROR: $*" >&2 + exit 1 +} + +need_value() { + [ "${2:-}" ] || error "$1 requires a value" +} + +version=${JERBOA_VERSION:-latest} +bindir=${JERBOA_INSTALL_DIR:-} +prefix=${PREFIX:-} + +while [ "$#" -gt 0 ]; do + case "$1" in + --version) + need_value "$1" "${2:-}" + version=$2 + shift 2 + ;; + --bindir) + need_value "$1" "${2:-}" + bindir=$2 + shift 2 + ;; + --prefix) + need_value "$1" "${2:-}" + prefix=$2 + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + error "unknown option: $1" + ;; + esac +done + +if [ -z "$bindir" ]; then + if [ -n "$prefix" ]; then + bindir="${prefix%/}/bin" + elif [ -n "${HOME:-}" ]; then + bindir="$HOME/.local/bin" + else + error "HOME is unset; set JERBOA_INSTALL_DIR or PREFIX" + fi +fi + +repo=${JERBOA_RELEASE_REPO:-~lisp/jerboa} +origin=${JERBOA_RELEASE_ORIGIN:-https://git.sr.ht} +repo_url="${origin%/}/$repo" + +download() { + src=$1 + dst=$2 + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$src" -o "$dst" + elif command -v fetch >/dev/null 2>&1; then + fetch -q -o "$dst" "$src" + elif command -v wget >/dev/null 2>&1; then + wget -q -O "$dst" "$src" + else + error "need curl, fetch, or wget to download Jerboa" + fi +} + +download_stdout() { + src=$1 + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$src" + elif command -v fetch >/dev/null 2>&1; then + fetch -q -o - "$src" + elif command -v wget >/dev/null 2>&1; then + wget -q -O - "$src" + else + error "need curl, fetch, or wget to download Jerboa" + fi +} + +sha256_file() { + path=$1 + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$path" | awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$path" | awk '{print $1}' + elif command -v sha256 >/dev/null 2>&1; then + sha256 -q "$path" + else + error "need sha256sum, shasum, or sha256" + fi +} + +highest_version() { + awk ' + $0 ~ /^v[0-9]+\.[0-9]+\.[0-9]+$/ { + split(substr($0, 2), part, ".") + major = part[1] + 0 + minor = part[2] + 0 + patch = part[3] + 0 + if (!seen || + major > best_major || + (major == best_major && minor > best_minor) || + (major == best_major && minor == best_minor && patch > best_patch)) { + seen = 1 + best = $0 + best_major = major + best_minor = minor + best_patch = patch + } + } + END { + if (seen) { + print best + } else { + exit 1 + } + } + ' +} + +resolve_latest_version() { + if command -v git >/dev/null 2>&1; then + git ls-remote --tags "$repo_url" 'v[0-9]*' \ + | awk '$2 ~ /^refs\/tags\/v[0-9]+\.[0-9]+\.[0-9]+$/ { sub(/^refs\/tags\//, "", $2); print $2 }' \ + | highest_version + else + download_stdout "${repo_url}/refs" \ + | sed -n 's|.*href="[^"]*/refs/\(v[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)".*|\1|p' \ + | highest_version + fi +} + +detect_target() { + if [ "${JERBOA_RELEASE_TARGET:-}" ]; then + echo "$JERBOA_RELEASE_TARGET" + return + fi + + os=$(uname -s) + arch=$(uname -m) + case "$os-$arch" in + Darwin-arm64) echo macos-arm64 ;; + Linux-x86_64|Linux-amd64) echo linux-amd64 ;; + Linux-aarch64|Linux-arm64) echo linux-arm64 ;; + FreeBSD-amd64|FreeBSD-x86_64) echo freebsd-amd64 ;; + *) error "unsupported platform: $os $arch" ;; + esac +} + +install_links() { + for link in jmcp jlsp jerbuild jpkg; do + ln -sf jerboa "$bindir/$link" + done +} + +if [ "$version" = latest ]; then + version=$(resolve_latest_version) || error "could not resolve latest Jerboa release" +fi + +case "$version" in + v[0-9]*.[0-9]*.[0-9]*) ;; + *) error "version must be latest or a tag like v0.2.3" ;; +esac + +target=$(detect_target) +case "$target" in + macos-arm64|linux-amd64|linux-arm64|freebsd-amd64) ;; + *) error "unsupported release target: $target" ;; +esac + +if [ -x "$bindir/jerboa" ]; then + installed=$("$bindir/jerboa" --version 2>/dev/null || true) + case "$installed" in + *"${version#v}"*) + mkdir -p "$bindir" + install_links + echo "Jerboa $version is already installed in $bindir" + exit 0 + ;; + esac +fi + +file="jerboa-${version}-${target}.tar.gz" +base=${JERBOA_RELEASE_BASE:-${repo_url}/refs/download/${version}} +url="${base%/}/${file}" +sum_url="${url}.sha256" + +tmp=$(mktemp -d "${TMPDIR:-/tmp}/jerboa-install.XXXXXX") +trap 'rm -rf "$tmp"' EXIT HUP INT TERM + +archive="$tmp/$file" +sum_file="$tmp/$file.sha256" + +echo "Installing Jerboa $version for $target" +echo "Downloading $url" +download "$url" "$archive" +download "$sum_url" "$sum_file" + +expected=$(awk '{print $1; exit}' "$sum_file") +actual=$(sha256_file "$archive") +if [ "$expected" != "$actual" ]; then + echo "ERROR: checksum mismatch for $file" >&2 + echo "expected: $expected" >&2 + echo "actual: $actual" >&2 + exit 1 +fi + +mkdir -p "$tmp/extract" +tar -xzf "$archive" -C "$tmp/extract" +root="$tmp/extract/jerboa-${version}-${target}" +[ -x "$root/bin/jerboa" ] || error "archive did not contain bin/jerboa" + +mkdir -p "$bindir" +cp "$root/bin/jerboa" "$bindir/jerboa" +chmod 0755 "$bindir/jerboa" +install_links + +echo "Installed Jerboa $version to $bindir" +echo "Installed commands: jerboa jmcp jlsp jerbuild jpkg" + +case ":${PATH:-}:" in + *":$bindir:"*) ;; + *) + echo "Add $bindir to PATH before running jerboa or jpkg." + ;; +esac