feat(launcher): complete Campaign LA11 pre-gate support

This commit is contained in:
Erik 2026-08-15 00:02:04 +02:00
parent f881e5b467
commit 134edabed2
19 changed files with 433 additions and 55 deletions

View file

@ -85,9 +85,11 @@ foreach ($release in @('A', 'B')) {
Require-PayloadFile "$release-client-win-x64" 'AcDream.App.exe'
Require-PayloadFile "$release-client-win-x64" 'acdream-headless.exe'
Require-PayloadFile "$release-launcher-win-x64" 'acdream-launcher.exe'
Require-PayloadFile "$release-launcher-win-x64" 'acdream-bake.exe'
Require-PayloadFile "$release-client-linux-x64" 'AcDream.App'
Require-PayloadFile "$release-client-linux-x64" 'acdream-headless'
Require-PayloadFile "$release-launcher-linux-x64" 'acdream-launcher'
Require-PayloadFile "$release-launcher-linux-x64" 'acdream-bake'
}
if (Test-Path -LiteralPath $OutputDirectory) {
@ -155,6 +157,7 @@ function New-DeterministicZip(
$executable = $relative -ceq 'AcDream.App' -or
$relative -ceq 'acdream-headless' -or
$relative -ceq 'acdream-launcher' -or
$relative -ceq 'acdream-bake' -or
$relative.EndsWith('.sh', [StringComparison]::Ordinal)
$mode = if ($executable) { 0x81ED } else { 0x81A4 }
$entry.ExternalAttributes = $mode -shl 16
@ -239,16 +242,26 @@ $server = @'
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$Root,
[Parameter(Mandatory = $true)][int]$Port
[Parameter(Mandatory = $true)][int]$Port,
[ValidateRange(0, 1000000)][int]$MaximumRequests = 0
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$expectedRoot = [IO.Path]::TrimEndingDirectorySeparator(
[IO.Path]::GetFullPath($PSScriptRoot))
$Root = [IO.Path]::TrimEndingDirectorySeparator([IO.Path]::GetFullPath($Root))
$pathComparison = if ($IsWindows) {
[StringComparison]::OrdinalIgnoreCase
} else { [StringComparison]::Ordinal }
if (-not [string]::Equals($Root, $expectedRoot, $pathComparison)) {
throw '-Root must be the directory containing serve-fixture.ps1.'
}
$prefix = "http://127.0.0.1:$Port/"
$listener = [Net.HttpListener]::new()
$listener.Prefixes.Add($prefix)
$listener.Start()
Write-Host "Campaign LA fixture listening on $prefix"
$servedRequests = 0
try {
while ($listener.IsListening) {
$context = $listener.GetContext()
@ -287,7 +300,13 @@ try {
$context.Response.StatusCode = 500
Write-Error $_
}
finally { $context.Response.Close() }
finally {
$context.Response.Close()
$servedRequests++
}
if ($MaximumRequests -gt 0 -and $servedRequests -ge $MaximumRequests) {
break
}
}
}
finally { $listener.Close() }
@ -301,8 +320,24 @@ param(
[string]$Root = $PSScriptRoot
)
Set-StrictMode -Version Latest
$path = Join-Path ([IO.Path]::GetFullPath($Root)) 'active-release.txt'
Set-Content -LiteralPath $path -Value $Release -Encoding ascii -NoNewline
$expectedRoot = [IO.Path]::TrimEndingDirectorySeparator(
[IO.Path]::GetFullPath($PSScriptRoot))
$Root = [IO.Path]::TrimEndingDirectorySeparator([IO.Path]::GetFullPath($Root))
$pathComparison = if ($IsWindows) {
[StringComparison]::OrdinalIgnoreCase
} else { [StringComparison]::Ordinal }
if (-not [string]::Equals($Root, $expectedRoot, $pathComparison)) {
throw '-Root must be the directory containing set-active-release.ps1.'
}
$path = Join-Path $Root 'active-release.txt'
$temporary = "$path.$([Guid]::NewGuid().ToString('N')).tmp"
try {
[IO.File]::WriteAllText($temporary, $Release, [Text.Encoding]::ASCII)
[IO.File]::Move($temporary, $path, $true)
}
finally {
if ([IO.File]::Exists($temporary)) { [IO.File]::Delete($temporary) }
}
Write-Host "Campaign LA fixture active release: $Release"
'@
$selector | Set-Content -LiteralPath (Join-Path $OutputDirectory 'set-active-release.ps1') -Encoding utf8NoBOM

View file

@ -68,16 +68,22 @@ $startedUtc = [DateTime]::UtcNow
function Protect-Text([string]$Text) {
if ($null -eq $Text) { return '' }
$protected = $Text
foreach ($name in @('ACDREAM_TEST_PASS', 'ACDREAM_LA_GATE_SECRET')) {
$value = [Environment]::GetEnvironmentVariable($name)
if (-not [string]::IsNullOrEmpty($value)) {
$protected = $protected.Replace($value, '<redacted>', [StringComparison]::Ordinal)
}
}
$protected = [Text.RegularExpressions.Regex]::Replace(
$protected,
'(?i)(--password|-password)(\s+|=)([^\s"'']+)',
'$1$2<redacted>')
$protected = [Text.RegularExpressions.Regex]::Replace(
$protected,
'(?i)\b(password|passwd|secret|token|credential|api[_-]?key)(\s*[:=]\s*)([^\s,;]+)',
'$1$2<redacted>')
$protected = [Text.RegularExpressions.Regex]::Replace(
$protected,
'(?i)(https?://)[^/\s:@]+:[^@\s/]+@',
'$1<redacted>@')
$protected = [Text.RegularExpressions.Regex]::Replace(
$protected,
'(?i)([?&](?:token|secret|password|credential|api[_-]?key)=)[^&\s]+',
'$1<redacted>')
$protected = [Text.RegularExpressions.Regex]::Replace(
$protected,
'(?i)("(?:password|credential|secret|token)"\s*:\s*")[^"]*(")',
@ -142,6 +148,11 @@ function Invoke-GateCommand(
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
foreach ($argument in $Arguments) { $startInfo.ArgumentList.Add($argument) }
foreach ($key in @($startInfo.Environment.Keys)) {
if ($key.StartsWith('ACDREAM_', [StringComparison]::OrdinalIgnoreCase)) {
$startInfo.Environment.Remove($key)
}
}
foreach ($entry in $Environment.GetEnumerator()) {
$startInfo.Environment[[string]$entry.Key] = [string]$entry.Value
}
@ -258,6 +269,34 @@ try {
'--', 'RunConfiguration.MaxCpuCount=1')
}
$headlessValidationConfig = Join-Path $OutputDirectory 'headless-k0.json'
Add-InternalCheck 'portable-headless-write-empty-config' {
[IO.File]::WriteAllText(
$headlessValidationConfig,
'{"version":1,"sessions":[]}',
[Text.UTF8Encoding]::new($false))
}
Invoke-DotNet 'portable-headless-help-no-connect' @(
'run', '--project', 'src/AcDream.Headless/AcDream.Headless.csproj',
'-c', 'Release', '--no-build', '--', '--help')
Invoke-DotNet 'portable-headless-validate-empty-no-connect' @(
'run', '--project', 'src/AcDream.Headless/AcDream.Headless.csproj',
'-c', 'Release', '--no-build', '--',
'validate', '--config', $headlessValidationConfig)
Add-InternalCheck 'portable-headless-native-permission' {
if (-not $IsWindows) {
$headlessExecutable = Join-Path `
$Repository 'src/AcDream.Headless/bin/Release/net10.0/acdream-headless'
if (-not (Test-Path -LiteralPath $headlessExecutable -PathType Leaf)) {
throw 'The native Headless build output is missing.'
}
$mode = [IO.File]::GetUnixFileMode($headlessExecutable)
if (($mode -band [IO.UnixFileMode]::UserExecute) -eq 0) {
throw 'The native Headless build output is not executable.'
}
}
}
foreach ($rid in @('win-x64', 'linux-x64')) {
$destination = Join-Path $publishDirectory $rid
Invoke-DotNet "publish-launcher-$rid" @(
@ -382,7 +421,8 @@ finally {
failures = @($failures)
redaction = [ordered]@{
applied = $true
environmentValuesNeverReported = @('ACDREAM_TEST_PASS', 'ACDREAM_LA_GATE_SECRET')
inheritedAcdreamEnvironmentCleared = $true
inheritedEnvironmentValuesRead = $false
credentialArgumentsAllowed = $false
}
artifacts = $artifacts

View file

@ -137,7 +137,7 @@ for ($lineIndex = 0; $lineIndex -lt $lines.Count; $lineIndex++) {
}
$eventName = Assert-String $root 'e'
if (-not $exactFields.ContainsKey($eventName)) {
throw "event '$eventName' is not in the v1 vocabulary"
throw 'event name is not in the v1 vocabulary'
}
$expected = $exactFields[$eventName]
if ($names.Count -ne $expected.Count -or
@ -221,7 +221,7 @@ for ($lineIndex = 0; $lineIndex -lt $lines.Count; $lineIndex++) {
if ($code -ne 0) { throw "terminal exit code is $code, expected 0" }
$expectedReason = if ($Mode -eq 'probe') { 'probe' } else { 'graceful' }
if ($reason -cne $expectedReason) {
throw "terminal reason is '$reason', expected '$expectedReason'"
throw "terminal reason does not match mode '$Mode'"
}
$terminalSeen = $true
}