Skip to content

Commit 8050491

Browse files
[xaprepare] Remove NuGet scanning from Step_GenerateCGManifest (#11299)
* Replace generated CGManifest.json with a static one The Component Governance (CG) Azure DevOps build task auto-detects NuGet packages by scanning `.csproj` files on the build machine, making the 287-line `Step_GenerateCGManifest` in xaprepare redundant. Only git submodules need manual registration via `CGManifest.json`, since CG cannot auto-detect them. This commit replaces the dynamic generation with a static `CGManifest.json` at the repo root containing only `type: git` entries for each submodule. Reference: - https://docs.opensource.microsoft.com/tools/cg/component-detection/cgmanifest.md - https://docs.opensource.microsoft.com/tools/cg/component-detection/build-task.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rename CGManifest.json to cgmanifest.json Match the canonical lowercase filename used by the CG docs and other dotnet repos (machinelearning, android-libraries, test-templates). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove NuGet scanning from Step_GenerateCGManifest The Component Governance (CG) Azure DevOps build task auto-detects NuGet packages by scanning `.csproj` files on the build machine, making the NuGet scanning in `Step_GenerateCGManifest` redundant. Strip the step down to only generate `cgmanifest.json` entries for git submodules, which CG cannot auto-detect. This removes the `MSBuildPackageReferenceInfo` class, `CGManifestEntry` abstraction, `DevelopmentDependencies` list, and all NuGet/MSBuild XML scanning code (-231 lines). The file continues to be generated at build time (not checked in) so that commit hashes stay current when dependabot bumps submodules. Reference: - https://docs.opensource.microsoft.com/tools/cg/component-detection/cgmanifest.md - https://docs.opensource.microsoft.com/tools/cg/component-detection/build-task.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix build: restore Name and LocalPath on GitSubmoduleInfo GeneratedSourceLinkJsonFile and Step_GenerateFiles use these properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1b0756f commit 8050491

1 file changed

Lines changed: 46 additions & 222 deletions

File tree

build-tools/xaprepare/xaprepare/Steps/Step_GenerateCGManifest.cs

Lines changed: 46 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -3,85 +3,49 @@
33
using System.IO;
44
using System.Linq;
55
using System.Threading.Tasks;
6-
using System.Xml.Linq;
76

87
namespace Xamarin.Android.Prepare
98
{
109
partial class Step_GenerateCGManifest : Step
1110
{
12-
static readonly HashSet<string> DevelopmentDependencies = new HashSet<string> (StringComparer.OrdinalIgnoreCase) {
13-
"7-Zip.CommandLine",
14-
"Kajabity.Tools.Java",
15-
"Mono.Linq.Expressions",
16-
"mono/debugger-libs",
17-
"Newtonsoft.Json",
18-
"System.CommandLine",
19-
"Xamarin.Forms",
20-
"xunit",
21-
"xunit.abstractions",
22-
"xunit.analyzers",
23-
"xunit.assert",
24-
"xunit.core",
25-
"xunit.extensibility.core",
26-
"xunit.extensibility.execution",
27-
"xunit.runner.utility",
28-
29-
// ???
30-
"Microsoft.Build",
31-
"Microsoft.Build.Framework",
32-
"Microsoft.Build.Tasks.Core",
33-
"Microsoft.Build.Utilities.Core",
34-
"Microsoft.VisualStudio.CoreUtility",
35-
"Microsoft.VisualStudio.Imaging",
36-
"Microsoft.VisualStudio.OLE.Interop",
37-
"Microsoft.VisualStudio.Shell.15.0",
38-
"Microsoft.VisualStudio.Shell.Framework",
39-
"Microsoft.VisualStudio.Shell.Interop",
40-
"Microsoft.VisualStudio.Shell.Interop.8.0",
41-
"Microsoft.VisualStudio.Shell.Interop.9.0",
42-
"Microsoft.VisualStudio.TextManager.Interop",
43-
"Microsoft.VisualStudio.TextManager.Interop.8.0",
44-
"Microsoft.VisualStudio.Threading",
45-
"Microsoft.VisualStudio.Utilities",
46-
"Microsoft.VisualStudio.Validation",
47-
"Microsoft.VSSDK.BuildTools",
48-
};
49-
5011
public Step_GenerateCGManifest ()
51-
: base ("Generate CGManifest.json")
12+
: base ("Generate cgmanifest.json")
5213
{}
5314

5415
protected override async Task<bool> Execute (Context context)
5516
{
56-
var nugets = MSBuildPackageReferenceInfo.GetPackageReferences ();
5717
var git = new GitRunner (context);
5818
var gitSubmoduleInfo = await git.ConfigList (new[]{"--blob", "HEAD:.gitmodules"});
5919
var gitSubmoduleStatus = await git.SubmoduleStatus ();
60-
var gitSubmodules = GitSubmoduleInfo.GetGitSubmodules (gitSubmoduleInfo, gitSubmoduleStatus);
20+
var gitSubmodules = GitSubmoduleInfo.GetGitSubmodules (gitSubmoduleInfo, gitSubmoduleStatus)
21+
.OrderBy (e => e.RepositoryUrl, StringComparer.OrdinalIgnoreCase);
6122

62-
var cgManifestEntries = ((IEnumerable<CGManifestEntry>) nugets).Concat (gitSubmodules)
63-
.OrderBy (e => e.Name);
64-
65-
var jsonPath = Path.Combine (Configurables.Paths.BuildBinDir, "CGManifest.json");
23+
var jsonPath = Path.Combine (Configurables.Paths.BuildBinDir, "cgmanifest.json");
6624
using var json = File.CreateText (jsonPath);
6725

6826
json.WriteLine ("{");
6927
json.WriteLine (" \"$schema\": \"https://json.schemastore.org/component-detection-manifest.json\",");
7028
json.WriteLine (" \"version\": 1,");
7129
json.WriteLine (" \"registrations\": [");
7230

73-
var properties = new Dictionary<string, string> ();
74-
7531
bool first = true;
7632

77-
foreach (var entry in cgManifestEntries) {
33+
foreach (var entry in gitSubmodules) {
7834
if (first) {
79-
first = false;
35+
first = false;
8036
} else {
8137
json.WriteLine (",");
8238
}
8339

84-
WriteComponent (json, entry, properties);
40+
json.WriteLine ($" {{");
41+
json.WriteLine ($" \"component\": {{");
42+
json.WriteLine ($" \"type\": \"git\",");
43+
json.WriteLine ($" \"git\": {{");
44+
json.WriteLine ($" \"commitHash\": \"{entry.CommitHash}\",");
45+
json.WriteLine ($" \"repositoryUrl\": \"{entry.RepositoryUrl}\"");
46+
json.WriteLine ($" }}");
47+
json.WriteLine ($" }}");
48+
json.Write ($" }}");
8549
}
8650

8751
json.WriteLine ();
@@ -90,53 +54,11 @@ protected override async Task<bool> Execute (Context context)
9054

9155
return true;
9256
}
93-
94-
void WriteComponent (TextWriter json, CGManifestEntry entry, Dictionary<string, string> properties)
95-
{
96-
string dev = DevelopmentDependencies.Contains (entry.Name)
97-
? "true"
98-
: "false";
99-
100-
properties.Clear ();
101-
entry.FillComponentProperties (properties);
102-
103-
json.WriteLine ($" {{");
104-
json.WriteLine ($" \"component\": {{");
105-
json.WriteLine ($" \"type\": \"{entry.Type.ToLowerInvariant ()}\",");
106-
json.WriteLine ($" \"{entry.Type}\": {{");
107-
bool firstProp = true;
108-
foreach (var key in properties.Keys.OrderBy (p => p, StringComparer.OrdinalIgnoreCase)) {
109-
var value = properties [key];
110-
if (firstProp) {
111-
firstProp = false;
112-
} else {
113-
json.WriteLine (",");
114-
}
115-
json.Write ($" \"{key}\": \"{value}\"");
116-
}
117-
json.WriteLine ();
118-
json.WriteLine ($" }}");
119-
json.WriteLine ($" }},");
120-
json.WriteLine ($" \"developmentDependency\":{dev}");
121-
json.Write ($" }}");
122-
}
12357
}
12458

125-
abstract class CGManifestEntry {
126-
127-
public abstract string Name {get;}
128-
public abstract string Type {get;}
129-
130-
public abstract void FillComponentProperties (Dictionary<string, string> properties);
131-
132-
protected CGManifestEntry ()
133-
{
134-
}
135-
}
136-
137-
sealed class GitSubmoduleInfo : CGManifestEntry {
138-
139-
public override string Name {
59+
sealed class GitSubmoduleInfo
60+
{
61+
public string Name {
14062
get {
14163
const string github = "github.com/";
14264
int i = RepositoryUrl.IndexOf (github, StringComparison.OrdinalIgnoreCase);
@@ -146,100 +68,84 @@ public override string Name {
14668
}
14769
}
14870

149-
public override string Type => "git";
150-
151-
public string RepositoryUrl {get; private set;} = String.Empty;
152-
public string CommitHash {get; private set;} = String.Empty;
153-
public string LocalPath {get; private set;} = String.Empty;
71+
public string RepositoryUrl { get; private set; } = String.Empty;
72+
public string CommitHash { get; private set; } = String.Empty;
73+
public string LocalPath { get; private set; } = String.Empty;
15474

15575
GitSubmoduleInfo ()
15676
{
15777
}
15878

159-
public override void FillComponentProperties (Dictionary<string, string> properties)
160-
{
161-
properties ["repositoryUrl"] = RepositoryUrl;
162-
properties ["commitHash"] = CommitHash;
163-
}
164-
16579
const string Submodule = "submodule.external/";
16680

16781
public static IEnumerable<GitSubmoduleInfo> GetGitSubmodules (List<string>? config, List<string>? submoduleStatus)
16882
{
16983
if (config == null) {
170-
yield return CreateEmptySubmoduleInfo ();
84+
yield break;
17185
}
17286

17387
string? entryId = null;
17488
string? path = null;
17589
string? url = null;
17690

177-
foreach (var line in config!) {
91+
foreach (var line in config) {
17892
if (!line.StartsWith (Submodule, StringComparison.Ordinal))
17993
continue;
18094

18195
string? id = GetSubmoduleId (line);
18296
if (id != entryId) {
18397
if (url != null && path != null)
184-
yield return CreateSubmoduleInfo (url, path);
98+
yield return CreateSubmoduleInfo (url, path, submoduleStatus);
18599

186100
entryId = id;
187101
path = null;
188102
url = null;
189103
}
190104

191-
const string Path = ".path=";
192-
const string Url = ".url=";
193-
const string Git = ".git";
105+
const string Path = ".path=";
106+
const string Url = ".url=";
107+
const string Git = ".git";
194108

195-
int pathIndex = line.IndexOf (Path, StringComparison.Ordinal);
109+
int pathIndex = line.IndexOf (Path, StringComparison.Ordinal);
196110
if (pathIndex > 0) {
197-
path = line.Substring (pathIndex + Path.Length);
111+
path = line.Substring (pathIndex + Path.Length);
198112
continue;
199113
}
200114

201-
int urlIndex = line.IndexOf (Url, StringComparison.Ordinal);
115+
int urlIndex = line.IndexOf (Url, StringComparison.Ordinal);
202116
if (urlIndex > 0) {
203-
int start = urlIndex + Url.Length;
204-
int count = line.Length - start;
117+
int start = urlIndex + Url.Length;
118+
int count = line.Length - start;
205119
if (line.EndsWith (Git, StringComparison.Ordinal))
206120
count -= Git.Length;
207-
url = line.Substring (start, count);
121+
url = line.Substring (start, count);
208122
continue;
209123
}
210124
}
211125

212126
if (url != null && path != null)
213-
yield return CreateSubmoduleInfo (url, path);
127+
yield return CreateSubmoduleInfo (url, path, submoduleStatus);
128+
}
214129

215-
GitSubmoduleInfo CreateSubmoduleInfo (string url, string path)
216-
{
217-
if (submoduleStatus == null) {
218-
return CreateEmptySubmoduleInfo ();
219-
}
130+
static GitSubmoduleInfo CreateSubmoduleInfo (string url, string path, List<string>? submoduleStatus)
131+
{
132+
string commitHash = String.Empty;
220133

221-
string? hash = null;
134+
if (submoduleStatus != null) {
222135
foreach (var e in submoduleStatus) {
223-
int pi = e.IndexOf (path, StringComparison.OrdinalIgnoreCase);
136+
int pi = e.IndexOf (path, StringComparison.OrdinalIgnoreCase);
224137
if (pi < 1 || e [pi - 1] != ' ')
225138
continue;
226-
hash = e.Substring (1, pi - 2);
139+
commitHash = e.Substring (1, pi - 2);
227140
break;
228141
}
229-
return new GitSubmoduleInfo () {
230-
LocalPath = path,
231-
RepositoryUrl = url,
232-
CommitHash = hash ?? String.Empty,
233-
};
234142
}
235143

236-
GitSubmoduleInfo CreateEmptySubmoduleInfo ()
237-
{
238-
return new GitSubmoduleInfo {
239-
RepositoryUrl = String.Empty,
240-
CommitHash = String.Empty,
241-
};
242-
}
144+
return new GitSubmoduleInfo {
145+
LocalPath = path,
146+
RepositoryUrl = url,
147+
CommitHash = commitHash,
148+
};
243149
}
244150

245151
static string? GetSubmoduleId (string line)
@@ -251,86 +157,4 @@ GitSubmoduleInfo CreateEmptySubmoduleInfo ()
251157
return line.Substring (Submodule.Length, lastDot - Submodule.Length);
252158
}
253159
}
254-
255-
sealed class MSBuildPackageReferenceInfo : CGManifestEntry {
256-
257-
string name;
258-
259-
public override string Name => name;
260-
public override string Type => "nuget";
261-
262-
public string Version {get; private set;}
263-
264-
MSBuildPackageReferenceInfo (string name, string version)
265-
{
266-
this.name = name;
267-
Version = version;
268-
}
269-
270-
public override void FillComponentProperties (Dictionary<string, string> properties)
271-
{
272-
properties ["name"] = Name;
273-
properties ["version"] = Version;
274-
}
275-
276-
static readonly XNamespace MSBuildXmlns = XNamespace.Get ("http://schemas.microsoft.com/developer/msbuild/2003");
277-
278-
public static IEnumerable<MSBuildPackageReferenceInfo> GetPackageReferences ()
279-
{
280-
var files = Directory.EnumerateFiles (BuildPaths.XamarinAndroidSourceRoot, "*.csproj", SearchOption.AllDirectories)
281-
.Concat (Directory.EnumerateFiles (BuildPaths.XamarinAndroidSourceRoot, "*.targets", SearchOption.AllDirectories))
282-
.Concat (Directory.EnumerateFiles (BuildPaths.XamarinAndroidSourceRoot, "*.projitems", SearchOption.AllDirectories))
283-
;
284-
var packages = new Dictionary<string, HashSet<string>> ();
285-
var versions = new Dictionary<string, HashSet<string>> ();
286-
foreach (var file in files) {
287-
var contents = File.ReadAllText (file);
288-
if (contents.IndexOf ("PackageReference", StringComparison.Ordinal) < 0 ||
289-
contents.IndexOf ("PropertyGroup", StringComparison.Ordinal) < 0)
290-
continue;
291-
var proj = XDocument.Parse (contents);
292-
var packageReferences = proj.Elements (MSBuildXmlns + "Project")
293-
.Elements (MSBuildXmlns + "ItemGroup")
294-
.Elements (MSBuildXmlns + "PackageReference");
295-
foreach (var packageReference in packageReferences) {
296-
var name = (string?) packageReference.Attribute ("Include");
297-
var version = (string?) packageReference.Attribute ("Version") ??
298-
packageReference.Element (MSBuildXmlns+"Version")?.Value;
299-
if (name == null || version == null)
300-
continue;
301-
if (!packages.TryGetValue (name, out var v)) {
302-
packages.Add (name, v = new HashSet<string> ());
303-
}
304-
v.Add (version);
305-
}
306-
var properties = proj.Elements (MSBuildXmlns + "Project")
307-
.Elements (MSBuildXmlns + "PropertyGroup")
308-
.Elements ();
309-
foreach (var property in properties) {
310-
var name = $"$({property.Name.LocalName})";
311-
if (!property.Name.LocalName.EndsWith ("Version", StringComparison.Ordinal))
312-
continue;
313-
if (!versions.TryGetValue (name, out var v)) {
314-
versions.Add (name, v = new HashSet<string> ());
315-
}
316-
v.Add (property.Value.Trim ());
317-
}
318-
}
319-
320-
foreach (var package in packages) {
321-
string name = package.Key;
322-
foreach (var version in package.Value) {
323-
if (version.Length > 0 && version [0] != '$') {
324-
yield return new MSBuildPackageReferenceInfo (name, version);
325-
continue;
326-
}
327-
if (!versions.TryGetValue (version, out var propertyVersions))
328-
continue;
329-
foreach (var propertyVersion in propertyVersions) {
330-
yield return new MSBuildPackageReferenceInfo (name, propertyVersion);
331-
}
332-
}
333-
}
334-
}
335-
}
336160
}

0 commit comments

Comments
 (0)