#!/usr/bin/env bash # shellcheck disable=SC2086 # Downloads and installs the latest superfile from # https://github.com/yorukot/superfile/releases # # Supported platforms: # macOS : Apple Silicon (arm64), Intel (x86_64) — via Homebrew or direct download # Linux : x86_64 (amd64), aarch64/arm64 — via direct download # Windows : Not supported — use install-superfile.ps1 (PowerShell) set -euo pipefail # ──────────────────────────────────────────── # Constants # ──────────────────────────────────────────── REPO="yorukot/superfile" API_URL="https://api.github.com/repos/${REPO}/releases" INSTALL_DIR="${HOME}/.local/bin" # ──────────────────────────────────────────── # Utility functions (all stderr output) # ──────────────────────────────────────────── info() { echo "[INFO] $*" >&2; } warn() { echo "[WARN] $*" >&2; } error() { echo "[ERROR] $*" >&2; exit 1; } success() { echo "[OK] $*" >&2; } # ──────────────────────────────────────────── # Ensure Homebrew is available on macOS # ──────────────────────────────────────────── ensure_brew() { if command -v brew &>/dev/null; then return 0 fi info "Homebrew is not installed. Installing Homebrew..." /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" if [ -d /opt/homebrew/bin ]; then export PATH="/opt/homebrew/bin:$PATH" fi if [ -d /usr/local/bin ]; then export PATH="/usr/local/bin:$PATH" fi command -v brew &>/dev/null || error "Homebrew installation failed. Install it manually: https://brew.sh/" success "Homebrew installed" } # ──────────────────────────────────────────── # Check required commands # ──────────────────────────────────────────── for cmd in curl uname; do command -v "$cmd" &>/dev/null || error "'$cmd' command is required." done # Fallback: use python3 if jq is not available if command -v jq &>/dev/null; then _json_latest_tag() { jq -r '[.[] | select(.prerelease==false)] | .[0].tag_name' } else command -v python3 &>/dev/null || error "'jq' or 'python3' is required." _json_latest_tag() { python3 -c " import sys, json data = json.load(sys.stdin) for r in data: if not r.get('prerelease'): print(r['tag_name']) break " } fi # ──────────────────────────────────────────── # OS / Architecture detection # ──────────────────────────────────────────── OS="$(uname -s)" ARCH="$(uname -m)" case "$OS" in Darwin | Linux) ;; *) error "Unsupported OS: $OS. This script only supports macOS and Linux." ;; esac # Returns: ":" detect_platform() { case "$OS" in Darwin) case "$ARCH" in arm64) echo "darwin-arm64:tar.gz" ;; x86_64) echo "darwin-amd64:tar.gz" ;; *) error "Unsupported macOS architecture: $ARCH" ;; esac ;; Linux) case "$ARCH" in x86_64|amd64) echo "linux-amd64:tar.gz" ;; aarch64|arm64) echo "linux-arm64:tar.gz" ;; *) error "Unsupported Linux architecture: $ARCH" ;; esac ;; esac } # ──────────────────────────────────────────── # Get latest stable release tag # ──────────────────────────────────────────── get_latest_tag() { info "Fetching latest release from GitHub API..." local releases releases="$(curl -fsSL "${API_URL}")" || error "Failed to call GitHub API." local tag tag="$(echo "$releases" | _json_latest_tag)" [[ -z "$tag" || "$tag" == "null" ]] && error "Could not find latest release tag." echo "$tag" } # ──────────────────────────────────────────── # Build download URL # ──────────────────────────────────────────── # Asset naming: superfile-{os}-{tag_version}-{arch}.tar.gz build_download_url() { local tag="$1" local platform="$2" # e.g. darwin-arm64 local ext="$3" # tar.gz | zip local version="${tag#v}" local filename="superfile-${platform}-${tag}.${ext}" echo "https://github.com/${REPO}/releases/download/${tag}/${filename}" } # ──────────────────────────────────────────── # Download, extract & install # ──────────────────────────────────────────── download_and_install() { local url="$1" local ext="$2" local tmpdir tmpdir="$(mktemp -d)" trap '[[ -d "${tmpdir:-}" ]] && rm -rf "$tmpdir"' EXIT local filename filename="${url##*/}" info "Downloading: $url" curl -fsSL --progress-bar -o "${tmpdir}/${filename}" "$url" \ || error "Download failed: $url" info "Extracting..." case "$ext" in tar.gz) command -v tar &>/dev/null || error "'tar' command is required." tar -xzf "${tmpdir}/${filename}" -C "$tmpdir" ;; zip) if command -v unzip &>/dev/null; then unzip -q "${tmpdir}/${filename}" -d "$tmpdir" elif command -v tar &>/dev/null; then tar -xf "${tmpdir}/${filename}" -C "$tmpdir" else error "'unzip' or 'tar' is required." fi ;; *) error "Unknown archive format: $ext" ;; esac # Binary name local bin_name="spf" [[ "$OS" == "MINGW"* || "$OS" == "MSYS"* || "$OS" == "CYGWIN"* ]] && bin_name="spf.exe" local bin_src bin_src="$(find "$tmpdir" -maxdepth 3 -type f -name "$bin_name" | head -1)" [[ -z "$bin_src" ]] && error "Cannot find '$bin_name' in the archive." # Create install directory mkdir -p "$INSTALL_DIR" local install_path="${INSTALL_DIR}/${bin_name}" info "Installing: ${install_path}" cp "$bin_src" "$install_path" chmod +x "$install_path" success "Installation complete!" info "Version check:" "$install_path" --version || true # PATH instructions if ! echo ":${PATH}:" | grep -q ":${INSTALL_DIR}:"; then warn "'${INSTALL_DIR}' is not in your PATH." warn "Add this line to your ~/.bashrc or ~/.zshrc:" warn " export PATH=\"\$HOME/.local/bin:\$PATH\"" fi } # ──────────────────────────────────────────── # Main # ──────────────────────────────────────────── main() { local platform_info platform ext tag url platform_info="$(detect_platform)" platform="${platform_info%%:*}" ext="${platform_info##*:}" info "Detected platform: $platform (format: $ext)" # macOS: try Homebrew first if [[ "$OS" == "Darwin" ]]; then if command -v spf &>/dev/null; then local current_ver current_ver="$(spf --version 2>/dev/null || echo 'unknown')" info "superfile is already installed ($current_ver). Skipping installation." else ensure_brew brew install superfile success "superfile installed via Homebrew" fi else if command -v spf &>/dev/null; then local current_ver current_ver="$(spf --version 2>/dev/null || echo 'unknown')" info "superfile is already installed ($current_ver). Skipping installation." else tag="$(get_latest_tag)" info "Latest release: $tag" url="$(build_download_url "$tag" "$platform" "$ext")" download_and_install "$url" "$ext" fi fi # Verify spf is available if command -v spf &>/dev/null; then success "superfile is available: $(command -v spf)" else warn "superfile was not found in PATH after installation. You may need to open a new terminal." fi } main "$@"