[CmdletBinding()] param( [switch]$SkipCoverage, [switch]$ExemptMiri, [string]$MiriExemptionReason, [switch]$ExemptFuzz, [string]$FuzzExemptionReason, [switch]$ExemptSanitizers, [string]$SanitizersExemptionReason, [switch]$ExemptNoPanic, [string]$NoPanicExemptionReason, [switch]$ExemptLoom, [string]$LoomExemptionReason, [ValidateSet('Debug', 'Release', 'ReleaseFast', 'ReleaseSize')] [string]$VerificationConfiguration = 'ReleaseFast' ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest $ProgressPreference = 'SilentlyContinue' $workspaceRoot = Split-Path -Parent $PSScriptRoot Push-Location -LiteralPath $workspaceRoot $script:StepResults = [System.Collections.Generic.List[object]]::new() function Resolve-CoverageTargetRoot { $override = [Environment]::GetEnvironmentVariable('MERCURY_JADE_COVERAGE_ROOT') if (-not [string]::IsNullOrWhiteSpace($override)) { return $override } $shortScratch = 'C:\tmp' if (Test-Path -LiteralPath $shortScratch -PathType Container) { return (Join-Path $shortScratch 'mtcov') } return (Join-Path ([System.IO.Path]::GetTempPath()) 'mtcov') } $coverageTargetRoot = Resolve-CoverageTargetRoot $coverageTargetDir = $null function Add-StepResult { param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [double]$DurationSeconds, [Parameter(Mandatory = $true)] [string]$Mode ) $script:StepResults.Add([pscustomobject]@{ Step = $Name Seconds = [math]::Round($DurationSeconds, 2) Mode = $Mode }) } function Resolve-BuildArguments { param( [Parameter(Mandatory = $true)] [string]$Configuration ) $arguments = @('build', '--workspace') switch ($Configuration) { 'Release' { $arguments += '--release' } 'ReleaseFast' { $arguments += @('--profile', 'release-fast') } 'ReleaseSize' { $arguments += @('--profile', 'release-size') } } return $arguments } function Add-ExemptionArguments { param( [Parameter(Mandatory = $true)] [System.Collections.Generic.List[string]]$Arguments, [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [bool]$Requested, [string]$Reason ) if (-not $Requested) { return } if ([string]::IsNullOrWhiteSpace($Reason)) { throw "${Name} exemption requires a non-empty reason." } $Arguments.Add("-Exempt${Name}") $Arguments.Add("-${Name}ExemptionReason") $Arguments.Add($Reason) } function Invoke-TimedNative { param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$FilePath, [Parameter(Mandatory = $true)] [string[]]$ArgumentList ) $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() & $FilePath @ArgumentList $exitCode = $LASTEXITCODE $stopwatch.Stop() Add-StepResult -Name $Name -DurationSeconds $stopwatch.Elapsed.TotalSeconds -Mode 'sequential' if ($exitCode -ne 0) { throw "Command failed with exit code ${exitCode}: $FilePath $($ArgumentList -join ' ')" } } function Invoke-TimedNativeWithEnvironment { param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$FilePath, [Parameter(Mandatory = $true)] [string[]]$ArgumentList, [Parameter(Mandatory = $true)] [hashtable]$Environment ) $previous = @{} foreach ($key in $Environment.Keys) { $item = Get-Item -LiteralPath "Env:$key" -ErrorAction SilentlyContinue $previous[$key] = [pscustomobject]@{ Exists = $null -ne $item Value = if ($null -ne $item) { $item.Value } else { $null } } Set-Item -LiteralPath "Env:$key" -Value ([string]$Environment[$key]) } try { Invoke-TimedNative -Name $Name -FilePath $FilePath -ArgumentList $ArgumentList } finally { foreach ($key in $previous.Keys) { if ($previous[$key].Exists) { Set-Item -LiteralPath "Env:$key" -Value $previous[$key].Value } else { Remove-Item -LiteralPath "Env:$key" -ErrorAction SilentlyContinue } } } } function Invoke-TimedNativeToFile { param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$FilePath, [Parameter(Mandatory = $true)] [string[]]$ArgumentList, [Parameter(Mandatory = $true)] [string]$OutputPath ) $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() & $FilePath @ArgumentList > $OutputPath $exitCode = $LASTEXITCODE $stopwatch.Stop() Add-StepResult -Name $Name -DurationSeconds $stopwatch.Elapsed.TotalSeconds -Mode 'sequential' if ($exitCode -ne 0) { throw "Command failed with exit code ${exitCode}: $FilePath $($ArgumentList -join ' ')" } } function Invoke-ParallelNativeSteps { param( [Parameter(Mandatory = $true)] [System.Collections.IEnumerable]$Steps ) $jobScript = { param( [string]$Name, [string]$FilePath, [string[]]$ArgumentList, [int]$Order, [string]$WorkingDirectory ) $ProgressPreference = 'SilentlyContinue' Set-Location -LiteralPath $WorkingDirectory $outputPath = Join-Path ([System.IO.Path]::GetTempPath()) ("mercury-jade-" + [System.Guid]::NewGuid() + '.log') $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $exitCode = 0 try { & $FilePath @ArgumentList *> $outputPath $exitCode = $LASTEXITCODE } catch { $_ | Out-String | Set-Content -LiteralPath $outputPath -Encoding utf8NoBOM $exitCode = if ($LASTEXITCODE -ne 0) { $LASTEXITCODE } else { 1 } } finally { $stopwatch.Stop() } return [pscustomobject]@{ Name = $Name FilePath = $FilePath Arguments = $ArgumentList -join ' ' OutputPath = $outputPath ExitCode = [int]$exitCode Seconds = [math]::Round($stopwatch.Elapsed.TotalSeconds, 2) Order = $Order } } $jobs = [System.Collections.Generic.List[object]]::new() $index = 0 foreach ($step in $Steps) { $jobs.Add((Start-Job -ScriptBlock $jobScript -ArgumentList @( [string]$step.Name, [string]$step.FilePath, [string[]]$step.ArgumentList, $index, $workspaceRoot ))) $index += 1 } Wait-Job -Job $jobs | Out-Null $failures = [System.Collections.Generic.List[string]]::new() foreach ($job in $jobs) { $result = Receive-Job -Job $job Remove-Job -Job $job -Force $output = '' if (Test-Path -LiteralPath $result.OutputPath) { $output = Get-Content -Raw -LiteralPath $result.OutputPath Remove-Item -LiteralPath $result.OutputPath -Force -ErrorAction SilentlyContinue } if (-not [string]::IsNullOrWhiteSpace($output)) { Write-Host '' Write-Host "[$($result.Name)]" Write-Host $output.TrimEnd() } Add-StepResult -Name $result.Name -DurationSeconds $result.Seconds -Mode 'parallel' if ($result.ExitCode -ne 0) { $failures.Add("$($result.Name) (exit $($result.ExitCode))") } } if ($failures.Count -gt 0) { throw "Parallel step(s) failed: $($failures -join ', ')" } } try { Invoke-TimedNative -Name 'cargo fmt' -FilePath 'cargo' -ArgumentList @('fmt', '--all', '--check') Invoke-TimedNative -Name 'cargo check' -FilePath 'cargo' -ArgumentList @('check', '--all-targets', '--all-features') if ($SkipCoverage) { Invoke-TimedNative -Name 'cargo nextest' -FilePath 'cargo' -ArgumentList @('nextest', 'run', '--all-features') } else { $coverageTargetDir = Join-Path $coverageTargetRoot ("mercury-jade-llvm-cov-" + [System.Guid]::NewGuid().ToString('N').Substring(0, 8)) $coverageEnvironment = @{ CARGO_INCREMENTAL = '0' RUSTC_WRAPPER = '' CARGO_TARGET_DIR = $coverageTargetDir } Invoke-TimedNativeWithEnvironment -Name 'cargo llvm-cov clean' -FilePath 'cargo' -Environment $coverageEnvironment -ArgumentList @('llvm-cov', 'clean', '--workspace') Invoke-TimedNativeWithEnvironment -Name 'cargo llvm-cov nextest' -FilePath 'cargo' -Environment $coverageEnvironment -ArgumentList @( 'llvm-cov', '--jobs', '1', 'nextest', '--all-features', '--summary-only', '--ignore-filename-regex', 'cli\.rs$|main\.rs$|flow_opcode_table\.rs$', '--fail-under-lines', '80' ) } Invoke-TimedNative -Name 'cargo clippy' -FilePath 'cargo' -ArgumentList @( 'clippy', '--all-targets', '--all-features', '--', '-D', 'warnings', '-W', 'clippy::pedantic', '-W', 'clippy::nursery' ) Invoke-TimedNative -Name 'cargo udeps' -FilePath 'cargo' -ArgumentList @('+nightly', 'udeps', '--all-targets', '--all-features') Invoke-TimedNative -Name 'cargo deny' -FilePath 'cargo' -ArgumentList @('deny', 'check') $fuzzManifest = Join-Path $workspaceRoot 'fuzz/Cargo.toml' if (Test-Path -LiteralPath $fuzzManifest) { Invoke-TimedNative -Name 'cargo fuzz udeps' -FilePath 'cargo' -ArgumentList @( '+nightly', 'udeps', '--manifest-path', $fuzzManifest, '--all-targets', '--all-features' ) $fuzzMetadataPath = Join-Path ([System.IO.Path]::GetTempPath()) ("mercury-fuzz-metadata-" + [System.Guid]::NewGuid() + '.json') try { Invoke-TimedNativeToFile -Name 'cargo fuzz metadata' -FilePath 'cargo' -ArgumentList @( 'metadata', '--manifest-path', $fuzzManifest, '--locked', '--format-version', '1' ) -OutputPath $fuzzMetadataPath Invoke-TimedNative -Name 'cargo fuzz deny' -FilePath 'cargo' -ArgumentList @( 'deny', 'check', '--config', (Join-Path $workspaceRoot 'deny.toml'), '--metadata-path', $fuzzMetadataPath ) } finally { Remove-Item -LiteralPath $fuzzMetadataPath -Force -ErrorAction SilentlyContinue } } else { throw "Fuzz workspace manifest is required by Jade and was not found: $fuzzManifest" } $jadeHardeningArguments = [System.Collections.Generic.List[string]]::new() $jadeHardeningArguments.Add('-NoProfile') $jadeHardeningArguments.Add('-File') $jadeHardeningArguments.Add((Join-Path $PSScriptRoot 'check-jade-hardening.ps1')) Add-ExemptionArguments -Arguments $jadeHardeningArguments -Name 'Miri' -Requested $ExemptMiri.IsPresent -Reason $MiriExemptionReason Add-ExemptionArguments -Arguments $jadeHardeningArguments -Name 'Fuzz' -Requested $ExemptFuzz.IsPresent -Reason $FuzzExemptionReason Add-ExemptionArguments -Arguments $jadeHardeningArguments -Name 'Sanitizers' -Requested $ExemptSanitizers.IsPresent -Reason $SanitizersExemptionReason Add-ExemptionArguments -Arguments $jadeHardeningArguments -Name 'NoPanic' -Requested $ExemptNoPanic.IsPresent -Reason $NoPanicExemptionReason Add-ExemptionArguments -Arguments $jadeHardeningArguments -Name 'Loom' -Requested $ExemptLoom.IsPresent -Reason $LoomExemptionReason Invoke-TimedNative -Name 'jade hardening' -FilePath 'pwsh' -ArgumentList $jadeHardeningArguments.ToArray() Invoke-TimedNative -Name "cargo build ($VerificationConfiguration)" -FilePath 'cargo' -ArgumentList (Resolve-BuildArguments -Configuration $VerificationConfiguration) Invoke-ParallelNativeSteps -Steps @( @{ Name = 'check-powershell' FilePath = 'pwsh' ArgumentList = @('-NoProfile', '-File', (Join-Path $PSScriptRoot 'check-powershell.ps1')) }, @{ Name = 'check-ai-prompt' FilePath = 'pwsh' ArgumentList = @( '-NoProfile', '-File', (Join-Path $PSScriptRoot 'check-ai-prompt.ps1'), '-Configuration', $VerificationConfiguration, '-SkipBuild' ) }, @{ Name = 'check-ai-skill' FilePath = 'pwsh' ArgumentList = @( '-NoProfile', '-File', (Join-Path $PSScriptRoot 'check-ai-skill.ps1'), '-Configuration', $VerificationConfiguration, '-SkipBuild' ) } ) Invoke-TimedNative -Name 'check-ecosystem' -FilePath 'pwsh' -ArgumentList @( '-NoProfile', '-File', (Join-Path $PSScriptRoot 'check-ecosystem.ps1'), '-Configuration', $VerificationConfiguration, '-SkipBuild', '-SkipPromptGeneration' ) Write-Host '' $script:StepResults | Sort-Object Step | Format-Table -AutoSize } finally { if ($null -ne $coverageTargetDir -and (Test-Path -LiteralPath $coverageTargetDir)) { $resolvedRoot = [System.IO.Path]::GetFullPath($coverageTargetRoot) $resolvedTarget = [System.IO.Path]::GetFullPath($coverageTargetDir) $rootPrefix = $resolvedRoot.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + [System.IO.Path]::DirectorySeparatorChar if ($resolvedTarget.StartsWith($rootPrefix, [System.StringComparison]::OrdinalIgnoreCase)) { Remove-Item -LiteralPath $coverageTargetDir -Recurse -Force -ErrorAction SilentlyContinue } } Pop-Location }