diff --git a/README.md b/README.md index 46fee7b..cef5911 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-bott **superfile:** ```bash -curl -fsSL "https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-superfile.sh?ref_type=heads" | bash +bash -c "$(curl -sLo- https://superfile.dev/install.sh)" ``` **dua:** @@ -228,16 +228,16 @@ btm spf ``` -| 단축키 | 동작 | -| ----------- | ------------------------ | -| `q` | 종료 | -| `h` `j` `k` `l` | 좌/하/상/우 이동 | -| `Enter` | 디렉토리 진입 / 파일 열기 | -| `y` | Yank (복사) | -| `d` | Delete (삭제) | -| `r` | Rename | -| `/` | 검색 | -| `Tab` | 패널 전환 | +| 단축키 | 동작 | +| --------------- | ------------------------- | +| `q` | 종료 | +| `h` `j` `k` `l` | 좌/하/상/우 이동 | +| `Enter` | 디렉토리 진입 / 파일 열기 | +| `y` | Yank (복사) | +| `d` | Delete (삭제) | +| `r` | Rename | +| `/` | 검색 | +| `Tab` | 패널 전환 | > **참고**: 실행 명령어는 `spf`입니다 (`superfile`이 아님). diff --git a/install-superfile.ps1 b/install-superfile.ps1 index e104383..7f8794b 100644 --- a/install-superfile.ps1 +++ b/install-superfile.ps1 @@ -1,248 +1,156 @@ #Requires -Version 5.1 <# .SYNOPSIS - Installs superfile terminal file manager from GitHub releases. + Installs superfile (terminal file manager) via winget. .DESCRIPTION - Auto-detects the latest release from - https://github.com/yorukot/superfile/releases, downloads the zip - file matching your Windows architecture, and installs it. + Installs superfile via winget (yorukot.superfile). + superfile is a modern terminal file manager with vim-style keybindings. - Supported architectures: - - x64 (AMD64 / Intel 64bit) - - ARM64 (Snapdragon X Elite, Qualcomm ARM, etc.) + Supported platforms: + - Windows x64 (AMD64 / Intel 64bit) + - Windows ARM64 -.PARAMETER InstallDir - Directory to install spf.exe. Default: $env:USERPROFILE\.local\bin - -.PARAMETER AddToPath - Whether to permanently add InstallDir to user PATH. Default: $true + Prerequisites: + - winget (Windows Package Manager) — built into Windows 10 1709+ .EXAMPLE .\install-superfile.ps1 - .\install-superfile.ps1 -InstallDir "C:\tools\bin" - .\install-superfile.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-superfile.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-superfile.ps1' -OutFile $t; & $t" - # With parameters: - powershell -ExecutionPolicy Bypass -c "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $t=\"$env:TEMP\install-superfile.ps1\"; iwr -useb 'https://gl.isitsoft.kr/publicproj/install-sh/-/raw/main/install-superfile.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 -) +param() Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' -# ──────────────────────────────────────────── -# Constants -# ──────────────────────────────────────────── -$REPO = "yorukot/superfile" -$API_URL = "https://api.github.com/repos/$REPO/releases" +# Force TLS 1.2+ for HTTPS downloads (required on Windows 10 / PowerShell 5.1) +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # ──────────────────────────────────────────── # 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 } +function Info { Write-Host "[INFO] $args" -ForegroundColor Cyan } +function Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow } +function Success { Write-Host "[OK] $args" -ForegroundColor Green } +function ErrExit { Write-Host "[ERROR] $args" -ForegroundColor Red; exit 1 } # ──────────────────────────────────────────── -# Enforce TLS 1.2+ for HTTPS downloads (required on Windows 10 / PowerShell 5.1) +# Step 1: Install superfile # ──────────────────────────────────────────── -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +Info "Installing superfile (terminal file manager)..." -# ──────────────────────────────────────────── -# Architecture detection -# ──────────────────────────────────────────── -function Get-Platform { - $envArch = $env:PROCESSOR_ARCHITEW6432 - if (-not $envArch) { $envArch = $env:PROCESSOR_ARCHITECTURE } - - switch -Regex ($envArch) { - '^AMD64$' { return "windows-amd64" } - '^ARM64$' { return "windows-arm64" } - '^x86$' { Write-Err "32-bit Windows is not supported by superfile." } +$spfCmd = Get-Command spf -ErrorAction SilentlyContinue +if ($spfCmd) { + $currentVer = & spf --version 2>$null + if (-not $currentVer) { $currentVer = "unknown" } + Info "superfile is already installed ($currentVer). Skipping installation." +} else { + if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { + ErrExit "winget is required. Install it from the Microsoft Store or https://learn.microsoft.com/en-us/windows/package-manager/winget/" } - try { - $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture - switch ($arch) { - 'X64' { return "windows-amd64" } - 'Arm64' { return "windows-arm64" } - 'X86' { Write-Err "32-bit Windows is not supported by superfile." } - default { Write-Err "Unsupported architecture: $arch" } - } - } catch { - Write-Err "Failed to detect architecture: $_" - } + winget install --id yorukot.superfile --accept-package-agreements --accept-source-agreements 2>&1 | Out-Null + Success "superfile installed via winget" } # ──────────────────────────────────────────── -# Get latest stable release tag +# Verify spf is available and add to PATH permanently if needed # ──────────────────────────────────────────── -function Get-LatestTag { - Write-Info "Fetching latest release from GitHub API..." +$spfBin = $null - try { - $releases = Invoke-RestMethod -Uri $API_URL -UseBasicParsing ` - -Headers @{ 'User-Agent' = 'install-superfile-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 +# 1) Try current PATH +$spfCmd = Get-Command spf -ErrorAction SilentlyContinue +if ($spfCmd) { + $spfBin = $spfCmd.Source } -# ──────────────────────────────────────────── -# Build download URL -# ──────────────────────────────────────────── -function Get-DownloadUrl { - param([string]$Tag, [string]$Platform) - # Asset naming: superfile-windows-v1.5.0-amd64.zip - $version = $Tag -replace '^v', '' - $filename = "superfile-${Platform}-${Tag}.zip" - return "https://github.com/$REPO/releases/download/$Tag/$filename" -} - -# ──────────────────────────────────────────── -# Download, extract & install -# ──────────────────────────────────────────── -function Install-Superfile { - param([string]$Url) - - $tmpDir = Join-Path $env:TEMP "superfile_install_$(Get-Random)" - New-Item -ItemType Directory -Path $tmpDir | Out-Null - - try { - $zipPath = Join-Path $tmpDir "superfile.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 - - $spfExe = Get-ChildItem -Path $tmpDir -Recurse -Filter "spf.exe" | - Select-Object -First 1 - if (-not $spfExe) { - Write-Err "Cannot find 'spf.exe' in the archive." - } - - if (-not (Test-Path $InstallDir)) { - New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null - } - - $destPath = Join-Path $InstallDir "spf.exe" - - # Check if spf.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 "spf.exe is currently running. Please close superfile and run this script again." - } - } - - Write-Info "Installing: $destPath" - Copy-Item -Path $spfExe.FullName -Destination $destPath -Force - - Write-Success "Installation complete!" - - Write-Info "Version check:" - try { - & $destPath --version - } catch { - Write-Warn "Cannot run version check (spf may need a new terminal session to start)." - } - - } finally { - if (Test-Path $tmpDir) { - Remove-Item -Path $tmpDir -Recurse -Force -ErrorAction SilentlyContinue - } +# 2) Refresh PATH from registry (winget may have added paths) +if (-not $spfBin) { + $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + $spfCmd = Get-Command spf -ErrorAction SilentlyContinue + if ($spfCmd) { + $spfBin = $spfCmd.Source } } -# ──────────────────────────────────────────── -# 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 +# 3) Search common locations where winget installs +if (-not $spfBin) { + $searchDirs = @( + (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Links'), + (Join-Path $env:LOCALAPPDATA 'Programs\superfile'), + 'C:\Program Files\superfile', + 'C:\Program Files (x86)\superfile' + ) + foreach ($dir in $searchDirs) { + if (Test-Path $dir) { + $candidate = Join-Path $dir 'spf.exe' + if (Test-Path $candidate) { $spfBin = $candidate; break } + } } - $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." + # Recurse under WinGet/Packages for spf + if (-not $spfBin -and (Test-Path (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Packages'))) { + $found = Get-ChildItem -Path (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Packages') -Recurse -Filter 'spf.exe' -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($found) { $spfBin = $found.FullName } + } } -# ──────────────────────────────────────────── -# Main -# ──────────────────────────────────────────── -function Main { - Write-Info "===== superfile install script (Windows PowerShell) =====" +if ($spfBin) { + # Add spf's directory to PATH for this session + $spfDir = Split-Path $spfBin + if ($env:Path -notlike "*$spfDir*") { + $env:Path = "$spfDir;$env:Path" + Info "Added $spfDir to PATH for this session" + } + Success "superfile is available: $spfBin" - $platform = Get-Platform - Write-Info "Detected platform: $platform" - - $tag = Get-LatestTag - Write-Info "Latest release: $tag" - - $url = Get-DownloadUrl -Tag $tag -Platform $platform - Install-Superfile -Url $url - - if ($AddToPath) { - Add-ToUserPath -Dir $InstallDir + # Permanently add spf's directory to user PATH if not already there + $userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User') + if ($userPath -notlike "*$spfDir*") { + [System.Environment]::SetEnvironmentVariable('Path', "$spfDir;$userPath", 'User') + Success "Added $spfDir to user PATH permanently (takes effect on next login)" } else { - Write-Warn "Skipping automatic PATH registration." - Write-Warn "Add to PATH manually: $InstallDir" + Info "$spfDir already in user PATH" } - - Write-Success "All tasks completed." - Write-Info "" - Write-Info "IMPORTANT: Close this terminal and open a new one to use superfile (spf)." - Write-Info "PATH changes require a new terminal session to take effect." - Write-Info "" - Write-Info "Getting started:" - Write-Info " spf - Start superfile file manager" - Write-Info " spf --help - Show help" - Write-Info "" - Write-Info "Note: The command name is 'spf', not 'superfile'." +} else { + Warn "superfile was not found in PATH after installation." + Warn "Please restart your terminal or add superfile's directory to your PATH manually." } -Main \ No newline at end of file +# ──────────────────────────────────────────── +# Step 2: Post-install notes +# ──────────────────────────────────────────── +Info "" +Info "==================================================" +Success "superfile installation complete!" +Info "==================================================" +if ($spfBin) { + Info "" + Info "superfile binary: $spfBin" +} +Info "" +Info "IMPORTANT: Close this terminal and open a new one to use superfile (spf)." +Info "PATH changes require a new terminal session to take effect." +Info "" +Info "Getting started:" +Info " spf - Start superfile file manager" +Info " spf --help - Show help" +Info "" +Info "Note: The command name is 'spf', not 'superfile'." +Info "" +Info "Key bindings (vim-style):" +Info " h/j/k/l - Left/Down/Up/Right" +Info " Enter - Open directory / file" +Info " y - Yank (copy)" +Info " d - Delete" +Info " r - Rename" +Info " / - Search" +Info " Tab - Switch panel" \ No newline at end of file diff --git a/install-superfile.sh b/install-superfile.sh index d010918..d9d8202 100755 --- a/install-superfile.sh +++ b/install-superfile.sh @@ -1,29 +1,26 @@ #!/usr/bin/env bash # shellcheck disable=SC2086 -# Downloads and installs the latest superfile from -# https://github.com/yorukot/superfile/releases +# Installs superfile (terminal file manager) — https://github.com/yorukot/superfile +# +# Uses the official installer on Mac/Linux: +# bash -c "$(curl -sLo- https://superfile.dev/install.sh)" # # Supported platforms: -# macOS : Apple Silicon (arm64), Intel (x86_64) — via Homebrew or direct download -# Linux : x86_64 (amd64), aarch64/arm64 — via direct download -# Windows : Not supported — use install-superfile.ps1 (PowerShell) +# macOS : Apple Silicon (arm64), Intel (x86_64) +# Linux : x86_64, aarch64/arm64 set -euo pipefail -# ──────────────────────────────────────────── -# Constants -# ──────────────────────────────────────────── -REPO="yorukot/superfile" -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; } +info() { echo "[INFO] $*" >&2; } +warn() { echo "[WARN] $*" >&2; } +error() { + echo "[ERROR] $*" >&2 + exit 1 +} success() { echo "[OK] $*" >&2; } # ──────────────────────────────────────────── @@ -48,201 +45,49 @@ ensure_brew() { # ──────────────────────────────────────────── # Check required commands # ──────────────────────────────────────────── -for cmd in curl uname; do +for cmd in curl; 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' - } +# ──────────────────────────────────────────── +# Install superfile +# ──────────────────────────────────────────── +info "Installing superfile (terminal file manager)..." + +if command -v spf &>/dev/null; then + CURRENT_VER="$(spf --version 2>/dev/null || echo 'unknown')" + info "superfile is already installed ($CURRENT_VER). Skipping installation." 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 -" - } + bash -c "$(curl -sLo- https://superfile.dev/install.sh)" + success "superfile installed via official installer" +fi + +# Verify spf is available +if command -v spf &>/dev/null; then + success "superfile is available: $(command -v spf)" +else + warn "superfile was not found in PATH after installation. You may need to open a new terminal." fi # ──────────────────────────────────────────── -# OS / Architecture detection +# Post-install notes # ──────────────────────────────────────────── -OS="$(uname -s)" -ARCH="$(uname -m)" - -case "$OS" in -Darwin | Linux) ;; -*) - error "Unsupported OS: $OS. This script only supports macOS and Linux." - ;; -esac - -# Returns: ":" -detect_platform() { - case "$OS" in - Darwin) - case "$ARCH" in - arm64) echo "darwin-arm64:tar.gz" ;; - x86_64) echo "darwin-amd64:tar.gz" ;; - *) error "Unsupported macOS architecture: $ARCH" ;; - esac - ;; - Linux) - case "$ARCH" in - x86_64|amd64) echo "linux-amd64:tar.gz" ;; - aarch64|arm64) echo "linux-arm64:tar.gz" ;; - *) error "Unsupported Linux architecture: $ARCH" ;; - esac - ;; -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 -# ──────────────────────────────────────────── -# Asset naming: superfile-{os}-{tag_version}-{arch}.tar.gz -build_download_url() { - local tag="$1" - local platform="$2" # e.g. darwin-arm64 - local ext="$3" # tar.gz | zip - local version="${tag#v}" - - local filename="superfile-${platform}-${tag}.${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." - fi - ;; - *) - error "Unknown archive format: $ext" - ;; - esac - - # Binary name - local bin_name="spf" - [[ "$OS" == "MINGW"* || "$OS" == "MSYS"* || "$OS" == "CYGWIN"* ]] && bin_name="spf.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 tag url - - platform_info="$(detect_platform)" - platform="${platform_info%%:*}" - ext="${platform_info##*:}" - - info "Detected platform: $platform (format: $ext)" - - # macOS: try Homebrew first - if [[ "$OS" == "Darwin" ]]; then - if command -v spf &>/dev/null; then - local current_ver - current_ver="$(spf --version 2>/dev/null || echo 'unknown')" - info "superfile is already installed ($current_ver). Skipping installation." - else - ensure_brew - brew install superfile - success "superfile installed via Homebrew" - fi - else - if command -v spf &>/dev/null; then - local current_ver - current_ver="$(spf --version 2>/dev/null || echo 'unknown')" - info "superfile 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 spf is available - if command -v spf &>/dev/null; then - success "superfile is available: $(command -v spf)" - else - warn "superfile was not found in PATH after installation. You may need to open a new terminal." - fi -} - -main "$@" \ No newline at end of file +info "" +info "==================================================" +success "superfile installation complete!" +info "==================================================" +info "" +info "Getting started:" +info " spf - Start superfile file manager" +info " spf --help - Show help" +info "" +info "Note: The command name is 'spf', not 'superfile'." +info "" +info "Key bindings (vim-style):" +info " h/j/k/l - Left/Down/Up/Right" +info " Enter - Open directory / file" +info " y - Yank (copy)" +info " d - Delete" +info " r - Rename" +info " / - Search" +info " Tab - Switch panel"