Files
git.stella-ops.org/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Infrastructure/Execution/FilesystemPackRunArtifactReader.cs
StellaOps Bot 17d45a6d30
Some checks failed
Airgap Sealed CI Smoke / sealed-smoke (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
feat: Implement Filesystem and MongoDB provenance writers for PackRun execution context
- Added `FilesystemPackRunProvenanceWriter` to write provenance manifests to the filesystem.
- Introduced `MongoPackRunArtifactReader` to read artifacts from MongoDB.
- Created `MongoPackRunProvenanceWriter` to store provenance manifests in MongoDB.
- Developed unit tests for filesystem and MongoDB provenance writers.
- Established `ITimelineEventStore` and `ITimelineIngestionService` interfaces for timeline event handling.
- Implemented `TimelineIngestionService` to validate and persist timeline events with hashing.
- Created PostgreSQL schema and migration scripts for timeline indexing.
- Added dependency injection support for timeline indexer services.
- Developed tests for timeline ingestion and schema validation.
2025-11-30 15:38:14 +02:00

76 lines
2.3 KiB
C#

using System.Text.Json;
using StellaOps.TaskRunner.Core.Execution;
namespace StellaOps.TaskRunner.Infrastructure.Execution;
public sealed class FilesystemPackRunArtifactReader : IPackRunArtifactReader
{
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
private readonly string rootPath;
public FilesystemPackRunArtifactReader(string rootPath)
{
ArgumentException.ThrowIfNullOrWhiteSpace(rootPath);
this.rootPath = Path.GetFullPath(rootPath);
}
public async Task<IReadOnlyList<PackRunArtifactRecord>> ListAsync(string runId, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(runId);
var manifestPath = Path.Combine(rootPath, Sanitize(runId), "artifact-manifest.json");
if (!File.Exists(manifestPath))
{
return Array.Empty<PackRunArtifactRecord>();
}
await using var stream = File.OpenRead(manifestPath);
var manifest = await JsonSerializer.DeserializeAsync<ArtifactManifest>(stream, SerializerOptions, cancellationToken)
.ConfigureAwait(false);
if (manifest is null || manifest.Outputs is null)
{
return Array.Empty<PackRunArtifactRecord>();
}
return manifest.Outputs
.OrderBy(output => output.Name, StringComparer.Ordinal)
.Select(output => new PackRunArtifactRecord(
output.Name,
output.Type,
output.SourcePath,
output.StoredPath,
output.Status,
output.Notes,
manifest.UploadedAt,
output.ExpressionJson))
.ToList();
}
private static string Sanitize(string value)
{
var safe = value.Trim();
foreach (var invalid in Path.GetInvalidFileNameChars())
{
safe = safe.Replace(invalid, '_');
}
return string.IsNullOrWhiteSpace(safe) ? "run" : safe;
}
private sealed record ArtifactManifest(
string RunId,
DateTimeOffset UploadedAt,
List<ArtifactRecord> Outputs);
private sealed record ArtifactRecord(
string Name,
string Type,
string? SourcePath,
string? StoredPath,
string Status,
string? Notes,
string? ExpressionJson);
}