diff --git a/README.md b/README.md index 6e75388..1329ea0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - [dua](https://github.com/byron/dua-cli) - 디스크 사용량 분석기 - [lazygit](https://github.com/jesseduffield/lazygit) - Git 터미널 UI - [Docker](https://www.docker.com/) - 컨테이너 플랫폼 (Linux 자동 설치, Mac/Windows는 안내) -- [ctop](https://github.com/bcicen/ctop) - 컨테이너 모니터링 +- [lazydocker](https://github.com/jesseduffield/lazydocker) / [ctop](https://github.com/bcicen/ctop) - 컨테이너 TUI (둘 중 하나 선택) - [uv](https://docs.astral.sh/uv/) - Python 패키지 관리자 - [pi](https://pi.dev/) - CLI AI 코딩 어시스턴트 + 추천 패키지 일괄 설치 @@ -47,12 +47,24 @@ curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazy curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-docker.sh?ref_type=heads" | bash ``` +**ctop / lazydocker** (컨테이너 모니터링 — 둘 중 하나 선택): + +> **ctop**은 실시간 컨테이너 메트릭 모니터링에 특화되어 있고, +> **lazydocker**은 직관적인 TUI로 컨테이너 관리(시작/중지/로그/재시작)에 특화되어 있습니다. +> Docker를 처음 사용한다면 lazydocker을 추천합니다. + **ctop:** ```bash curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-ctop.sh?ref_type=heads" | bash ``` +**lazydocker:** + +```bash +curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazydocker.sh?ref_type=heads" | bash +``` + **uv:** ```bash @@ -100,12 +112,24 @@ powershell -ExecutionPolicy Bypass -c "[Net.ServicePointManager]::SecurityProtoc **Docker:** [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop/)를 설치하세요. 또는 WSL2에서 리눅스용 `install-docker.sh`를 실행하세요. +**ctop / lazydocker** (컨테이너 모니터링 — 둘 중 하나 선택): + +> **ctop**은 실시간 컨테이너 메트릭 모니터링에 특화되어 있고, +> **lazydocker**은 직관적인 TUI로 컨테이너 관리(시작/중지/로그/재시작)에 특화되어 있습니다. +> Docker를 처음 사용한다면 lazydocker을 추천합니다. + **ctop:** ```powershell powershell -ExecutionPolicy Bypass -c "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $t=\"$env:TEMP\install-ctop.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-ctop.ps1' -OutFile $t; & $t" ``` +**lazydocker:** + +```powershell +powershell -ExecutionPolicy Bypass -c "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $t=\"$env:TEMP\install-lazydocker.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazydocker.ps1' -OutFile $t; & $t" +``` + **uv:** ```powershell @@ -241,9 +265,16 @@ docker build -t . # 이미지 빌드 > > `install-docker.sh`는 Linux 전용입니다. 설치 후 현재 사용자를 `docker` 그룹에 추가하며, 재로그인 후 `docker` 명령어를 sudo 없이 사용할 수 있습니다. -### ctop +### ctop / lazydocker -Docker/Podman 컨테이너 모니터링 터미널 UI 도구입니다. +> **ctop**과 **lazydocker**은 모두 컨테이너 모니터링 TUI 도구입니다. 둘 중 하나를 선택하세요. +> +> | | ctop | lazydocker | +> | ---- | ------------------ | -------------------------------------- | +> | 특징 | 실시간 메트릭 중심 | 직관적 관리 TUI | +> | 추천 | 모니터링 위주 | 컨테이너 관리 위주, Docker 초보자 추천 | + +**ctop** — Docker/Podman 컨테이너 모니터링 터미널 UI - **실시간 컨테이너 메트릭** (CPU, 메모리, 네트워크, I/O) - **컨테이너별 상태** 표시 및 필터링 @@ -266,6 +297,30 @@ ctop > **macOS Apple Silicon 참고**: ctop은 native arm64 빌드를 제공하지 않습니다. Rosetta 2를 통해 amd64 바이너리로 실행됩니다. +**lazydocker** — 직관적인 Docker/Podman 관리 터미널 UI + +- **컨테이너/이미지/볼륨** 목록 및 상태 +- **컨테이너 로그** 실시간 보기 +- **컨테이너 시작/정지/재시작/삭제** +- **컨테이너 셸 진입** (exec) +- **Docker Compose** 서비스 관리 +- **커스텀 명령어** 키 바인딩 + +```bash +lazydocker +``` + +| 단축키 | 동작 | +| ------- | ----------------------- | +| `q` | 종료 | +| `↑` `↓` | 리스트 이동 | +| `Enter` | 컨테이너 로그 보기 | +| `s` | 컨테이너 정지 | +| `r` | 컨테이너 재시작 | +| `e` | 컨테이너 셸 진입 (exec) | +| `x` | 컨테이너 제거 | +| `[` `]` | 패널 간 이동 | + ### Pi Coding Agent CLI 기반 AI 코딩 어시스턴트입니다. `install-pi` 스크립트는 Pi 본체와 함께 다음 추천 패키지를 일괄 설치합니다. diff --git a/install-lazydocker.ps1 b/install-lazydocker.ps1 new file mode 100644 index 0000000..d934a36 --- /dev/null +++ b/install-lazydocker.ps1 @@ -0,0 +1,241 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Installs lazydocker terminal UI for Docker from GitHub releases. + +.DESCRIPTION + Auto-detects the latest release from + https://github.com/jesseduffield/lazydocker/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 lazydocker.exe. Default: $env:USERPROFILE\.local\bin + +.PARAMETER AddToPath + Whether to permanently add InstallDir to user PATH. Default: $true + +.EXAMPLE + .\install-lazydocker.ps1 + .\install-lazydocker.ps1 -InstallDir "C:\tools\bin" + .\install-lazydocker.ps1 -AddToPath $false + + # One-liner install from remote (works on Windows 10+): + powershell -ExecutionPolicy Bypass -c "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $t=\"$env:TEMP\install-lazydocker.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazydocker.ps1' -OutFile $t; & $t" + + # With parameters: + powershell -ExecutionPolicy Bypass -c "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $t=\"$env:TEMP\install-lazydocker.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazydocker.ps1' -OutFile $t; & $t -InstallDir 'C:\tools\bin'" + +.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/lazydocker" +$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 HTTPS downloads (required on Windows 10 / PowerShell 5.1) +# ──────────────────────────────────────────── +[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_x86" } + } + + try { + $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture + switch ($arch) { + 'X64' { return "Windows_x86_64" } + 'Arm64' { return "Windows_arm64" } + 'X86' { return "Windows_x86" } + 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-lazydocker-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 = "lazydocker_${version}_${Platform}.zip" + return "https://github.com/$REPO/releases/download/$Tag/$filename" +} + +# ──────────────────────────────────────────── +# Download, extract & install +# ──────────────────────────────────────────── +function Install-Lazydocker { + param([string]$Url) + + $tmpDir = Join-Path $env:TEMP "lazydocker_install_$(Get-Random)" + New-Item -ItemType Directory -Path $tmpDir | Out-Null + + try { + $zipPath = Join-Path $tmpDir "lazydocker.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 + + $lazydockerExe = Get-ChildItem -Path $tmpDir -Recurse -Filter "lazydocker.exe" | + Select-Object -First 1 + if (-not $lazydockerExe) { + Write-Err "Cannot find 'lazydocker.exe' in the archive." + } + + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + + $destPath = Join-Path $InstallDir "lazydocker.exe" + + # Check if lazydocker.exe is currently running (locked) + if (Test-Path $destPath) { + try { + $testStream = [System.IO.File]::Open($destPath, 'Open', 'ReadWrite', 'Delete') + $testStream.Close() + $testStream.Dispose() + } catch { + Write-Err "lazydocker.exe is currently running. Please close lazydocker and run this script again." + } + } + + Write-Info "Installing: $destPath" + Copy-Item -Path $lazydockerExe.FullName -Destination $destPath -Force + + Write-Success "Installation complete!" + + Write-Info "Version check:" + try { + & $destPath --version + } catch { + Write-Warn "Cannot run version check (lazydocker may need a new terminal session to start)." + } + + } 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 "===== lazydocker 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-Lazydocker -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 "" + Write-Info "IMPORTANT: Close this terminal and open a new one to use lazydocker." + Write-Info "PATH changes require a new terminal session to take effect." +} + +Main \ No newline at end of file diff --git a/install-lazydocker.sh b/install-lazydocker.sh new file mode 100755 index 0000000..1ae6186 --- /dev/null +++ b/install-lazydocker.sh @@ -0,0 +1,263 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2086 + +# Downloads and installs the latest lazydocker from +# https://github.com/jesseduffield/lazydocker/releases +# +# Supported platforms: +# macOS : Apple Silicon (arm64), Intel (x86_64) +# Linux : x86_64, aarch64/arm64, armv6, armv7, x86 (32-bit) +# Windows : Not supported — use install-lazydocker.ps1 (PowerShell) + +set -euo pipefail + +# ──────────────────────────────────────────── +# Constants +# ──────────────────────────────────────────── +REPO="jesseduffield/lazydocker" +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; } + +# ──────────────────────────────────────────── +# Ensure Homebrew is available on macOS +# ──────────────────────────────────────────── +ensure_brew() { + if command -v brew &>/dev/null; then + return 0 + fi + info "Homebrew is not installed. Installing Homebrew..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + if [ -d /opt/homebrew/bin ]; then + export PATH="/opt/homebrew/bin:$PATH" + fi + if [ -d /usr/local/bin ]; then + export PATH="/usr/local/bin:$PATH" + fi + command -v brew &>/dev/null || error "Homebrew installation failed. Install it manually: https://brew.sh/" + success "Homebrew installed" +} + +# ──────────────────────────────────────────── +# 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: ":" +# platform matches lazydocker's GitHub release asset naming +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 ────────────────────────────────────────────── + Linux) + case "$arch" in + x86_64 | amd64) echo "Linux_x86_64:tar.gz" ;; + aarch64 | arm64) echo "Linux_arm64:tar.gz" ;; + armv7l) echo "Linux_armv7:tar.gz" ;; + armv6l) echo "Linux_armv6:tar.gz" ;; + i686 | i386) echo "Linux_x86:tar.gz" ;; + *) error "Unsupported Linux architecture: $arch" ;; + esac + ;; + + # ── Windows (Git Bash / MSYS2 / Cygwin) ─────────────── + MINGW* | MSYS* | CYGWIN*) + error "Windows is not supported by this script. Use install-lazydocker.ps1 instead." + ;; + + *) + 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="lazydocker_${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 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' is required to extract .zip files." + fi + ;; + *) + error "Unknown archive format: $ext" + ;; + esac + + # Binary name + local bin_name="lazydocker" + + 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_macos tag url + + platform_info="$(detect_platform)" + platform="${platform_info%%:*}" + ext="${platform_info##*:}" + + info "Detected platform: $platform (format: $ext)" + + is_macos="false" + [[ "$platform" == "Darwin"* ]] && is_macos="true" + + # macOS: try Homebrew first + if [[ "$is_macos" == "true" ]]; then + if command -v lazydocker &>/dev/null; then + local current_ver + current_ver="$(lazydocker --version 2>/dev/null || echo 'unknown')" + info "lazydocker is already installed ($current_ver). Skipping installation." + else + ensure_brew + brew install lazydocker + success "lazydocker installed via Homebrew" + fi + else + if command -v lazydocker &>/dev/null; then + local current_ver + current_ver="$(lazydocker --version 2>/dev/null || echo 'unknown')" + info "lazydocker is already installed ($current_ver). Skipping installation." + else + tag="$(get_latest_tag)" + info "Latest release: $tag" + + url="$(build_download_url "$tag" "$platform" "$ext")" + download_and_install "$url" "$ext" + fi + fi + + # Verify lazydocker is available + if command -v lazydocker &>/dev/null; then + success "lazydocker is available: $(command -v lazydocker)" + else + warn "lazydocker was not found in PATH after installation. You may need to open a new terminal." + fi +} + +main "$@"