param([switch]$ResumeAndroidSmoke) $ErrorActionPreference = 'Stop' $projectRoot = 'C:/git/MagnetismHeroes' Set-Location -LiteralPath $projectRoot $env:PATH = 'C:/Users/baram/AppData/Local/Unity/bin;' + $env:PATH $evidence = Join-Path $projectRoot 'artifacts/verification/VisualUpgrade/FinalProduct' New-Item -ItemType Directory -Path $evidence -Force | Out-Null $sourceCommit = (& git rev-parse HEAD).Trim() if ([bool](& git status --porcelain)) { throw 'Final source must be clean.' } $productPaths = @(& git ls-files Assets Packages ProjectSettings tools) $before = [ordered]@{} foreach ($path in $productPaths) { $before[$path] = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash } $beforePath = Join-Path $evidence 'product-before.json' if ($ResumeAndroidSmoke) { $originalBefore = Get-Content -LiteralPath $beforePath -Raw | ConvertFrom-Json -AsHashtable if (@($productPaths | Where-Object { $originalBefore[$_] -ne $before[$_] }).Count -ne 0) { throw 'Product source differs from the original final-check source.' } } else { $before | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $beforePath -Encoding utf8 } $env:MAGNETISM_VISUAL_EVIDENCE = Join-Path $evidence 'EditorCapture' $env:MAGNETISM_PROOF_COMMIT = $sourceCommit $env:MAGNETISM_PROOF_DIRTY = 'false' $record = [ordered]@{sourceCommit=$sourceCommit;startedUtc=[DateTime]::UtcNow.ToString('o');status='running';phases=@();productFiles=$productPaths.Count} $recordPath = Join-Path $evidence 'completion.json' $phases = @('EditMode','PlayMode','AndroidBuild','AndroidSmoke') function Confirm-AndroidReadiness { $sdkAdb = Join-Path $env:LOCALAPPDATA 'Android/Sdk/platform-tools/adb.exe' $readiness = [ordered]@{startedUtc=[DateTime]::UtcNow.ToString('o');serial='emulator-5554';adb=$sdkAdb;commands=@();status='running'} foreach ($arguments in @(@('kill-server'), @('start-server'), @('-s','emulator-5554','get-state'), @('-s','emulator-5554','shell','getprop','sys.boot_completed'))) { $step = $readiness.commands.Count $stdout = Join-Path $evidence "adb-readiness-$step.stdout.log" $stderr = Join-Path $evidence "adb-readiness-$step.stderr.log" $client = Start-Process -FilePath $sdkAdb -ArgumentList $arguments -WindowStyle Hidden -PassThru -RedirectStandardOutput $stdout -RedirectStandardError $stderr if (-not $client.WaitForExit(30000)) { Stop-Process -Id $client.Id; throw "ADB readiness command timed out: $arguments" } $client.Refresh() $output = (Get-Content -LiteralPath $stdout -Raw -ErrorAction SilentlyContinue) $readiness.commands += [ordered]@{arguments=$arguments;exitCode=$client.ExitCode;stdout=$stdout;stderr=$stderr;output=$output} $readiness | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $evidence 'android-readiness.json') -Encoding utf8 if ($client.ExitCode -ne 0) { throw "ADB readiness command failed: $arguments" } if ($step -eq 2 -and $output.Trim() -ne 'device') { throw 'Dedicated AVD is not online.' } if ($step -eq 3 -and $output.Trim() -ne '1') { throw 'Dedicated AVD has not completed boot.' } } $readiness.status = 'passed' $readiness.finishedUtc = [DateTime]::UtcNow.ToString('o') $readiness | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $evidence 'android-readiness.json') -Encoding utf8 } if ($ResumeAndroidSmoke) { $previous = Get-Content -LiteralPath $recordPath -Raw | ConvertFrom-Json -AsHashtable $passed = @($previous.phases | Where-Object { $_.phase -ne 'AndroidSmoke' }) if ($previous.sourceCommit -ne $sourceCommit -or $passed.Count -ne 3 -or @($passed | Where-Object { $_.exitCode -ne 0 -or $_.status -ne 'passed' }).Count -ne 0) { throw 'Cannot resume without three successful phases on this source.' } $record.startedUtc = $previous.startedUtc $record.resumedUtc = [DateTime]::UtcNow.ToString('o') $record.phases = $passed $record.failedAttempts = @($previous.phases | Where-Object { $_.phase -eq 'AndroidSmoke' }) $phases = @('AndroidSmoke') } try { foreach ($phase in $phases) { if ($phase -eq 'AndroidSmoke') { Confirm-AndroidReadiness } $outerLog = Join-Path $evidence ($phase.ToLowerInvariant() + '-outer.log') $started = [DateTime]::UtcNow.ToString('o') Write-Output "Starting $phase at $started" & pwsh -NoProfile -File tools/verify.ps1 -Phase $phase -Milestone Final *> $outerLog $actualExit = $LASTEXITCODE $phaseRecordPath = Join-Path $projectRoot ('artifacts/verification/Final/' + $phase.ToLowerInvariant() + '-evidence.json') $phaseRecord = Get-Content -LiteralPath $phaseRecordPath -Raw | ConvertFrom-Json $record.phases += [ordered]@{phase=$phase;command="pwsh -NoProfile -File tools/verify.ps1 -Phase $phase -Milestone Final";startedUtc=$started;finishedUtc=[DateTime]::UtcNow.ToString('o');exitCode=$actualExit;status=$phaseRecord.status;sourceCommit=$phaseRecord.sourceCommit;sourceDirty=$phaseRecord.sourceDirty;sourceDirtyEnd=$phaseRecord.sourceDirtyEnd;outerLog=$outerLog;evidence=$phaseRecordPath} $record | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $recordPath -Encoding utf8 if ($actualExit -ne 0 -or $phaseRecord.status -ne 'passed' -or $phaseRecord.sourceCommit -ne $sourceCommit -or $phaseRecord.sourceDirty -or $phaseRecord.sourceDirtyEnd) { throw "$phase failed. See $outerLog" } Write-Output "$phase exit $actualExit, status $($phaseRecord.status)" } $after = [ordered]@{} foreach ($path in $productPaths) { $after[$path] = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash } $after | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $evidence 'product-after.json') -Encoding utf8 $changed = @($productPaths | Where-Object { $before[$_] -ne $after[$_] }) $record.productHashesUnchanged = $changed.Count -eq 0 $record.changedProducts = $changed if ($changed.Count -ne 0 -or [bool](& git status --porcelain) -or (& git rev-parse HEAD).Trim() -ne $sourceCommit) { throw 'Product source changed during final checks.' } $record.status = 'passed' } catch { $record.status='failed'; $record.error=$_.Exception.Message; Write-Output $record.error } finally { $record.finishedUtc=[DateTime]::UtcNow.ToString('o'); $record | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $recordPath -Encoding utf8 } if ($record.status -ne 'passed') { exit 1 } Write-Output 'All four final phases passed on the same clean integrated product source.'