From eeeffccd9199c4bacd1e7e9d6762d206626f0d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B1=84=EB=AC=B8=EC=B0=BD=40MBA?= Date: Thu, 24 Sep 2026 06:07:14 +0900 Subject: [PATCH] =?UTF-8?q?Window=EC=97=90=EC=84=9C=20psmux.exe=EB=A5=BC?= =?UTF-8?q?=20{=ED=99=88}/.local/bin=20=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install-tmux.ps1 | 203 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 65 deletions(-) diff --git a/install-tmux.ps1 b/install-tmux.ps1 index a1422db..d301fa3 100644 --- a/install-tmux.ps1 +++ b/install-tmux.ps1 @@ -46,6 +46,12 @@ function ErrExit { Write-Host "[ERROR] $args" -ForegroundColor Red; exit 1 } # Constants # ──────────────────────────────────────────── $ConfFile = Join-Path $env:USERPROFILE '.tmux.conf' +$LocalBin = Join-Path $env:USERPROFILE '.local\bin' + +# Ensure $LocalBin directory exists +if (-not (Test-Path $LocalBin)) { + New-Item -ItemType Directory -Path $LocalBin -Force | Out-Null +} # ──────────────────────────────────────────── # Step 0: Ensure OpenSSH Server (비활성화됨) @@ -79,94 +85,161 @@ $ConfFile = Join-Path $env:USERPROFILE '.tmux.conf' # ──────────────────────────────────────────── -# Step 1: Install tmux +# Step 1: Install tmux (psmux) and copy *.exe to ~/.local/bin # ──────────────────────────────────────────── -Info "Installing tmux..." +Info "Installing tmux (psmux)..." -$tmuxCmd = Get-Command tmux -ErrorAction SilentlyContinue -if ($tmuxCmd) { - $currentVer = & tmux -V 2>$null - if (-not $currentVer) { $currentVer = "unknown" } - Info "tmux 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/" - } +$localTmux = Join-Path $LocalBin 'tmux.exe' +$localPsmux = Join-Path $LocalBin 'psmux.exe' - winget install psmux --accept-package-agreements --accept-source-agreements 2>&1 | Out-Null - Success "tmux (psmux) installed via winget" -} - -# ──────────────────────────────────────────── -# Verify tmux is available and add to PATH permanently if needed -# ──────────────────────────────────────────── -$tmuxBin = $null - -# 1) Try current PATH -$tmuxCmd = Get-Command tmux -ErrorAction SilentlyContinue -if ($tmuxCmd) { - $tmuxBin = $tmuxCmd.Source -} - -# 2) Refresh PATH from registry (winget may have added paths) -if (-not $tmuxBin) { - $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'Machine') - $tmuxCmd = Get-Command tmux -ErrorAction SilentlyContinue - if ($tmuxCmd) { - $tmuxBin = $tmuxCmd.Source +$needsInstall = $true +if (Test-Path $localTmux) { + $currentVer = & $localTmux -V 2>$null + if (-not $currentVer) { $currentVer = & $localTmux --version 2>$null } + if ($currentVer) { + Info "tmux is already installed in $LocalBin ($currentVer)." + $needsInstall = $false } } -# 3) Search common locations where winget installs tmux / psmux -if (-not $tmuxBin) { +if ($needsInstall) { + $wingetAvailable = [bool](Get-Command winget -ErrorAction SilentlyContinue) + $installed = $false + + if ($wingetAvailable) { + try { + Info "Running winget install psmux..." + winget install psmux --accept-package-agreements --accept-source-agreements 2>&1 | Out-Null + Success "tmux (psmux) installed via winget" + $installed = $true + } catch { + Warn "winget install failed: $_" + } + } + + # Fallback to direct download from GitHub releases if winget is missing or failed + if (-not $installed) { + Info "Downloading psmux directly from GitHub releases..." + $arch = $env:PROCESSOR_ARCHITECTURE + $psmuxZipAsset = if ($arch -eq "ARM64") { "psmux-v3.3.8-windows-arm64.zip" } else { "psmux-v3.3.8-windows-x64.zip" } + $downloadUrl = "https://github.com/psmux/psmux/releases/download/v3.3.8/$psmuxZipAsset" + + try { + $latestRel = Invoke-RestMethod -Uri "https://api.github.com/repos/psmux/psmux/releases/latest" -UseBasicParsing -TimeoutSec 5 + $assetPattern = if ($arch -eq "ARM64") { "psmux-*-windows-arm64.zip" } else { "psmux-*-windows-x64.zip" } + $matchedAsset = $latestRel.assets | Where-Object { $_.name -like $assetPattern } | Select-Object -First 1 + if ($matchedAsset) { + $downloadUrl = $matchedAsset.browser_download_url + } + } catch { + # Use fallback URL + } + + $tmpZip = Join-Path $env:TEMP "psmux_$(Get-Random).zip" + try { + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri $downloadUrl -OutFile $tmpZip -UseBasicParsing + $ProgressPreference = 'Continue' + + Add-Type -AssemblyName System.IO.Compression.FileSystem + $archive = [System.IO.Compression.ZipFile]::OpenRead($tmpZip) + foreach ($entry in $archive.Entries) { + if ($entry.Name.EndsWith(".exe", [System.StringComparison]::OrdinalIgnoreCase)) { + $destPath = Join-Path $LocalBin $entry.Name + [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $destPath, $true) + Success "Extracted $($entry.Name) to $LocalBin" + } + } + $archive.Dispose() + $installed = $true + } catch { + Warn "Failed to download psmux archive: $_" + } finally { + if (Test-Path $tmpZip) { Remove-Item $tmpZip -Force -ErrorAction SilentlyContinue } + } + } + + # Search for winget-installed *.exe files and copy them to $LocalBin $searchDirs = @( - (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Links'), (Join-Path $env:LOCALAPPDATA 'Programs\psmux'), (Join-Path $env:LOCALAPPDATA 'Programs\tmux-windows'), + (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Links'), 'C:\Program Files\psmux', 'C:\Program Files\tmux-windows', 'C:\Program Files (x86)\psmux', 'C:\Program Files (x86)\tmux-windows' ) - foreach ($dir in $searchDirs) { - if (Test-Path $dir) { - foreach ($exe in @('tmux.exe', 'psmux.exe')) { - $candidate = Join-Path $dir $exe - if (Test-Path $candidate) { $tmuxBin = $candidate; break } - } - } - if ($tmuxBin) { break } + + $wingetPackages = Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Packages' + if (Test-Path $wingetPackages) { + $pkgDirs = Get-ChildItem -Path $wingetPackages -Directory -Filter "*psmux*" -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName + if ($pkgDirs) { $searchDirs += $pkgDirs } } - # Recurse under WinGet/Packages for tmux or psmux - if (-not $tmuxBin -and (Test-Path (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Packages'))) { - foreach ($filter in @('tmux.exe', 'psmux.exe')) { - $found = Get-ChildItem -Path (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Packages') -Recurse -Filter $filter -ErrorAction SilentlyContinue | Select-Object -First 1 - if ($found) { $tmuxBin = $found.FullName; break } + foreach ($dir in $searchDirs) { + if (Test-Path $dir) { + $candidateExes = Get-ChildItem -Path $dir -Filter "*.exe" -ErrorAction SilentlyContinue + if ($candidateExes) { + foreach ($exe in $candidateExes) { + if ($exe.Name -in @('tmux.exe', 'psmux.exe', 'pmux.exe')) { + Copy-Item -Path $exe.FullName -Destination (Join-Path $LocalBin $exe.Name) -Force + Success "Copied $($exe.Name) to $LocalBin" + } + } + } } + if (Test-Path $localTmux) { break } + } + + # If tmux.exe is still not in $LocalBin but psmux.exe is, create tmux.exe copy + if (-not (Test-Path $localTmux) -and (Test-Path $localPsmux)) { + Copy-Item -Path $localPsmux -Destination $localTmux -Force + Success "Created tmux.exe alias from psmux.exe in $LocalBin" } } -if ($tmuxBin) { - # Add tmux's directory to PATH for this session - $tmuxDir = Split-Path $tmuxBin - if ($env:Path -notlike "*$tmuxDir*") { - $env:Path = "$tmuxDir;$env:Path" - Info "Added $tmuxDir to PATH for this session" - } - Success "tmux is available: $tmuxBin" +# ──────────────────────────────────────────── +# Verify tmux in $LocalBin and add $LocalBin to PATH permanently +# ──────────────────────────────────────────── +$tmuxBin = $null +if (Test-Path $localTmux) { + $tmuxBin = $localTmux +} elseif (Test-Path $localPsmux) { + $tmuxBin = $localPsmux +} - # Permanently add tmux's directory to user PATH if not already there - $userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User') - if ($userPath -notlike "*$tmuxDir*") { - [System.Environment]::SetEnvironmentVariable('Path', "$tmuxDir;$userPath", 'User') - Success "Added $tmuxDir to user PATH permanently (takes effect on next login)" - } else { - Info "$tmuxDir already in user PATH" +# Add $LocalBin to current session PATH +if ($env:Path -notlike "*$LocalBin*") { + $env:Path = "$LocalBin;$env:Path" + Info "Added $LocalBin to PATH for this session" +} + +# Permanently add $LocalBin to User PATH if not already present +$userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User') +if (-not $userPath) { $userPath = "" } +$pathParts = $userPath -split ';' | Where-Object { $_ -ne '' } +$normalizedLocalBin = $LocalBin.TrimEnd('\') +$alreadyInUserPath = $false +foreach ($p in $pathParts) { + if ($p.Trim().TrimEnd('\') -eq $normalizedLocalBin) { + $alreadyInUserPath = $true + break } +} + +if (-not $alreadyInUserPath) { + $newUserPath = if ($userPath) { "$normalizedLocalBin;$userPath" } else { $normalizedLocalBin } + [System.Environment]::SetEnvironmentVariable('Path', $newUserPath, 'User') + Success "Added $LocalBin to User PATH permanently" } else { - Warn "tmux was not found in PATH after installation." - Warn "Please restart your terminal or add tmux's directory to your PATH manually." + Info "$LocalBin is already in User PATH" +} + +if ($tmuxBin) { + Success "tmux is available: $tmuxBin" +} else { + Warn "tmux was not found in $LocalBin after installation." + Warn "Please check if winget or manual installation succeeded." } # ────────────────────────────────────────────