diff --git a/README.md b/README.md new file mode 100644 index 0000000..b073f52 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# install-sh + +터미널 UI 도구 설치 스크립트 모음 + +## 지원 도구 + +- [bottom](https://github.com/ClementTsang/bottom) - 호스트 모니터링 +- [lazygit](https://github.com/jesseduffield/lazygit) - Git 터미널 UI + +## 설치 + +### MacOS / Linux + +**bottom:** +```bash +curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-bottom.sh?ref_type=heads" | bash +``` + +**lazygit:** +```bash +curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazygit.sh?ref_type=heads" | bash +``` + +### Windows + +**PowerShell** (관리자 권한 권장): + +bottom: +```powershell +powershell -c "iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-bottom.ps1'|iex" +``` + +lazygit: +```powershell +powershell -c "iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazygit.ps1'|iex" +``` + +**CMD** (기본 명령 프롬프트): + +bottom: +```cmd +curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-bottom.bat" -o install.bat && install.bat +``` + +lazygit: +```cmd +curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazygit.bat" -o install.bat && install.bat +``` + +## 도구별 사용법 + +### bottom + +호스트 모니터링 터미널 UI 도구입니다. + +- **CPU, 메모리, 디스크, 네트워크** 실시간 모니터링 +- **프로세스 관리** (CPU/메모리 사용률별 정렬, 검색, 종료) +- **온도 센서 모니터링** +- **GPU 모니터링** (NVIDIA/AMD 지원) + +```bash +btm +``` + +| 단축키 | 동작 | +|--------|------| +| `q` / `Esc` | 종료 | +| `F1` ~ `F5` | 탭 전환 | +| `↑` `↓` | 프로세스 선택 | +| `k` | 선택한 프로세스 종료 | +| `/` | 검색 | + +### lazygit + +Git을 위한 터미널 UI 도구입니다. + +- **편리한 단축키**로 모든 git 작업 수행 +- **커밋, 브랜치, stash, rebase** 시각적 관리 +- **diff 뷰**로 변경사항 확인 +- **merge conflict** 해결 도구 + +```bash +lazygit +``` + +| 단축키 | 동작 | +|--------|------| +| `q` | 종료 | +| `↓` `↑` `→` `←` | 화면 간 이동 | +| `Space` | 파일 Stage/Unstage | +| `c` | 커밋 작성 | +| `P` `p` | Push / Pull | +| `?` | 도움말 | + +--- + +*GitLab: https://gitlab.com/publicproj/install-sh* diff --git a/install-bottom.bat b/install-bottom.bat index 99523d7..3af1372 100644 --- a/install-bottom.bat +++ b/install-bottom.bat @@ -1,56 +1,56 @@ @echo off :: ============================================================ :: install-bottom.bat -:: bottom(btm) 프로세스 모니터 설치 스크립트 (Windows 배치파일) +:: Install script for bottom(btm) process monitor (Windows batch) :: -:: 이 파일은 PowerShell 스크립트(install-bottom.ps1)를 호출합니다. -:: PowerShell 5.1 이상이 필요합니다 (Windows 10/11 기본 탑재). +:: This file calls the PowerShell script (install-bottom.ps1). +:: PowerShell 5.1 or higher is required (included in Windows 10/11). :: -:: 사용법: +:: Usage: :: install-bottom.bat -:: install-bottom.bat "C:\tools\bin" <- 설치 경로 지정 +:: install-bottom.bat "C:\tools\bin" <- specify install path :: ============================================================ -:: ── UTF-8 코드페이지로 전환(한글 출력 깨짐 방지) ── +:: ── Switch to UTF-8 codepage ── chcp 65001 >nul setlocal EnableDelayedExpansion -echo [INFO] install-bottom.bat 시작 +echo [INFO] install-bottom.bat started -:: ── PowerShell 존재 여부 확인 ──────────────────────────────── +:: ── Check if PowerShell exists ──────────────────────────────── where powershell >nul 2>&1 if %ERRORLEVEL% neq 0 ( - echo [ERROR] PowerShell을 찾을 수 없습니다. - echo Windows 7 이상에서는 PowerShell이 기본 탑재됩니다. + echo [ERROR] PowerShell not found. + echo PowerShell is included in Windows 7 and later. pause exit /b 1 ) -:: ── 이 배치파일과 같은 폴더의 install-bottom.ps1 위치 확인 ── +:: ── Check location of install-bottom.ps1 in same folder ── set "PS1_PATH=%~dp0install-bottom.ps1" if not exist "%PS1_PATH%" ( - echo [ERROR] install-bottom.ps1 파일을 찾을 수 없습니다. - echo 이 배치파일과 같은 폴더에 install-bottom.ps1 이 있어야 합니다. - echo 위치: %PS1_PATH% + echo [ERROR] install-bottom.ps1 not found. + echo install-bottom.ps1 must be in the same folder as this batch file. + echo Location: %PS1_PATH% pause exit /b 1 ) -:: ── 설치 경로 파라미터 처리 ──────────────────────────────── +:: ── Process install path parameter ──────────────────────────────── set "EXTRA_ARGS=" if not "%~1"=="" ( set "EXTRA_ARGS=-InstallDir ""%~1""" - echo [INFO] 설치 경로 지정: %~1 + echo [INFO] Install path specified: %~1 ) -echo [INFO] PowerShell 스크립트를 실행합니다... +echo [INFO] Running PowerShell script... echo. -:: ── PowerShell 실행 ───────────────────────────────────────── -:: -ExecutionPolicy Bypass : 실행 정책 우회 (현재 사용자 설정 변경 없음) -:: -NoProfile : 프로필 로드 생략 (빠른 시작) -:: -NonInteractive : 대화형 프롬프트 억제 +:: ── Run PowerShell ───────────────────────────────────────── +:: -ExecutionPolicy Bypass : Bypass execution policy (no user config change) +:: -NoProfile : Skip profile loading (faster startup) +:: -NonInteractive : Suppress interactive prompts powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive ^ -File "%PS1_PATH%" %EXTRA_ARGS% @@ -58,10 +58,10 @@ set "PS_EXIT=%ERRORLEVEL%" echo. if %PS_EXIT% equ 0 ( - echo [INFO] 설치가 완료되었습니다. - echo 새 터미널(cmd/PowerShell)을 열면 'btm' 명령어를 사용할 수 있습니다. + echo [INFO] Installation complete. + echo Open a new terminal (cmd/PowerShell) to use the 'btm' command. ) else ( - echo [ERROR] 설치 중 오류가 발생했습니다. (종료 코드: %PS_EXIT%) + echo [ERROR] An error occurred during installation. (Exit code: %PS_EXIT%) ) echo. diff --git a/install-bottom.ps1 b/install-bottom.ps1 index 349f8b2..36107a5 100644 --- a/install-bottom.ps1 +++ b/install-bottom.ps1 @@ -1,22 +1,22 @@ -#Requires -Version 5.1 +#Requires -Version 5.1 <# .SYNOPSIS - bottom(btm) 프로세스 모니터를 GitHub Nightly 최신 버전으로 설치합니다. + Installs bottom(btm) process monitor from GitHub Nightly releases. .DESCRIPTION - https://github.com/ClementTsang/bottom/releases 에서 최신 Nightly 릴리스를 - 자동으로 탐지하고, 현재 Windows 아키텍처에 맞는 zip 파일을 다운로드하여 - 설치합니다. + Auto-detects the latest Nightly release from + https://github.com/ClementTsang/bottom/releases, downloads the zip + file matching your Windows architecture, and installs it. - 지원 아키텍처: + Supported architectures: - x64 (AMD64 / Intel 64bit) - - ARM64 (Snapdragon X Elite, Qualcomm ARM 등) + - ARM64 (Snapdragon X Elite, Qualcomm ARM, etc.) .PARAMETER InstallDir - btm.exe 를 설치할 디렉토리. 기본값: $env:USERPROFILE\.local\bin + Directory to install btm.exe. Default: $env:USERPROFILE\.local\bin .PARAMETER AddToPath - InstallDir 을 영구적으로 사용자 PATH에 추가할지 여부. 기본값: $true + Whether to permanently add InstallDir to user PATH. Default: $true .EXAMPLE .\install-bottom.ps1 @@ -24,7 +24,7 @@ .\install-bottom.ps1 -AddToPath $false .NOTES - 실행 정책 오류 발생 시: + If execution policy error occurs: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser #> @@ -38,13 +38,13 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # ──────────────────────────────────────────── -# 상수 +# Constants # ──────────────────────────────────────────── $REPO = "ClementTsang/bottom" $API_URL = "https://api.github.com/repos/$REPO/releases" # ──────────────────────────────────────────── -# 유틸리티 함수 +# Utility functions # ──────────────────────────────────────────── function Write-Info { param([string]$msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan } function Write-Warn { param([string]$msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow } @@ -52,18 +52,19 @@ function Write-Success { param([string]$msg) Write-Host "[OK] $msg" -Foregrou function Write-Err { param([string]$msg) Write-Host "[ERROR] $msg" -ForegroundColor Red; exit 1 } # ──────────────────────────────────────────── -# TLS 1.2 강제 (Windows 7/8.1 대비) +# Enforce TLS 1.2 (for Windows 7/8.1 compatibility) # ──────────────────────────────────────────── [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # ──────────────────────────────────────────── -# 아키텍처 감지 +# Architecture detection # ──────────────────────────────────────────── function Get-Platform { - # 1) 환경변수를 먼저 확인 (Windows on ARM의 32-bit PowerShell에서도 OS 아키를 정확히 판별) - # - 네이티브 프로세스 : PROCESSOR_ARCHITECTURE = AMD64 / ARM64 / x86 + # 1) Check environment variables first (even 32-bit PowerShell on + # Windows ARM can correctly detect OS architecture) + # - Native process : PROCESSOR_ARCHITECTURE = AMD64 / ARM64 / x86 # - WOW64(32bit on 64bit): PROCESSOR_ARCHITECTURE = x86, - # PROCESSOR_ARCHITEW6432 = AMD64 또는 ARM64 (실제 OS 아키) + # PROCESSOR_ARCHITEW6432 = AMD64 or ARM64 (actual OS arch) $envArch = $env:PROCESSOR_ARCHITEW6432 if (-not $envArch) { $envArch = $env:PROCESSOR_ARCHITECTURE } @@ -73,47 +74,47 @@ function Get-Platform { '^x86$' { return "i686-pc-windows-msvc" } } - # 2) Fallback: .NET RuntimeInformation (PS 5.1 + .NET Framework 4.7.1+ 필요) + # 2) Fallback: .NET RuntimeInformation (requires PS 5.1 + .NET Framework 4.7.1+) try { $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture switch ($arch) { 'X64' { return "x86_64-pc-windows-msvc" } 'Arm64' { return "aarch64-pc-windows-msvc" } 'X86' { return "i686-pc-windows-msvc" } - default { Write-Err "지원하지 않는 아키텍처: $arch" } + default { Write-Err "Unsupported architecture: $arch" } } } catch { - Write-Err "아키텍처 감지에 실패했습니다: $_" + Write-Err "Failed to detect architecture: $_" } } # ──────────────────────────────────────────── -# 최신 Nightly 릴리스 태그 가져오기 +# Get latest Nightly release tag # ──────────────────────────────────────────── function Get-LatestNightlyTag { - Write-Info "GitHub API로 최신 Nightly 릴리스 정보를 조회합니다..." + Write-Info "Fetching latest Nightly release from GitHub API..." try { $releases = Invoke-RestMethod -Uri $API_URL -UseBasicParsing ` -Headers @{ 'User-Agent' = 'install-bottom-ps1' } } catch { - Write-Err "GitHub API 호출에 실패했습니다: $_" + Write-Err "Failed to call GitHub API: $_" } - # prerelease=true 이고 tag_name 이 'nightly-' 로 시작하는 첫 번째 항목 + # Get first item where prerelease=true and tag_name starts with 'nightly-' $nightly = $releases | Where-Object { $_.prerelease -eq $true -and $_.tag_name -like 'nightly-*' } | Select-Object -First 1 if (-not $nightly) { - Write-Err "Nightly 릴리스를 찾을 수 없습니다." + Write-Err "Could not find Nightly release." } return $nightly.tag_name } # ──────────────────────────────────────────── -# 다운로드 URL 조합 +# Build download URL # ──────────────────────────────────────────── function Get-DownloadUrl { param([string]$Tag, [string]$Triple) @@ -122,55 +123,55 @@ function Get-DownloadUrl { } # ──────────────────────────────────────────── -# 다운로드 & 압축 해제 & 설치 +# Download, extract & install # ──────────────────────────────────────────── function Install-Bottom { param([string]$Url) - # 임시 디렉토리 생성 + # Create temp directory $tmpDir = Join-Path $env:TEMP "bottom_install_$(Get-Random)" New-Item -ItemType Directory -Path $tmpDir | Out-Null try { $zipPath = Join-Path $tmpDir "bottom.zip" - Write-Info "다운로드 중: $Url" + Write-Info "Downloading: $Url" try { - # PowerShell 5+ 에서 진행률 표시 + # Suppress progress bar in PowerShell 5+ $ProgressPreference = 'SilentlyContinue' Invoke-WebRequest -Uri $Url -OutFile $zipPath -UseBasicParsing $ProgressPreference = 'Continue' } catch { - Write-Err "다운로드 실패: $_" + Write-Err "Download failed: $_" } - Write-Info "압축 해제 중..." + Write-Info "Extracting..." Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force - # btm.exe 찾기 + # Find btm.exe $btmExe = Get-ChildItem -Path $tmpDir -Recurse -Filter "btm.exe" | Select-Object -First 1 if (-not $btmExe) { - Write-Err "압축 파일에서 'btm.exe'를 찾을 수 없습니다." + Write-Err "Cannot find 'btm.exe' in the archive." } - # 설치 디렉토리 생성 + # Create install directory if (-not (Test-Path $InstallDir)) { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null } $destPath = Join-Path $InstallDir "btm.exe" - Write-Info "설치 중: $destPath" + Write-Info "Installing: $destPath" Copy-Item -Path $btmExe.FullName -Destination $destPath -Force - Write-Success "설치 완료!" + Write-Success "Installation complete!" - # 버전 확인 - Write-Info "버전 확인:" + # Check version + Write-Info "Version check:" & $destPath --version } finally { - # 임시 파일 정리 + # Cleanup temp files if (Test-Path $tmpDir) { Remove-Item -Path $tmpDir -Recurse -Force -ErrorAction SilentlyContinue } @@ -178,42 +179,42 @@ function Install-Bottom { } # ──────────────────────────────────────────── -# PATH 등록 (사용자 환경변수) +# Add to PATH (user environment variable) # ──────────────────────────────────────────── function Add-ToUserPath { param([string]$Dir) $currentPath = [Environment]::GetEnvironmentVariable('PATH', 'User') if (-not $currentPath) { $currentPath = '' } - # 결과가 단일 항목일 때도 배열로 강제 (-contains 안전성 확보) + # Force array even with single result (for safe -contains check) $dirs = @($currentPath -split ';' | Where-Object { $_ -ne '' }) if ($dirs -contains $Dir) { - Write-Info "'$Dir' 은 이미 PATH에 등록되어 있습니다." + Write-Info "'$Dir' is already in your PATH." return } $newPath = ($dirs + $Dir) -join ';' [Environment]::SetEnvironmentVariable('PATH', $newPath, 'User') - # 현재 세션에도 반영 + # Also reflect in current session $env:PATH = "$env:PATH;$Dir" - Write-Success "'$Dir' 을 사용자 PATH에 영구 등록했습니다." - Write-Warn "새 터미널을 열어야 PATH 변경이 적용됩니다." + Write-Success "'$Dir' has been permanently added to user PATH." + Write-Warn "Open a new terminal for PATH changes to take effect." } # ──────────────────────────────────────────── # 메인 # ──────────────────────────────────────────── function Main { - Write-Info "===== bottom(btm) 설치 스크립트 (Windows PowerShell) =====" + Write-Info "===== bottom(btm) install script (Windows PowerShell) =====" $triple = Get-Platform - Write-Info "감지된 플랫폼: $triple" + Write-Info "Detected platform: $triple" $tag = Get-LatestNightlyTag - Write-Info "최신 Nightly 태그: $tag" + Write-Info "Latest Nightly tag: $tag" $url = Get-DownloadUrl -Tag $tag -Triple $triple Install-Bottom -Url $url @@ -221,12 +222,12 @@ function Main { if ($AddToPath) { Add-ToUserPath -Dir $InstallDir } else { - Write-Warn "PATH 자동 등록을 건너뜁니다." - Write-Warn "수동으로 PATH에 추가하세요: $InstallDir" + Write-Warn "Skipping automatic PATH registration." + Write-Warn "Add to PATH manually: $InstallDir" } - Write-Success "모든 작업이 완료되었습니다." - Write-Info "사용법: btm" + Write-Success "All tasks completed." + Write-Info "Usage: btm" } Main diff --git a/install-bottom.sh b/install-bottom.sh index 5d1a028..612de61 100755 --- a/install-bottom.sh +++ b/install-bottom.sh @@ -1,15 +1,15 @@ #!/usr/bin/env bash # shellcheck disable=SC2086 -# https://github.com/ClementTsang/bottom/releases 에서 Nightly 마지막 버전 목록을 확인하고 -# 현재 OS와 CPU 등을 파악한 후 다운로드 받아서 설치 +# 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 설치 -# (네이티브 PowerShell/CMD는 지원 안 함 → WSL 권장) +# Windows : Git Bash / MSYS2 / Cygwin (.zip install) +# (Native PowerShell/CMD not supported → use WSL) set -euo pipefail @@ -35,13 +35,13 @@ for cmd in curl uname; do command -v "$cmd" &>/dev/null || error "'$cmd' 명령어가 필요합니다." done -# jq가 없으면 python3으로 JSON 파싱 (fallback) +# 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' 또는 'python3' 중 하나가 필요합니다." + command -v python3 &>/dev/null || error "'jq' or 'python3' is required." _json_nightly_tag() { python3 -c " import sys, json @@ -88,9 +88,9 @@ detect_platform() { 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' 으로 소스 빌드를 권장합니다." ;; + # 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" ;; *) @@ -107,11 +107,11 @@ detect_platform() { ;; # ── Windows (Git Bash / MSYS2 / Cygwin) ─────────────── - # 네이티브 PowerShell/CMD는 bash가 없으므로 이 스크립트 실행 불가 - # WSL은 Linux로 감지되므로 여기에 해당 안 됨 + # 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 환경(Git Bash/MSYS2/Cygwin)이 감지되었습니다." - warn "WSL(Windows Subsystem for Linux) 사용을 권장합니다." + 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" ;; @@ -128,34 +128,34 @@ detect_platform() { } # ──────────────────────────────────────────── -# 최신 Nightly 릴리스 태그 가져오기 +# Get latest Nightly release tag # ──────────────────────────────────────────── get_latest_nightly_tag() { - info "GitHub API로 최신 Nightly 릴리스 정보를 조회합니다..." + info "Fetching latest Nightly release from GitHub API..." local releases - releases="$(curl -fsSL "${API_URL}")" || error "GitHub API 호출에 실패했습니다." + releases="$(curl -fsSL "${API_URL}")" || error "Failed to call GitHub API." local tag tag="$(echo "$releases" | _json_nightly_tag)" - [[ -z "$tag" || "$tag" == "null" ]] && error "Nightly 릴리스 태그를 찾을 수 없습니다." + [[ -z "$tag" || "$tag" == "null" ]] && error "Could not find Nightly release tag." echo "$tag" } # ──────────────────────────────────────────── -# 다운로드 URL 조합 +# Build download URL # ──────────────────────────────────────────── build_download_url() { local tag="$1" - local triple="$2" # 플랫폼 트리플 (e.g. x86_64-apple-darwin) + 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" @@ -169,77 +169,77 @@ download_and_install() { local filename filename="${url##*/}" - info "다운로드 중: $url" + info "Downloading: $url" curl -fsSL --progress-bar -o "${tmpdir}/${filename}" "$url" \ - || error "다운로드 실패: $url" + || error "Download failed: $url" - info "압축 해제 중..." + info "Extracting..." case "$ext" in tar.gz) - command -v tar &>/dev/null || error "'tar' 명령어가 필요합니다." + 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 는 zip 지원 + # Windows 10+ tar.exe supports zip tar -xf "${tmpdir}/${filename}" -C "$tmpdir" else - error "'unzip' 또는 'tar'(Windows 10+) 명령어가 필요합니다." + error "'unzip' or 'tar' (Windows 10+) is required." fi ;; *) - error "알 수 없는 압축 포맷: $ext" + error "Unknown archive format: $ext" ;; esac - # Windows는 btm.exe, 나머지는 btm + # 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 "압축 파일에서 '$bin_name' 실행파일을 찾을 수 없습니다." + [[ -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 "설치 중: ${install_path}" + info "Installing: ${install_path}" cp "$bin_src" "$install_path" chmod +x "$install_path" - success "설치 완료!" - info "버전 확인:" + success "Installation complete!" + info "Version check:" "$install_path" --version || true - # PATH 안내 + # PATH instructions if ! echo ":${PATH}:" | grep -q ":${INSTALL_DIR}:"; then - warn "'${INSTALL_DIR}' 가 PATH에 없습니다." - warn "다음 줄을 ~/.bashrc 또는 ~/.zshrc 에 추가하세요:" + 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%%:*}" # ':' 앞부분 - ext="${platform_info##*:}" # ':' 뒷부분 + triple="${platform_info%%:*}" # Before ':' + ext="${platform_info##*:}" # After ':' - info "감지된 플랫폼: $triple (압축 포맷: $ext)" + info "Detected platform: $triple (format: $ext)" - # Windows 여부 판단 + # Check if Windows is_windows="false" [[ "$triple" == *"windows"* ]] && is_windows="true" tag="$(get_latest_nightly_tag)" - info "최신 Nightly 태그: $tag" + info "Latest Nightly tag: $tag" url="$(build_download_url "$tag" "$triple" "$ext")" download_and_install "$url" "$ext" "$is_windows" diff --git a/install-lazygit.bat b/install-lazygit.bat new file mode 100644 index 0000000..7889780 --- /dev/null +++ b/install-lazygit.bat @@ -0,0 +1,69 @@ +@echo off +:: ============================================================ +:: install-lazygit.bat +:: Install script for lazygit terminal UI for git (Windows batch) +:: +:: This file calls the PowerShell script (install-lazygit.ps1). +:: PowerShell 5.1 or higher is required (included in Windows 10/11). +:: +:: Usage: +:: install-lazygit.bat +:: install-lazygit.bat "C:\tools\bin" <- specify install path +:: ============================================================ + +:: ── Switch to UTF-8 codepage ── +chcp 65001 >nul + +setlocal EnableDelayedExpansion + +echo [INFO] install-lazygit.bat started + +:: ── Check if PowerShell exists ──────────────────────────────── +where powershell >nul 2>&1 +if %ERRORLEVEL% neq 0 ( + echo [ERROR] PowerShell not found. + echo PowerShell is included in Windows 7 and later. + pause + exit /b 1 +) + +:: ── Check location of install-lazygit.ps1 in same folder ── +set "PS1_PATH=%~dp0install-lazygit.ps1" +if not exist "%PS1_PATH%" ( + echo [ERROR] install-lazygit.ps1 not found. + echo install-lazygit.ps1 must be in the same folder as this batch file. + echo Location: %PS1_PATH% + pause + exit /b 1 +) + +:: ── Process install path parameter ──────────────────────────────── +set "EXTRA_ARGS=" +if not "%~1"=="" ( + set "EXTRA_ARGS=-InstallDir ""%~1""" + echo [INFO] Install path specified: %~1 +) + +echo [INFO] Running PowerShell script... +echo. + +:: ── Run PowerShell ───────────────────────────────────────── +:: -ExecutionPolicy Bypass : Bypass execution policy (no user config change) +:: -NoProfile : Skip profile loading (faster startup) +:: -NonInteractive : Suppress interactive prompts +powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive ^ + -File "%PS1_PATH%" %EXTRA_ARGS% + +set "PS_EXIT=%ERRORLEVEL%" + +echo. +if %PS_EXIT% equ 0 ( + echo [INFO] Installation complete. + echo Open a new terminal (cmd/PowerShell) to use the 'lazygit' command. +) else ( + echo [ERROR] An error occurred during installation. (Exit code: %PS_EXIT%) +) + +echo. +pause +exit /b %PS_EXIT% diff --git a/install-lazygit.ps1 b/install-lazygit.ps1 new file mode 100644 index 0000000..638979e --- /dev/null +++ b/install-lazygit.ps1 @@ -0,0 +1,217 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Installs lazygit terminal UI for git from GitHub releases. + +.DESCRIPTION + Auto-detects the latest release from + https://github.com/jesseduffield/lazygit/releases, downloads the zip + file matching your Windows architecture, and installs it. + + Supported architectures: + - x64 (AMD64 / Intel 64bit) + - ARM64 (Snapdragon X Elite, Qualcomm ARM, etc.) + +.PARAMETER InstallDir + Directory to install lazygit.exe. Default: $env:USERPROFILE\.local\bin + +.PARAMETER AddToPath + Whether to permanently add InstallDir to user PATH. Default: $true + +.EXAMPLE + .\install-lazygit.ps1 + .\install-lazygit.ps1 -InstallDir "C:\tools\bin" + .\install-lazygit.ps1 -AddToPath $false + +.NOTES + If execution policy error occurs: + Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +#> + +[CmdletBinding()] +param( + [string] $InstallDir = "$env:USERPROFILE\.local\bin", + [bool] $AddToPath = $true +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# ──────────────────────────────────────────── +# Constants +# ──────────────────────────────────────────── +$REPO = "jesseduffield/lazygit" +$API_URL = "https://api.github.com/repos/$REPO/releases" + +# ──────────────────────────────────────────── +# Utility functions +# ──────────────────────────────────────────── +function Write-Info { param([string]$msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan } +function Write-Warn { param([string]$msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow } +function Write-Success { param([string]$msg) Write-Host "[OK] $msg" -ForegroundColor Green } +function Write-Err { param([string]$msg) Write-Host "[ERROR] $msg" -ForegroundColor Red; exit 1 } + +# ──────────────────────────────────────────── +# Enforce TLS 1.2 (for Windows 7/8.1 compatibility) +# ──────────────────────────────────────────── +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +# ──────────────────────────────────────────── +# Architecture detection +# ──────────────────────────────────────────── +function Get-Platform { + $envArch = $env:PROCESSOR_ARCHITEW6432 + if (-not $envArch) { $envArch = $env:PROCESSOR_ARCHITECTURE } + + switch -Regex ($envArch) { + '^AMD64$' { return "windows_x86_64" } + '^ARM64$' { return "windows_arm64" } + '^x86$' { return "windows_32-bit" } + } + + try { + $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture + switch ($arch) { + 'X64' { return "windows_x86_64" } + 'Arm64' { return "windows_arm64" } + 'X86' { return "windows_32-bit" } + default { Write-Err "Unsupported architecture: $arch" } + } + } catch { + Write-Err "Failed to detect architecture: $_" + } +} + +# ──────────────────────────────────────────── +# Get latest stable release tag +# ──────────────────────────────────────────── +function Get-LatestTag { + Write-Info "Fetching latest release from GitHub API..." + + try { + $releases = Invoke-RestMethod -Uri $API_URL -UseBasicParsing ` + -Headers @{ 'User-Agent' = 'install-lazygit-ps1' } + } catch { + Write-Err "Failed to call GitHub API: $_" + } + + $latest = $releases | Where-Object { $_.prerelease -eq $false } | Select-Object -First 1 + + if (-not $latest) { + Write-Err "Could not find latest release." + } + + return $latest.tag_name +} + +# ──────────────────────────────────────────── +# Build download URL +# ──────────────────────────────────────────── +function Get-DownloadUrl { + param([string]$Tag, [string]$Platform) + $version = $Tag -replace '^v', '' + $filename = "lazygit_${version}_${Platform}.zip" + return "https://github.com/$REPO/releases/download/$Tag/$filename" +} + +# ──────────────────────────────────────────── +# Download, extract & install +# ──────────────────────────────────────────── +function Install-Lazygit { + param([string]$Url) + + $tmpDir = Join-Path $env:TEMP "lazygit_install_$(Get-Random)" + New-Item -ItemType Directory -Path $tmpDir | Out-Null + + try { + $zipPath = Join-Path $tmpDir "lazygit.zip" + + Write-Info "Downloading: $Url" + try { + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri $Url -OutFile $zipPath -UseBasicParsing + $ProgressPreference = 'Continue' + } catch { + Write-Err "Download failed: $_" + } + + Write-Info "Extracting..." + Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force + + $lazygitExe = Get-ChildItem -Path $tmpDir -Recurse -Filter "lazygit.exe" | + Select-Object -First 1 + if (-not $lazygitExe) { + Write-Err "Cannot find 'lazygit.exe' in the archive." + } + + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + + $destPath = Join-Path $InstallDir "lazygit.exe" + Write-Info "Installing: $destPath" + Copy-Item -Path $lazygitExe.FullName -Destination $destPath -Force + + Write-Success "Installation complete!" + + Write-Info "Version check:" + & $destPath --version + + } finally { + if (Test-Path $tmpDir) { + Remove-Item -Path $tmpDir -Recurse -Force -ErrorAction SilentlyContinue + } + } +} + +# ──────────────────────────────────────────── +# Add to PATH (user environment variable) +# ──────────────────────────────────────────── +function Add-ToUserPath { + param([string]$Dir) + + $currentPath = [Environment]::GetEnvironmentVariable('PATH', 'User') + if (-not $currentPath) { $currentPath = '' } + $dirs = @($currentPath -split ';' | Where-Object { $_ -ne '' }) + + if ($dirs -contains $Dir) { + Write-Info "'$Dir' is already in your PATH." + return + } + + $newPath = ($dirs + $Dir) -join ';' + [Environment]::SetEnvironmentVariable('PATH', $newPath, 'User') + + $env:PATH = "$env:PATH;$Dir" + + Write-Success "'$Dir' has been permanently added to user PATH." + Write-Warn "Open a new terminal for PATH changes to take effect." +} + +# ──────────────────────────────────────────── +# Main +# ──────────────────────────────────────────── +function Main { + Write-Info "===== lazygit install script (Windows PowerShell) =====" + + $platform = Get-Platform + Write-Info "Detected platform: $platform" + + $tag = Get-LatestTag + Write-Info "Latest release: $tag" + + $url = Get-DownloadUrl -Tag $tag -Platform $platform + Install-Lazygit -Url $url + + if ($AddToPath) { + Add-ToUserPath -Dir $InstallDir + } else { + Write-Warn "Skipping automatic PATH registration." + Write-Warn "Add to PATH manually: $InstallDir" + } + + Write-Success "All tasks completed." + Write-Info "Usage: lazygit" +} + +Main diff --git a/install-lazygit.sh b/install-lazygit.sh new file mode 100644 index 0000000..df40f81 --- /dev/null +++ b/install-lazygit.sh @@ -0,0 +1,242 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2086 + +# Downloads and installs the latest stable lazygit from +# https://github.com/jesseduffield/lazygit/releases +# +# Supported platforms: +# macOS : Apple Silicon (arm64), Intel (x86_64) +# Linux : x86_64, aarch64/arm64, armv6 +# FreeBSD : x86_64 +# Windows : Git Bash / MSYS2 / Cygwin (.zip install) + +set -euo pipefail + +# ──────────────────────────────────────────── +# Constants +# ──────────────────────────────────────────── +REPO="jesseduffield/lazygit" +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; } + +# ──────────────────────────────────────────── +# 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 +# ──────────────────────────────────────────── +# Returns: ":" +# asset_name = lazygit_X.XX.X_PLATFORM +# archive_format = tar.gz | zip +detect_platform() { + local os arch + + os="$(uname -s)" + arch="$(uname -m)" + + case "$os" in + # ── macOS ────────────────────────────────────────────── + Darwin) + case "$arch" in + arm64) echo "darwin_arm64:tar.gz" ;; + x86_64) echo "darwin_x86_64:tar.gz" ;; + *) error "Unsupported macOS architecture: $arch" ;; + esac + ;; + + # ── Linux (including Raspberry Pi) ────────────────── + Linux) + case "$arch" in + x86_64|amd64) + echo "linux_x86_64:tar.gz" ;; + aarch64|arm64) + # RPi 4/5, 64bit OS + echo "linux_arm64:tar.gz" ;; + armv7l) + # RPi 2/3, 32bit OS - use armv6 as fallback + echo "linux_armv6:tar.gz" ;; + armv6l) + # RPi Zero / 1 + echo "linux_armv6:tar.gz" ;; + i686|i386) + echo "linux_32-bit:tar.gz" ;; + *) + error "Unsupported Linux architecture: $arch" ;; + esac + ;; + + # ── FreeBSD ──────────────────────────────────────────── + FreeBSD) + case "$arch" in + amd64|x86_64) echo "freebsd_x86_64:tar.gz" ;; + *) error "FreeBSD x86_64 is only supported (current: $arch)" ;; + esac + ;; + + # ── Windows (Git Bash / MSYS2 / Cygwin) ─────────────── + MINGW*|MSYS*|CYGWIN*) + warn "Windows environment (Git Bash/MSYS2/Cygwin) detected." + warn "Native .exe will be installed." + case "$arch" in + x86_64) echo "windows_x86_64:zip" ;; + aarch64) echo "windows_arm64:zip" ;; + i686) echo "windows_32-bit:zip" ;; + *) error "Unsupported Windows architecture: $arch" ;; + esac + ;; + + *) + error "Unsupported OS: $os (uname -a: $(uname -a))" + ;; + 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 +# ──────────────────────────────────────────── +build_download_url() { + local tag="$1" + local platform="$2" # e.g. darwin_arm64, linux_x86_64 + local ext="$3" # tar.gz | zip + local version="${tag#v}" # Remove 'v' prefix + local filename="lazygit_${version}_${platform}.${ext}" + echo "https://github.com/${REPO}/releases/download/${tag}/${filename}" +} + +# ──────────────────────────────────────────── +# Download, extract & install +# ──────────────────────────────────────────── +download_and_install() { + local url="$1" + local ext="$2" + 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 + tar -xf "${tmpdir}/${filename}" -C "$tmpdir" + else + error "'unzip' or 'tar' (Windows 10+) is required." + fi + ;; + *) + error "Unknown archive format: $ext" + ;; + esac + + # Binary name + local bin_name="lazygit" + [[ "$is_windows" == "true" ]] && bin_name="lazygit.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 is_windows tag url + + platform_info="$(detect_platform)" + platform="${platform_info%%:*}" + ext="${platform_info##*:}" + + info "Detected platform: $platform (format: $ext)" + + is_windows="false" + [[ "$platform" == "windows"* ]] && is_windows="true" + + tag="$(get_latest_tag)" + info "Latest release: $tag" + + url="$(build_download_url "$tag" "$platform" "$ext")" + download_and_install "$url" "$ext" "$is_windows" +} + +main "$@"