scripts/Verify-QuietCliLayout.ps1
|
#requires -Version 7.6 [CmdletBinding()] param([string]$OutputPath=(Join-Path $PSScriptRoot '../verification/v2-layout-validation.json')) $ErrorActionPreference='Stop' $core=Split-Path -Parent $PSScriptRoot $ecosystem=Split-Path -Parent $core $checks=[Collections.Generic.List[object]]::new() $report=[ordered]@{SchemaVersion=2;ExecutedAtUtc=[DateTimeOffset]::UtcNow.ToString('o');Scope='QuietCli ecosystem layout and Linux runtime';PowerShell=$PSVersionTable.PSVersion.ToString();Outcome='Running'} try{ $runtime=Import-Module (Join-Path $core 'FrenchExDev.QuietCli.PoSh.psd1') -PassThru -DisableNameChecking foreach($name in @('quiet','pump')){ $command=Get-Command $name -Module $runtime.Name if($command.CommandType -ne 'Function'){throw "Missing public function: $name"} foreach($manifestPath in @('FrenchExDev.QuietCli.PoSh.psd1','src/QuietCli.psd1')){ $manifest=Import-PowerShellDataFile (Join-Path $core $manifestPath) if($name -notin $manifest.FunctionsToExport){throw "Missing manifest export: $name"} } $checks.Add(@{Name='PublicCommand';Command=$name;Outcome='Success'}) } foreach($name in @('Invoke-QuietCli','New-QuietContract')){ $command=Get-Command $name -Module $runtime.Name if(!$command.Parameters.ContainsKey('OnOutput')){throw "Missing streaming parameter on $name"} $checks.Add(@{Name='StreamingInterface';Command=$name;Parameter='OnOutput';Outcome='Success'}) } $acceptancePath=Join-Path $core 'verification/v2-validation.json' $acceptance=Get-Content -LiteralPath $acceptancePath -Raw|ConvertFrom-Json -AsHashtable if($acceptance.Outcome -ne 'Success' -or $acceptance.Failed -or $acceptance.Skipped -or $acceptance.DiscoveryErrors -or !$acceptance.AnalyzerRun -or $acceptance.Analyzer.Count){throw 'Current Linux acceptance and analysis evidence is not successful.'} $report.Acceptance=@{Passed=$acceptance.Passed;Failed=$acceptance.Failed;Skipped=$acceptance.Skipped;DiscoveryErrors=$acceptance.DiscoveryErrors;AnalyzerDiagnostics=$acceptance.Analyzer.Count;ReportSHA256=(Get-FileHash $acceptancePath).Hash} $moduleProofs=@() foreach($path in @((Join-Path $ecosystem 'FrenchExDev.QuietCli.Git.PoSh/FrenchExDev.QuietCli.Git.PoSh.psd1')|Where-Object {Test-Path -LiteralPath $_})){ $validation=Test-QuietModule -Path $path -AsObject if($validation.Outcome -ne 'Success'){throw ($validation.Data.Failures -join '; ')} $moduleProofs+=@{Path=[IO.Path]::GetRelativePath($ecosystem,$path);Hash=$validation.Data.Hash;Checks=$validation.Data.Checks;Outcome=$validation.Outcome} } $report.GitModules=$moduleProofs if(Test-Path -LiteralPath (Join-Path $core 'modules')){throw 'Consumer modules must remain separate from the runtime workspace.'} $report.EngineModuleCopies=0 $syntax=@(foreach($path in Get-ChildItem -LiteralPath $ecosystem -Recurse -File|Where-Object Extension -In @('.ps1','.psm1','.psd1')){ $tokens=$null;$errors=$null $null=[Management.Automation.Language.Parser]::ParseFile($path.FullName,[ref]$tokens,[ref]$errors) foreach($errorItem in $errors){@{Path=[IO.Path]::GetRelativePath($ecosystem,$path.FullName);Message=$errorItem.Message}} }) if($syntax.Count){$report.SyntaxErrors=$syntax;throw 'PowerShell syntax errors remain.'} $report.SyntaxErrors=@() $report.RequiredRuntimeModules=@($runtime.RequiredModules|ForEach-Object Name) $report.ContainerInit=([IO.File]::ReadAllText('/proc/1/comm').Trim() -eq 'podman-init') if(!$IsLinux -or !$report.ContainerInit){throw 'Qualification requires the owned Linux container with init.'} $report.CIExecution='Not run; local Linux validation only' $report.Checks=$checks.ToArray() $report.Outcome='Success' }catch{ $report.Outcome='Failed' $report.Error=$_.Exception.Message $report.Checks=$checks.ToArray() } [IO.File]::WriteAllText([IO.Path]::GetFullPath($OutputPath),($report|ConvertTo-Json -Depth 12),[Text.UTF8Encoding]::new($false)) [pscustomobject]@{Outcome=$report.Outcome;Data=@{Checks=$checks.Count;GitModules=$report.GitModules;Acceptance=$report.Acceptance;Error=$report.Error;Report=[IO.Path]::GetFullPath($OutputPath)}}|ConvertTo-Json -Depth 8 -Compress if($report.Outcome -ne 'Success'){exit 1} |