fix(tools): improve build script discovery and update Verifier to System.CommandLine v8+

Build script:
- Add Get-RepoRelativePath() helper for cross-platform path handling
- Exclude node_modules and bin/obj from solution discovery

Verifier:
- Replace deprecated SetHandler with SetAction handler pattern
- Use GetRequiredValue/GetValue instead of GetValueForOption
- Replace SetDefaultValue with DefaultValueFactory property
- Remove CommandLineBuilder wrapper (built into framework now)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
master
2026-03-09 07:53:08 +02:00
parent e6094e3b53
commit e0c79e0dc0
4 changed files with 82 additions and 62 deletions

View File

@@ -31,8 +31,29 @@ $ErrorActionPreference = 'Continue'
$repoRoot = Split-Path -Parent $PSScriptRoot $repoRoot = Split-Path -Parent $PSScriptRoot
$srcDir = Join-Path $repoRoot 'src' $srcDir = Join-Path $repoRoot 'src'
function Get-RepoRelativePath {
param(
[Parameter(Mandatory = $true)]
[string]$Root,
[Parameter(Mandatory = $true)]
[string]$Path
)
$normalizedRoot = [System.IO.Path]::GetFullPath($Root).TrimEnd('\', '/')
$normalizedPath = [System.IO.Path]::GetFullPath($Path)
if ($normalizedPath.StartsWith($normalizedRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
return $normalizedPath.Substring($normalizedRoot.Length).TrimStart('\', '/')
}
return $normalizedPath
}
$solutions = Get-ChildItem -Path $srcDir -Filter '*.sln' -Recurse | $solutions = Get-ChildItem -Path $srcDir -Filter '*.sln' -Recurse |
Where-Object { $_.Name -ne 'StellaOps.sln' } | Where-Object {
$_.Name -ne 'StellaOps.sln' -and
$_.FullName -notmatch '[\\/](node_modules|bin|obj)[\\/]'
} |
Sort-Object FullName Sort-Object FullName
if ($solutions.Count -eq 0) { if ($solutions.Count -eq 0) {
@@ -50,7 +71,7 @@ $testFail = @()
$testSkipped = @() $testSkipped = @()
foreach ($sln in $solutions) { foreach ($sln in $solutions) {
$rel = [System.IO.Path]::GetRelativePath($repoRoot, $sln.FullName) $rel = Get-RepoRelativePath -Root $repoRoot -Path $sln.FullName
Write-Host "--- BUILD: $rel ---" -ForegroundColor Yellow Write-Host "--- BUILD: $rel ---" -ForegroundColor Yellow
dotnet build $sln.FullName --configuration $Configuration --nologo -v quiet dotnet build $sln.FullName --configuration $Configuration --nologo -v quiet

View File

@@ -13,13 +13,11 @@
using StellaOps.Verifier; using StellaOps.Verifier;
using System.CommandLine; using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;
var bundleOption = new Option<FileInfo>("--bundle", ["-b"]) var bundleOption = new Option<FileInfo>("--bundle", ["-b"])
{ {
Description = "Path to the evidence bundle to verify", Description = "Path to the evidence bundle to verify",
IsRequired = true Required = true
}; };
var trustedKeysOption = new Option<FileInfo?>("--trusted-keys", ["-k"]) var trustedKeysOption = new Option<FileInfo?>("--trusted-keys", ["-k"])
@@ -39,33 +37,33 @@ var outputOption = new Option<FileInfo?>("--output", ["-o"])
var formatOption = new Option<ReportFormat>("--format", ["-f"]) var formatOption = new Option<ReportFormat>("--format", ["-f"])
{ {
Description = "Report output format" Description = "Report output format",
DefaultValueFactory = _ => ReportFormat.Markdown
}; };
formatOption.SetDefaultValue(ReportFormat.Markdown);
var verifySignaturesOption = new Option<bool>("--verify-signatures") var verifySignaturesOption = new Option<bool>("--verify-signatures")
{ {
Description = "Verify bundle manifest signatures" Description = "Verify bundle manifest signatures",
DefaultValueFactory = _ => true
}; };
verifySignaturesOption.SetDefaultValue(true);
var verifyTimestampsOption = new Option<bool>("--verify-timestamps") var verifyTimestampsOption = new Option<bool>("--verify-timestamps")
{ {
Description = "Verify RFC 3161 timestamps" Description = "Verify RFC 3161 timestamps",
DefaultValueFactory = _ => true
}; };
verifyTimestampsOption.SetDefaultValue(true);
var verifyDigestsOption = new Option<bool>("--verify-digests") var verifyDigestsOption = new Option<bool>("--verify-digests")
{ {
Description = "Verify blob digests" Description = "Verify blob digests",
DefaultValueFactory = _ => true
}; };
verifyDigestsOption.SetDefaultValue(true);
var verifyPairsOption = new Option<bool>("--verify-pairs") var verifyPairsOption = new Option<bool>("--verify-pairs")
{ {
Description = "Verify pair artifacts (SBOM, delta-sig)" Description = "Verify pair artifacts (SBOM, delta-sig)",
DefaultValueFactory = _ => true
}; };
verifyPairsOption.SetDefaultValue(true);
var quietOption = new Option<bool>("--quiet", ["-q"]) var quietOption = new Option<bool>("--quiet", ["-q"])
{ {
@@ -92,19 +90,19 @@ var verifyCommand = new Command("verify", "Verify an evidence bundle")
verboseOption verboseOption
}; };
verifyCommand.SetHandler(async (context) => verifyCommand.SetAction(async (parseResult, ct) =>
{ {
var bundle = context.ParseResult.GetValueForOption(bundleOption)!; var bundle = parseResult.GetRequiredValue(bundleOption);
var trustedKeys = context.ParseResult.GetValueForOption(trustedKeysOption); var trustedKeys = parseResult.GetValue(trustedKeysOption);
var trustProfile = context.ParseResult.GetValueForOption(trustProfileOption); var trustProfile = parseResult.GetValue(trustProfileOption);
var output = context.ParseResult.GetValueForOption(outputOption); var output = parseResult.GetValue(outputOption);
var format = context.ParseResult.GetValueForOption(formatOption); var format = parseResult.GetValue(formatOption);
var verifySignatures = context.ParseResult.GetValueForOption(verifySignaturesOption); var verifySignatures = parseResult.GetValue(verifySignaturesOption);
var verifyTimestamps = context.ParseResult.GetValueForOption(verifyTimestampsOption); var verifyTimestamps = parseResult.GetValue(verifyTimestampsOption);
var verifyDigests = context.ParseResult.GetValueForOption(verifyDigestsOption); var verifyDigests = parseResult.GetValue(verifyDigestsOption);
var verifyPairs = context.ParseResult.GetValueForOption(verifyPairsOption); var verifyPairs = parseResult.GetValue(verifyPairsOption);
var quiet = context.ParseResult.GetValueForOption(quietOption); var quiet = parseResult.GetValue(quietOption);
var verbose = context.ParseResult.GetValueForOption(verboseOption); var verbose = parseResult.GetValue(verboseOption);
var options = new VerifierOptions var options = new VerifierOptions
{ {
@@ -122,8 +120,7 @@ verifyCommand.SetHandler(async (context) =>
}; };
var verifier = new BundleVerifier(); var verifier = new BundleVerifier();
var exitCode = await verifier.VerifyAsync(options, context.GetCancellationToken()); return await verifier.VerifyAsync(options, ct);
context.ExitCode = exitCode;
}); });
var infoCommand = new Command("info", "Display bundle information without verification") var infoCommand = new Command("info", "Display bundle information without verification")
@@ -133,19 +130,18 @@ var infoCommand = new Command("info", "Display bundle information without verifi
quietOption quietOption
}; };
infoCommand.SetHandler(async (context) => infoCommand.SetAction(async (parseResult, ct) =>
{ {
var bundle = context.ParseResult.GetValueForOption(bundleOption)!; var bundle = parseResult.GetRequiredValue(bundleOption);
var format = context.ParseResult.GetValueForOption(formatOption); var format = parseResult.GetValue(formatOption);
var quiet = context.ParseResult.GetValueForOption(quietOption); var quiet = parseResult.GetValue(quietOption);
var verifier = new BundleVerifier(); var verifier = new BundleVerifier();
var exitCode = await verifier.ShowInfoAsync( return await verifier.ShowInfoAsync(
bundle.FullName, bundle.FullName,
format, format,
quiet, quiet,
context.GetCancellationToken()); ct);
context.ExitCode = exitCode;
}); });
var rootCommand = new RootCommand("Stella Ops Bundle Verifier - Offline evidence bundle verification") var rootCommand = new RootCommand("Stella Ops Bundle Verifier - Offline evidence bundle verification")
@@ -154,16 +150,17 @@ var rootCommand = new RootCommand("Stella Ops Bundle Verifier - Offline evidence
infoCommand infoCommand
}; };
// Add version option try
rootCommand.AddOption(new Option<bool>("--version", "Show version information")); {
return await rootCommand.Parse(args).InvokeAsync();
var parser = new CommandLineBuilder(rootCommand) }
.UseDefaults() catch (OperationCanceledException)
.UseExceptionHandler((ex, context) => {
{ Console.Error.WriteLine("Verification cancelled.");
Console.Error.WriteLine($"Error: {ex.Message}"); return 2;
context.ExitCode = 2; }
}) catch (Exception ex)
.Build(); {
Console.Error.WriteLine($"Error: {ex.Message}");
return await parser.InvokeAsync(args); return 2;
}

View File

@@ -20,14 +20,6 @@
<Product>Stella Ops Bundle Verifier</Product> <Product>Stella Ops Bundle Verifier</Product>
<Description>Standalone verifier for Stella Ops evidence bundles</Description> <Description>Standalone verifier for Stella Ops evidence bundles</Description>
<!-- Single-file and self-contained publishing -->
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<!-- Optimize for size --> <!-- Optimize for size -->
<InvariantGlobalization>true</InvariantGlobalization> <InvariantGlobalization>true</InvariantGlobalization>
<DebuggerSupport>false</DebuggerSupport> <DebuggerSupport>false</DebuggerSupport>
@@ -37,6 +29,14 @@
<MetadataUpdaterSupport>false</MetadataUpdaterSupport> <MetadataUpdaterSupport>false</MetadataUpdaterSupport>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(PublishSingleFile)' == 'true'">
<SelfContained>true</SelfContained>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
<!-- Runtime identifiers for cross-platform builds --> <!-- Runtime identifiers for cross-platform builds -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' == ''"> <PropertyGroup Condition="'$(RuntimeIdentifier)' == ''">
<RuntimeIdentifiers>win-x64;linux-x64;linux-musl-x64;osx-x64;osx-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;linux-x64;linux-musl-x64;osx-x64;osx-arm64</RuntimeIdentifiers>
@@ -46,6 +46,12 @@
<PackageReference Include="System.CommandLine" /> <PackageReference Include="System.CommandLine" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Remove="__Tests\**\*.cs" />
<EmbeddedResource Remove="__Tests\**\*" />
<None Remove="__Tests\**\*" />
</ItemGroup>
<!-- Minimal dependencies for standalone operation --> <!-- Minimal dependencies for standalone operation -->
<ItemGroup> <ItemGroup>
<!-- No database, network, or heavy framework dependencies --> <!-- No database, network, or heavy framework dependencies -->

View File

@@ -13,16 +13,12 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<UseConcelierTestInfra>false</UseConcelierTestInfra>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentAssertions" /> <PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector"> <PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>