Files
git.stella-ops.org/src/Scanner/__Libraries/StellaOps.Scanner.Reachability/ReachabilityUnionPublisher.cs
2026-02-01 21:37:40 +02:00

84 lines
2.8 KiB
C#

using StellaOps.Scanner.Cache.Abstractions;
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace StellaOps.Scanner.Reachability;
/// <summary>
/// Packages a reachability union graph into a deterministic zip, stores it in CAS, and returns the CAS reference.
/// </summary>
public sealed class ReachabilityUnionPublisher
{
private readonly ReachabilityUnionWriter writer;
public ReachabilityUnionPublisher(ReachabilityUnionWriter writer)
{
this.writer = writer ?? throw new ArgumentNullException(nameof(writer));
}
public async Task<ReachabilityUnionPublishResult> PublishAsync(
ReachabilityUnionGraph graph,
IFileContentAddressableStore cas,
string workRoot,
string analysisId,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(graph);
ArgumentNullException.ThrowIfNull(cas);
ArgumentException.ThrowIfNullOrWhiteSpace(workRoot);
ArgumentException.ThrowIfNullOrWhiteSpace(analysisId);
var result = await writer.WriteAsync(graph, workRoot, analysisId, cancellationToken).ConfigureAwait(false);
var folder = Path.GetDirectoryName(result.MetaPath)!;
var zipPath = Path.Combine(folder, "reachability.zip");
CreateZip(folder, zipPath);
var sha = ComputeSha256(zipPath);
await using var zipStream = File.OpenRead(zipPath);
var casEntry = await cas.PutAsync(new FileCasPutRequest(sha, zipStream, leaveOpen: false), cancellationToken).ConfigureAwait(false);
return new ReachabilityUnionPublishResult(
Sha256: sha,
RelativePath: casEntry.RelativePath,
Records: result.Nodes.RecordCount + result.Edges.RecordCount + (result.Facts?.RecordCount ?? 0));
}
private static void CreateZip(string sourceDir, string destinationZip)
{
if (File.Exists(destinationZip))
{
File.Delete(destinationZip);
}
var files = Directory.EnumerateFiles(sourceDir, "*", SearchOption.TopDirectoryOnly)
.OrderBy(f => f, StringComparer.Ordinal)
.ToList();
using var zip = ZipFile.Open(destinationZip, ZipArchiveMode.Create);
foreach (var file in files)
{
var entryName = Path.GetFileName(file);
zip.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
}
}
private static string ComputeSha256(string path)
{
using var sha = SHA256.Create();
using var stream = File.OpenRead(path);
return Convert.ToHexString(sha.ComputeHash(stream)).ToLowerInvariant();
}
}
public sealed record ReachabilityUnionPublishResult(
string Sha256,
string RelativePath,
int Records);