feat: add controlled local PXP-OA parity workflow

This commit is contained in:
codex
2026-08-06 11:17:33 +03:00
parent 0879d716dd
commit 75a1097653
8 changed files with 212 additions and 3 deletions
@@ -121,7 +121,9 @@ public sealed class LocalPackageStore(string root, IFileHasher hasher, IPackageL
private async Task<bool> PointerMatchesContentAsync(Pointer pointer,string id,string version,CancellationToken ct)
{ if(pointer.SchemaVersion==1) return true; try { var content=await ContentFingerprintAsync(id,version,ct); return string.Equals(pointer.ManifestSha256,content.Manifest,StringComparison.OrdinalIgnoreCase)&&string.Equals(pointer.PayloadSetSha256,content.PayloadSet,StringComparison.OrdinalIgnoreCase)&&pointer.PayloadFiles is not null&&pointer.PayloadFiles.SequenceEqual(content.Files); } catch{return false;} }
private async Task<bool> ExistingMatchesAsync(string directory,PackageManifest expected,CancellationToken ct)
{ try { var stored=PackageManifestJson.Parse(await File.ReadAllTextAsync(Path.Combine(directory,StoredManifestFileName),ct)); if(stored!=expected) return false; foreach(var file in expected.Files){var path=PackageFilePath(directory,file.Path);var info=new FileInfo(path);if(!info.Exists||info.Length!=file.Length||!string.Equals(await hasher.Sha256Async(path,ct),file.Sha256,StringComparison.OrdinalIgnoreCase))return false;}return true;}catch{return false;} }
{ try { var stored=PackageManifestJson.Parse(await File.ReadAllTextAsync(Path.Combine(directory,StoredManifestFileName),ct)); if(!ManifestEquivalent(stored,expected)) return false; foreach(var file in expected.Files){var path=PackageFilePath(directory,file.Path);var info=new FileInfo(path);if(!info.Exists||info.Length!=file.Length||!string.Equals(await hasher.Sha256Async(path,ct),file.Sha256,StringComparison.OrdinalIgnoreCase))return false;}return true;}catch{return false;} }
private static bool ManifestEquivalent(PackageManifest left,PackageManifest right) =>
left.SchemaVersion==right.SchemaVersion && left.PackageId==right.PackageId && left.Version==right.Version && left.Product==right.Product && left.ProductYear==right.ProductYear && string.Equals(left.Architecture,right.Architecture,StringComparison.OrdinalIgnoreCase) && left.EntryAssembly==right.EntryAssembly && left.Startup==right.Startup && string.Equals(left.RolloutChannel,right.RolloutChannel,StringComparison.OrdinalIgnoreCase) && left.Files.Count==right.Files.Count && left.Files.OrderBy(x=>x.Path,StringComparer.Ordinal).SequenceEqual(right.Files.OrderBy(x=>x.Path,StringComparer.Ordinal));
private async Task<bool> MatchesImmutableManifestAsync(ImmutableLocalPackage package,PackageRequirement requirement,CancellationToken ct)
{ try { if (!package.LocalPackageRoot.Equals(VersionDirectory(package.PackageId,package.Version),StringComparison.OrdinalIgnoreCase)) return false; var manifest=PackageManifestJson.Parse(await File.ReadAllTextAsync(Path.Combine(package.LocalPackageRoot,StoredManifestFileName),ct)); if (!string.Equals(manifest.PackageId,requirement.PackageId,StringComparison.Ordinal)||!string.Equals(manifest.Version,requirement.RequiredVersion,StringComparison.Ordinal)||!string.Equals(manifest.Product,requirement.Product,StringComparison.Ordinal)||manifest.ProductYear!=requirement.ProductYear||!string.Equals(manifest.Architecture,requirement.Architecture,StringComparison.OrdinalIgnoreCase)||!string.Equals(manifest.RolloutChannel,requirement.Channel,StringComparison.OrdinalIgnoreCase)) return false; foreach(var f in manifest.Files){var p=PackageFilePath(package.LocalPackageRoot,f.Path); var i=new FileInfo(p); if(!i.Exists||i.Length!=f.Length||!string.Equals(await hasher.Sha256Async(p,ct),f.Sha256,StringComparison.OrdinalIgnoreCase)) return false;} return true; } catch { return false; } }
private static string PackageFilePath(string packageRoot, string relativePath)