diff --git a/README.md b/README.md index c34d96c..51b71bc 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ - [bottom](https://github.com/ClementTsang/bottom) - 호스트 모니터링 - [lazygit](https://github.com/jesseduffield/lazygit) - Git 터미널 UI +- [ctop](https://github.com/bcicen/ctop) - 컨테이너 모니터링 ## 설치 @@ -21,6 +22,11 @@ curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-bott curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazygit.sh?ref_type=heads" | bash ``` +**ctop:** +```bash +curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-ctop.sh?ref_type=heads" | bash +``` + ### Windows **PowerShell**: @@ -35,6 +41,11 @@ lazygit: powershell -ExecutionPolicy Bypass -c "$t=\"$env:TEMP\install-lazygit.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-lazygit.ps1' -OutFile $t; & $t" ``` +ctop: +```powershell +powershell -ExecutionPolicy Bypass -c "$t=\"$env:TEMP\install-ctop.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-ctop.ps1' -OutFile $t; & $t" +``` + ## 도구별 사용법 @@ -82,6 +93,31 @@ lazygit | `P` `p` | Push / Pull | | `?` | 도움말 | +### ctop + +Docker/Podman 컨테이너 모니터링 터미널 UI 도구입니다. + +- **실시간 컨테이너 메트릭** (CPU, 메모리, 네트워크, I/O) +- **컨테이너별 상태** 표시 및 필터링 +- **컨테이너 로그** 바로 보기 +- **정지/시작/재시작** 등 컨테이너 관리 + +```bash +ctop +``` + +| 단축키 | 동작 | +| ----------- | -------------------- | +| `q` / `Esc` | 종료 | +| `↑` `↓` | 컨테이너 선택 | +| `l` | 컨테이너 로그 보기 | +| `a` | 모든 컨테이너 표시 | +| `f` | 필터 | +| `S` | 정지 | +| `R` | 재시작 | + +> **macOS Apple Silicon 참고**: ctop은 native arm64 빌드를 제공하지 않습니다. Rosetta 2를 통해 amd64 바이너리로 실행됩니다. + --- *GitLab: https://gitlab.com/publicproj/install-sh* diff --git a/install-ctop.ps1 b/install-ctop.ps1 new file mode 100644 index 0000000..aa14633 --- /dev/null +++ b/install-ctop.ps1 @@ -0,0 +1,226 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Installs ctop (container terminal UI) from GitHub releases. + +.DESCRIPTION + Auto-detects the latest release from + https://github.com/bcicen/ctop/releases, downloads the binary + matching your Windows architecture, and installs it. + + Supported architectures: + - x64 (AMD64 / Intel 64bit) + + NOTE: ctop only provides a windows-amd64 binary. ARM64 is not supported. + +.PARAMETER InstallDir + Directory to install ctop.exe. Default: $env:USERPROFILE\.local\bin + +.PARAMETER AddToPath + Whether to permanently add InstallDir to user PATH. Default: $true + +.EXAMPLE + .\install-ctop.ps1 + .\install-ctop.ps1 -InstallDir "C:\tools\bin" + .\install-ctop.ps1 -AddToPath $false + + # One-liner install from remote (download then run; -ExecutionPolicy Bypass for current process only): + powershell -ExecutionPolicy Bypass -c "$t=\"$env:TEMP\install-ctop.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-ctop.ps1' -OutFile $t; & $t" + + # With parameters: + powershell -ExecutionPolicy Bypass -c "$t=\"$env:TEMP\install-ctop.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-ctop.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 = "bcicen/ctop" +$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 { + # ctop only provides windows-amd64 binary + $envArch = $env:PROCESSOR_ARCHITEW6432 + if (-not $envArch) { $envArch = $env:PROCESSOR_ARCHITECTURE } + + switch -Regex ($envArch) { + '^AMD64$' { return "windows-amd64" } + '^ARM64$' { + Write-Warn "ctop does not provide a native ARM64 Windows build." + Write-Warn "Falling back to amd64 binary (may run via emulation)." + return "windows-amd64" + } + '^x86$' { Write-Err "ctop does not provide a 32-bit Windows build." } + } + + try { + $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture + switch ($arch) { + 'X64' { return "windows-amd64" } + 'Arm64' { + Write-Warn "ctop does not provide a native ARM64 Windows build." + Write-Warn "Falling back to amd64 binary (may run via emulation)." + return "windows-amd64" + } + 'X86' { Write-Err "ctop does not provide a 32-bit Windows build." } + 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-ctop-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 +# ──────────────────────────────────────────── +# ctop release assets: ctop-{version}-{os}-{arch} (raw binary, no .exe extension) +function Get-DownloadUrl { + param([string]$Tag, [string]$Platform) + $version = $Tag -replace '^v', '' + $filename = "ctop-${version}-${Platform}" + return "https://github.com/$REPO/releases/download/$Tag/$filename" +} + +# ──────────────────────────────────────────── +# Download & install (raw binary — no archive extraction needed) +# ──────────────────────────────────────────── +function Install-Ctop { + param([string]$Url) + + $tmpDir = Join-Path $env:TEMP "ctop_install_$(Get-Random)" + New-Item -ItemType Directory -Path $tmpDir | Out-Null + + try { + $binPath = Join-Path $tmpDir "ctop" + + Write-Info "Downloading: $Url" + try { + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri $Url -OutFile $binPath -UseBasicParsing + $ProgressPreference = 'Continue' + } catch { + Write-Err "Download failed: $_" + } + + # Create install directory + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + + $destPath = Join-Path $InstallDir "ctop.exe" + Write-Info "Installing: $destPath" + Copy-Item -Path $binPath -Destination $destPath -Force + + Write-Success "Installation complete!" + + Write-Info "Version check:" + & $destPath -v + + } 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 "===== ctop 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-Ctop -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: ctop" +} + +Main \ No newline at end of file diff --git a/install-ctop.sh b/install-ctop.sh new file mode 100755 index 0000000..57f6d98 --- /dev/null +++ b/install-ctop.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2086 + +# Downloads and installs the latest ctop (container terminal UI) from +# https://github.com/bcicen/ctop/releases +# +# Supported platforms: +# macOS : Intel (x86_64), Apple Silicon via Rosetta 2 (arm64) +# Linux : x86_64 (amd64), aarch64/arm64, arm, ppc64le +# Windows : Not supported — use install-ctop.ps1 (PowerShell) + +set -euo pipefail + +# ──────────────────────────────────────────── +# Constants +# ──────────────────────────────────────────── +REPO="bcicen/ctop" +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: "-" matching ctop's GitHub release asset naming +detect_platform() { + local os arch + + os="$(uname -s)" + arch="$(uname -m)" + + case "$os" in + # ── macOS ────────────────────────────────────────────── + Darwin) + case "$arch" in + x86_64) + echo "darwin-amd64" + ;; + arm64) + # ctop does not provide a native darwin-arm64 binary. + # Use the amd64 binary via Rosetta 2 (works on Apple Silicon). + if [[ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" == "1" ]] \ + || command -v arch &>/dev/null; then + warn "ctop has no native Apple Silicon build." + warn "Using amd64 binary via Rosetta 2." + echo "darwin-amd64" + else + error "No native Apple Silicon build available and Rosetta 2 is not installed." \ + "Install Rosetta 2: softwareupdate --install-rosetta" + fi + ;; + *) + error "Unsupported macOS architecture: $arch" + ;; + esac + ;; + + # ── Linux ────────────────────────────────────────────── + Linux) + case "$arch" in + x86_64|amd64) echo "linux-amd64" ;; + aarch64|arm64) echo "linux-arm64" ;; + armv7l|armhf) echo "linux-arm" ;; + ppc64le) echo "linux-ppc64le" ;; + *) + error "Unsupported Linux architecture: $arch" + ;; + esac + ;; + + # ── Windows (Git Bash / MSYS2 / Cygwin) ─────────────── + MINGW*|MSYS*|CYGWIN*) + error "Windows is not supported by this script. Use install-ctop.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 +# ──────────────────────────────────────────── +# ctop release assets follow the pattern: ctop-{version}-{os}-{arch} +# where {version} has the 'v' prefix stripped from the git tag. +build_download_url() { + local tag="$1" + local platform="$2" # e.g. darwin-amd64, linux-amd64 + local version="${tag#v}" # Remove 'v' prefix (v0.7.7 → 0.7.7) + local filename="ctop-${version}-${platform}" + echo "https://github.com/${REPO}/releases/download/${tag}/${filename}" +} + +# ──────────────────────────────────────────── +# Download & install (raw binary — no archive extraction needed) +# ──────────────────────────────────────────── +download_and_install() { + local url="$1" + + 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" + + # ctop provides raw binaries (not archives), so skip extraction + local bin_src="${tmpdir}/${filename}" + + # Create install directory + mkdir -p "$INSTALL_DIR" + + local install_path="${INSTALL_DIR}/ctop" + info "Installing: ${install_path}" + cp "$bin_src" "$install_path" + chmod +x "$install_path" + + success "Installation complete!" + info "Version check:" + "$install_path" -v || 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 tag url + + platform="$(detect_platform)" + info "Detected platform: $platform" + + tag="$(get_latest_tag)" + info "Latest release: $tag" + + url="$(build_download_url "$tag" "$platform")" + download_and_install "$url" +} + +main "$@" \ No newline at end of file