[CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Candidate, [Parameter(Mandatory = $true)] [string]$ExpectedPredecessorSha256, [Parameter(Mandatory = $true)] [string]$ExpectedCandidateSha256, [Parameter(Mandatory = $true)] [string]$DockerNamesCandidate, [Parameter(Mandatory = $true)] [string]$ExpectedDockerNamesSha256, [string]$ExpectedDockerNamesPredecessorSha256 = "absent", [string]$RunnerRoot = "D:\NDC_MISSIONCORE\runtime\derived\e23-runner-20260724-002" ) $ErrorActionPreference = "Stop" $target = Join-Path $RunnerRoot "Invoke-E15PersistentShadowRun.ps1" $dockerNamesTarget = Join-Path $RunnerRoot "MissionCoreDockerNames.ps1" $expectedPredecessor = $ExpectedPredecessorSha256.ToLowerInvariant() $expectedCandidate = $ExpectedCandidateSha256.ToLowerInvariant() $expectedDockerNames = $ExpectedDockerNamesSha256.ToLowerInvariant() $expectedDockerNamesPredecessor = ( $ExpectedDockerNamesPredecessorSha256.ToLowerInvariant() ) $predecessor = ( Get-FileHash -Algorithm SHA256 -LiteralPath $target ).Hash.ToLowerInvariant() if ($predecessor -ne $expectedPredecessor) { throw "Persistent launcher predecessor digest changed" } if ( (Get-FileHash -Algorithm SHA256 -LiteralPath $Candidate).Hash.ToLowerInvariant() ` -ne $expectedCandidate ) { throw "Persistent launcher candidate digest changed" } $dockerNamesCandidateDigest = ( Get-FileHash -Algorithm SHA256 -LiteralPath $DockerNamesCandidate ).Hash.ToLowerInvariant() if ($dockerNamesCandidateDigest -ne $expectedDockerNames) { throw "Docker names candidate digest changed" } $hadDockerNames = Test-Path -LiteralPath $dockerNamesTarget -PathType Leaf $dockerNamesPredecessor = if ($hadDockerNames) { ( Get-FileHash -Algorithm SHA256 -LiteralPath $dockerNamesTarget ).Hash.ToLowerInvariant() } else { "absent" } if ($dockerNamesPredecessor -ne $expectedDockerNamesPredecessor) { throw "Docker names predecessor digest changed" } $backup = Join-Path $RunnerRoot ( "Invoke-E15PersistentShadowRun.ps1.rollback-" + [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssZ") + "-" + $predecessor.Substring(0, 12) ) $dockerNamesBackup = Join-Path $RunnerRoot ( "MissionCoreDockerNames.ps1.rollback-" + [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssZ") + "-" + $dockerNamesPredecessor.Substring( 0, [Math]::Min(12, $dockerNamesPredecessor.Length) ) ) Copy-Item -LiteralPath $target -Destination $backup if ($hadDockerNames) { Copy-Item -LiteralPath $dockerNamesTarget -Destination $dockerNamesBackup } try { Copy-Item -LiteralPath $DockerNamesCandidate ` -Destination $dockerNamesTarget -Force Copy-Item -LiteralPath $Candidate -Destination $target -Force $installed = ( Get-FileHash -Algorithm SHA256 -LiteralPath $target ).Hash.ToLowerInvariant() if ($installed -ne $expectedCandidate) { throw "Persistent launcher replacement digest changed" } $installedDockerNames = ( Get-FileHash -Algorithm SHA256 -LiteralPath $dockerNamesTarget ).Hash.ToLowerInvariant() if ($installedDockerNames -ne $expectedDockerNames) { throw "Docker names replacement digest changed" } foreach ($scriptPath in @($dockerNamesTarget, $target)) { $tokens = $null $parseErrors = $null [Management.Automation.Language.Parser]::ParseFile( $scriptPath, [ref]$tokens, [ref]$parseErrors ) | Out-Null if (@($parseErrors).Count -ne 0) { throw "Persistent launcher deployment has PowerShell parse errors" } } } catch { Copy-Item -LiteralPath $backup -Destination $target -Force if ($hadDockerNames) { Copy-Item -LiteralPath $dockerNamesBackup ` -Destination $dockerNamesTarget -Force } else { Remove-Item -LiteralPath $dockerNamesTarget ` -Force -ErrorAction SilentlyContinue } throw } [ordered]@{ SchemaVersion = "missioncore.worker-persistent-launcher-deploy-result/v1" PredecessorSha256 = $predecessor CandidateSha256 = ( Get-FileHash -Algorithm SHA256 -LiteralPath $target ).Hash.ToLowerInvariant() DockerNamesPredecessorSha256 = $dockerNamesPredecessor DockerNamesCandidateSha256 = ( Get-FileHash -Algorithm SHA256 -LiteralPath $dockerNamesTarget ).Hash.ToLowerInvariant() DockerNamesBackup = if ($hadDockerNames) { $dockerNamesBackup } else { $null } Backup = $backup } | ConvertTo-Json -Compress