Restructure solution layout by module

This commit is contained in:
master
2025-10-28 15:10:40 +02:00
parent 95daa159c4
commit d870da18ce
4103 changed files with 192899 additions and 187024 deletions

View File

@@ -0,0 +1,6 @@
namespace StellaOps.Scanner.Sbomer.BuildXPlugin.Cas;
/// <summary>
/// Result of persisting bytes into the local CAS.
/// </summary>
public sealed record CasWriteResult(string Algorithm, string Digest, string Path);

View File

@@ -0,0 +1,74 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace StellaOps.Scanner.Sbomer.BuildXPlugin.Cas;
/// <summary>
/// Minimal filesystem-backed CAS used when the BuildX generator runs inside CI.
/// </summary>
public sealed class LocalCasClient
{
private readonly string rootDirectory;
private readonly string algorithm;
public LocalCasClient(LocalCasOptions options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
algorithm = options.Algorithm.ToLowerInvariant();
if (!string.Equals(algorithm, "sha256", StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Only the sha256 algorithm is supported.", nameof(options));
}
rootDirectory = Path.GetFullPath(options.RootDirectory);
}
public Task<CasWriteResult> VerifyWriteAsync(CancellationToken cancellationToken)
{
ReadOnlyMemory<byte> probe = "stellaops-buildx-probe"u8.ToArray();
return WriteAsync(probe, cancellationToken);
}
public async Task<CasWriteResult> WriteAsync(ReadOnlyMemory<byte> content, CancellationToken cancellationToken)
{
var digest = ComputeDigest(content.Span);
var path = BuildObjectPath(digest);
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
await using var stream = new FileStream(
path,
FileMode.Create,
FileAccess.Write,
FileShare.Read,
bufferSize: 16 * 1024,
FileOptions.Asynchronous | FileOptions.SequentialScan);
await stream.WriteAsync(content, cancellationToken).ConfigureAwait(false);
await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
return new CasWriteResult(algorithm, digest, path);
}
private string BuildObjectPath(string digest)
{
// Layout: <root>/<algorithm>/<first two>/<rest>.bin
var prefix = digest.Substring(0, 2);
var suffix = digest[2..];
return Path.Combine(rootDirectory, algorithm, prefix, $"{suffix}.bin");
}
private static string ComputeDigest(ReadOnlySpan<byte> content)
{
Span<byte> buffer = stackalloc byte[32];
SHA256.HashData(content, buffer);
return Convert.ToHexString(buffer).ToLowerInvariant();
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace StellaOps.Scanner.Sbomer.BuildXPlugin.Cas;
/// <summary>
/// Configuration for the on-disk content-addressable store used during CI.
/// </summary>
public sealed record LocalCasOptions
{
private string rootDirectory = string.Empty;
private string algorithm = "sha256";
public string RootDirectory
{
get => rootDirectory;
init
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Root directory must be provided.", nameof(value));
}
rootDirectory = value;
}
}
public string Algorithm
{
get => algorithm;
init
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Algorithm must be provided.", nameof(value));
}
algorithm = value;
}
}
}