Skip to content

Commit d7812e7

Browse files
committed
Add baselining mechanism to the build comparison tool
Accepts an optiona; json file with a list of baseline entries. Baseline a few well known items.
1 parent baa6d97 commit d7812e7

File tree

9 files changed

+367
-185
lines changed

9 files changed

+367
-185
lines changed

eng/pipelines/templates/steps/vmr-validate-asset-baseline.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ steps:
4848
-vmrAssetBasePath "$(Build.ArtifactStagingDirectory)\vmr-assets"
4949
-msftAssetBasePath "$(Build.ArtifactStagingDirectory)\base-assets"
5050
-report "$(Build.SourcesDirectory)\artifacts\AssetBaselines\BaselineComparison.xml"
51+
-baseline "$(Build.SourcesDirectory)\src\sdk\eng\vmr-msft-comparison-baseline.json"
5152
displayName: Validate Asset Baselines
5253

5354
- task: 1ES.PublishPipelineArtifact@1

eng/vmr-msft-comparison-baseline.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"issueType": "MissingPackageContent",
4+
"descriptionMatch": "mscordaccore_(arm64|amd64)_(arm64|amd64|x86)_10.0.\\d{2}.\\d+.dll",
5+
"justification": "mscordaccoree's version is baked into the file name and varies build to build."
6+
},
7+
{
8+
"issueType": "ExtraPackageContent",
9+
"descriptionMatch": "mscordaccore_(arm64|amd64)_(arm64|amd64|x86)_10.0.\\d{2}.\\d+.dll",
10+
"justification": "mscordaccoree's version is baked into the file name and varies build to build."
11+
},
12+
{
13+
"issueType": "AssemblyVersionMismatch",
14+
"descriptionMatch": ".resources.dll",
15+
"justification": "Resource dlls get per-build versioning but this doesn't matter."
16+
},
17+
{
18+
"issueType": "MissingShipping",
19+
"descriptionMatch": "Aspire.*",
20+
"justification": "Aspire to be removed from the VMR. Also .NET does not ship these packages."
21+
}
22+
]
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System.Xml.Linq;
2+
using System.Xml.Serialization;
3+
/// <summary>
4+
/// Represents the mapping between base build and VMR build for a specific asset.
5+
/// </summary>
6+
public class AssetMapping
7+
{
8+
/// <summary>
9+
/// Gets or sets the identifier of the asset.
10+
/// </summary>
11+
[XmlAttribute("Id")]
12+
public string Id { get; set; }
13+
14+
/// <summary>
15+
/// Gets or sets the type of the asset.
16+
/// </summary>
17+
[XmlAttribute("Type")]
18+
public AssetType AssetType { get; set; } = AssetType.Unknown;
19+
20+
/// <summary>
21+
/// Gets a value indicating whether a corresponding element was found in the diff manifest.
22+
/// </summary>
23+
[XmlIgnore]
24+
public bool DiffElementFound { get => DiffManifestElement != null; }
25+
26+
/// <summary>
27+
/// Gets a value indicating whether a corresponding file was found in the diff build.
28+
/// </summary>
29+
[XmlIgnore]
30+
public bool DiffFileFound { get => DiffFilePath != null; }
31+
32+
/// <summary>
33+
/// Gets or sets the path to the diff file.
34+
/// </summary>
35+
[XmlElement("DiffFile")]
36+
public string DiffFilePath { get; set; }
37+
38+
/// <summary>
39+
/// Gets or sets the XML element from the diff manifest.
40+
/// </summary>
41+
[XmlIgnore]
42+
public XElement DiffManifestElement { get; set; }
43+
44+
/// <summary>
45+
/// Gets or sets the path to the base build file.
46+
/// </summary>
47+
[XmlElement("BaseFile")]
48+
public string BaseBuildFilePath { get; set; }
49+
50+
/// <summary>
51+
/// Gets or sets the XML element from the base build manifest.
52+
/// </summary>
53+
[XmlIgnore]
54+
public XElement BaseBuildManifestElement
55+
{
56+
get; set;
57+
}
58+
59+
/// <summary>
60+
/// Gets or sets the list of errors encountered during evaluation.
61+
/// </summary>
62+
public List<string> EvaluationErrors { get; set; } = new List<string>();
63+
64+
/// <summary>
65+
/// Gets or sets the list of issues identified for this asset.
66+
/// </summary>
67+
public List<Issue> Issues { get; set; } = new List<Issue>();
68+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+

2+
/// <summary>
3+
/// Defines the type of asset being processed in the build comparison tool.
4+
/// </summary>
5+
public enum AssetType
6+
{
7+
/// <summary>
8+
/// Represents a random non-package file in the build.
9+
/// </summary>
10+
Blob,
11+
12+
/// <summary>
13+
/// Represents a NuGet package asset.
14+
/// </summary>
15+
Package,
16+
17+
/// <summary>
18+
/// Represents an asset of unknown type.
19+
/// </summary>
20+
Unknown
21+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System.Text.RegularExpressions;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
using System.Xml.Serialization;
5+
6+
public class BaselineEntry
7+
{
8+
[XmlIgnore]
9+
/// <summary>
10+
/// Regex used to match the ID of the asset.
11+
/// </summary>
12+
public Regex IdMatch { get; set; }
13+
[XmlIgnore]
14+
/// <summary>
15+
/// Issue type to baseline
16+
/// </summary>
17+
public IssueType? IssueType { get; set; }
18+
[XmlIgnore]
19+
/// <summary>
20+
/// Regex used to match the description of the asset.
21+
/// </summary>
22+
public Regex DescriptionMatch { get; set; }
23+
[XmlAttribute]
24+
/// <summary>
25+
/// Justification for the baseline.
26+
/// </summary>
27+
public string Justification { get; set; }
28+
}
29+
30+
public class Baseline
31+
{
32+
private List<BaselineEntry> _entries;
33+
public Baseline(string filePath)
34+
{
35+
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
36+
{
37+
throw new ArgumentException($"Baseline file not found: {filePath}");
38+
}
39+
40+
string jsonContent = File.ReadAllText(filePath);
41+
42+
// Configure JSON deserialization options with custom converters
43+
var options = new JsonSerializerOptions
44+
{
45+
PropertyNameCaseInsensitive = true,
46+
Converters =
47+
{
48+
new RegexJsonConverter(),
49+
new JsonStringEnumConverter()
50+
}
51+
};
52+
53+
// Deserialize the JSON into a list of BaselineEntry objects
54+
_entries = JsonSerializer.Deserialize<List<BaselineEntry>>(jsonContent, options) ?? new List<BaselineEntry>();
55+
56+
// Validate the baseline entries. An entry must have a Justification and an IssueType,
57+
// and must have at least one of IdMatch or DescriptionMatch.
58+
59+
foreach (var entry in _entries)
60+
{
61+
if (string.IsNullOrEmpty(entry.Justification))
62+
{
63+
throw new ArgumentException("Justification cannot be null or empty.");
64+
}
65+
if (entry.IssueType == null)
66+
{
67+
throw new ArgumentException("IssueType cannot be null.");
68+
}
69+
if (entry.IdMatch == null && entry.DescriptionMatch == null)
70+
{
71+
throw new ArgumentException("At least one of IdMatch or DescriptionMatch must be provided.");
72+
}
73+
}
74+
}
75+
76+
// Check which baseline entries match against the given asset issue.
77+
public List<BaselineEntry> GetMatchingBaselineEntries(Issue assetIssue, AssetMapping assetMapping)
78+
{
79+
var matchingEntries = new List<BaselineEntry>();
80+
foreach (var entry in _entries)
81+
{
82+
if (entry.IssueType == assetIssue.IssueType &&
83+
(entry.IdMatch == null || entry.IdMatch.IsMatch(assetMapping.Id)) &&
84+
(entry.DescriptionMatch == null || entry.DescriptionMatch.IsMatch(assetIssue.Description)))
85+
{
86+
matchingEntries.Add(entry);
87+
}
88+
}
89+
return matchingEntries;
90+
}
91+
92+
// Custom JSON converter for Regex objects
93+
private class RegexJsonConverter : JsonConverter<Regex>
94+
{
95+
public override Regex Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
96+
{
97+
string pattern = reader.GetString();
98+
return pattern != null ? new Regex(pattern, RegexOptions.Compiled) : null;
99+
}
100+
101+
public override void Write(Utf8JsonWriter writer, Regex value, JsonSerializerOptions options)
102+
{
103+
writer.WriteStringValue(value?.ToString());
104+
}
105+
}
106+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Xml.Serialization;
2+
/// <summary>
3+
/// Contains the results of a build comparison between Microsoft and VMR builds.
4+
/// </summary>
5+
public class ComparisonReport
6+
{
7+
/// <summary>
8+
/// Gets the number of assets with identified issues.
9+
/// </summary>
10+
public int IssueCount { get => AssetsWithIssues.Sum(a => a.Issues.Where(i => i.Baseline == null).Count()); }
11+
12+
/// <summary>
13+
/// Gets the number of assets with evaluation errors.
14+
/// </summary>
15+
public int ErrorCount { get => AssetsWithErrors.Sum(a => a.EvaluationErrors.Count); }
16+
17+
/// <summary>
18+
/// Gets the total number of assets analyzed in the report.
19+
/// </summary>
20+
public int BaselineCount { get => AssetsWithIssues.Sum(a => a.Issues.Where(i => i.Baseline != null).Count()) +
21+
AssetsWithErrors.Sum(a => a.Issues.Where(i => i.Baseline != null).Count()) +
22+
AssetsWithoutIssues.Sum(a => a.Issues.Where(i => i.Baseline != null).Count()); }
23+
/// <summary>
24+
/// Gets or sets the list of assets that have issues.
25+
/// </summary>
26+
public List<AssetMapping> AssetsWithIssues { get; set; }
27+
28+
/// <summary>
29+
/// Gets or sets the list of assets that have issues.
30+
/// </summary>
31+
public List<AssetMapping> AssetsWithErrors { get; set; }
32+
33+
/// <summary>
34+
/// Gets or sets the list of assets that have issues.
35+
/// </summary>
36+
public List<AssetMapping> AssetsWithoutIssues { get; set; }
37+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Xml.Serialization;
2+
/// <summary>
3+
/// Represents an issue identified during asset comparison.
4+
/// </summary>
5+
public class Issue
6+
{
7+
/// <summary>
8+
/// Gets or sets the type of issue.
9+
/// </summary>
10+
[XmlAttribute("Type")]
11+
public IssueType IssueType { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets a description of the issue.
15+
/// </summary>
16+
[XmlAttribute("Description")]
17+
public string Description { get; set; }
18+
19+
/// <summary>
20+
/// Matching baseline entries for this issue.
21+
/// </summary>
22+
public BaselineEntry Baseline { get; set; }
23+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+

2+
/// <summary>
3+
/// Defines types of issues that can be identified during asset comparison.
4+
/// </summary>
5+
public enum IssueType
6+
{
7+
/// <summary>
8+
/// Indicates a shipping asset is missing in the VMR build.
9+
/// </summary>
10+
MissingShipping,
11+
12+
/// <summary>
13+
/// Indicates a non-shipping asset is missing in the VMR build.
14+
/// </summary>
15+
MissingNonShipping,
16+
17+
/// <summary>
18+
/// Indicates an asset is classified differently between base and VMR builds.
19+
/// </summary>
20+
MisclassifiedAsset,
21+
22+
/// <summary>
23+
/// Indicates a version mismatch between assemblies in base and VMR builds.
24+
/// </summary>
25+
AssemblyVersionMismatch,
26+
MissingPackageContent,
27+
ExtraPackageContent,
28+
}

0 commit comments

Comments
 (0)