#!/usr/bin/env bash # shellcheck disable=SC2086 # https://github.com/ClementTsang/bottom/releases 에서 Nightly 마지막 버전 목록을 확인하고 # 현재 OS와 CPU 등을 파악한 후 다운로드 받아서 설치 # # 지원 플랫폼: # 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 설치 # (네이티브 PowerShell/CMD는 지원 안 함 → 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 # jq가 없으면 python3으로 JSON 파싱 (fallback) 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' 또는 'python3' 중 하나가 필요합니다." _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) # bottom은 ARMv6용 사전 빌드 바이너리를 제공하지 않습니다. # (armv7 musl/gnu 바이너리는 ARMv6에서 동작하지 않음) error "ARMv6(RPi Zero/1)용 사전 빌드 바이너리가 제공되지 않습니다. '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) ─────────────── # 네이티브 PowerShell/CMD는 bash가 없으므로 이 스크립트 실행 불가 # WSL은 Linux로 감지되므로 여기에 해당 안 됨 MINGW*|MSYS*|CYGWIN*) warn "Windows 환경(Git Bash/MSYS2/Cygwin)이 감지되었습니다." warn "WSL(Windows Subsystem for Linux) 사용을 권장합니다." # 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 } # ──────────────────────────────────────────── # 최신 Nightly 릴리스 태그 가져오기 # ──────────────────────────────────────────── get_latest_nightly_tag() { info "GitHub API로 최신 Nightly 릴리스 정보를 조회합니다..." local releases releases="$(curl -fsSL "${API_URL}")" || error "GitHub API 호출에 실패했습니다." local tag tag="$(echo "$releases" | _json_nightly_tag)" [[ -z "$tag" || "$tag" == "null" ]] && error "Nightly 릴리스 태그를 찾을 수 없습니다." echo "$tag" } # ──────────────────────────────────────────── # 다운로드 URL 조합 # ──────────────────────────────────────────── build_download_url() { local tag="$1" local triple="$2" # 플랫폼 트리플 (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_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 "다운로드 중: $url" curl -fsSL --progress-bar -o "${tmpdir}/${filename}" "$url" \ || error "다운로드 실패: $url" info "압축 해제 중..." case "$ext" in tar.gz) command -v tar &>/dev/null || error "'tar' 명령어가 필요합니다." 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 는 zip 지원 tar -xf "${tmpdir}/${filename}" -C "$tmpdir" else error "'unzip' 또는 'tar'(Windows 10+) 명령어가 필요합니다." fi ;; *) error "알 수 없는 압축 포맷: $ext" ;; esac # Windows는 btm.exe, 나머지는 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 "압축 파일에서 '$bin_name' 실행파일을 찾을 수 없습니다." # 설치 디렉토리 생성 mkdir -p "$INSTALL_DIR" local install_path="${INSTALL_DIR}/${bin_name}" info "설치 중: ${install_path}" cp "$bin_src" "$install_path" chmod +x "$install_path" success "설치 완료!" info "버전 확인:" "$install_path" --version || true # PATH 안내 if ! echo ":${PATH}:" | grep -q ":${INSTALL_DIR}:"; then warn "'${INSTALL_DIR}' 가 PATH에 없습니다." warn "다음 줄을 ~/.bashrc 또는 ~/.zshrc 에 추가하세요:" warn " export PATH=\"\$HOME/.local/bin:\$PATH\"" fi } # ──────────────────────────────────────────── # 메인 # ──────────────────────────────────────────── main() { local platform_info triple ext is_windows tag url platform_info="$(detect_platform)" triple="${platform_info%%:*}" # ':' 앞부분 ext="${platform_info##*:}" # ':' 뒷부분 info "감지된 플랫폼: $triple (압축 포맷: $ext)" # Windows 여부 판단 is_windows="false" [[ "$triple" == *"windows"* ]] && is_windows="true" tag="$(get_latest_nightly_tag)" info "최신 Nightly 태그: $tag" url="$(build_download_url "$tag" "$triple" "$ext")" download_and_install "$url" "$ext" "$is_windows" } main "$@"