#!/bin/sh # Copyright 2026 StoatWorks Software Inc. All rights reserved. set -eu download_base=${STOATIFY_DOWNLOAD_BASE_URL:-https://www.stoatify.com/downloads/cli} install_dir=${STOATIFY_INSTALL_DIR:-"$HOME/.local/bin"} destination="$install_dir/stoatify" fail() { printf 'Stoatify installer: %s\n' "$1" >&2 exit 1 } need() { command -v "$1" >/dev/null 2>&1 || fail "$1 is required" } need curl need tar need awk need sed case $(uname -s) in Darwin) release_os=darwin ;; Linux) release_os=linux ;; *) fail "unsupported operating system: $(uname -s)" ;; esac case $(uname -m) in x86_64 | amd64) release_arch=amd64 ;; arm64 | aarch64) release_arch=arm64 ;; *) fail "unsupported architecture: $(uname -m)" ;; esac temporary_dir=$(mktemp -d 2>/dev/null || mktemp -d -t stoatify-install) trap 'rm -rf "$temporary_dir"' EXIT HUP INT TERM manifest="$temporary_dir/checksums.txt" printf 'Checking the latest Stoatify CLI release...\n' curl -fsSL "$download_base/latest/checksums.txt" -o "$manifest" || fail "could not download the checksum manifest" archive_suffix="_${release_os}_${release_arch}.tar.gz" archive_name=$(awk -v suffix="$archive_suffix" '$2 ~ (suffix "$") { print $2 }' "$manifest") archive_count=$(printf '%s\n' "$archive_name" | awk 'NF { count++ } END { print count + 0 }') [ "$archive_count" -eq 1 ] || fail "the checksum manifest does not contain exactly one build for this system" version=$(printf '%s\n' "$archive_name" | sed -n "s/^stoatify_\(.*\)${archive_suffix}$/\1/p") printf '%s\n' "$version" | awk '/^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$/ { found=1 } END { exit(found ? 0 : 1) }' || fail "the release version is invalid" profile_block() { profile=$1 path_line=$2 mkdir -p "$(dirname "$profile")" profile_temp="$temporary_dir/profile.$(printf '%s' "$profile" | sed 's/[^A-Za-z0-9]/_/g')" if [ -f "$profile" ]; then awk ' $0 == "# >>> Stoatify CLI >>>" { managed=1; next } $0 == "# <<< Stoatify CLI <<<" { managed=0; next } !managed { print } ' "$profile" > "$profile_temp" else : > "$profile_temp" fi { printf '# >>> Stoatify CLI >>>\n' printf '%s\n' "$path_line" printf '# <<< Stoatify CLI <<<\n' } >> "$profile_temp" mv "$profile_temp" "$profile" } quoted_install_dir=$(printf '%s' "$install_dir" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\$/\\$/g; s/`/\\`/g') posix_path_line="case \":\$PATH:\" in *:\"$quoted_install_dir\":*) ;; *) export PATH=\"$quoted_install_dir:\$PATH\" ;; esac" profile_block "$HOME/.profile" "$posix_path_line" default_shell=$(basename "${SHELL:-sh}") case "$default_shell" in bash) profile_block "$HOME/.bashrc" "$posix_path_line" [ ! -f "$HOME/.bash_profile" ] || profile_block "$HOME/.bash_profile" "$posix_path_line" ;; zsh) profile_block "$HOME/.zshrc" "$posix_path_line" ;; fish) fish_dir=$(printf '%s' "$install_dir" | sed "s/'/'\\\\''/g") profile_block "$HOME/.config/fish/config.fish" "fish_add_path -g '$fish_dir'" ;; esac case ":$PATH:" in *:"$install_dir":*) ;; *) PATH="$install_dir:$PATH"; export PATH ;; esac installed_version= if [ -x "$destination" ]; then installed_version=$("$destination" version 2>/dev/null | sed -n 's/^stoatify version \([^ ]*\).*/\1/p' || true) fi if [ "$installed_version" = "$version" ]; then printf 'Stoatify CLI %s is already installed at %s\n' "$version" "$destination" printf 'Open a new terminal or add %s to PATH in this shell.\n' "$install_dir" exit 0 fi archive="$temporary_dir/$archive_name" curl -fsSL "$download_base/v$version/$archive_name" -o "$archive" || fail "could not download $archive_name" expected_hash=$(awk -v name="$archive_name" '$2 == name { print $1 }' "$manifest") [ -n "$expected_hash" ] || fail "the archive checksum is missing" if command -v sha256sum >/dev/null 2>&1; then actual_hash=$(sha256sum "$archive" | awk '{ print $1 }') elif command -v shasum >/dev/null 2>&1; then actual_hash=$(shasum -a 256 "$archive" | awk '{ print $1 }') elif command -v openssl >/dev/null 2>&1; then actual_hash=$(openssl dgst -sha256 "$archive" | awk '{ print $NF }') else fail "sha256sum, shasum, or openssl is required for checksum verification" fi [ "$actual_hash" = "$expected_hash" ] || fail "checksum verification failed" extract_dir="$temporary_dir/extracted" mkdir -p "$extract_dir" tar -xzf "$archive" -C "$extract_dir" || fail "could not extract the archive" [ -f "$extract_dir/stoatify" ] || fail "the archive does not contain the Stoatify CLI" chmod 0755 "$extract_dir/stoatify" downloaded_version=$("$extract_dir/stoatify" version 2>/dev/null | sed -n 's/^stoatify version \([^ ]*\).*/\1/p' || true) [ "$downloaded_version" = "$version" ] || fail "the downloaded binary version does not match the release" mkdir -p "$install_dir" staged="$install_dir/.stoatify.$$.tmp" cp "$extract_dir/stoatify" "$staged" chmod 0755 "$staged" mv -f "$staged" "$destination" printf 'Installed Stoatify CLI %s at %s\n' "$version" "$destination" printf 'Open a new terminal or add %s to PATH in this shell.\n' "$install_dir"