feat: add explicit package activation receipts

This commit is contained in:
codex
2026-08-06 08:20:50 +03:00
parent 20f4b6a7b8
commit c4c611b35b
2 changed files with 19 additions and 0 deletions
+3
View File
@@ -17,6 +17,9 @@ public sealed record ValidationResult(IReadOnlyList<ValidationIssue> Issues)
{ public bool IsValid => Issues.Count == 0; public static ValidationResult Success { get; } = new([]); }
public sealed record PackageProgress(string Stage, int Completed, int Total);
public sealed record ActivationResult(string CurrentVersion, string? PreviousVersion);
public sealed record PackageActivationReceipt(string OperationId, string PackageId, string Version, string? PreviousVersion, DateTimeOffset ActivatedUtc);
public sealed record PackageActivationResult(string Outcome, PackageActivationReceipt? Receipt);
public sealed record PackageRollbackResult(string Outcome, string? CurrentVersion);
public sealed record PackageRequirement(string PackageId, string Product, int ProductYear, string Architecture, string Channel, string RequiredVersion, string RosterIdentity, bool AllowOfflineFallback);
public sealed record CatalogPackage(PackageManifest Manifest, string SourceRoot);
public sealed record PackageResolutionEvidence(string Code, string Detail);
@@ -29,6 +29,22 @@ public sealed class LocalPackageStore(string root, IFileHasher hasher) : ILocalP
var temp = PointerPath(id) + ".tmp"; await File.WriteAllTextAsync(temp, JsonSerializer.Serialize(new Pointer(version, prior?.Current)), ct); File.Move(temp, PointerPath(id), true);
return new(version, prior?.Current);
}
public async Task<PackageActivationResult> ActivateImmutableAsync(ImmutableLocalPackage package, PackageRequirement requirement, CancellationToken ct)
{
if (!string.Equals(package.PackageId, requirement.PackageId, StringComparison.Ordinal) || !string.Equals(package.Version, requirement.RequiredVersion, StringComparison.Ordinal)) return new("Blocked", null);
var prior = await ReadPointer(package.PackageId, ct);
if (prior?.Current == package.Version) return new("AlreadyActiveExact", new(Guid.NewGuid().ToString("N"), package.PackageId, package.Version, prior.Previous, DateTimeOffset.UtcNow));
var activated = await ActivateAsync(package.PackageId, package.Version, ct);
return new("Activated", new(Guid.NewGuid().ToString("N"), package.PackageId, activated.CurrentVersion, activated.PreviousVersion, DateTimeOffset.UtcNow));
}
public async Task<PackageRollbackResult> RollbackAsync(PackageActivationReceipt receipt, CancellationToken ct)
{
var current=await GetCurrentVersionAsync(receipt.PackageId,ct);
if (!string.Equals(current,receipt.Version,StringComparison.Ordinal)) return new("RollbackBlockedStateChanged",current);
if (string.IsNullOrWhiteSpace(receipt.PreviousVersion)) return new("RollbackNotApplicable",current);
var restored=await ActivateAsync(receipt.PackageId,receipt.PreviousVersion,ct);
return new("RolledBack",restored.CurrentVersion);
}
public async Task<string?> RollbackAsync(string id, CancellationToken ct) { var p = await ReadPointer(id, ct); return p?.Previous is null ? null : (await ActivateAsync(id, p.Previous, ct)).CurrentVersion; }
public async Task BindAsync(PackageRequirement requirement, string version, CancellationToken ct)
{