Browse Source

Merge branch 'master' into importcompleted-typo-fix

pull/12117/head
Max Katz 3 years ago
committed by GitHub
parent
commit
95c1d85b44
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      .nuke/build.schema.json
  2. 145
      nukebuild/ApiDiffValidation.cs
  3. 19
      nukebuild/Build.cs
  4. 22
      nukebuild/BuildParameters.cs
  5. 4
      nukebuild/Shims.cs
  6. 2
      nukebuild/_build.csproj

28
.nuke/build.schema.json

@ -6,17 +6,18 @@
"build": {
"type": "object",
"properties": {
"Configuration": {
"type": "string",
"description": "configuration"
"api-baseline": {
"type": "string"
},
"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",
@ -89,17 +90,16 @@
"RunRenderTests",
"RunTests",
"RunToolsTests",
"ValidateApiDiff",
"ZipFiles"
]
}
},
"SkipPreviewer": {
"type": "boolean",
"description": "skip-previewer"
"skip-previewer": {
"type": "boolean"
},
"SkipTests": {
"type": "boolean",
"description": "skip-tests"
"skip-tests": {
"type": "boolean"
},
"Target": {
"type": "array",
@ -124,10 +124,14 @@
"RunRenderTests",
"RunTests",
"RunToolsTests",
"ValidateApiDiff",
"ZipFiles"
]
}
},
"update-api-suppression": {
"type": "boolean"
},
"Verbosity": {
"type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",

145
nukebuild/ApiDiffValidation.cs

@ -0,0 +1,145 @@
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 System.Threading.Tasks;
using Nuke.Common.Tooling;
public static class ApiDiffValidation
{
private static readonly HttpClient s_httpClient = new();
public static async Task ValidatePackage(
Tool apiCompatTool, string packagePath, string 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!);
}
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))
{
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)!);
await using (var baselineDllFile = File.Create(baselineDllRealPath))
{
await baselineDll.entry.Open().CopyToAsync(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)!);
await using (var targetDllFile = File.Create(targetDllRealPath))
{
await targetDll.entry.Open().CopyToAsync(targetDllFile);
}
left.Add(baselineDllPath);
right.Add(targetDllPath);
}
if (left.Any())
{
var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """;
if (File.Exists(suppressionFile))
{
args += $""" --suppression-file="{suppressionFile}" """;
}
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)));
}
}
}
}
private static IReadOnlyCollection<(string target, ZipArchiveEntry entry)> GetDlls(ZipArchive archive)
{
return archive.Entries
.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/"),
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 async Task<Stream> DownloadBaselinePackage(string packagePath, string baselineVersion)
{
Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion);
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+(?:-.+)?)$""", "");
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);
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Downloading baseline package for {packagePath} failed.\r" + ex.Message, ex);
}
}
}

19
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", Framework = "net6.0")]
Tool ApiCompatTool;
protected override void OnBuildInitialized()
{
Parameters = new BuildParameters(this);
@ -278,7 +282,17 @@ partial class Build : NukeBuild
RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." +
Parameters.Version + ".nupkg");
});
Target ValidateApiDiff => _ => _
.DependsOn(CreateNugetPackages)
.Executes(async () =>
{
await Task.WhenAll(
Directory.GetFiles(Parameters.NugetRoot).Select(nugetPackage => ApiDiffValidation.ValidatePackage(
ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline,
Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression)));
});
Target RunTests => _ => _
.DependsOn(RunCoreLibsTests)
.DependsOn(RunRenderTests)
@ -288,7 +302,8 @@ partial class Build : NukeBuild
Target Package => _ => _
.DependsOn(RunTests)
.DependsOn(CreateNugetPackages);
.DependsOn(CreateNugetPackages)
.DependsOn(ValidateApiDiff);
Target CiAzureLinux => _ => _
.DependsOn(RunTests);

22
nukebuild/BuildParameters.cs

@ -10,18 +10,24 @@ 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(Name = "api-baseline")]
public string ApiValidationBaseline { get; set; }
[Parameter(Name = "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 string ApiValidationBaseline { get; }
public bool UpdateApiValidationSuppression { get; }
public AbsolutePath ApiValidationSuppressionFiles { get; }
public BuildParameters(Build b)
{
@ -103,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 ?? IsLocalBuild;
if (IsRunningOnAzure)
{
if (!IsNuGetRelease)
@ -125,6 +136,7 @@ public partial class Build
FileZipSuffix = Version + ".zip";
ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix);
ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix);
ApiValidationSuppressionFiles = RootDirectory / "api";
}
string GetVersion()

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

2
nukebuild/_build.csproj

@ -22,6 +22,8 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageDownload Include="Microsoft.DotNet.ApiCompat.Tool" Version="[7.0.305]" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save