#!/usr/bin/env bash # shellcheck disable=SC2086 # Downloads and installs the latest Nightly version from # https://github.com/ClementTsang/bottom/releases # # Supported platforms: # macOS : Apple Silicon (arm64), Intel (x86_64) # Linux : x86_64, aarch64(RPi4/5), armv7(RPi2/3 32bit), armv6(RPi Zero/1), i686 # FreeBSD : x86_64 # Windows : Git Bash / MSYS2 / Cygwin (.zip install) # (Native PowerShell/CMD not supported → use WSL) set -euo pipefail # ──────────────────────────────────────────── # 상수 정의 # ──────────────────────────────────────────── REPO="ClementTsang/bottom" API_URL="https://api.github.com/repos/${REPO}/releases" INSTALL_DIR="${HOME}/.local/bin" # ──────────────────────────────────────────── # 유틸리티 함수 (모두 stderr 출력) # ──────────────────────────────────────────── info() { echo "[INFO] $*" >&2; } warn() { echo "[WARN] $*" >&2; } error() { echo "[ERROR] $*" >&2; exit 1; } success() { echo "[OK] $*" >&2; } # ──────────────────────────────────────────── # 필수 명령어 확인 # ──────────────────────────────────────────── for cmd in curl uname; do command -v "$cmd" &>/dev/null || error "'$cmd' 명령어가 필요합니다." done # Fallback: use python3 if jq is not available if command -v jq &>/dev/null; then _json_nightly_tag() { jq -r '[.[] | select(.prerelease==true and (.tag_name | startswith("nightly-")))] | .[0].tag_name' } else command -v python3 &>/dev/null || error "'jq' or 'python3' is required." _json_nightly_tag() { python3 -c " import sys, json data = json.load(sys.stdin) for r in data: if r.get('prerelease') and r.get('tag_name','').startswith('nightly-'): print(r['tag_name']) break " } fi # ──────────────────────────────────────────── # OS / 아키텍처 감지 # ──────────────────────────────────────────── # 반환값 형식: "<플랫폼 트리플>:<압축포맷>" # 압축포맷 = tar.gz | zip detect_platform() { local os arch os="$(uname -s)" arch="$(uname -m)" case "$os" in # ── macOS ────────────────────────────────────────────── Darwin) case "$arch" in arm64) echo "aarch64-apple-darwin:tar.gz" ;; x86_64) echo "x86_64-apple-darwin:tar.gz" ;; *) error "지원하지 않는 macOS 아키텍처: $arch" ;; esac ;; # ── Linux (라즈베리파이 전 모델 포함) ────────────────── Linux) case "$arch" in x86_64|amd64) echo "x86_64-unknown-linux-musl:tar.gz" ;; aarch64|arm64) # RPi 4/5, 64bit OS echo "aarch64-unknown-linux-musl:tar.gz" ;; armv7l) # RPi 2/3, 32bit OS (ARMv7) echo "armv7-unknown-linux-musleabihf:tar.gz" ;; armv6l) # RPi Zero / 1 (ARMv6) # No pre-built binaries available for ARMv6. # (armv7 musl/gnu binaries don't work on ARMv6) error "No pre-built binaries for ARMv6 (RPi Zero/1). Build from source: 'cargo install bottom'" ;; i686|i386) echo "i686-unknown-linux-musl:tar.gz" ;; *) error "지원하지 않는 Linux 아키텍처: $arch" ;; esac ;; # ── FreeBSD ──────────────────────────────────────────── FreeBSD) case "$arch" in amd64|x86_64) echo "x86_64-unknown-freebsd:tar.gz" ;; *) error "FreeBSD는 x86_64만 지원합니다 (현재: $arch)" ;; esac ;; # ── Windows (Git Bash / MSYS2 / Cygwin) ─────────────── # Native PowerShell/CMD don't have bash, so this script won't run # WSL is detected as Linux, not Windows MINGW*|MSYS*|CYGWIN*) warn "Windows environment (Git Bash/MSYS2/Cygwin) detected." warn "WSL (Windows Subsystem for Linux) is recommended." # Windows에는 tar.exe(Windows 10 1803+)가 있어 .zip 처리 가능 case "$arch" in x86_64) echo "x86_64-pc-windows-msvc:zip" ;; aarch64) echo "aarch64-pc-windows-msvc:zip" ;; i686) echo "i686-pc-windows-msvc:zip" ;; *) error "지원하지 않는 Windows 아키텍처: $arch" ;; esac ;; *) error "지원하지 않는 OS: $os (uname -a: $(uname -a))" ;; esac } # ──────────────────────────────────────────── # Get latest Nightly release tag # ──────────────────────────────────────────── get_latest_nightly_tag() { info "Fetching latest Nightly release from GitHub API..." local releases releases="$(curl -fsSL "${API_URL}")" || error "Failed to call GitHub API." local tag tag="$(echo "$releases" | _json_nightly_tag)" [[ -z "$tag" || "$tag" == "null" ]] && error "Could not find Nightly release tag." echo "$tag" } # ──────────────────────────────────────────── # Build download URL # ──────────────────────────────────────────── build_download_url() { local tag="$1" local triple="$2" # Platform triple (e.g. x86_64-apple-darwin) local ext="$3" # tar.gz | zip local filename="bottom_${triple}.${ext}" echo "https://github.com/${REPO}/releases/download/${tag}/${filename}" } # ──────────────────────────────────────────── # Download, extract & install # ──────────────────────────────────────────── download_and_install() { local url="$1" local ext="$2" # tar.gz | zip local is_windows="${3:-false}" 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 # Windows 10+ tar.exe supports zip tar -xf "${tmpdir}/${filename}" -C "$tmpdir" else error "'unzip' or 'tar' (Windows 10+) is required." fi ;; *) error "Unknown archive format: $ext" ;; esac # Windows uses btm.exe, others use btm local bin_name="btm" [[ "$is_windows" == "true" ]] && bin_name="btm.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 triple ext is_windows tag url platform_info="$(detect_platform)" triple="${platform_info%%:*}" # Before ':' ext="${platform_info##*:}" # After ':' info "Detected platform: $triple (format: $ext)" # Check if Windows is_windows="false" [[ "$triple" == *"windows"* ]] && is_windows="true" tag="$(get_latest_nightly_tag)" info "Latest Nightly tag: $tag" url="$(build_download_url "$tag" "$triple" "$ext")" download_and_install "$url" "$ext" "$is_windows" } main "$@"