From 3e2ac2571371d9a9de6d2fa9234bf83da601df07 Mon Sep 17 00:00:00 2001 From: Jerry Chae Date: Wed, 13 May 2026 19:29:04 +0900 Subject: [PATCH] =?UTF-8?q?git=20=EC=9D=B4=20=EC=97=86=EC=9C=BC=EB=A9=B4?= =?UTF-8?q?=20=EC=84=A4=EC=B9=98=ED=95=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install-lazygit.ps1 | 152 ++++++++++++++++++++ install-lazygit.sh | 336 ++++++++++++++++++++++++++------------------ 2 files changed, 348 insertions(+), 140 deletions(-) diff --git a/install-lazygit.ps1 b/install-lazygit.ps1 index 24796a7..096bdbb 100644 --- a/install-lazygit.ps1 +++ b/install-lazygit.ps1 @@ -48,6 +48,8 @@ $ErrorActionPreference = 'Stop' # ──────────────────────────────────────────── $REPO = "jesseduffield/lazygit" $API_URL = "https://api.github.com/repos/$REPO/releases" +$GIT_REPO = "git-for-windows/git" +$GIT_API_URL = "https://api.github.com/repos/$GIT_REPO/releases" # ──────────────────────────────────────────── # Utility functions @@ -62,6 +64,156 @@ function Write-Err { param([string]$msg) Write-Host "[ERROR] $msg" -Foregrou # ──────────────────────────────────────────── [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +# ──────────────────────────────────────────── +# Check for git (required by lazygit) +# ──────────────────────────────────────────── +function Install-GitPortable { + Write-Info "git is not installed. Installing PortableGit..." + + # Detect architecture + $envArch = $env:PROCESSOR_ARCHITEW6432 + if (-not $envArch) { $envArch = $env:PROCESSOR_ARCHITECTURE } + + $gitArch = "" + if ($envArch -eq "AMD64") { + $gitArch = "64-bit" + } elseif ($envArch -eq "ARM64") { + $gitArch = "arm64" + } else { + Write-Err "Unsupported architecture for PortableGit: $envArch" + } + + # Find latest Git for Windows release + Write-Info "Fetching latest Git for Windows release..." + try { + $releases = Invoke-RestMethod -Uri $GIT_API_URL -UseBasicParsing ` + -Headers @{ 'User-Agent' = 'install-lazygit-ps1' } + } catch { + Write-Err "Failed to call GitHub API for Git: $_" + } + + $latestRelease = $releases | Where-Object { $_.prerelease -eq $false } | Select-Object -First 1 + if (-not $latestRelease) { + Write-Err "Could not find latest Git for Windows release." + } + + $tagName = $latestRelease.tag_name + # Git for Windows tags look like "v2.54.0.windows.1" + $gitVersion = $tagName -replace '^v', '' -replace '\.windows\.\d+$', '' + Write-Info "Latest Git for Windows: $tagName (version $gitVersion)" + + # Find the PortableGit asset for our architecture + $portablePattern = "PortableGit-${gitVersion}-${gitArch}.7z.exe" + $asset = $latestRelease.assets | Where-Object { $_.name -eq $portablePattern } | Select-Object -First 1 + + if (-not $asset) { + Write-Err "Could not find PortableGit asset matching '$portablePattern' in release $tagName." + } + + $downloadUrl = $asset.browser_download_url + $installPath = Join-Path $env:USERPROFILE "PortableGit" + + # Download + $tmpDir = Join-Path $env:TEMP "git_install_$(Get-Random)" + New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null + $zipPath = Join-Path $tmpDir "$portablePattern" + + try { + Write-Info "Downloading: $downloadUrl" + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing + $ProgressPreference = 'Continue' + + # PortableGit .7z.exe is a self-extracting archive + # Run it to extract to the install path + Write-Info "Extracting PortableGit to $installPath..." + + # Try 7z first, then fall back to running the .7z.exe self-extractor + $extracted = $false + if (Get-Command 7z -ErrorAction SilentlyContinue) { + & 7z x $zipPath -o"$installPath" -y 2>&1 | Out-Null + $extracted = $true + } else { + # The .7z.exe is a self-extracting archive; run it with -y and -o"path" + # It needs to be run as an executable + try { + & $zipPath -y -o"$installPath" 2>&1 | Out-Null + $extracted = $true + } catch { + Write-Warn "Self-extractor failed, trying PowerShell expansion..." + } + } + + if (-not $extracted) { + Write-Err "Cannot extract PortableGit. Install 7-Zip or install git manually from https://git-scm.com/download/win" + } + + } finally { + if (Test-Path $tmpDir) { + Remove-Item -Path $tmpDir -Recurse -Force -ErrorAction SilentlyContinue + } + } + + # Verify git.exe + $gitExe = Join-Path $installPath "cmd\git.exe" + if (-not (Test-Path $gitExe)) { + # Try bin directory as fallback + $gitExe = Join-Path $installPath "bin\git.exe" + } + if (-not (Test-Path $gitExe)) { + # Search recursively + $found = Get-ChildItem -Path $installPath -Recurse -Filter "git.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($found) { $gitExe = $found.FullName } + } + + if (-not (Test-Path $gitExe)) { + Write-Err "git.exe not found after extraction. Please install git manually." + } + + # Add git's directory to PATH for this session and permanently + $gitDir = Split-Path $gitExe + if ($env:Path -notlike "*$gitDir*") { + $env:Path = "$gitDir;$env:Path" + Write-Info "Added $gitDir to PATH for this session" + } + + $userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User') + if ($userPath -notlike "*$gitDir*") { + [System.Environment]::SetEnvironmentVariable('Path', "$gitDir;$userPath", 'User') + Write-Success "Added $gitDir to user PATH permanently" + } else { + Write-Info "$gitDir already in user PATH" + } + + # Also add the main bin dir for bash, ssh, etc. + $gitBinDir = Join-Path $installPath "bin" + if ((Test-Path $gitBinDir) -and ($env:Path -notlike "*$gitBinDir*")) { + $env:Path = "$gitBinDir;$env:Path" + $userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User') + if ($userPath -notlike "*$gitBinDir*") { + [System.Environment]::SetEnvironmentVariable('Path', "$gitBinDir;$userPath", 'User') + Write-Success "Added $gitBinDir to user PATH permanently" + } + } + + Write-Success "PortableGit installed to $installPath" +} + +# Check if git is available +$gitCmd = Get-Command git -ErrorAction SilentlyContinue +if (-not $gitCmd) { + Install-GitPortable + + # Refresh PATH and re-check + $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + $gitCmd = Get-Command git -ErrorAction SilentlyContinue + if (-not $gitCmd) { + Write-Err "git installation failed. Please install git manually from https://git-scm.com/download/win and re-run this script." + } +} + +Write-Success "git is available: $($gitCmd.Source)" + # ──────────────────────────────────────────── # Architecture detection # ──────────────────────────────────────────── diff --git a/install-lazygit.sh b/install-lazygit.sh index df40f81..6f121ce 100644 --- a/install-lazygit.sh +++ b/install-lazygit.sh @@ -22,27 +22,77 @@ INSTALL_DIR="${HOME}/.local/bin" # ──────────────────────────────────────────── # Utility functions (all stderr output) # ──────────────────────────────────────────── -info() { echo "[INFO] $*" >&2; } -warn() { echo "[WARN] $*" >&2; } -error() { echo "[ERROR] $*" >&2; exit 1; } +info() { echo "[INFO] $*" >&2; } +warn() { echo "[WARN] $*" >&2; } +error() { + echo "[ERROR] $*" >&2 + exit 1 +} success() { echo "[OK] $*" >&2; } +# ──────────────────────────────────────────── +# Check for git (required by lazygit) +# ──────────────────────────────────────────── +if ! command -v git &>/dev/null; then + info "git is not installed. Installing git..." + case "$(uname -s)" in + Darwin) + if command -v brew &>/dev/null; then + brew install -y git + success "git installed via Homebrew" + else + error "Homebrew (brew) is required to install git on macOS. Install it from https://brew.sh/" + fi + ;; + Linux) + if command -v apt-get &>/dev/null; then + sudo apt-get update -y && sudo apt-get install -y git + success "git installed via apt-get" + elif command -v dnf &>/dev/null; then + sudo dnf install -y git + success "git installed via dnf" + elif command -v yum &>/dev/null; then + sudo yum install -y git + success "git installed via yum" + elif command -v zypper &>/dev/null; then + sudo zypper -n install git + success "git installed via zypper" + elif command -v pacman &>/dev/null; then + sudo pacman -S --noconfirm git + success "git installed via pacman" + else + error "No supported package manager found. Install git manually." + fi + ;; + *) + error "Cannot auto-install git on this OS. Please install git manually." + ;; + esac + + # Verify git is now available + if ! command -v git &>/dev/null; then + error "git installation failed. Please install git manually and re-run this script." + fi +fi + +info "git is available: $(command -v git)" + # ──────────────────────────────────────────── # Check required commands # ──────────────────────────────────────────── for cmd in curl uname; do - command -v "$cmd" &>/dev/null || error "'$cmd' command is required." + 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' - } + _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 " + 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: @@ -50,7 +100,7 @@ for r in data: print(r['tag_name']) break " - } + } fi # ──────────────────────────────────────────── @@ -60,183 +110,189 @@ fi # asset_name = lazygit_X.XX.X_PLATFORM # archive_format = tar.gz | zip detect_platform() { - local os arch + local os arch - os="$(uname -s)" - arch="$(uname -m)" + 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 - ;; + 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 - ;; + # ── 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 - ;; + # ── 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 - ;; + # ── 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 + *) + error "Unsupported OS: $os (uname -a: $(uname -a))" + ;; + esac } # ──────────────────────────────────────────── # Get latest stable release tag # ──────────────────────────────────────────── get_latest_tag() { - info "Fetching latest release from GitHub API..." + info "Fetching latest release from GitHub API..." - local releases - releases="$(curl -fsSL "${API_URL}")" || error "Failed to call GitHub API." + local releases + releases="$(curl -fsSL "${API_URL}")" || error "Failed to call GitHub API." - local tag - tag="$(echo "$releases" | _json_latest_tag)" + local tag + tag="$(echo "$releases" | _json_latest_tag)" - [[ -z "$tag" || "$tag" == "null" ]] && error "Could not find latest release tag." - echo "$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}" + 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 url="$1" + local ext="$2" + local is_windows="${3:-false}" - local tmpdir - tmpdir="$(mktemp -d)" - trap '[[ -d "${tmpdir:-}" ]] && rm -rf "$tmpdir"' EXIT + local tmpdir + tmpdir="$(mktemp -d)" + trap '[[ -d "${tmpdir:-}" ]] && rm -rf "$tmpdir"' EXIT - local filename - filename="${url##*/}" + local filename + filename="${url##*/}" - info "Downloading: $url" - curl -fsSL --progress-bar -o "${tmpdir}/${filename}" "$url" \ - || error "Download failed: $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 + 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" + # 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." + 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" + # 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" + 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 + 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 + # 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 + local platform_info platform ext is_windows tag url - platform_info="$(detect_platform)" - platform="${platform_info%%:*}" - ext="${platform_info##*:}" + platform_info="$(detect_platform)" + platform="${platform_info%%:*}" + ext="${platform_info##*:}" - info "Detected platform: $platform (format: $ext)" + info "Detected platform: $platform (format: $ext)" - is_windows="false" - [[ "$platform" == "windows"* ]] && is_windows="true" + is_windows="false" + [[ "$platform" == "windows"* ]] && is_windows="true" - tag="$(get_latest_tag)" - info "Latest release: $tag" + tag="$(get_latest_tag)" + info "Latest release: $tag" - url="$(build_download_url "$tag" "$platform" "$ext")" - download_and_install "$url" "$ext" "$is_windows" + url="$(build_download_url "$tag" "$platform" "$ext")" + download_and_install "$url" "$ext" "$is_windows" } main "$@"