$ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $version = "9.8.7-rc3" $root = Join-Path ([System.IO.Path]::GetTempPath()) ("dosh-installer-test-" + [guid]::NewGuid()) $webRoot = Join-Path $root "web" $repoRoot = Join-Path $webRoot "repo" $releaseRoot = Join-Path $repoRoot "releases\download\v$version" $rawRoot = Join-Path $repoRoot "raw\branch\main" $stageRoot = Join-Path $root "stage\dosh" $requestLog = Join-Path $root "requests.log" $prefix = Join-Path $root "prefix" $home = Join-Path $root "home" $unrelated = Join-Path $root "unrelated" $job = $null $savedBinaryVersion = $env:DOSH_BINARY_VERSION $savedBinaryExact = $env:DOSH_BINARY_EXACT function Invoke-TestInstaller($HomePath, $WorkingDirectory, $InstallPrefix, $Repository) { $savedHome = $env:HOME $savedUserProfile = $env:USERPROFILE try { $env:HOME = $HomePath $env:USERPROFILE = $HomePath Push-Location $WorkingDirectory try { $powershell = (Get-Process -Id $PID).Path & $powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "..\install.ps1") -Role client -Repo $Repository -Prefix $InstallPrefix -BinaryRequired if ($LASTEXITCODE -ne 0) { throw "Windows installer exited with status $LASTEXITCODE" } } finally { Pop-Location } } finally { $env:HOME = $savedHome $env:USERPROFILE = $savedUserProfile } } try { New-Item -ItemType Directory -Force -Path $releaseRoot, $rawRoot, (Join-Path $stageRoot "bin"), $home, $unrelated | Out-Null @" [package] name = "dosh" version = "$version" "@ | Set-Content -Encoding ascii -NoNewline (Join-Path $rawRoot "Cargo.toml") @" [package] name = "not-dosh" version = "99.0.0" "@ | Set-Content -Encoding ascii -NoNewline (Join-Path $unrelated "Cargo.toml") "$version`n" | Set-Content -Encoding ascii -NoNewline (Join-Path $stageRoot "VERSION") "fixture" | Set-Content -Encoding ascii -NoNewline (Join-Path $stageRoot "bin\dosh-client.exe") $archive = Join-Path $releaseRoot "dosh-windows-x86_64.zip" Compress-Archive -Force -Path $stageRoot -DestinationPath $archive $digest = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant() "$digest dosh-windows-x86_64.zip`n" | Set-Content -Encoding ascii -NoNewline "$archive.sha256" $probe = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0) $probe.Start() $port = ([System.Net.IPEndPoint]$probe.LocalEndpoint).Port $probe.Stop() $listenPrefix = "http://127.0.0.1:$port/" $job = Start-Job -ArgumentList $webRoot, $listenPrefix, $requestLog -ScriptBlock { param($Root, $Prefix, $Log) $listener = [System.Net.HttpListener]::new() $listener.Prefixes.Add($Prefix) $listener.Start() try { while ($listener.IsListening) { $context = $listener.GetContext() $path = [System.Uri]::UnescapeDataString($context.Request.Url.AbsolutePath.TrimStart('/')) Add-Content -Encoding ascii -Path $Log -Value $context.Request.Url.AbsolutePath $file = Join-Path $Root ($path.Replace('/', [System.IO.Path]::DirectorySeparatorChar)) if (Test-Path -LiteralPath $file -PathType Leaf) { $bytes = [System.IO.File]::ReadAllBytes($file) $context.Response.StatusCode = 200 $context.Response.ContentLength64 = $bytes.Length if ($context.Request.HttpMethod -ne "HEAD") { $context.Response.OutputStream.Write($bytes, 0, $bytes.Length) } } else { $context.Response.StatusCode = 404 } $context.Response.Close() } } finally { $listener.Stop() } } $ready = $false for ($attempt = 0; $attempt -lt 50; $attempt++) { try { Invoke-WebRequest -UseBasicParsing -Uri "${listenPrefix}repo/raw/branch/main/Cargo.toml" | Out-Null $ready = $true break } catch { Start-Sleep -Milliseconds 100 } } if (-not $ready) { throw "installer fixture HTTP server did not start" } $env:DOSH_BINARY_VERSION = "v0.1.17" Remove-Item Env:DOSH_BINARY_EXACT -ErrorAction SilentlyContinue Invoke-TestInstaller $home $unrelated $prefix "${listenPrefix}repo.git" if (-not (Test-Path (Join-Path $prefix "bin\dosh-client.exe"))) { throw "Windows installer did not install dosh-client.exe" } $requests = Get-Content $requestLog -Raw if ($requests -notmatch "/repo/raw/branch/main/Cargo.toml") { throw "Windows installer did not read the repository release version" } if ($requests -notmatch "/repo/releases/download/v$([regex]::Escape($version))/dosh-windows-x86_64.zip") { throw "Windows installer did not request the repository-declared release artifact" } if ($requests -match "/releases/latest" -or $requests -match "99.0.0") { throw "Windows installer used a stable redirect or unrelated Cargo.toml" } "fixture-v2" | Set-Content -Encoding ascii -NoNewline (Join-Path $stageRoot "bin\dosh-client.exe") Compress-Archive -Force -Path $stageRoot -DestinationPath $archive $digest = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant() "$digest dosh-windows-x86_64.zip`n" | Set-Content -Encoding ascii -NoNewline "$archive.sha256" $installedAlias = Join-Path $prefix "bin\dosh.exe" $lock = [System.IO.File]::Open($installedAlias, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::None) try { Invoke-TestInstaller $home $unrelated $prefix "${listenPrefix}repo.git" $pending = @(Get-ChildItem (Join-Path $prefix "bin") -Filter "dosh.exe.pending.*") if ($pending.Count -ne 1) { throw "locked Windows executable was not staged exactly once" } } finally { $lock.Dispose() } $replaced = $false for ($attempt = 0; $attempt -lt 100; $attempt++) { if ((Get-Content $installedAlias -Raw) -eq "fixture-v2" -and -not (Get-ChildItem (Join-Path $prefix "bin") -Filter "dosh.exe.pending.*")) { $replaced = $true break } Start-Sleep -Milliseconds 100 } if (-not $replaced) { throw "staged Windows executable was not applied after its lock was released" } Write-Host "Windows installer runtime test passed" } finally { $env:DOSH_BINARY_VERSION = $savedBinaryVersion $env:DOSH_BINARY_EXACT = $savedBinaryExact if ($job) { Stop-Job $job -ErrorAction SilentlyContinue Remove-Job $job -Force -ErrorAction SilentlyContinue } Remove-Item -Recurse -Force $root -ErrorAction SilentlyContinue }