6 changed files with 170 additions and 5 deletions
@ -0,0 +1,123 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.IO.Compression; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Text.RegularExpressions; |
|||
using Nuke.Common.Tooling; |
|||
|
|||
public static class ApiDiffValidation |
|||
{ |
|||
public static void ValidatePackage( |
|||
Tool apiCompatTool, string packagePath, Version baselineVersion, |
|||
string suppressionFilesFolder, bool updateSuppressionFile) |
|||
{ |
|||
if (baselineVersion is null) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Build \"api-baseline\" parameter must be set when running Nuke CreatePackages"); |
|||
} |
|||
|
|||
if (!Directory.Exists(suppressionFilesFolder)) |
|||
{ |
|||
Directory.CreateDirectory(suppressionFilesFolder!); |
|||
} |
|||
|
|||
using (var baselineStream = DownloadBaselinePackage(packagePath, baselineVersion)) |
|||
using (var target = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.Read), ZipArchiveMode.Read)) |
|||
using (var baseline = new ZipArchive(baselineStream, ZipArchiveMode.Read)) |
|||
using (Helpers.UseTempDir(out var tempFolder)) |
|||
{ |
|||
var targetDlls = GetDlls(target); |
|||
var baselineDlls = GetDlls(baseline); |
|||
|
|||
var left = new List<string>(); |
|||
var right = new List<string>(); |
|||
|
|||
var suppressionFile = Path.Combine(suppressionFilesFolder, Path.GetFileName(packagePath) + ".xml"); |
|||
|
|||
foreach (var baselineDll in baselineDlls) |
|||
{ |
|||
var baselineDllPath = Path.Combine("baseline", baselineDll.target, baselineDll.entry.Name); |
|||
var baselineDllRealPath = Path.Combine(tempFolder, baselineDllPath); |
|||
Directory.CreateDirectory(Path.GetDirectoryName(baselineDllRealPath)!); |
|||
using (var baselineDllFile = File.Create(baselineDllRealPath)) |
|||
{ |
|||
baselineDll.entry.Open().CopyTo(baselineDllFile); |
|||
} |
|||
|
|||
var targetDll = targetDlls.FirstOrDefault(e => |
|||
e.target == baselineDll.target && e.entry.Name == baselineDll.entry.Name); |
|||
if (targetDll.entry is null) |
|||
{ |
|||
throw new InvalidOperationException($"Some assemblies are missing in the new package: {baselineDll.entry.Name} for {baselineDll.target}"); |
|||
} |
|||
|
|||
var targetDllPath = Path.Combine("target", targetDll.target, targetDll.entry.Name); |
|||
var targetDllRealPath = Path.Combine(tempFolder, targetDllPath); |
|||
Directory.CreateDirectory(Path.GetDirectoryName(targetDllRealPath)!); |
|||
using (var targetDllFile = File.Create(targetDllRealPath)) |
|||
{ |
|||
targetDll.entry.Open().CopyTo(targetDllFile); |
|||
} |
|||
|
|||
left.Add(baselineDllPath); |
|||
right.Add(targetDllPath); |
|||
} |
|||
|
|||
var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; |
|||
updateSuppressionFile = true; |
|||
if (File.Exists(suppressionFile)) |
|||
{ |
|||
args += $""" --suppression-file="{suppressionFile}" """; |
|||
} |
|||
if (updateSuppressionFile) |
|||
{ |
|||
args += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file=true """; |
|||
} |
|||
|
|||
apiCompatTool(args, tempFolder); |
|||
} |
|||
} |
|||
|
|||
private static IReadOnlyCollection<(string target, ZipArchiveEntry entry)> GetDlls(ZipArchive archive) |
|||
{ |
|||
return archive.Entries |
|||
.Where(e => Path.GetExtension(e.FullName) == ".dll") |
|||
.Select(e => ( |
|||
entry: e, |
|||
isRef: e.FullName.Contains("ref/"), |
|||
target: Path.GetDirectoryName(e.FullName)!.Split('/').Last()) |
|||
) |
|||
.GroupBy(e => (e.target, e.entry.Name)) |
|||
.Select(g => g.MaxBy(e => e.isRef)) |
|||
.Select(e => (e.target, e.entry)) |
|||
.ToArray(); |
|||
} |
|||
|
|||
static Stream DownloadBaselinePackage(string packagePath, Version baselineVersion) |
|||
{ |
|||
Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion); |
|||
|
|||
try |
|||
{ |
|||
var packageId = Regex.Replace( |
|||
Path.GetFileNameWithoutExtension(packagePath), |
|||
"""(\.\d+\.\d+\.\d+)$""", ""); |
|||
|
|||
using var httpClient = new HttpClient(); |
|||
using var response = httpClient.Send(new HttpRequestMessage(HttpMethod.Get, |
|||
$"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}")); |
|||
using var stream = response.Content.ReadAsStream(); |
|||
var memoryStream = new MemoryStream(); |
|||
stream.CopyTo(memoryStream); |
|||
memoryStream.Seek(0, SeekOrigin.Begin); |
|||
return memoryStream; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw new InvalidOperationException($"Downloading baseline package for {packagePath} failed.\r" + ex.Message, ex); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue