# https://penguin.ooo/install.ps1 - PenguinHarness installer entry point for Windows. # # GitHub Pages cannot serve HTTP redirects, so this thin forwarder IS the # stable install URL. It selects an immutable OSS release when that mirror is # available, otherwise it falls back to the matching GitHub Release, then runs # the real installer while forwarding every argument it was given. Usage: # # irm https://penguin.ooo/install.ps1 | iex # & ([scriptblock]::Create((irm https://penguin.ooo/install.ps1))) -Version v0.2.0 # & { $ForwardedArgs = @($args) $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $OssOrigin = "https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com" $OssReleaseRoot = "$OssOrigin/releases" $GitHubReleaseRoot = "https://github.com/Prism-Shadow/penguin-harness/releases/download" $GitHubLatestBase = "https://github.com/Prism-Shadow/penguin-harness/releases/latest/download" function Fail([string]$Message) { throw "error: $Message" } function Test-HttpsUrl([string]$Value) { try { $Uri = [Uri]$Value } catch { return $false } return $Uri.IsAbsoluteUri -and $Uri.Scheme -eq "https" } function Test-ReleaseTag([string]$Value) { return $Value -match '^v[0-9A-Za-z][0-9A-Za-z._-]*$' } function Try-DownloadFile([string]$Uri, [string]$OutFile, [int]$TimeoutSec) { try { Invoke-WebRequest -Uri $Uri -OutFile $OutFile -UseBasicParsing -TimeoutSec $TimeoutSec return $true } catch { Remove-Item -LiteralPath $OutFile -Force -ErrorAction SilentlyContinue return $false } } try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { # .NET builds where the enum is immutable already default to TLS 1.2+. } $SourceMode = if ($env:PENGUIN_DOWNLOAD_SOURCE) { $env:PENGUIN_DOWNLOAD_SOURCE.ToLowerInvariant() } else { "auto" } if ($SourceMode -notin @("auto", "oss", "github")) { Fail "PENGUIN_DOWNLOAD_SOURCE must be auto, oss, or github" } $RequestedVersion = if ($env:PENGUIN_VERSION) { $env:PENGUIN_VERSION } else { "" } for ($i = 0; $i -lt $ForwardedArgs.Count; $i++) { if ([string]$ForwardedArgs[$i] -ieq "-Version" -and $i + 1 -lt $ForwardedArgs.Count) { $RequestedVersion = [string]$ForwardedArgs[$i + 1] $i++ } } if ($RequestedVersion -and -not (Test-ReleaseTag $RequestedVersion)) { Fail "invalid release version: $RequestedVersion" } # Download fully first, then run: executing a piped stream directly would run a # truncated download line by line, and the real installer moves the old # bin/lib/web/node aside before moving the new ones in - a cut connection # mid-way must never leave a half-executed installer. The installer runs as an # in-memory script block (not a script file): script files are subject to the # execution policy, which is Restricted by default on client Windows - while the # user has already consented to remote code by piping this forwarder into iex. # Neither this forwarder nor the installer calls `exit`, which in iex/script-block # context would terminate the user's whole PowerShell session. $TmpDir = Join-Path ([IO.Path]::GetTempPath()) "penguin-forwarder-$PID" $InstallerPath = Join-Path $TmpDir "install.ps1" $ManifestPath = Join-Path $TmpDir "latest.json" $SelectedBase = "" $FallbackBase = "" $OriginalBase = [Environment]::GetEnvironmentVariable("PENGUIN_DOWNLOAD_BASE_URL", "Process") $OriginalFallback = [Environment]::GetEnvironmentVariable("PENGUIN_DOWNLOAD_FALLBACK_BASE_URL", "Process") try { if (Test-Path -LiteralPath $TmpDir) { Remove-Item -LiteralPath $TmpDir -Recurse -Force } New-Item -ItemType Directory -Path $TmpDir | Out-Null if ($OriginalBase) { $SelectedBase = $OriginalBase.TrimEnd('/') $FallbackBase = if ($OriginalFallback) { $OriginalFallback.TrimEnd('/') } else { "" } if (-not (Test-HttpsUrl $SelectedBase)) { Fail "PENGUIN_DOWNLOAD_BASE_URL must be an absolute HTTPS URL" } if ($FallbackBase -and -not (Test-HttpsUrl $FallbackBase)) { Fail "PENGUIN_DOWNLOAD_FALLBACK_BASE_URL must be an absolute HTTPS URL" } if (-not (Try-DownloadFile "$SelectedBase/install.ps1" $InstallerPath 30)) { Fail "could not download the installer from the configured mirror." } } elseif ($SourceMode -eq "github") { $SelectedBase = if ($RequestedVersion) { "$GitHubReleaseRoot/$RequestedVersion" } else { $GitHubLatestBase } if (-not (Try-DownloadFile "$SelectedBase/install.ps1" $InstallerPath 30)) { Fail "could not download the installer from GitHub. Check your network, then retry." } } else { $OssTag = $RequestedVersion $OssBase = if ($OssTag) { "$OssReleaseRoot/$OssTag" } else { "" } if (-not $OssTag -and (Try-DownloadFile "$OssOrigin/latest.json" $ManifestPath 8)) { try { $Manifest = [IO.File]::ReadAllText($ManifestPath, [Text.UTF8Encoding]::new($false)) | ConvertFrom-Json $CandidateTag = [string]$Manifest.tag $CandidateBase = ([string]$Manifest.releaseBaseUrl).TrimEnd('/') if ([int]$Manifest.schemaVersion -eq 1 -and (Test-ReleaseTag $CandidateTag) -and $CandidateBase -eq "$OssReleaseRoot/$CandidateTag") { $OssTag = $CandidateTag $OssBase = $CandidateBase } } catch { $OssTag = "" $OssBase = "" } } if ($OssBase -and (Try-DownloadFile "$OssBase/install.ps1" $InstallerPath 30)) { $SelectedBase = $OssBase if ($SourceMode -eq "auto") { $FallbackBase = "$GitHubReleaseRoot/$OssTag" } } elseif ($SourceMode -eq "oss") { Fail "the OSS mirror is unavailable or its release metadata is invalid." } else { $SelectedBase = if ($OssTag) { "$GitHubReleaseRoot/$OssTag" } else { $GitHubLatestBase } if (-not (Try-DownloadFile "$SelectedBase/install.ps1" $InstallerPath 30)) { Fail "could not download the installer from GitHub. Check your network, then retry." } } } $env:PENGUIN_DOWNLOAD_BASE_URL = $SelectedBase if ($FallbackBase) { $env:PENGUIN_DOWNLOAD_FALLBACK_BASE_URL = $FallbackBase } else { Remove-Item Env:\PENGUIN_DOWNLOAD_FALLBACK_BASE_URL -ErrorAction SilentlyContinue } $InstallerText = [IO.File]::ReadAllText($InstallerPath, [Text.UTF8Encoding]::new($false)) $Installer = [scriptblock]::Create($InstallerText) & $Installer @ForwardedArgs } finally { if ($null -eq $OriginalBase) { Remove-Item Env:\PENGUIN_DOWNLOAD_BASE_URL -ErrorAction SilentlyContinue } else { $env:PENGUIN_DOWNLOAD_BASE_URL = $OriginalBase } if ($null -eq $OriginalFallback) { Remove-Item Env:\PENGUIN_DOWNLOAD_FALLBACK_BASE_URL -ErrorAction SilentlyContinue } else { $env:PENGUIN_DOWNLOAD_FALLBACK_BASE_URL = $OriginalFallback } Remove-Item -LiteralPath $TmpDir -Recurse -Force -ErrorAction SilentlyContinue } } @args