// -----------------------------------------------------------------------------
// 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);
}