From 96f21b6cbf3cdc3d6eb96dc7a0e3eb22ea15c273 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 6 Jul 2023 14:17:01 -0700 Subject: [PATCH 1/9] Create initial ApiDiffValidation implementation --- .nuke/build.schema.json | 10 +++ nukebuild/ApiDiffValidation.cs | 123 +++++++++++++++++++++++++++++++++ nukebuild/Build.cs | 21 +++++- nukebuild/BuildParameters.cs | 15 +++- nukebuild/Shims.cs | 4 +- nukebuild/_build.csproj | 2 + 6 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 nukebuild/ApiDiffValidation.cs diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index d2f2ee36d5..8bc812b0e2 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -6,6 +6,10 @@ "build": { "type": "object", "properties": { + "ApiValidationBaseline": { + "type": "string", + "description": "api-baseline" + }, "Configuration": { "type": "string", "description": "configuration" @@ -89,6 +93,7 @@ "RunRenderTests", "RunTests", "RunToolsTests", + "ValidateApiDiff", "ZipFiles" ] } @@ -124,10 +129,15 @@ "RunRenderTests", "RunTests", "RunToolsTests", + "ValidateApiDiff", "ZipFiles" ] } }, + "UpdateApiValidationSuppression": { + "type": "boolean", + "description": "update-api-suppression" + }, "Verbosity": { "type": "string", "description": "Logging verbosity during build execution. Default is 'Normal'", diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs new file mode 100644 index 0000000000..5112f87728 --- /dev/null +++ b/nukebuild/ApiDiffValidation.cs @@ -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(); + var right = new List(); + + 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); + } + } +} diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 524c9fa4e4..644c1267d6 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -36,6 +36,10 @@ using MicroCom.CodeGenerator; partial class Build : NukeBuild { BuildParameters Parameters { get; set; } + + [PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll")] + Tool ApiCompatTool; + protected override void OnBuildInitialized() { Parameters = new BuildParameters(this); @@ -278,7 +282,19 @@ partial class Build : NukeBuild RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." + Parameters.Version + ".nupkg"); }); - + + Target ValidateApiDiff => _ => _ + .DependsOn(CreateNugetPackages) + .Executes(() => + { + foreach (var nugetPackage in Directory.GetFiles(Parameters.NugetRoot)) + { + ApiDiffValidation.ValidatePackage( + ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline, + Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression); + } + }); + Target RunTests => _ => _ .DependsOn(RunCoreLibsTests) .DependsOn(RunRenderTests) @@ -288,7 +304,8 @@ partial class Build : NukeBuild Target Package => _ => _ .DependsOn(RunTests) - .DependsOn(CreateNugetPackages); + .DependsOn(CreateNugetPackages) + .DependsOn(ValidateApiDiff); Target CiAzureLinux => _ => _ .DependsOn(RunTests); diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index dfa914d1db..67ed086e20 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -22,6 +22,12 @@ public partial class Build [Parameter("skip-previewer")] public bool SkipPreviewer { get; set; } + [Parameter("api-baseline")] + public string ApiValidationBaseline { get; set; } + + [Parameter("update-api-suppression")] + public bool UpdateApiValidationSuppression { get; set; } + public class BuildParameters { public string Configuration { get; } @@ -57,7 +63,9 @@ public partial class Build public string FileZipSuffix { get; } public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; } - + public Version ApiValidationBaseline { get; } + public bool UpdateApiValidationSuppression { get; } + public AbsolutePath ApiValidationSuppressionFiles { get; } public BuildParameters(Build b) { @@ -65,6 +73,10 @@ public partial class Build Configuration = b.Configuration ?? "Release"; SkipTests = b.SkipTests; SkipPreviewer = b.SkipPreviewer; + ApiValidationBaseline = b.ApiValidationBaseline is not null ? + new Version(b.ApiValidationBaseline) : + new Version(11, 0); + UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; // CONFIGURATION MainRepo = "https://github.com/AvaloniaUI/Avalonia"; @@ -125,6 +137,7 @@ public partial class Build FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix); ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix); + ApiValidationSuppressionFiles = RootDirectory / "api"; } string GetVersion() diff --git a/nukebuild/Shims.cs b/nukebuild/Shims.cs index 6f79972ad6..eecfcf6da1 100644 --- a/nukebuild/Shims.cs +++ b/nukebuild/Shims.cs @@ -9,12 +9,12 @@ using Numerge; public partial class Build { - static void Information(string info) + internal static void Information(string info) { Logger.Info(info); } - static void Information(string info, params object[] args) + internal static void Information(string info, params object[] args) { Logger.Info(info, args); } diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 30e1200220..43453833d7 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -22,6 +22,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + From 6eaf0f9e4f12e32a6851de7a167f9440891a3f4c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 6 Jul 2023 14:17:10 -0700 Subject: [PATCH 2/9] Break the API --- src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs | 4 ++-- src/Avalonia.Controls.ColorPicker/HsvComponent.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs index b1d15d6b17..fb981db762 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs @@ -24,14 +24,14 @@ namespace Avalonia.Controls /// The new/updated color that triggered the change event. public ColorChangedEventArgs(Color oldColor, Color newColor) { - OldColor = oldColor; + //OldColor = oldColor; NewColor = newColor; } /// /// Gets the old/original color from before the change event. /// - public Color OldColor { get; private set; } + //public Color OldColor { get; private set; } /// /// Gets the new/updated color that triggered the change event. diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs index 1a7a13166a..998633a8e1 100644 --- a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls /// /// Also see: /// - Alpha = 0, + // Alpha = 0, /// /// The Hue component. From c72aa402a8e5fbc84bf47b2afbd862f537055682 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 6 Jul 2023 14:25:54 -0700 Subject: [PATCH 3/9] Run in parallel --- nukebuild/ApiDiffValidation.cs | 18 +++++++++--------- nukebuild/Build.cs | 10 ++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 5112f87728..0ebded3b5a 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -5,11 +5,12 @@ using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; +using System.Threading.Tasks; using Nuke.Common.Tooling; public static class ApiDiffValidation { - public static void ValidatePackage( + public static async Task ValidatePackage( Tool apiCompatTool, string packagePath, Version baselineVersion, string suppressionFilesFolder, bool updateSuppressionFile) { @@ -24,7 +25,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(suppressionFilesFolder!); } - using (var baselineStream = DownloadBaselinePackage(packagePath, baselineVersion)) + using (var baselineStream = await 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)) @@ -44,7 +45,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(Path.GetDirectoryName(baselineDllRealPath)!); using (var baselineDllFile = File.Create(baselineDllRealPath)) { - baselineDll.entry.Open().CopyTo(baselineDllFile); + await baselineDll.entry.Open().CopyToAsync(baselineDllFile); } var targetDll = targetDlls.FirstOrDefault(e => @@ -59,7 +60,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(Path.GetDirectoryName(targetDllRealPath)!); using (var targetDllFile = File.Create(targetDllRealPath)) { - targetDll.entry.Open().CopyTo(targetDllFile); + await targetDll.entry.Open().CopyToAsync(targetDllFile); } left.Add(baselineDllPath); @@ -67,7 +68,6 @@ public static class ApiDiffValidation } var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; - updateSuppressionFile = true; if (File.Exists(suppressionFile)) { args += $""" --suppression-file="{suppressionFile}" """; @@ -96,7 +96,7 @@ public static class ApiDiffValidation .ToArray(); } - static Stream DownloadBaselinePackage(string packagePath, Version baselineVersion) + static async Task DownloadBaselinePackage(string packagePath, Version baselineVersion) { Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion); @@ -107,11 +107,11 @@ public static class ApiDiffValidation """(\.\d+\.\d+\.\d+)$""", ""); using var httpClient = new HttpClient(); - using var response = httpClient.Send(new HttpRequestMessage(HttpMethod.Get, + using var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}")); - using var stream = response.Content.ReadAsStream(); + await using var stream = await response.Content.ReadAsStreamAsync(); var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); + await stream.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); return memoryStream; } diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 644c1267d6..7c921cb630 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -285,14 +285,12 @@ partial class Build : NukeBuild Target ValidateApiDiff => _ => _ .DependsOn(CreateNugetPackages) - .Executes(() => + .Executes(async () => { - foreach (var nugetPackage in Directory.GetFiles(Parameters.NugetRoot)) - { - ApiDiffValidation.ValidatePackage( + await Task.WhenAll( + Directory.GetFiles(Parameters.NugetRoot).Select(nugetPackage => ApiDiffValidation.ValidatePackage( ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline, - Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression); - } + Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression))); }); Target RunTests => _ => _ From 65e29787dd5338a333f625b711e063c29a562dff Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 11:35:10 -0700 Subject: [PATCH 4/9] Parameters fixes --- .nuke/build.schema.json | 30 ++++++++++++------------------ nukebuild/ApiDiffValidation.cs | 19 +++++++++++-------- nukebuild/BuildParameters.cs | 21 ++++++++++----------- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 8bc812b0e2..b802589fc7 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -6,21 +6,18 @@ "build": { "type": "object", "properties": { - "ApiValidationBaseline": { - "type": "string", - "description": "api-baseline" + "api-baseline": { + "type": "string" }, - "Configuration": { - "type": "string", - "description": "configuration" + "configuration": { + "type": "string" }, "Continue": { "type": "boolean", "description": "Indicates to continue a previously failed build attempt" }, - "ForceNugetVersion": { - "type": "string", - "description": "force-nuget-version" + "force-nuget-version": { + "type": "string" }, "Help": { "type": "boolean", @@ -98,13 +95,11 @@ ] } }, - "SkipPreviewer": { - "type": "boolean", - "description": "skip-previewer" + "skip-previewer": { + "type": "boolean" }, - "SkipTests": { - "type": "boolean", - "description": "skip-tests" + "skip-tests": { + "type": "boolean" }, "Target": { "type": "array", @@ -134,9 +129,8 @@ ] } }, - "UpdateApiValidationSuppression": { - "type": "boolean", - "description": "update-api-suppression" + "update-api-suppression": { + "type": "boolean" }, "Verbosity": { "type": "string", diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 0ebded3b5a..41259d9b2f 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -10,8 +10,10 @@ using Nuke.Common.Tooling; public static class ApiDiffValidation { + private static readonly HttpClient s_httpClient = new(); + public static async Task ValidatePackage( - Tool apiCompatTool, string packagePath, Version baselineVersion, + Tool apiCompatTool, string packagePath, string baselineVersion, string suppressionFilesFolder, bool updateSuppressionFile) { if (baselineVersion is null) @@ -25,7 +27,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(suppressionFilesFolder!); } - using (var baselineStream = await DownloadBaselinePackage(packagePath, baselineVersion)) + await using (var baselineStream = await 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)) @@ -43,7 +45,7 @@ public static class ApiDiffValidation 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)) + await using (var baselineDllFile = File.Create(baselineDllRealPath)) { await baselineDll.entry.Open().CopyToAsync(baselineDllFile); } @@ -58,7 +60,7 @@ public static class ApiDiffValidation 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)) + await using (var targetDllFile = File.Create(targetDllRealPath)) { await targetDll.entry.Open().CopyToAsync(targetDllFile); } @@ -96,7 +98,7 @@ public static class ApiDiffValidation .ToArray(); } - static async Task DownloadBaselinePackage(string packagePath, Version baselineVersion) + static async Task DownloadBaselinePackage(string packagePath, string baselineVersion) { Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion); @@ -106,9 +108,10 @@ public static class ApiDiffValidation Path.GetFileNameWithoutExtension(packagePath), """(\.\d+\.\d+\.\d+)$""", ""); - using var httpClient = new HttpClient(); - using var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, - $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}")); + using var response = await s_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, + $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}"), HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + await using var stream = await response.Content.ReadAsStreamAsync(); var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream); diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index 67ed086e20..75c859f2e8 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -10,22 +10,22 @@ using static Nuke.Common.IO.PathConstruction; public partial class Build { - [Parameter("configuration")] + [Parameter(Name = "configuration")] public string Configuration { get; set; } - [Parameter("skip-tests")] + [Parameter(Name = "skip-tests")] public bool SkipTests { get; set; } - [Parameter("force-nuget-version")] + [Parameter(Name = "force-nuget-version")] public string ForceNugetVersion { get; set; } - [Parameter("skip-previewer")] + [Parameter(Name = "skip-previewer")] public bool SkipPreviewer { get; set; } - [Parameter("api-baseline")] + [Parameter(Name = "api-baseline")] public string ApiValidationBaseline { get; set; } - [Parameter("update-api-suppression")] + [Parameter(Name = "update-api-suppression")] public bool UpdateApiValidationSuppression { get; set; } public class BuildParameters @@ -63,7 +63,7 @@ public partial class Build public string FileZipSuffix { get; } public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; } - public Version ApiValidationBaseline { get; } + public string ApiValidationBaseline { get; } public bool UpdateApiValidationSuppression { get; } public AbsolutePath ApiValidationSuppressionFiles { get; } @@ -73,10 +73,6 @@ public partial class Build Configuration = b.Configuration ?? "Release"; SkipTests = b.SkipTests; SkipPreviewer = b.SkipPreviewer; - ApiValidationBaseline = b.ApiValidationBaseline is not null ? - new Version(b.ApiValidationBaseline) : - new Version(11, 0); - UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; // CONFIGURATION MainRepo = "https://github.com/AvaloniaUI/Avalonia"; @@ -115,6 +111,9 @@ public partial class Build // VERSION Version = b.ForceNugetVersion ?? GetVersion(); + ApiValidationBaseline = b.ApiValidationBaseline ?? new Version(new Version(Version).Major, 0).ToString(); + UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; + if (IsRunningOnAzure) { if (!IsNuGetRelease) From 8097a1c9934d19373aee0062bc5ce57b5f42ab03 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:19:00 -0700 Subject: [PATCH 5/9] Throw on any output error --- nukebuild/ApiDiffValidation.cs | 29 ++++++++++++++++++++--------- nukebuild/Build.cs | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 41259d9b2f..91bd54fd89 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -69,17 +69,28 @@ public static class ApiDiffValidation right.Add(targetDllPath); } - var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; - if (File.Exists(suppressionFile)) + if (left.Any()) { - args += $""" --suppression-file="{suppressionFile}" """; - } - if (updateSuppressionFile) - { - args += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file=true """; - } + var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; + if (File.Exists(suppressionFile)) + { + args += $""" --suppression-file="{suppressionFile}" """; + } - apiCompatTool(args, tempFolder); + if (updateSuppressionFile) + { + args += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file=true """; + } + + var result = apiCompatTool(args, tempFolder) + .Where(t => t.Type == OutputType.Err).ToArray(); + if (result.Any()) + { + throw new AggregateException( + $"ApiDiffValidation task has failed for \"{Path.GetFileName(packagePath)}\" package", + result.Select(r => new Exception(r.Text))); + } + } } } diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 7c921cb630..b82c446249 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -37,7 +37,7 @@ partial class Build : NukeBuild { BuildParameters Parameters { get; set; } - [PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll")] + [PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll", Framework = "net6.0")] Tool ApiCompatTool; protected override void OnBuildInitialized() From 003bed5c80599dcb18997d64cfe3917d3adbd578 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:21:52 -0700 Subject: [PATCH 6/9] Update exclusions --- nukebuild/ApiDiffValidation.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 91bd54fd89..1bcb761cbb 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -97,7 +97,9 @@ public static class ApiDiffValidation private static IReadOnlyCollection<(string target, ZipArchiveEntry entry)> GetDlls(ZipArchive archive) { return archive.Entries - .Where(e => Path.GetExtension(e.FullName) == ".dll") + .Where(e => Path.GetExtension(e.FullName) == ".dll" + // Exclude analyzers and build task, as we don't care about breaking changes there + && !e.FullName.Contains("analyzers/") && !e.Name.Contains("Avalonia.Build.Tasks")) .Select(e => ( entry: e, isRef: e.FullName.Contains("ref/"), From 36b74afa17f587a3438bf677fdb0d1587754aac4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:24:38 -0700 Subject: [PATCH 7/9] Set UpdateApiValidationSuppression to true by default on local build --- nukebuild/BuildParameters.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index 75c859f2e8..897fdc84f1 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -26,7 +26,7 @@ public partial class Build public string ApiValidationBaseline { get; set; } [Parameter(Name = "update-api-suppression")] - public bool UpdateApiValidationSuppression { get; set; } + public bool? UpdateApiValidationSuppression { get; set; } public class BuildParameters { @@ -112,7 +112,7 @@ public partial class Build Version = b.ForceNugetVersion ?? GetVersion(); ApiValidationBaseline = b.ApiValidationBaseline ?? new Version(new Version(Version).Major, 0).ToString(); - UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; + UpdateApiValidationSuppression = b.UpdateApiValidationSuppression ?? IsLocalBuild; if (IsRunningOnAzure) { From 13c6fbe1dbd87e558cc9db0a54665bacb11a4349 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:31:44 -0700 Subject: [PATCH 8/9] Revert "Break the API" This reverts commit f043f6b1617c85706c7c33741024a09b5b8c7d89. --- src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs | 4 ++-- src/Avalonia.Controls.ColorPicker/HsvComponent.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs index fb981db762..b1d15d6b17 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs @@ -24,14 +24,14 @@ namespace Avalonia.Controls /// The new/updated color that triggered the change event. public ColorChangedEventArgs(Color oldColor, Color newColor) { - //OldColor = oldColor; + OldColor = oldColor; NewColor = newColor; } /// /// Gets the old/original color from before the change event. /// - //public Color OldColor { get; private set; } + public Color OldColor { get; private set; } /// /// Gets the new/updated color that triggered the change event. diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs index 998633a8e1..1a7a13166a 100644 --- a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls /// /// Also see: /// - // Alpha = 0, + Alpha = 0, /// /// The Hue component. From c755ea628bf832fbdd9b8a575980d86564aedc0c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 23:39:13 -0700 Subject: [PATCH 9/9] Fix regex --- nukebuild/ApiDiffValidation.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 1bcb761cbb..a236178c83 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -117,9 +117,15 @@ public static class ApiDiffValidation try { + /* + Gets package name from versions like: + Avalonia.0.10.0-preview1 + Avalonia.11.0.999-cibuild0037534-beta + Avalonia.11.0.0 + */ var packageId = Regex.Replace( Path.GetFileNameWithoutExtension(packagePath), - """(\.\d+\.\d+\.\d+)$""", ""); + """(\.\d+\.\d+\.\d+(?:-.+)?)$""", ""); using var response = await s_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}"), HttpCompletionOption.ResponseHeadersRead);