#!/bin/sh set -eu BASE_URL=${SUPAFORK_INSTALL_BASE_URL:-https://beta.supafork.com} VERSION=${SUPAFORK_VERSION:-} INSTALL_DIR=${SUPAFORK_INSTALL_DIR:-"$HOME/.supafork/bin"} command -v curl >/dev/null 2>&1 || { printf '%s\n' "supafork: curl is required" >&2 exit 1 } command -v tar >/dev/null 2>&1 || { printf '%s\n' "supafork: tar is required" >&2 exit 1 } if [ -z "$VERSION" ]; then VERSION=$(curl -fsSL "$BASE_URL/releases/beta/version") fi case "$VERSION" in ""|*[!0-9A-Za-z.-]*) printf '%s\n' "supafork: invalid release version" >&2 exit 1 ;; esac case "$(uname -s)" in Darwin) os=darwin ;; Linux) os=linux if { command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; } || ls /lib/ld-musl-*.so.1 >/dev/null 2>&1; then printf '%s\n' "supafork: Alpine/musl Linux is not supported yet" >&2 exit 1 fi ;; *) printf 'supafork: unsupported operating system: %s\n' "$(uname -s)" >&2 exit 1 ;; esac case "$(uname -m)" in arm64|aarch64) arch=arm64 ;; x86_64|amd64) arch=x64 ;; *) printf 'supafork: unsupported architecture: %s\n' "$(uname -m)" >&2 exit 1 ;; esac artifact="supafork-$os-$arch.tar.gz" release_url="$BASE_URL/releases/$VERSION" temporary=$(mktemp -d "${TMPDIR:-/tmp}/supafork.XXXXXX") trap 'rm -rf "$temporary"' EXIT HUP INT TERM printf 'Installing supafork %s for %s/%s...\n' "$VERSION" "$os" "$arch" curl -fsSL "$release_url/$artifact" -o "$temporary/$artifact" curl -fsSL "$release_url/checksums.txt" -o "$temporary/checksums.txt" expected=$(awk -v name="$artifact" '$2 == name { print $1 }' "$temporary/checksums.txt") if [ -z "$expected" ]; then printf '%s\n' "supafork: release checksum is missing" >&2 exit 1 fi if command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$temporary/$artifact" | awk '{ print $1 }') elif command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$temporary/$artifact" | awk '{ print $1 }') else printf '%s\n' "supafork: sha256sum or shasum is required" >&2 exit 1 fi if [ "$actual" != "$expected" ]; then printf '%s\n' "supafork: checksum verification failed" >&2 exit 1 fi tar -xzf "$temporary/$artifact" -C "$temporary" test -f "$temporary/supafork" || { printf '%s\n' "supafork: release archive is invalid" >&2 exit 1 } mkdir -p "$INSTALL_DIR" target="$INSTALL_DIR/supafork" pending="$INSTALL_DIR/.supafork.$$" cp "$temporary/supafork" "$pending" chmod 755 "$pending" mv -f "$pending" "$target" ln -sf supafork "$INSTALL_DIR/sf" "$target" --version printf 'Installed supafork and sf to %s\n' "$INSTALL_DIR" case ":$PATH:" in *:"$INSTALL_DIR":*) ;; *) if [ "$INSTALL_DIR" = "$HOME/.supafork/bin" ]; then profile= if [ -n "${ZDOTDIR:-}" ] && [ -f "$ZDOTDIR/.zshrc" ]; then profile="$ZDOTDIR/.zshrc" elif [ -f "$HOME/.zshrc" ]; then profile="$HOME/.zshrc" elif [ -f "$HOME/.bashrc" ]; then profile="$HOME/.bashrc" elif [ -f "$HOME/.bash_profile" ]; then profile="$HOME/.bash_profile" elif [ -f "$HOME/.profile" ]; then profile="$HOME/.profile" fi if [ -n "$profile" ] && ! grep -Fq '.supafork/bin' "$profile"; then printf '\nexport PATH="$HOME/.supafork/bin:$PATH"\n' >> "$profile" printf 'Added supafork to PATH in %s\n' "$profile" else printf '%s\n' 'Add $HOME/.supafork/bin to PATH to use it in a new shell.' fi else printf 'Add %s to PATH to use it in a new shell.\n' "$INSTALL_DIR" fi ;; esac # Match npm and source installs: setup is best-effort and must never make the # binary installation fail. Piped installs (`curl | sh`) leave stdin on the # pipe and npm lifecycle scripts capture stdin AND stdout, so re-attach the # post-install step to the controlling terminal when one exists — otherwise # the interactive repo picker silently degrades to a static table. # `sf update` re-runs this script for the binary swap only; it sets # SUPAFORK_SKIP_POST_INSTALL=1 because onboarding already happened. if [ -n "${SUPAFORK_SKIP_POST_INSTALL:-}" ]; then exit 0 fi # Resolve the real device (ttysNNN / pts/N): Bun on macOS cannot kqueue the # /dev/tty alias for stdout and crashes with EINVAL. tty_dev=$(ps -o tty= -p $$ 2>/dev/null | tr -d ' ') case "$tty_dev" in "" | *\?*) tty_dev= ;; *) tty_dev="/dev/$tty_dev" ;; esac if [ -t 0 ] && [ -t 1 ]; then "$target" __post-install || true elif [ -n "$tty_dev" ] && [ -r "$tty_dev" ] && [ -w "$tty_dev" ]; then "$target" __post-install <"$tty_dev" >"$tty_dev" 2>"$tty_dev" || true else "$target" __post-install