// ----------------------------------------------------------------------------- // RebuildService.cs // Sprint: SPRINT_20260119_005 Reproducible Rebuild Integration // Task: REPR-001 through REPR-007 - Service Orchestration // Description: Main rebuild service orchestrating all backends. // ----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace StellaOps.BinaryIndex.GroundTruth.Reproducible; /// /// Main rebuild service implementation. /// public sealed class RebuildService : IRebuildService { private readonly ReproduceDebianClient _reproduceDebianClient; private readonly LocalRebuildBackend _localBackend; private readonly AirGapRebuildBundleService _airGapService; private readonly RebuildServiceOptions _options; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// public RebuildService( ReproduceDebianClient reproduceDebianClient, LocalRebuildBackend localBackend, AirGapRebuildBundleService airGapService, IOptions options, ILogger logger) { _reproduceDebianClient = reproduceDebianClient; _localBackend = localBackend; _airGapService = airGapService; _options = options.Value; _logger = logger; } /// public async Task RequestRebuildAsync( RebuildRequest request, CancellationToken cancellationToken = default) { request.Validate(); _logger.LogInformation( "Requesting rebuild for {Package} {Version} via {Backend}", request.Package, request.Version, request.PreferredBackend); // For now, generate a job ID and start the rebuild var jobId = Guid.NewGuid().ToString("N")[..12]; // Store the request for status tracking // In production, would persist to database return jobId; } /// public async Task GetStatusAsync( string jobId, CancellationToken cancellationToken = default) { // In production, would query from database/job queue return new RebuildStatus { JobId = jobId, State = RebuildState.Queued, CurrentStage = "Pending" }; } /// public async Task DownloadArtifactsAsync( string jobId, string outputDirectory, CancellationToken cancellationToken = default) { Directory.CreateDirectory(outputDirectory); var artifacts = await _reproduceDebianClient.DownloadArtifactsAsync( jobId, outputDirectory, cancellationToken); return RebuildResult.Successful( jobId, artifacts, artifacts.Count > 0, RebuildBackend.ReproduceDebian); } /// public async Task RebuildLocalAsync( string buildinfoPath, LocalRebuildOptions? options = null, CancellationToken cancellationToken = default) { if (!File.Exists(buildinfoPath)) { return RebuildResult.Failed( Guid.NewGuid().ToString("N")[..12], $"Buildinfo file not found: {buildinfoPath}", backend: RebuildBackend.Local); } return await _localBackend.RebuildAsync(buildinfoPath, options, cancellationToken); } /// public async Task QueryExistingRebuildAsync( string package, string version, string architecture, CancellationToken cancellationToken = default) { _logger.LogDebug( "Querying existing rebuild for {Package} {Version} {Arch}", package, version, architecture); var buildInfo = await _reproduceDebianClient.QueryBuildAsync( package, version, architecture, cancellationToken); if (buildInfo is null) { return null; } return new RebuildInfo { JobId = buildInfo.Id, Package = buildInfo.Package, Version = buildInfo.Version, Architecture = buildInfo.Architecture, Reproducible = buildInfo.Reproducible, BuiltAt = buildInfo.CompletedAt ?? buildInfo.StartedAt ?? DateTimeOffset.MinValue, Backend = RebuildBackend.ReproduceDebian }; } } /// /// Configuration for the rebuild service. /// public sealed record RebuildServiceOptions { /// /// Gets the default backend to use. /// public RebuildBackend DefaultBackend { get; init; } = RebuildBackend.ReproduceDebian; /// /// Gets the output directory for artifacts. /// public string OutputDirectory { get; init; } = Path.Combine(Path.GetTempPath(), "stella-rebuilds"); /// /// Gets whether to prefer local rebuilds. /// public bool PreferLocalRebuild { get; init; } = false; /// /// Gets the job retention period. /// public TimeSpan JobRetention { get; init; } = TimeSpan.FromDays(30); }