feat: Implement Runtime Facts ingestion service and NDJSON reader
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Added RuntimeFactsNdjsonReader for reading NDJSON formatted runtime facts.
- Introduced IRuntimeFactsIngestionService interface and its implementation.
- Enhanced Program.cs to register new services and endpoints for runtime facts.
- Updated CallgraphIngestionService to include CAS URI in stored artifacts.
- Created RuntimeFactsValidationException for validation errors during ingestion.
- Added tests for RuntimeFactsIngestionService and RuntimeFactsNdjsonReader.
- Implemented SignalsSealedModeMonitor for compliance checks in sealed mode.
- Updated project dependencies for testing utilities.
This commit is contained in:
master
2025-11-10 07:56:15 +02:00
parent 9df52d84aa
commit 69c59defdc
132 changed files with 19718 additions and 9334 deletions

View File

@@ -198,7 +198,12 @@ internal sealed class CompositeScanAnalyzerDispatcher : IScanAnalyzerDispatcher
var cacheAdapter = new LanguageAnalyzerSurfaceCache(cache, surfaceEnvironment.Settings.Tenant);
var usageHints = LanguageUsageHints.Empty;
var analyzerContext = new LanguageAnalyzerContext(workspacePath, context.TimeProvider, usageHints, services);
var analyzerContext = new LanguageAnalyzerContext(
workspacePath,
context.TimeProvider,
usageHints,
services,
context.Analysis);
var results = new Dictionary<string, LanguageAnalyzerResult>(StringComparer.OrdinalIgnoreCase);
var fragments = new List<LayerComponentFragment>();

View File

@@ -239,6 +239,7 @@ internal sealed class SurfaceManifestPublisher : ISurfaceManifestPublisher
ArtifactDocumentFormat.EntryTraceNdjson => "entrytrace.ndjson",
ArtifactDocumentFormat.EntryTraceGraphJson => "entrytrace.graph",
ArtifactDocumentFormat.ComponentFragmentJson => "layer.fragments",
ArtifactDocumentFormat.ObservationJson => "observation.json",
ArtifactDocumentFormat.SurfaceManifestJson => "surface.manifest",
ArtifactDocumentFormat.CycloneDxJson => "cdx-json",
ArtifactDocumentFormat.CycloneDxProtobuf => "cdx-protobuf",

View File

@@ -165,6 +165,20 @@ internal sealed class SurfaceManifestStageExecutor : IScanStageExecutor
View: "inventory"));
}
if (context.Analysis.TryGet<AnalyzerObservationPayload>(ScanAnalysisKeys.DenoObservationPayload, out var denoObservation) &&
denoObservation is not null)
{
payloads.Add(new SurfaceManifestPayload(
ArtifactDocumentType.SurfaceObservation,
ArtifactDocumentFormat.ObservationJson,
Kind: denoObservation.Kind,
MediaType: denoObservation.MediaType,
Content: denoObservation.Content,
View: denoObservation.View,
Metadata: NormalizeObservationMetadata(denoObservation.Metadata),
RegisterArtifact: true));
}
return payloads;
}
@@ -277,6 +291,28 @@ internal sealed class SurfaceManifestStageExecutor : IScanStageExecutor
return digest.Trim();
}
private static IReadOnlyDictionary<string, string>? NormalizeObservationMetadata(
IReadOnlyDictionary<string, string?>? metadata)
{
if (metadata is null || metadata.Count == 0)
{
return null;
}
var normalized = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var pair in metadata)
{
if (string.IsNullOrWhiteSpace(pair.Key) || string.IsNullOrWhiteSpace(pair.Value))
{
continue;
}
normalized[pair.Key.Trim()] = pair.Value!.Trim();
}
return normalized.Count == 0 ? null : normalized;
}
private string ComputeDigest(ReadOnlySpan<byte> content)
{
var hex = _hash.ComputeHashHex(content, HashAlgorithms.Sha256);