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:
@@ -31,8 +31,29 @@ $ErrorActionPreference = 'Continue'
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$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 |
|
||||
Where-Object { $_.Name -ne 'StellaOps.sln' } |
|
||||
Where-Object {
|
||||
$_.Name -ne 'StellaOps.sln' -and
|
||||
$_.FullName -notmatch '[\\/](node_modules|bin|obj)[\\/]'
|
||||
} |
|
||||
Sort-Object FullName
|
||||
|
||||
if ($solutions.Count -eq 0) {
|
||||
@@ -50,7 +71,7 @@ $testFail = @()
|
||||
$testSkipped = @()
|
||||
|
||||
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
|
||||
|
||||
dotnet build $sln.FullName --configuration $Configuration --nologo -v quiet
|
||||
|
||||
@@ -13,13 +13,11 @@
|
||||
|
||||
using StellaOps.Verifier;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Builder;
|
||||
using System.CommandLine.Parsing;
|
||||
|
||||
var bundleOption = new Option<FileInfo>("--bundle", ["-b"])
|
||||
{
|
||||
Description = "Path to the evidence bundle to verify",
|
||||
IsRequired = true
|
||||
Required = true
|
||||
};
|
||||
|
||||
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"])
|
||||
{
|
||||
Description = "Report output format"
|
||||
Description = "Report output format",
|
||||
DefaultValueFactory = _ => ReportFormat.Markdown
|
||||
};
|
||||
formatOption.SetDefaultValue(ReportFormat.Markdown);
|
||||
|
||||
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")
|
||||
{
|
||||
Description = "Verify RFC 3161 timestamps"
|
||||
Description = "Verify RFC 3161 timestamps",
|
||||
DefaultValueFactory = _ => true
|
||||
};
|
||||
verifyTimestampsOption.SetDefaultValue(true);
|
||||
|
||||
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")
|
||||
{
|
||||
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"])
|
||||
{
|
||||
@@ -92,19 +90,19 @@ var verifyCommand = new Command("verify", "Verify an evidence bundle")
|
||||
verboseOption
|
||||
};
|
||||
|
||||
verifyCommand.SetHandler(async (context) =>
|
||||
verifyCommand.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var bundle = context.ParseResult.GetValueForOption(bundleOption)!;
|
||||
var trustedKeys = context.ParseResult.GetValueForOption(trustedKeysOption);
|
||||
var trustProfile = context.ParseResult.GetValueForOption(trustProfileOption);
|
||||
var output = context.ParseResult.GetValueForOption(outputOption);
|
||||
var format = context.ParseResult.GetValueForOption(formatOption);
|
||||
var verifySignatures = context.ParseResult.GetValueForOption(verifySignaturesOption);
|
||||
var verifyTimestamps = context.ParseResult.GetValueForOption(verifyTimestampsOption);
|
||||
var verifyDigests = context.ParseResult.GetValueForOption(verifyDigestsOption);
|
||||
var verifyPairs = context.ParseResult.GetValueForOption(verifyPairsOption);
|
||||
var quiet = context.ParseResult.GetValueForOption(quietOption);
|
||||
var verbose = context.ParseResult.GetValueForOption(verboseOption);
|
||||
var bundle = parseResult.GetRequiredValue(bundleOption);
|
||||
var trustedKeys = parseResult.GetValue(trustedKeysOption);
|
||||
var trustProfile = parseResult.GetValue(trustProfileOption);
|
||||
var output = parseResult.GetValue(outputOption);
|
||||
var format = parseResult.GetValue(formatOption);
|
||||
var verifySignatures = parseResult.GetValue(verifySignaturesOption);
|
||||
var verifyTimestamps = parseResult.GetValue(verifyTimestampsOption);
|
||||
var verifyDigests = parseResult.GetValue(verifyDigestsOption);
|
||||
var verifyPairs = parseResult.GetValue(verifyPairsOption);
|
||||
var quiet = parseResult.GetValue(quietOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
var options = new VerifierOptions
|
||||
{
|
||||
@@ -122,8 +120,7 @@ verifyCommand.SetHandler(async (context) =>
|
||||
};
|
||||
|
||||
var verifier = new BundleVerifier();
|
||||
var exitCode = await verifier.VerifyAsync(options, context.GetCancellationToken());
|
||||
context.ExitCode = exitCode;
|
||||
return await verifier.VerifyAsync(options, ct);
|
||||
});
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
infoCommand.SetHandler(async (context) =>
|
||||
infoCommand.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var bundle = context.ParseResult.GetValueForOption(bundleOption)!;
|
||||
var format = context.ParseResult.GetValueForOption(formatOption);
|
||||
var quiet = context.ParseResult.GetValueForOption(quietOption);
|
||||
var bundle = parseResult.GetRequiredValue(bundleOption);
|
||||
var format = parseResult.GetValue(formatOption);
|
||||
var quiet = parseResult.GetValue(quietOption);
|
||||
|
||||
var verifier = new BundleVerifier();
|
||||
var exitCode = await verifier.ShowInfoAsync(
|
||||
return await verifier.ShowInfoAsync(
|
||||
bundle.FullName,
|
||||
format,
|
||||
quiet,
|
||||
context.GetCancellationToken());
|
||||
context.ExitCode = exitCode;
|
||||
ct);
|
||||
});
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// Add version option
|
||||
rootCommand.AddOption(new Option<bool>("--version", "Show version information"));
|
||||
|
||||
var parser = new CommandLineBuilder(rootCommand)
|
||||
.UseDefaults()
|
||||
.UseExceptionHandler((ex, context) =>
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
context.ExitCode = 2;
|
||||
})
|
||||
.Build();
|
||||
|
||||
return await parser.InvokeAsync(args);
|
||||
try
|
||||
{
|
||||
return await rootCommand.Parse(args).InvokeAsync();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Console.Error.WriteLine("Verification cancelled.");
|
||||
return 2;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -20,14 +20,6 @@
|
||||
<Product>Stella Ops Bundle Verifier</Product>
|
||||
<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 -->
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<DebuggerSupport>false</DebuggerSupport>
|
||||
@@ -37,6 +29,14 @@
|
||||
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
|
||||
</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 -->
|
||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' == ''">
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;linux-musl-x64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
@@ -46,6 +46,12 @@
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="__Tests\**\*.cs" />
|
||||
<EmbeddedResource Remove="__Tests\**\*" />
|
||||
<None Remove="__Tests\**\*" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Minimal dependencies for standalone operation -->
|
||||
<ItemGroup>
|
||||
<!-- No database, network, or heavy framework dependencies -->
|
||||
|
||||
@@ -13,16 +13,12 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<UseConcelierTestInfra>false</UseConcelierTestInfra>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<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">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
Reference in New Issue
Block a user