#!/usr/bin/env bash # shellcheck disable=SC2086 # GitHub Releases에서 최신 Helix 에디터 TUI를 다운로드하고 필수 런타임을 설치합니다. # Helix: https://github.com/helix-editor/helix # # 지원 플랫폼: # macOS : Apple Silicon (arm64), Intel (x86_64) # Linux : x86_64, aarch64(arm64) set -euo pipefail # ──────────────────────────────────────────── # 상수 정의 # ──────────────────────────────────────────── REPO="helix-editor/helix" API_URL="https://api.github.com/repos/${REPO}/releases" FALLBACK_TAG="25.07.1" INSTALL_DIR="${HOME}/.local/bin" HELIX_CONFIG_DIR="${HOME}/.config/helix" # ──────────────────────────────────────────── # 유틸리티 함수 (모두 stderr 출력) # ──────────────────────────────────────────── info() { echo "[INFO] $*" >&2; } warn() { echo "[WARN] $*" >&2; } error() { echo "[ERROR] $*" >&2; exit 1; } success() { echo "[OK] $*" >&2; } # ──────────────────────────────────────────── # 필수 명령어 확인 # ──────────────────────────────────────────── for cmd in curl tar xz uname; do command -v "$cmd" &>/dev/null || error "'$cmd' 명령어가 필요합니다. 먼저 설치해 주세요." done # Fallback: jq가 없을 경우 python3 또는 grep을 사용해 JSON 분석 if command -v jq &>/dev/null; then _json_latest_tag() { jq -r '[.[] | select(.prerelease==false and ((.assets | length) > 0))] | .[0].tag_name' } elif command -v python3 &>/dev/null; then _json_latest_tag() { python3 -c " import sys, json data = json.load(sys.stdin) for r in data: if not r.get('prerelease') and r.get('assets'): print(r.get('tag_name', '')) break " } else _json_latest_tag() { grep -oE '"tag_name":[[:space:]]*"[^"]+"' | head -1 | sed -E 's/.*"([^"]+)"$/\1/' } fi # ──────────────────────────────────────────── # OS 및 아키텍처 분석 # ──────────────────────────────────────────── # 반환 형식: "아카이브아키텍처명" detect_platform() { local os arch os="$(uname -s)" arch="$(uname -m)" case "$os" in # ── macOS ────────────────────────────────────────────── Darwin) case "$arch" in arm64) echo "aarch64-macos" ;; x86_64) echo "x86_64-macos" ;; *) error "지원하지 않는 macOS 아키텍처입니다: $arch" ;; esac ;; # ── Linux ────────────────────────────────────────────── Linux) case "$arch" in x86_64|amd64) echo "x86_64-linux" ;; aarch64|arm64) echo "aarch64-linux" ;; *) error "지원하지 않는 Linux 아키텍처입니다: $arch" ;; esac ;; *) error "지원하지 않는 운영체제입니다: $os (uname -a: $(uname -a))" ;; esac } # ──────────────────────────────────────────── # 최신 릴리스 태그 가져오기 # ──────────────────────────────────────────── get_latest_release_tag() { info "GitHub API를 통해 Helix 최신 릴리스 버전을 조회하는 중..." local release_json="" tag="" release_json="$(curl -fsSL -H "User-Agent: install-helix-sh" --connect-timeout 10 "${API_URL}" 2>/dev/null)" || true if [[ -n "$release_json" ]]; then tag="$(echo "$release_json" | _json_latest_tag 2>/dev/null || true)" fi if [[ -z "$tag" || "$tag" == "null" ]]; then warn "최신 릴리스 태그 조회 실패. 검증된 폴백 버전(${FALLBACK_TAG})을 사용합니다." tag="${FALLBACK_TAG}" fi echo "$tag" } # ──────────────────────────────────────────── # 메인 설치 로직 # ──────────────────────────────────────────── main() { local platform_arch platform_arch="$(detect_platform)" local tag version tag="$(get_latest_release_tag)" version="${tag#v}" # v25.07.1 형태에서 v 접두사 제거 info "설치 환경 감지: $platform_arch ($tag 버전)" local asset_name="helix-${version}-${platform_arch}" local archive_name="${asset_name}.tar.xz" local download_url="https://github.com/helix-editor/helix/releases/download/${tag}/${archive_name}" # 임시 작업 디렉토리 생성 local tmp_dir tmp_dir="$(mktemp -d)" local tmp_archive="${tmp_dir}/${archive_name}" info "아카이브 다운로드 중: $download_url" if ! curl -fsSL "$download_url" -o "$tmp_archive"; then if [[ "$tag" != "$FALLBACK_TAG" ]]; then tag="${FALLBACK_TAG}" version="${tag#v}" asset_name="helix-${version}-${platform_arch}" archive_name="${asset_name}.tar.xz" download_url="https://github.com/helix-editor/helix/releases/download/${tag}/${archive_name}" warn "다운로드 실패. 폴백 버전(${FALLBACK_TAG})으로 재시도합니다: $download_url" if ! curl -fsSL "$download_url" -o "$tmp_archive"; then rm -rf "$tmp_dir" error "바이너리 다운로드에 실패했습니다. URL: $download_url" fi else rm -rf "$tmp_dir" error "바이너리 다운로드에 실패했습니다. URL: $download_url" fi fi info "압축 해제 중..." tar -xf "$tmp_archive" -C "$tmp_dir" local extracted_folder="${tmp_dir}/${asset_name}" if [ -d "${extracted_folder}-source" ]; then # 간혹 폴더 이름 뒤에 -source가 붙는 예외 방어 extracted_folder="${extracted_folder}-source" fi # 1) 바이너리 설치 mkdir -p "$INSTALL_DIR" local dest_binary="${INSTALL_DIR}/hx" info "바이너리 배포 중: $dest_binary" cp "${extracted_folder}/hx" "$dest_binary" chmod +x "$dest_binary" # 2) 런타임(runtime) 디렉토리 설치 # 테마, 구문 강조 트리 등을 로드하기 위해 Helix는 runtime 디렉토리가 필수적입니다. local dest_runtime="${HELIX_CONFIG_DIR}/runtime" if [ -d "$dest_runtime" ]; then warn "기존 Helix 런타임 디렉토리가 감지되었습니다: $dest_runtime" local backup_runtime="${dest_runtime}.bak.$(date +%Y%m%d%H%M%S)" info "기존 런타임을 백업합니다: $backup_runtime" mv "$dest_runtime" "$backup_runtime" fi mkdir -p "$HELIX_CONFIG_DIR" info "런타임 디렉토리 배포 중: $dest_runtime" cp -r "${extracted_folder}/runtime" "$dest_runtime" # 임시 디렉토리 청소 rm -rf "$tmp_dir" success "Helix 에디터 ($tag) 설치가 성공적으로 완료되었습니다!" info "실행 명령어: hx" info "런타임 경로: $dest_runtime" # PATH 환경 변수 확인 및 안내 if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then warn "설치 경로($INSTALL_DIR)가 현재 PATH 환경 변수에 등록되어 있지 않습니다." warn "사용하시는 쉘 프로필 파일(예: ~/.bashrc 또는 ~/.zshrc)에 아래 설정을 추가해 주세요:" warn " export PATH=\"\$HOME/.local/bin:\$PATH\"" fi } main "$@"