Something went wrong. Try again.
Personal fork of prime agent. Mirror of: https://mnemosyne.sigilzero.dev/sigilzero/prime-agent
Something went wrong. Try again.
12 kB · 199 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200# Windows installer channel-selection regression: missing stable artifacts# must fail, explicit beta must work, and the production default stays stable.# Uses a local HTTP fixture with a stub payload; the Windows runtime battery# separately verifies the real executable and production one-liner.
$ErrorActionPreference = 'Stop'
$repo = (Get-Location).Path$scratch = Join-Path ([IO.Path]::GetTempPath()) ("prime-agent-channel-fallback-{0}" -f [Guid]::NewGuid().ToString('N'))New-Item -ItemType Directory -Path $scratch | Out-Null
# The Python interpreter for the local http server (the install e2e's pattern).$py = if (Get-Command python3 -ErrorAction SilentlyContinue) { 'python3' } else { 'python' }
$stableVersion = '9.9.9'$betaVersion = '9.9.9-beta.1'$platform = 'win32-x64'
# The stub artifact the beta release prefix serves: a tarball whose payload# is a placeholder exe (the install only checks the payload exists).$payload = Join-Path $scratch 'payload'New-Item -ItemType Directory -Path $payload | Out-NullSet-Content -LiteralPath (Join-Path $payload 'prime-agent.exe') -Value 'stub payload for the channel-fallback regression test'$artifactFile = "prime-agent-$betaVersion-$platform.tar.gz"$artifact = Join-Path $scratch $artifactFile& tar -czf $artifact -C $payload prime-agent.exeif ($LASTEXITCODE -ne 0) { throw 'the stub artifact build failed (tar)' }$artifactSha = (Get-FileHash -LiteralPath $artifact -Algorithm SHA256).Hash.ToLower()
# Start with a missing stable artifact and an available beta artifact.$channel = Join-Path $scratch 'channel'$releaseDir = Join-Path $channel "releases\v$betaVersion"New-Item -ItemType Directory -Path $releaseDir -Force | Out-NullMove-Item -LiteralPath $artifact -Destination $releaseDirSet-Content -LiteralPath (Join-Path $releaseDir 'SHA256SUMS') -Value "$artifactSha $artifactFile"Set-Content -LiteralPath (Join-Path $channel 'stable') -Value $stableVersion -NoNewlineSet-Content -LiteralPath (Join-Path $channel 'latest.json') -Value ('{"version": "v' + $stableVersion + '", "binaries": [], "binaries_v2": []}')Set-Content -LiteralPath (Join-Path $channel 'beta') -Value $betaVersion -NoNewline$betaRow = '{"platform": "' + $platform + '", "file": "' + $artifactFile + '", "sha256": "' + $artifactSha + '"}'Set-Content -LiteralPath (Join-Path $channel 'beta.json') -Value ('{"version": "v' + $betaVersion + '", "binaries": [' + $betaRow + '], "binaries_v2": [' + $betaRow + ']}')
# One install run under a given channel knob, with the transcript captured.# It dials the channel server's $baseUrl, which the try block below learns# from the server itself.function Invoke-Installer { param([string]$ChannelKnob, [string]$Prefix) Remove-Item -Path 'Env:PRIME_AGENT_DOWNLOAD_BASE_URL', 'Env:PRIME_AGENT_RELEASE_CHANNEL', 'Env:PRIME_AGENT_RUST_PREFIX', 'Env:PRIME_AGENT_ALLOW_HTTP', 'Env:PRIME_AGENT_VERSION' -ErrorAction SilentlyContinue $env:PRIME_AGENT_DOWNLOAD_BASE_URL = $baseUrl $env:PRIME_AGENT_ALLOW_HTTP = '1' $env:PRIME_AGENT_RUST_PREFIX = $Prefix if ($ChannelKnob) { $env:PRIME_AGENT_RELEASE_CHANNEL = $ChannelKnob } $lines = @(& pwsh -NoProfile -File (Join-Path $repo 'install.ps1') 2>&1 | ForEach-Object { "$_" }) return [pscustomobject]@{ Lines = $lines; Exit = $LASTEXITCODE }}
# The server picks and holds its own port (http.server on port 0: the OS# hands the port out atomically, so no other process can claim it) and# announces it on stdout; -u flushes the announcement immediately, and the# test reads it back from the log.$serverLog = Join-Path $scratch 'channel-server.log'$server = Start-Process -FilePath $py -ArgumentList '-u','-m','http.server','0','--bind','127.0.0.1','--directory',$channel -PassThru -WindowStyle Hidden -RedirectStandardOutput $serverLog# install.ps1 writes the User PATH (the PATH-parity flow) and installs uv# when the machine has none (the astral route); the harness strips exactly# its own scratch-prefixed PATH entries in the cleanup below.# THE UV INSTALL IS ISOLATED (the reviewers' finding): ownership of a file# in the shared ~/.local/bin is unprovable, so the harness never lets the# installer touch the shared dir - install.ps1 honors# PRIME_AGENT_UV_BIN_DIR and the harness points it at its scratch.# The kernel pre-warm's writes (the venv, uv's cache, uv's pythons) would# land in the real user profile too; the harness steers all of them into# its own scratch dir through the product's override knobs and restores# the caller's values in the cleanup (the discipline install.ps1 itself# runs).$callerKernelVenv = $env:PRIME_AGENT_KERNEL_VENV$callerUvCacheDir = $env:UV_CACHE_DIR$callerUvPythonDir = $env:UV_PYTHON_INSTALL_DIR$callerUvBinDir = $env:PRIME_AGENT_UV_BIN_DIRtry { $env:PRIME_AGENT_KERNEL_VENV = Join-Path $scratch 'kernel-venv' $env:UV_CACHE_DIR = Join-Path $scratch 'uv-cache' $env:UV_PYTHON_INSTALL_DIR = Join-Path $scratch 'uv-python' $env:PRIME_AGENT_UV_BIN_DIR = Join-Path $scratch 'uv-bin' # The port the server itself announced, then readiness is it answering # a request for this test's own channel (beta.json): bounded deadlines, # and a dead child fails fast instead of hanging the installer. $deadline = (Get-Date).AddSeconds(30) $port = $null while ($null -eq $port) { if ($server.HasExited) { throw "the local channel server exited early" } if ((Get-Date) -gt $deadline) { throw "the local channel server did not announce its port within 30s" } $announced = Select-String -LiteralPath $serverLog -Pattern 'port (\d+)' -ErrorAction SilentlyContinue | Select-Object -First 1 if ($announced) { $port = [int]$announced.Matches[0].Groups[1].Value } if ($null -eq $port) { Start-Sleep -Milliseconds 100 } } $baseUrl = "http://127.0.0.1:$port" $ready = $false while (-not $ready) { if ($server.HasExited) { throw "the local channel server exited early (port $port)" } if ((Get-Date) -gt $deadline) { throw "the local channel server did not answer on $baseUrl within 30s" } try { $probe = Invoke-WebRequest -Uri "$baseUrl/beta.json" -TimeoutSec 2 if ($probe.StatusCode -eq 200) { $ready = $true } } catch { Start-Sleep -Milliseconds 100 } }
foreach ($selection in @('default', 'stable')) { $prefix = Join-Path $scratch "prefix-missing-$selection" $knob = if ($selection -eq 'default') { $null } else { $selection } $run = Invoke-Installer -ChannelKnob $knob -Prefix $prefix $transcript = $run.Lines -join [Environment]::NewLine if ($run.Exit -eq 0 -or $transcript -notmatch [regex]::Escape('no artifact row for platform win32-x64 in the stable manifest')) { $run.Lines | Write-Host throw "$selection must refuse the missing stable artifact" } if (Test-Path (Join-Path $prefix 'share\prime-agent\prime-agent.exe')) { throw "$selection unexpectedly published a payload" } }
# Explicit beta succeeds independently of the incomplete stable release. $prefixBeta = Join-Path $scratch 'prefix-beta' $runBeta = Invoke-Installer -ChannelKnob 'beta' -Prefix $prefixBeta if ($runBeta.Exit -ne 0) { $runBeta.Lines | Write-Host throw 'the explicit beta install failed' } $marker = Get-Content -LiteralPath (Join-Path $prefixBeta 'share\prime-agent\.prime-agent-install') -Raw if ($marker -ne "install-rust.sh channel beta`nversion $betaVersion") { throw "unexpected beta marker: $marker" }
# Publish stable and verify the production default selects its artifact. $stableFile = "prime-agent-$stableVersion-$platform.tar.gz" $stableDir = Join-Path $channel "releases\v$stableVersion" New-Item -ItemType Directory -Path $stableDir -Force | Out-Null Copy-Item -LiteralPath (Join-Path $releaseDir $artifactFile) -Destination (Join-Path $stableDir $stableFile) Set-Content -LiteralPath (Join-Path $stableDir 'SHA256SUMS') -Value "$artifactSha $stableFile" $stableRow = '{"platform": "' + $platform + '", "file": "' + $stableFile + '", "sha256": "' + $artifactSha + '"}' Set-Content -LiteralPath (Join-Path $channel 'latest.json') -Value ('{"version": "v' + $stableVersion + '", "binaries": [' + $stableRow + '], "binaries_v2": [' + $stableRow + ']}') $prefixStable = Join-Path $scratch 'prefix-stable' $runStable = Invoke-Installer -ChannelKnob $null -Prefix $prefixStable if ($runStable.Exit -ne 0) { $runStable.Lines | Write-Host throw 'the default stable install failed' } $marker = Get-Content -LiteralPath (Join-Path $prefixStable 'share\prime-agent\.prime-agent-install') -Raw if ($marker -ne "install-rust.sh channel stable`nversion $stableVersion") { throw "unexpected stable marker: $marker" } foreach ($prefix in @($prefixBeta, $prefixStable)) { if (-not (Test-Path (Join-Path $prefix 'share\prime-agent\prime-agent.exe') -PathType Leaf)) { throw "missing payload in $prefix" } }
Write-Host "WIN_CHANNEL_SELECTION default=$stableVersion explicit-beta=$betaVersion; missing stable artifacts refused"} finally { Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue # The User PATH: strip ONLY the entries this test added - the ones # under its own scratch dir - from the CURRENT registry value, never # a stale snapshot restore (a whole-value overwrite would clobber any # external PATH change made while the test ran) and never through # [Environment]::SetEnvironmentVariable (it flattens a REG_EXPAND_SZ # Path to plain REG_SZ with this run's expansion frozen in). The raw # value rides out with its registry kind intact; a Path this test # created from nothing is deleted again; a Path it never touched is # not rewritten at all. A cleanup failure is recorded and the # remaining steps still run (the loud tail below reports it). $pathCleanFailed = $false $envKey = $null try { $envKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $true) $rawUserPath = $envKey.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) $rawUserKind = [Microsoft.Win32.RegistryValueKind]::ExpandString if ($envKey.GetValueNames() -contains 'Path') { $rawUserKind = $envKey.GetValueKind('Path') } $allEntries = @($rawUserPath -split ';') $keptEntries = @($allEntries | Where-Object { -not $_.Trim().StartsWith($scratch, [System.StringComparison]::OrdinalIgnoreCase) }) if ($keptEntries.Count -lt $allEntries.Count) { if ($keptEntries.Count -gt 0) { $envKey.SetValue('Path', ($keptEntries -join ';'), $rawUserKind) } else { $envKey.DeleteValue('Path', $false) } } } catch { $pathCleanFailed = $true } finally { if ($envKey) { $envKey.Close() } } # The uv install needs no cleanup: it never left the scratch dir (the # PRIME_AGENT_UV_BIN_DIR knob above steered the installer), and the # user's ~/.local/bin was never touched at all. $env:PRIME_AGENT_KERNEL_VENV = $callerKernelVenv $env:UV_CACHE_DIR = $callerUvCacheDir $env:UV_PYTHON_INSTALL_DIR = $callerUvPythonDir $env:PRIME_AGENT_UV_BIN_DIR = $callerUvBinDir Remove-Item -Recurse -Force $scratch -ErrorAction SilentlyContinue if ($pathCleanFailed) { throw 'could not clean the user PATH entries this test added' }}