#!/bin/sh # Download a published Drum release without a compiler or sudo. set -eu fail() { printf '%s\n' "drum: $*" >&2 exit 1 } for tool in curl tar uname mktemp awk; do command -v "$tool" >/dev/null 2>&1 || fail "$tool is required." done # Choose a release for this operating system and architecture. case "$(uname -s)" in Darwin) os=apple-darwin ;; Linux) os=unknown-linux-musl ;; *) fail 'Supported platforms: macOS and Linux.' ;; esac case "$(uname -m)" in arm64|aarch64) arch=aarch64 ;; x86_64|amd64) arch=x86_64 ;; *) fail 'Supported architectures: Intel/AMD 64-bit and ARM64.' ;; esac target="$arch-$os" base=https://github.com/drumhq/drum-cli/releases fetch() { curl --fail --silent --show-error --location \ --proto '=https' --proto-redir '=https' --tlsv1.2 \ --connect-timeout 10 --max-time 180 --max-filesize 100000000 \ "$1" -o "$2" } work=$(mktemp -d "${TMPDIR:-/tmp}/drum-install.XXXXXXXX") || fail 'Could not create a temporary directory.' staged= cleanup() { rm -rf "$work" if [ -n "$staged" ]; then rm -f "$staged" fi } trap cleanup EXIT trap 'exit 1' HUP INT TERM # Resolve the latest published version, then pin all downloads to its tag. fetch "$base/latest/download/version.txt" "$work/version.txt" || fail 'No published CLI release is available. Please try again later.' [ "$(wc -c < "$work/version.txt")" -le 128 ] || fail 'Invalid release version.' version=$(cat "$work/version.txt") case "$version" in *[!0-9.]*) fail 'Invalid release version.' ;; esac printf '%s\n' "$version" | LC_ALL=C grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || fail 'Invalid release version.' asset="drum-$target.tar.gz" fetch "$base/download/v$version/$asset" "$work/$asset" || fail 'Could not download this platform’s release.' fetch "$base/download/v$version/$asset.sha256" "$work/checksum" || fail 'Could not download the checksum.' # Verify the archive before extracting or replacing an installed binary. expected=$(awk -v file="$asset" '$2 == file { print $1 }' "$work/checksum") [ "${#expected}" -eq 64 ] || fail 'Invalid checksum file.' case "$expected" in *[!0-9a-fA-F]*) fail 'Invalid checksum.' ;; esac if command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$work/$asset" | awk '{print $1}') elif command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$work/$asset" | awk '{print $1}') else fail 'sha256sum or shasum is required.' fi [ "$actual" = "$expected" ] || fail 'Checksum verification failed; nothing was installed.' # The release archive must contain exactly one regular file named drum. [ "$(tar -tzf "$work/$asset")" = drum ] || fail 'Invalid release archive.' [ "$(tar -tvzf "$work/$asset" | cut -c 1)" = '-' ] || fail 'Invalid release entry type.' tar -xzf "$work/$asset" -C "$work" drum [ -f "$work/drum" ] && [ ! -L "$work/drum" ] || fail 'Invalid binary.' chmod 755 "$work/drum" [ "$("$work/drum" --version)" = "drum $version" ] || fail 'Downloaded binary has an unexpected version.' # Update an existing user installation, or install into ~/.local/bin. : "${HOME:?HOME is required}" install_dir=${DRUM_INSTALL_DIR:-} if [ -z "$install_dir" ]; then existing=$(command -v drum 2>/dev/null || true) case "$existing" in "$HOME"/*) if [ -f "$existing" ] && [ ! -L "$existing" ] && [ -w "$(dirname "$existing")" ]; then install_dir=$(dirname "$existing") fi ;; esac fi install_dir=${install_dir:-"$HOME/.local/bin"} case "$install_dir" in /*) ;; *) fail 'Install directory must be an absolute path.' ;; esac mkdir -p "$install_dir" [ ! -L "$install_dir/drum" ] || fail 'Refusing to overwrite a symlink named drum.' staged=$(mktemp "$install_dir/.drum.XXXXXXXX") || fail 'Install directory is not writable.' cp "$work/drum" "$staged" chmod 755 "$staged" mv -f "$staged" "$install_dir/drum" staged= printf 'Installed Drum %s to %s/drum\n' "$version" "$install_dir" case ":${PATH:-}:" in *":$install_dir:"*) printf 'Get started: drum init\n' ;; *) printf 'Add this directory to PATH in your shell configuration:\n export PATH="%s:$PATH"\n' "$install_dir" ;; esac