<# .SYNOPSIS EasyTier Windows Installer .DESCRIPTION Download EasyTier from GitHub Release and install it. Copies binaries to the install directory and updates the system PATH. .PARAMETER Version Target version: "latest", "stable", or a specific tag like "v2.6.1". Default: "latest" .PARAMETER InstallDir Directory to install EasyTier binaries. Default: "$env:ProgramFiles\EasyTier" .EXAMPLE .\install.ps1 .\install.ps1 -Version v2.6.1 .\install.ps1 -InstallDir "C:\EasyTier" .NOTES Administrator privileges are required. After installation, run: easytier-cli service install to register EasyTier as a system service. #> param( [Parameter(Position = 0)] [ValidatePattern('^(stable|latest|v?\d+\.\d+\.\d+(-[^\s]+)?)$')] [string]$Version = 'latest', [Parameter(Position = 1)] [string]$InstallDir = "$env:ProgramFiles\EasyTier" ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' # Force TLS 1.2+ for GitHub API and download requests [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 # --------------------------------------------------------------------------- # Constants # --------------------------------------------------------------------------- $GITHUB_REPO = 'EasyTier/EasyTier' $GITHUB_API = "https://api.github.com/repos/$GITHUB_REPO" $GITHUB_RELEASE_URL = "https://github.com/$GITHUB_REPO/releases" # --------------------------------------------------------------------------- # Administrator check # --------------------------------------------------------------------------- $currentPrincipal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Error 'Please run this script as Administrator.' exit 1 } # --------------------------------------------------------------------------- # Architecture detection # --------------------------------------------------------------------------- # Check PROCESSOR_ARCHITEW6432 first to correctly identify 64-bit OS when # running under 32-bit PowerShell (WoW64), where PROCESSOR_ARCHITECTURE # reports 'x86' even on a 64-bit machine. $cpuArch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } switch ($cpuArch) { 'AMD64' { $arch = 'x86_64' } 'ARM64' { $arch = 'arm64' } 'x86' { $arch = 'i686' } default { Write-Error "Unsupported processor architecture: $cpuArch" exit 1 } } $assetBaseName = "easytier-windows-$arch" Write-Host '' Write-Host ' ===============================================' -ForegroundColor Cyan Write-Host ' EasyTier Windows Installer ' -ForegroundColor Cyan Write-Host ' ===============================================' -ForegroundColor Cyan Write-Host '' Write-Host " Architecture : $arch" -ForegroundColor White Write-Host '' # --------------------------------------------------------------------------- # Step 1 - Resolve release version # --------------------------------------------------------------------------- Write-Host '[1/5] Querying GitHub Release info...' -ForegroundColor Yellow try { if ($Version -eq 'latest' -or $Version -eq 'stable') { $releaseInfo = Invoke-RestMethod ` -Uri "$GITHUB_API/releases/latest" ` -Headers @{ 'User-Agent' = 'EasyTier-Installer/1.0' } ` -ErrorAction Stop } else { $tag = if ($Version -notmatch '^v') { "v$Version" } else { $Version } $releaseInfo = Invoke-RestMethod ` -Uri "$GITHUB_API/releases/tags/$tag" ` -Headers @{ 'User-Agent' = 'EasyTier-Installer/1.0' } ` -ErrorAction Stop } } catch { Write-Error "Failed to fetch release info from GitHub: $_`nPlease check your network or visit $GITHUB_RELEASE_URL to download manually." exit 1 } $releaseVersion = $releaseInfo.tag_name $assetZipName = "$assetBaseName-$releaseVersion.zip" Write-Host " Version : $releaseVersion" -ForegroundColor Green # --------------------------------------------------------------------------- # Step 2 - Find download URL # --------------------------------------------------------------------------- Write-Host '' Write-Host '[2/5] Resolving download URL...' -ForegroundColor Yellow $asset = $releaseInfo.assets | Where-Object { $_.name -eq $assetZipName } | Select-Object -First 1 if (-not $asset) { $availableAssets = ($releaseInfo.assets | Select-Object -ExpandProperty name) -join ', ' Write-Error "Asset '$assetZipName' not found in release $releaseVersion.`nAvailable: $availableAssets`nVisit $GITHUB_RELEASE_URL to download manually." exit 1 } $downloadUrl = $asset.browser_download_url Write-Host " URL : $downloadUrl" -ForegroundColor DarkGray # --------------------------------------------------------------------------- # Step 3 - Download ZIP # --------------------------------------------------------------------------- Write-Host '' Write-Host "[3/5] Downloading $assetZipName ..." -ForegroundColor Yellow $tempDir = Join-Path $env:TEMP "easytier-install-$(Get-Random)" $zipPath = Join-Path $tempDir $assetZipName New-Item -ItemType Directory -Force -Path $tempDir | Out-Null try { Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -ErrorAction Stop $sizeMB = [math]::Round((Get-Item $zipPath).Length / 1MB, 2) Write-Host " Download complete ($sizeMB MB)" -ForegroundColor Green } catch { Write-Error "Download failed: $_" Remove-Item -Recurse -Force $tempDir -ErrorAction SilentlyContinue exit 1 } # --------------------------------------------------------------------------- # Step 4 - Extract & copy to install directory # --------------------------------------------------------------------------- Write-Host '' Write-Host '[4/5] Extracting and copying files...' -ForegroundColor Yellow $extractDir = Join-Path $tempDir 'extracted' New-Item -ItemType Directory -Force -Path $extractDir | Out-Null try { Expand-Archive -Path $zipPath -DestinationPath $extractDir -Force -ErrorAction Stop } catch { Write-Error "Extraction failed: $_" Remove-Item -Recurse -Force $tempDir -ErrorAction SilentlyContinue exit 1 } # ZIP may contain a sub-directory; find exe files recursively and flatten $exeFiles = Get-ChildItem -Path $extractDir -Filter '*.exe' -Recurse if (-not $exeFiles) { Remove-Item -Recurse -Force $tempDir -ErrorAction SilentlyContinue Write-Error 'No .exe files found after extraction. The ZIP may be malformed.' exit 1 } $binSourceDir = $exeFiles[0].DirectoryName try { New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null Get-ChildItem -Path $binSourceDir | Copy-Item -Destination $InstallDir -Force } catch { Remove-Item -Recurse -Force $tempDir -ErrorAction SilentlyContinue Write-Error "Failed to copy files to install directory: $_" exit 1 } Write-Host " Installed to: $InstallDir" -ForegroundColor Green # --------------------------------------------------------------------------- # Step 5 - Update system PATH # --------------------------------------------------------------------------- Write-Host '' Write-Host '[5/5] Updating system PATH...' -ForegroundColor Yellow $systemPath = [Environment]::GetEnvironmentVariable('PATH', 'Machine') # Split on ';' and normalize (trim trailing backslash, case-insensitive) for an exact match $pathEntries = $systemPath -split ';' | ForEach-Object { $_.TrimEnd('\') } $normalizedInstallDir = $InstallDir.TrimEnd('\') if ($pathEntries -inotcontains $normalizedInstallDir) { [Environment]::SetEnvironmentVariable('PATH', "$systemPath;$InstallDir", 'Machine') $env:PATH = "$env:PATH;$InstallDir" Write-Host " Added $InstallDir to system PATH" -ForegroundColor Green } else { Write-Host " $InstallDir is already in PATH, skipping" -ForegroundColor DarkGray } # --------------------------------------------------------------------------- # Cleanup # --------------------------------------------------------------------------- try { Remove-Item -Recurse -Force $tempDir -ErrorAction SilentlyContinue } catch { Write-Warning "Could not remove temp dir $tempDir : $_" } # --------------------------------------------------------------------------- # Done # --------------------------------------------------------------------------- Write-Host '' Write-Host " [OK] EasyTier $releaseVersion installation complete!" -ForegroundColor Green Write-Host '' Write-Host " Install dir : $InstallDir" -ForegroundColor White Write-Host ' User guide : https://easytier.cn/en/guide/network/decentralized-networking.html' -ForegroundColor DarkGray Write-Host '' Write-Host ' NOTE: If PATH was just updated, please restart your terminal.' -ForegroundColor DarkYellow Write-Host ''