#!/bin/sh # sup bootstrap installer # Usage: curl -sSf https://raw.githubusercontent.com/soma-org/sup/main/install.sh | sh set -eu # Configuration REPO="soma-org/sup" RELEASES_URL="https://github.com/${REPO}/releases" # Colors CYAN='\033[0;36m' GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[0;33m' NC='\033[0m' printf '%bsup installer%b\n' "${CYAN}" "${NC}" printf 'The SOMA toolchain installer\n\n' # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- download_file() { url=$1 output=$2 if command -v curl >/dev/null 2>&1; then if [ -n "${AUTH_HEADER:-}" ]; then if [ "$output" = "-" ]; then curl -fsSL -H "$AUTH_HEADER" "$url" else curl -fsSL -H "$AUTH_HEADER" "$url" -o "$output" fi else if [ "$output" = "-" ]; then curl -fsSL "$url" else curl -fsSL "$url" -o "$output" fi fi elif command -v wget >/dev/null 2>&1; then if [ -n "${AUTH_HEADER:-}" ]; then if [ "$output" = "-" ]; then wget --quiet --header="$AUTH_HEADER" -O- "$url" else wget --quiet --header="$AUTH_HEADER" "$url" -O "$output" fi else if [ "$output" = "-" ]; then wget --quiet -O- "$url" else wget --quiet "$url" -O "$output" fi fi else printf '%bError: neither curl nor wget found%b\n' "${RED}" "${NC}" exit 1 fi } # --------------------------------------------------------------------------- # Platform detection # --------------------------------------------------------------------------- detect_platform() { case "$(uname -s)" in Linux*) OS="linux" ;; Darwin*) OS="macos" ;; MINGW*|MSYS*|CYGWIN*) OS="windows" ;; *) printf '%bUnsupported OS: %s%b\n' "${RED}" "$(uname -s)" "${NC}" exit 1 ;; esac case "$(uname -m)" in x86_64|amd64) ARCH="x86_64" ;; aarch64|arm64) ARCH="aarch64" ;; *) printf '%bUnsupported architecture: %s%b\n' "${RED}" "$(uname -m)" "${NC}" exit 1 ;; esac if [ "$OS" = "windows" ]; then BIN_NAME="sup.exe" else BIN_NAME="sup" fi printf 'Detected platform: %s-%s\n' "$OS" "$ARCH" } # --------------------------------------------------------------------------- # Install directory (with fallback chain) # --------------------------------------------------------------------------- get_install_dir() { # Explicit override if [ -n "${SUP_HOME:-}" ]; then dir="${SUP_HOME}/bin" mkdir -p "$dir" echo "$dir" return fi # Prefer ~/.local/bin local_bin="$HOME/.local/bin" if [ -d "$local_bin" ] || mkdir -p "$local_bin" 2>/dev/null; then echo "$local_bin" return fi # Fallback to /usr/local/bin if writable if [ -w "/usr/local/bin" ]; then echo "/usr/local/bin" return fi # Last resort mkdir -p "$HOME/bin" echo "$HOME/bin" } # --------------------------------------------------------------------------- # Fetch latest release tag # --------------------------------------------------------------------------- find_latest_tag() { printf 'Finding latest release...\n' API_URL="https://api.github.com/repos/${REPO}/releases/latest" TAG=$(download_file "$API_URL" - 2>/dev/null \ | grep '"tag_name"' \ | head -1 \ | sed 's/.*: *"//;s/".*//') if [ -z "${TAG:-}" ]; then printf '%bError: could not determine latest release tag%b\n' "${RED}" "${NC}" exit 1 fi printf 'Latest release: %s\n' "$TAG" } # --------------------------------------------------------------------------- # Download, verify, extract, install # --------------------------------------------------------------------------- download_and_install() { if [ "$OS" = "windows" ]; then ASSET="sup-${OS}-${ARCH}.zip" else ASSET="sup-${OS}-${ARCH}.tar.gz" fi URL="${RELEASES_URL}/download/${TAG}/${ASSET}" INSTALL_DIR=$(get_install_dir) TMP=$(mktemp -d 2>/dev/null || mktemp -d -t sup.XXXXXX) || { printf '%bError: failed to create temporary directory%b\n' "${RED}" "${NC}" exit 1 } trap 'rm -rf "$TMP"' EXIT HUP INT TERM printf 'Downloading %s...\n' "$ASSET" download_file "$URL" "${TMP}/${ASSET}" # SHA256 verification verify_sha256 # Extract printf 'Extracting...\n' if [ "$OS" = "windows" ]; then unzip -q "${TMP}/${ASSET}" -d "${TMP}" else tar xzf "${TMP}/${ASSET}" -C "${TMP}" fi if [ ! -f "${TMP}/${BIN_NAME}" ]; then printf '%bError: %s not found in archive%b\n' "${RED}" "$BIN_NAME" "${NC}" exit 1 fi mkdir -p "$INSTALL_DIR" mv "${TMP}/${BIN_NAME}" "${INSTALL_DIR}/${BIN_NAME}" chmod +x "${INSTALL_DIR}/${BIN_NAME}" printf '%bInstalled sup to %s/%s%b\n' "${GREEN}" "$INSTALL_DIR" "$BIN_NAME" "${NC}" } # --------------------------------------------------------------------------- # SHA256 verification # --------------------------------------------------------------------------- verify_sha256() { if [ "${SUP_SKIP_VERIFY:-0}" = "1" ]; then printf '%bWarning: skipping checksum verification (SUP_SKIP_VERIFY=1)%b\n' "${YELLOW}" "${NC}" return fi SUMS_URL="${RELEASES_URL}/download/${TAG}/SHA256SUMS" SUMS_FILE="${TMP}/SHA256SUMS" if ! download_file "$SUMS_URL" "$SUMS_FILE" 2>/dev/null; then printf '%bError: could not download SHA256SUMS for verification%b\n' "${RED}" "${NC}" printf ' To skip verification: SUP_SKIP_VERIFY=1\n' exit 1 fi if [ ! -f "$SUMS_FILE" ] || [ ! -s "$SUMS_FILE" ]; then printf '%bError: SHA256SUMS is empty or missing%b\n' "${RED}" "${NC}" printf ' To skip verification: SUP_SKIP_VERIFY=1\n' exit 1 fi EXPECTED=$(awk -v asset="$ASSET" '$2 == asset { print $1 }' "$SUMS_FILE") if [ -z "$EXPECTED" ]; then printf '%bError: no SHA256 entry for %s%b\n' "${RED}" "$ASSET" "${NC}" printf ' To skip verification: SUP_SKIP_VERIFY=1\n' exit 1 fi if command -v sha256sum >/dev/null 2>&1; then ACTUAL=$(sha256sum "${TMP}/${ASSET}" | awk '{print $1}') elif command -v shasum >/dev/null 2>&1; then ACTUAL=$(shasum -a 256 "${TMP}/${ASSET}" | awk '{print $1}') else printf '%bError: no sha256sum or shasum found for verification%b\n' "${RED}" "${NC}" printf ' To skip verification: SUP_SKIP_VERIFY=1\n' exit 1 fi if [ "$ACTUAL" = "$EXPECTED" ]; then printf ' %bSHA256 verified ✓%b\n' "${GREEN}" "${NC}" else printf '%bError: SHA256 mismatch!%b\n' "${RED}" "${NC}" printf ' expected: %s\n' "$EXPECTED" printf ' actual: %s\n' "$ACTUAL" exit 1 fi } # --------------------------------------------------------------------------- # Post-install verification # --------------------------------------------------------------------------- verify_install() { INSTALLED="${INSTALL_DIR}/${BIN_NAME}" if [ -x "$INSTALLED" ]; then VERSION=$("$INSTALLED" --version 2>/dev/null || echo "") if [ -n "$VERSION" ]; then printf ' %b%s%b\n' "${GREEN}" "$VERSION" "${NC}" fi fi } # --------------------------------------------------------------------------- # Check for existing soma binary that might conflict # --------------------------------------------------------------------------- check_existing_binaries() { if [ "$OS" = "windows" ]; then soma_bin="soma.exe" else soma_bin="soma" fi if command -v "$soma_bin" >/dev/null 2>&1; then existing=$(command -v "$soma_bin") # Don't warn if it's in our install dir (managed by sup) case "$existing" in "${INSTALL_DIR}/"*) ;; *) printf '\n%bWarning: soma is already installed at %s%b\n' "${YELLOW}" "$existing" "${NC}" printf 'This may conflict with sup-managed binaries.\n' printf 'Ensure %s appears BEFORE %s in your PATH,\n' "$INSTALL_DIR" "$(dirname "$existing")" printf 'or remove the existing binary.\n' ;; esac fi } # --------------------------------------------------------------------------- # PATH setup hint # --------------------------------------------------------------------------- setup_path_hint() { case ":${PATH}:" in *":${INSTALL_DIR}:"*) printf '%b%s is already in your PATH%b\n' "${GREEN}" "$INSTALL_DIR" "${NC}" ;; *) printf '\n%bWarning: %s is not in your PATH%b\n' "${YELLOW}" "$INSTALL_DIR" "${NC}" if [ "$OS" = "windows" ]; then printf 'Add it in PowerShell:\n' printf ' %b$env:Path += ";%s"%b\n' "${GREEN}" "$INSTALL_DIR" "${NC}" printf '\nTo make it permanent, add via:\n' printf ' System Properties > Environment Variables\n' else SHELL_NAME="$(basename "${SHELL:-/bin/sh}")" case "$SHELL_NAME" in zsh) printf 'Add to ~/.zshrc:\n' printf ' %bexport PATH="%s:$PATH"%b\n' "${GREEN}" "$INSTALL_DIR" "${NC}" ;; fish) printf 'Run:\n' printf ' %bfish_add_path %s%b\n' "${GREEN}" "$INSTALL_DIR" "${NC}" ;; *) printf 'Add to your shell profile:\n' printf ' %bexport PATH="%s:$PATH"%b\n' "${GREEN}" "$INSTALL_DIR" "${NC}" ;; esac fi ;; esac } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- main() { AUTH_HEADER="" if [ -n "${GITHUB_TOKEN:-}" ]; then AUTH_HEADER="Authorization: Bearer $GITHUB_TOKEN" fi detect_platform find_latest_tag download_and_install verify_install check_existing_binaries setup_path_hint printf '\n%bsup installed successfully!%b\n' "${GREEN}" "${NC}" printf '\nQuick start:\n' printf ' sup install soma # install latest SOMA CLI\n' printf ' sup install soma@mainnet # install mainnet version\n' printf ' sup list # see available components\n' } main "$@"