Skip to content

Commit e512ef4

Browse files
authored
[release/3.1] Add flexible baseline patterns: avoid updating online baseline with each release (#1752)
1 parent 5326a4c commit e512ef4

File tree

6 files changed

+147
-67
lines changed

6 files changed

+147
-67
lines changed

tools-local/prebuilt-baseline-online.xml

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,17 @@
11
<UsageData>
22
<CreatedByRid>centos.7-x64</CreatedByRid>
3-
<ProjectDirectories>
4-
<Dir>artifacts/src/ApplicationInsights-dotnet.53b80940842204f78708a538628288ff5d741a1d/</Dir>
5-
<Dir>artifacts/src/arcade.15f00efd583eab4372b2e9ca25bd80ace5b119ad/</Dir>
6-
<Dir>artifacts/src/extensions.1774c15ac24c65513fa9fc1f4fbb69be9a2a4e25/</Dir>
7-
<Dir>artifacts/src/xdt.c01a538851a8ab1a1fbeb2e6243f391fff7587b4/</Dir>
8-
<Dir>artifacts/src/aspnetcore-tooling.02423c0c6c445b12abea9fee03ebdb2e18b4a2dd/</Dir>
9-
<Dir>artifacts/src/aspnetcore.c75b3f7a2fb9fe21fd96c93c070fdfa88a2fbe97/</Dir>
10-
<Dir>artifacts/src/cli.781686b7534a8203e3944c2b111df5a279d9c43e/</Dir>
11-
<Dir>artifacts/src/cliCommandLineParser.0e89c2116ad28e404ba56c14d1c3f938caa25a01/</Dir>
12-
<Dir>artifacts/src/common.6e37cdfe96ac8b06a923242120169fafacd720e6/</Dir>
13-
<Dir>artifacts/src/installer.c423b556b530ff8a168386d63821acb6dfc6ee5d/</Dir>
14-
<Dir>artifacts/src/core-setup.9c1330deddc14e8c564e30402d6a644c43110778/</Dir>
15-
<Dir>artifacts/src/core-setup.9c1330deddc14e8c564e30402d6a644c43110778/</Dir>
16-
<Dir>artifacts/src/coreclr.f897710bc4efe6a046068fde0acf641667a8fff5/</Dir>
17-
<Dir>artifacts/src/coreclr.f897710bc4efe6a046068fde0acf641667a8fff5/</Dir>
18-
<Dir>artifacts/src/corefx.d6302a72f8eafa326d7572a02acf8f3021ebb9e8/</Dir>
19-
<Dir>artifacts/src/corefx.d6302a72f8eafa326d7572a02acf8f3021ebb9e8/</Dir>
20-
<Dir>artifacts/src/fsharp.dc86ab5d2c46e9a7c49290e7e2270ab1eeb0c61e/</Dir>
21-
<Dir>artifacts/src/known-good-tests./</Dir>
22-
<Dir>artifacts/src/known-good./</Dir>
23-
<Dir>artifacts/src/linker.1127689f262d52ea8ff68ef03d706fa62b3b40a1/</Dir>
24-
<Dir>artifacts/src/msbuild.e901037fe1815eae17424f860412d0b967d09461/</Dir>
25-
<Dir>src/netcorecli-fsc/</Dir>
26-
<Dir>artifacts/src/Newtonsoft.Json.cac0690ad133c5e166ce5642dc71175791404fad/</Dir>
27-
<Dir>artifacts/src/Newtonsoft.Json.e43dae94c26f0c30e9095327a3a9eac87193923d/</Dir>
28-
<Dir>artifacts/src/NuGet.Client.6f8eb3a2e1db6b458451b9cfd2a4f5557769b041/</Dir>
29-
<Dir>src/package-source-build/</Dir>
30-
<Dir>artifacts/src/roslyn.d8180a5ecafb92adcfbfe8cf9199eb23be1a1ccf/</Dir>
31-
<Dir>artifacts/src/sdk.82e2b68c9ef7f28307114c4ab4d43c11e6f7cd52/</Dir>
32-
<Dir>artifacts/src/sourcelink.f175b06862f889474b689a57527e489101c774cc/</Dir>
33-
<Dir>artifacts/src/standard.a5b5f2e1e369972c8ff1e2183979fab6099f52ef/</Dir>
34-
<Dir>artifacts/src/templating.10dad0fa67c3cd3db542c29d591de026ed45147b/</Dir>
35-
<Dir>artifacts/src/toolset.d04a69c978e9099d743ce68c667fafe9a1a7d59e/</Dir>
36-
<Dir>artifacts/src/vstest.55e7e45431c9c05656c999b902686e7402664573/</Dir>
37-
<Dir>artifacts/src/websdk.3478a1379fe3765b28eb3f7cb889fb45233fb09c/</Dir>
38-
<Dir>artifacts/src/xliff-tasks.173ee3bd61c9549557eefa3cfb718bdef157cb87/</Dir>
39-
<Dir>Tools/</Dir>
40-
<Dir>tools-local/tasks/</Dir>
41-
<Dir>artifacts/obj/</Dir>
42-
<Dir></Dir>
43-
</ProjectDirectories>
3+
<IgnorePatterns>
4+
<UsagePattern IdentityGlob="Microsoft.AspNetCore.App.Ref/3.*" />
5+
<UsagePattern IdentityGlob="Microsoft.DotNet.Web.ItemTemplates/3.1.*" />
6+
<UsagePattern IdentityGlob="Microsoft.DotNet.Web.ProjectTemplates.3.1/3.1.*" />
7+
<UsagePattern IdentityGlob="Microsoft.DotNet.Web.Spa.ProjectTemplates.3.1/3.1.*" />
8+
<UsagePattern IdentityGlob="Microsoft.Dotnet.WinForms.ProjectTemplates/3.1.*" />
9+
<UsagePattern IdentityGlob="Microsoft.DotNet.Wpf.ProjectTemplates/3.1.*" />
10+
</IgnorePatterns>
4411
<Usages>
4512
<Usage Id="MicroBuild.Core" Version="0.2.0" IsDirectDependency="true" IsAutoReferenced="true" />
4613
<Usage Id="MicroBuild.Core" Version="0.3.0" IsDirectDependency="true" />
4714
<Usage Id="MicroBuild.Core.Sentinel" Version="1.0.0" IsDirectDependency="true" IsAutoReferenced="true" />
48-
<Usage Id="Microsoft.AspNetCore.App.Ref" Version="3.0.1" />
49-
<Usage Id="Microsoft.AspNetCore.App.Ref" Version="3.1.8" />
5015
<Usage Id="Microsoft.Bcl.AsyncInterfaces" Version="1.1.0" />
5116
<Usage Id="Microsoft.Build" Version="15.7.179" IsDirectDependency="true" />
5217
<Usage Id="Microsoft.Build.CentralPackageVersions" Version="2.0.1" />
@@ -81,19 +46,15 @@
8146
<Usage Id="Microsoft.DotNet.Web.ItemTemplates" Version="2.1.14" />
8247
<Usage Id="Microsoft.DotNet.Web.ItemTemplates" Version="2.2.8" />
8348
<Usage Id="Microsoft.DotNet.Web.ItemTemplates" Version="3.0.1" />
84-
<Usage Id="Microsoft.DotNet.Web.ItemTemplates" Version="3.1.8" />
8549
<Usage Id="Microsoft.DotNet.Web.ProjectTemplates.2.1" Version="2.1.14" />
8650
<Usage Id="Microsoft.DotNet.Web.ProjectTemplates.2.2" Version="2.2.8" />
8751
<Usage Id="Microsoft.DotNet.Web.ProjectTemplates.3.0" Version="3.0.1" />
88-
<Usage Id="Microsoft.DotNet.Web.ProjectTemplates.3.1" Version="3.1.8" />
8952
<Usage Id="Microsoft.DotNet.Web.Spa.ProjectTemplates.2.1" Version="2.1.14" />
9053
<Usage Id="Microsoft.DotNet.Web.Spa.ProjectTemplates.2.2" Version="2.2.8" />
9154
<Usage Id="Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0" Version="3.0.1" />
92-
<Usage Id="Microsoft.DotNet.Web.Spa.ProjectTemplates.3.1" Version="3.1.8" />
9355
<Usage Id="Microsoft.Dotnet.WinForms.ProjectTemplates" Version="4.8.0-rc2.19462.10" />
9456
<Usage Id="Microsoft.Dotnet.WinForms.ProjectTemplates" Version="4.8.1-servicing.20412.5" />
9557
<Usage Id="Microsoft.DotNet.Wpf.ProjectTemplates" Version="3.0.0" />
96-
<Usage Id="Microsoft.DotNet.Wpf.ProjectTemplates" Version="3.1.8-servicing.20412.3" />
9758
<Usage Id="Microsoft.Extensions.DependencyModel" Version="2.1.0" IsDirectDependency="true" />
9859
<Usage Id="Microsoft.NETCore.App" Version="2.0.0" IsDirectDependency="true" IsAutoReferenced="true" />
9960
<Usage Id="Microsoft.NETCore.App" Version="2.1.0" IsDirectDependency="true" IsAutoReferenced="true" />

tools-local/tasks/Directory.Build.props

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@
3535
Include="$(SdkReferenceDir)NuGet.*.dll"
3636
Exclude="$(SdkReferenceDir)NuGet.CommandLine.XPlat.dll" />
3737

38-
<SdkAssemblyReference
39-
Include="@(SdkAssembly -> '%(FileName)')"
40-
HintPath="$(SdkReferenceDir)%(Identity).dll" />
38+
<SdkAssemblyReference Include="@(SdkAssembly -> '%(FullPath)')" />
4139
</ItemGroup>
4240

4341
</Project>

tools-local/tasks/Microsoft.DotNet.SourceBuild.Tasks.XPlat/UsageReport/UsageData.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class UsageData
1313
public string CreatedByRid { get; set; }
1414
public string[] ProjectDirectories { get; set; }
1515
public PackageIdentity[] NeverRestoredTarballPrebuilts { get; set; }
16+
public UsagePattern[] IgnorePatterns { get; set; }
1617
public Usage[] Usages { get; set; }
1718

1819
public XElement ToXml() => new XElement(
@@ -29,6 +30,10 @@ public class UsageData
2930
NeverRestoredTarballPrebuilts
3031
.OrderBy(id => id)
3132
.Select(id => id.ToXElement())),
33+
IgnorePatterns?.Any() != true ? null : new XElement(
34+
nameof(IgnorePatterns),
35+
IgnorePatterns
36+
.Select(p => p.ToXml())),
3237
Usages?.Any() != true ? null : new XElement(
3338
nameof(Usages),
3439
Usages
@@ -40,16 +45,20 @@ public class UsageData
4045
{
4146
CreatedByRid = xml.Element(nameof(CreatedByRid))
4247
?.Value,
43-
ProjectDirectories = xml.Element(nameof(ProjectDirectories)) == null ? new string[] { } :
44-
xml.Element(nameof(ProjectDirectories)).Elements()
48+
ProjectDirectories =
49+
(xml.Element(nameof(ProjectDirectories))?.Elements()).NullAsEmpty()
4550
.Select(x => x.Value)
4651
.ToArray(),
47-
NeverRestoredTarballPrebuilts = xml.Element(nameof(NeverRestoredTarballPrebuilts)) == null ? new PackageIdentity[] { } :
48-
xml.Element(nameof(NeverRestoredTarballPrebuilts)).Elements()
52+
NeverRestoredTarballPrebuilts =
53+
(xml.Element(nameof(NeverRestoredTarballPrebuilts))?.Elements()).NullAsEmpty()
4954
.Select(XmlParsingHelpers.ParsePackageIdentity)
5055
.ToArray(),
51-
Usages = xml.Element(nameof(Usages)) == null ? new Usage[] { } :
52-
xml.Element(nameof(Usages)).Elements()
56+
IgnorePatterns =
57+
(xml.Element(nameof(IgnorePatterns))?.Elements()).NullAsEmpty()
58+
.Select(UsagePattern.Parse)
59+
.ToArray(),
60+
Usages =
61+
(xml.Element(nameof(Usages))?.Elements()).NullAsEmpty()
5362
.Select(Usage.Parse)
5463
.ToArray()
5564
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Text.RegularExpressions;
6+
using System.Xml.Linq;
7+
8+
namespace Microsoft.DotNet.SourceBuild.Tasks.UsageReport
9+
{
10+
public class UsagePattern
11+
{
12+
public string IdentityRegex { get; set; }
13+
14+
public string IdentityGlob { get; set; }
15+
16+
public XElement ToXml() => new XElement(
17+
nameof(UsagePattern),
18+
IdentityRegex.ToXAttributeIfNotNull(nameof(IdentityRegex)),
19+
IdentityGlob.ToXAttributeIfNotNull(nameof(IdentityGlob)));
20+
21+
public static UsagePattern Parse(XElement xml) => new UsagePattern
22+
{
23+
IdentityRegex = xml.Attribute(nameof(IdentityRegex))?.Value,
24+
IdentityGlob = xml.Attribute(nameof(IdentityGlob))?.Value
25+
};
26+
27+
public Regex CreateRegex()
28+
{
29+
if (!string.IsNullOrEmpty(IdentityRegex))
30+
{
31+
return new Regex(IdentityRegex, RegexOptions.IgnoreCase | RegexOptions.Compiled);
32+
}
33+
34+
if (!string.IsNullOrEmpty(IdentityGlob))
35+
{
36+
// Escape regex characters like '.', but handle '*' as regex '.*'.
37+
string regex = Regex.Escape(IdentityGlob).Replace("\\*", ".*");
38+
39+
return new Regex(
40+
$"^{regex}$",
41+
RegexOptions.IgnoreCase | RegexOptions.Compiled);
42+
}
43+
44+
return new Regex("");
45+
}
46+
}
47+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Xml.Linq;
6+
7+
namespace Microsoft.DotNet.SourceBuild.Tasks.UsageReport
8+
{
9+
public class UsageValidationData
10+
{
11+
/// <summary>
12+
/// A human-readable report of new and removed prebuilts.
13+
/// </summary>
14+
public XElement Report { get; set; }
15+
16+
/// <summary>
17+
/// The actual usage data from the build. Can be used as a baseline in future runs.
18+
/// </summary>
19+
public UsageData ActualUsageData { get; set; }
20+
}
21+
}

tools-local/tasks/Microsoft.DotNet.SourceBuild.Tasks.XPlat/UsageReport/ValidateUsageAgainstBaseline.cs

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Collections.Generic;
99
using System.IO;
1010
using System.Linq;
11+
using System.Text.RegularExpressions;
1112
using System.Xml.Linq;
1213

1314
namespace Microsoft.DotNet.SourceBuild.Tasks.UsageReport
@@ -31,8 +32,30 @@ public class ValidateUsageAgainstBaseline : Task
3132
public override bool Execute()
3233
{
3334
var used = UsageData.Parse(XElement.Parse(File.ReadAllText(DataFile)));
34-
var baseline = UsageData.Parse(XElement.Parse(File.ReadAllText(BaselineDataFile)));
3535

36+
string baselineText = File.ReadAllText(BaselineDataFile);
37+
38+
var baseline = UsageData.Parse(XElement.Parse(baselineText));
39+
40+
UsageValidationData data = GetUsageValidationData(baseline, used);
41+
42+
Directory.CreateDirectory(Path.GetDirectoryName(OutputBaselineFile));
43+
File.WriteAllText(OutputBaselineFile, data.ActualUsageData.ToXml().ToString());
44+
45+
Directory.CreateDirectory(Path.GetDirectoryName(OutputReportFile));
46+
File.WriteAllText(OutputReportFile, data.Report.ToString());
47+
48+
return !Log.HasLoggedErrors;
49+
}
50+
51+
public UsageValidationData GetUsageValidationData(UsageData baseline, UsageData used)
52+
{
53+
// Remove prebuilts from the used data if the baseline says to ignore them. Do this
54+
// first, so the new generated baseline doesn't list usages that are ignored by a
55+
// pattern anyway.
56+
ApplyBaselineIgnorePatterns(used, baseline);
57+
58+
// Find new, removed, and unchanged usage after filtering patterns.
3659
Comparison<PackageIdentity> diff = Compare(
3760
used.Usages.Select(u => u.GetIdentityWithoutRid()).Distinct(),
3861
baseline.Usages.Select(u => u.GetIdentityWithoutRid()).Distinct());
@@ -57,6 +80,7 @@ public override bool Execute()
5780
.Where(u => diff.Added.Contains(u.GetIdentityWithoutRid()))
5881
.Select(u => u.ToXml())));
5982
}
83+
6084
if (diff.Removed.Any())
6185
{
6286
tellUserToUpdateBaseline = true;
@@ -66,6 +90,7 @@ public override bool Execute()
6690

6791
report.Add(new XElement("Removed", diff.Removed.Select(id => id.ToXElement())));
6892
}
93+
6994
if (diff.Unchanged.Any())
7095
{
7196
Log.LogMessage(
@@ -98,23 +123,42 @@ public override bool Execute()
98123
{
99124
usage.AssetsFile = null;
100125
}
101-
used.Usages = used.Usages.Distinct().ToArray();
102-
103-
Directory.CreateDirectory(Path.GetDirectoryName(OutputBaselineFile));
104-
File.WriteAllText(OutputBaselineFile, used.ToXml().ToString());
105126

106-
Directory.CreateDirectory(Path.GetDirectoryName(OutputReportFile));
107-
File.WriteAllText(OutputReportFile, report.ToString());
127+
used.ProjectDirectories = null;
128+
used.Usages = used.Usages.Distinct().ToArray();
108129

109130
if (tellUserToUpdateBaseline)
110131
{
111132
Log.LogWarning(
112-
"Prebuilt usages are different from the baseline. If detected changes are " +
113-
"acceptable, update baseline with:\n" +
114-
$"cp '{OutputBaselineFile}' '{BaselineDataFile}'");
133+
"Prebuilt usages are different from the baseline found at " +
134+
$"'{BaselineDataFile}'. If it's acceptable to update the baseline, copy the " +
135+
$"contents of the automatically generated baseline '{OutputBaselineFile}'.");
115136
}
116137

117-
return !Log.HasLoggedErrors;
138+
return new UsageValidationData
139+
{
140+
Report = report,
141+
ActualUsageData = used
142+
};
143+
}
144+
145+
private static void ApplyBaselineIgnorePatterns(UsageData actual, UsageData baseline)
146+
{
147+
Regex[] ignoreUsageRegexes = baseline.IgnorePatterns.NullAsEmpty()
148+
.Select(p => p.CreateRegex())
149+
.ToArray();
150+
151+
actual.IgnorePatterns = baseline.IgnorePatterns;
152+
153+
var ignoredUsages = actual.Usages
154+
.Where(usage =>
155+
{
156+
string id = $"{usage.PackageIdentity.Id}/{usage.PackageIdentity.Version}";
157+
return ignoreUsageRegexes.Any(r => r.IsMatch(id));
158+
})
159+
.ToArray();
160+
161+
actual.Usages = actual.Usages.Except(ignoredUsages).ToArray();
118162
}
119163

120164
private static Comparison<T> Compare<T>(IEnumerable<T> actual, IEnumerable<T> baseline)

0 commit comments

Comments
 (0)