#!/usr/bin/env bash set -euo pipefail APP=rt REPO="ryangerardwilson/rt" APP_HOME="$HOME/.${APP}" INSTALL_DIR="$APP_HOME/bin" APP_DIR="$APP_HOME/app" MUTED='\033[0;2m' RED='\033[0;31m' ORANGE='\033[38;5;214m' NC='\033[0m' usage() { cat < Install a specific version (e.g., 0.1.0 or v0.1.0) -b Install from a local binary instead of downloading -n Do not modify shell config to add to PATH EOF } requested_version=${VERSION:-} no_modify_path=false binary_path="" while [[ $# -gt 0 ]]; do case "$1" in -h) usage; exit 0 ;; -v) [[ -n "${2:-}" ]] || { echo -e "${RED}Error: -v requires an argument${NC}"; exit 1; } requested_version="$2" shift 2 ;; -b) [[ -n "${2:-}" ]] || { echo -e "${RED}Error: -b requires a path${NC}"; exit 1; } binary_path="$2" shift 2 ;; -n) no_modify_path=true shift ;; *) echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2 shift ;; esac done print_message() { local level=$1 local message=$2 local color="${NC}" [[ "$level" == "error" ]] && color="${RED}" echo -e "${color}${message}${NC}" } mkdir -p "$INSTALL_DIR" # Install from local binary if [[ -n "$binary_path" ]]; then [[ -f "$binary_path" ]] || { print_message error "Binary not found: $binary_path"; exit 1; } print_message info "\n${MUTED}Installing ${NC}${APP}${MUTED} from local binary: ${NC}${binary_path}" cp "$binary_path" "${INSTALL_DIR}/${APP}" chmod 755 "${INSTALL_DIR}/${APP}" specific_version="local" else raw_os=$(uname -s) arch=$(uname -m) if [[ "$raw_os" != "Linux" ]]; then print_message error "Unsupported OS: $raw_os (this installer supports Linux only)" exit 1 fi if [[ "$arch" != "x86_64" ]]; then print_message error "Unsupported arch: $arch (this installer supports x86_64 only)" exit 1 fi command -v curl >/dev/null 2>&1 || { print_message error "'curl' is required but not installed."; exit 1; } command -v tar >/dev/null 2>&1 || { print_message error "'tar' is required but not installed."; exit 1; } filename="${APP}-linux-x64.tar.gz" mkdir -p "$APP_DIR" if [[ -z "$requested_version" ]]; then url="https://github.com/${REPO}/releases/latest/download/${filename}" specific_version="$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p' || true)" [[ -n "$specific_version" ]] || specific_version="latest" else requested_version="${requested_version#v}" url="https://github.com/${REPO}/releases/download/v${requested_version}/${filename}" specific_version="${requested_version}" http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/${REPO}/releases/tag/v${requested_version}") if [[ "$http_status" == "404" ]]; then print_message error "Release v${requested_version} not found" print_message info "${MUTED}See available releases: ${NC}https://github.com/${REPO}/releases" exit 1 fi fi if command -v "${APP}" >/dev/null 2>&1 && [[ "$specific_version" != "latest" ]]; then installed_version=$(${APP} --version 2>/dev/null || true) if [[ -n "$installed_version" && "$installed_version" == "$specific_version" ]]; then print_message info "${MUTED}${APP} version ${NC}${specific_version}${MUTED} already installed${NC}" exit 0 fi fi print_message info "\n${MUTED}Installing ${NC}${APP} ${MUTED}version: ${NC}${specific_version}" tmp_dir="${TMPDIR:-/tmp}/${APP}_install_$$" mkdir -p "$tmp_dir" curl -# -L -o "$tmp_dir/$filename" "$url" tar -xzf "$tmp_dir/$filename" -C "$tmp_dir" if [[ ! -f "$tmp_dir/${APP}/${APP}" ]]; then print_message error "Archive did not contain expected directory '${APP}/${APP}'" print_message info "Expected: $tmp_dir/${APP}/${APP}" exit 1 fi rm -rf "$APP_DIR" mkdir -p "$APP_DIR" mv "$tmp_dir/${APP}" "$APP_DIR" rm -rf "$tmp_dir" cat > "${INSTALL_DIR}/${APP}" </dev/null; then print_message info "${MUTED}PATH entry already present in ${NC}$config_file" elif [[ -w "$config_file" ]]; then { echo "" echo "# ${APP}" echo "$command" } >> "$config_file" print_message info "${MUTED}Added ${NC}${APP}${MUTED} to PATH in ${NC}$config_file" else print_message info "Add this to your shell config:" print_message info " $command" fi } if [[ "$no_modify_path" != "true" ]]; then if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config} current_shell=$(basename "${SHELL:-bash}") case "$current_shell" in zsh) config_candidates=("$HOME/.zshrc" "$HOME/.zshenv" "$XDG_CONFIG_HOME/zsh/.zshrc" "$XDG_CONFIG_HOME/zsh/.zshenv") ;; bash) config_candidates=("$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile" "$XDG_CONFIG_HOME/bash/.bashrc" "$XDG_CONFIG_HOME/bash/.bash_profile") ;; fish) config_candidates=("$HOME/.config/fish/config.fish") ;; *) config_candidates=("$HOME/.profile" "$HOME/.bashrc") ;; esac config_file="" for f in "${config_candidates[@]}"; do if [[ -f "$f" ]]; then config_file="$f"; break; fi done if [[ -z "$config_file" ]]; then print_message info "${MUTED}No shell config file found. Manually add:${NC}" print_message info " export PATH=$INSTALL_DIR:\$PATH" else if [[ "$current_shell" == "fish" ]]; then add_to_path "$config_file" "fish_add_path $INSTALL_DIR" else add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" fi fi fi fi echo "" print_message info "${MUTED}Installed ${NC}${APP}${MUTED} to ${NC}${INSTALL_DIR}/${APP}" print_message info "${MUTED}Run:${NC} ${APP} -h" echo ""