Skip to content

Commit 6458f0b

Browse files
committed
generate and submit dependency graphs
1 parent e9a295e commit 6458f0b

26 files changed

Lines changed: 1426 additions & 109 deletions

nuget/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,8 @@ RUN chmod +x $DEPENDABOT_HOME/dependabot-updater/bin/run
8787
# .NET install targeting packs
8888
RUN pwsh $DEPENDABOT_HOME/dependabot-updater/bin/install-targeting-packs.ps1
8989

90+
# Extract the Dependabot gem version for runtime use
91+
RUN sed -n 's/.*VERSION = "\(.*\)"/\1/p' $DEPENDABOT_HOME/common/lib/dependabot.rb > $DEPENDABOT_HOME/.dependabot-version
92+
9093
# Enable MSBuild operations with a shallow clone for repos that use the Nerdbank.GitVersioning package
9194
ENV NBGV_GitEngine=Disabled

nuget/helpers/lib/NuGetUpdater/Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<PackageVersion Include="OpenTelemetry" Version="1.15.3" />
2828
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
2929
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
30+
<PackageVersion Include="packageurl-dotnet" Version="2.0.0" />
3031
<PackageVersion Include="Semver" Version="3.0.0" />
3132
<PackageVersion Include="System.CommandLine" Version="2.0.3" />
3233
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.3" />
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System.Text;
2+
using System.Text.Json;
3+
4+
using NuGetUpdater.Core.Run;
5+
using NuGetUpdater.Core.Run.ApiModel;
6+
using NuGetUpdater.Core.Test;
7+
using NuGetUpdater.Core.Test.Update;
8+
9+
using Xunit;
10+
11+
namespace NuGetUpdater.Cli.Test;
12+
13+
using TestFile = (string Path, string Content);
14+
15+
internal static class EntryPointTestHelper
16+
{
17+
internal static async Task RunAsync(
18+
string commandName,
19+
TestFile[] files,
20+
Job job,
21+
string[] expectedUrls,
22+
MockNuGetPackage[]? packages = null,
23+
string? repoContentsPath = null,
24+
int expectedExitCode = 0)
25+
{
26+
using var tempDirectory = new TemporaryDirectory();
27+
28+
// write test files
29+
foreach (var testFile in files)
30+
{
31+
var fullPath = Path.Join(tempDirectory.DirectoryPath, testFile.Path);
32+
var directory = Path.GetDirectoryName(fullPath)!;
33+
Directory.CreateDirectory(directory);
34+
await File.WriteAllTextAsync(fullPath, testFile.Content);
35+
}
36+
37+
// write job file
38+
var jobPath = Path.Combine(tempDirectory.DirectoryPath, "job.json");
39+
await File.WriteAllTextAsync(jobPath, JsonSerializer.Serialize(new { Job = job }, RunWorker.SerializerOptions));
40+
41+
// save packages
42+
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDirectory.DirectoryPath);
43+
44+
var actualUrls = new List<string>();
45+
using var http = TestHttpServer.CreateTestStringServer((method, url) =>
46+
{
47+
actualUrls.Add($"{method} {new Uri(url).PathAndQuery}");
48+
return (200, "ok");
49+
});
50+
var args = new List<string>()
51+
{
52+
commandName,
53+
"--job-path",
54+
jobPath,
55+
"--repo-contents-path",
56+
repoContentsPath ?? tempDirectory.DirectoryPath,
57+
"--api-url",
58+
http.BaseUrl,
59+
"--job-id",
60+
"TEST-ID",
61+
"--base-commit-sha",
62+
"BASE-COMMIT-SHA"
63+
};
64+
65+
var output = new StringBuilder();
66+
// redirect stdout
67+
var originalOut = Console.Out;
68+
Console.SetOut(new StringWriter(output));
69+
int result = -1;
70+
try
71+
{
72+
result = await Program.Main(args.ToArray());
73+
}
74+
catch
75+
{
76+
// restore stdout
77+
Console.SetOut(originalOut);
78+
throw;
79+
}
80+
81+
Assert.True(result == expectedExitCode, $"Expected exit code {expectedExitCode} but got {result}.\nSTDOUT:\n" + output.ToString());
82+
Assert.Equal(expectedUrls, actualUrls);
83+
}
84+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using NuGetUpdater.Core.Run.ApiModel;
2+
using NuGetUpdater.Core.Test;
3+
4+
using Xunit;
5+
6+
namespace NuGetUpdater.Cli.Test;
7+
8+
using TestFile = (string Path, string Content);
9+
10+
public partial class EntryPointTests
11+
{
12+
public class Graph
13+
{
14+
[Fact]
15+
public async Task Graph_Simple()
16+
{
17+
// verify we can pass command line arguments for graph command
18+
await RunAsync(
19+
packages:
20+
[
21+
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
22+
],
23+
files:
24+
[
25+
("Directory.Build.props", "<Project />"),
26+
("Directory.Build.targets", "<Project />"),
27+
("Directory.Packages.props", """
28+
<Project>
29+
<PropertyGroup>
30+
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
31+
</PropertyGroup>
32+
</Project>
33+
"""),
34+
("src/project.csproj", """
35+
<Project Sdk="Microsoft.NET.Sdk">
36+
<PropertyGroup>
37+
<TargetFramework>net8.0</TargetFramework>
38+
</PropertyGroup>
39+
<ItemGroup>
40+
<PackageReference Include="Some.Package" Version="1.0.0" />
41+
</ItemGroup>
42+
</Project>
43+
""")
44+
],
45+
job: new Job()
46+
{
47+
Source = new()
48+
{
49+
Provider = "github",
50+
Repo = "test",
51+
Directory = "src",
52+
}
53+
},
54+
expectedUrls:
55+
[
56+
"POST /update_jobs/TEST-ID/create_dependency_submission",
57+
"PATCH /update_jobs/TEST-ID/mark_as_processed",
58+
]
59+
);
60+
}
61+
62+
private static Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
63+
=> EntryPointTestHelper.RunAsync("graph", files, job, expectedUrls, packages, repoContentsPath, expectedExitCode);
64+
}
65+
}

nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -143,65 +143,7 @@ await RunAsync(
143143
);
144144
}
145145

146-
private static async Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
147-
{
148-
using var tempDirectory = new TemporaryDirectory();
149-
150-
// write test files
151-
foreach (var testFile in files)
152-
{
153-
var fullPath = Path.Join(tempDirectory.DirectoryPath, testFile.Path);
154-
var directory = Path.GetDirectoryName(fullPath)!;
155-
Directory.CreateDirectory(directory);
156-
await File.WriteAllTextAsync(fullPath, testFile.Content);
157-
}
158-
159-
// write job file
160-
var jobPath = Path.Combine(tempDirectory.DirectoryPath, "job.json");
161-
await File.WriteAllTextAsync(jobPath, JsonSerializer.Serialize(new { Job = job }, RunWorker.SerializerOptions));
162-
163-
// save packages
164-
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDirectory.DirectoryPath);
165-
166-
var actualUrls = new List<string>();
167-
using var http = TestHttpServer.CreateTestStringServer((method, url) =>
168-
{
169-
actualUrls.Add($"{method} {new Uri(url).PathAndQuery}");
170-
return (200, "ok");
171-
});
172-
var args = new List<string>()
173-
{
174-
"run",
175-
"--job-path",
176-
jobPath,
177-
"--repo-contents-path",
178-
repoContentsPath ?? tempDirectory.DirectoryPath,
179-
"--api-url",
180-
http.BaseUrl,
181-
"--job-id",
182-
"TEST-ID",
183-
"--base-commit-sha",
184-
"BASE-COMMIT-SHA"
185-
};
186-
187-
var output = new StringBuilder();
188-
// redirect stdout
189-
var originalOut = Console.Out;
190-
Console.SetOut(new StringWriter(output));
191-
int result = -1;
192-
try
193-
{
194-
result = await Program.Main(args.ToArray());
195-
}
196-
catch
197-
{
198-
// restore stdout
199-
Console.SetOut(originalOut);
200-
throw;
201-
}
202-
203-
Assert.True(result == expectedExitCode, $"Expected exit code {expectedExitCode} but got {result}.\nSTDOUT:\n" + output.ToString());
204-
Assert.Equal(expectedUrls, actualUrls);
205-
}
146+
private static Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
147+
=> EntryPointTestHelper.RunAsync("run", files, job, expectedUrls, packages, repoContentsPath, expectedExitCode);
206148
}
207149
}

nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,24 @@ namespace NuGetUpdater.Cli.Commands;
88

99
internal static class CloneCommand
1010
{
11-
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
12-
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
13-
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
14-
{
15-
Required = true,
16-
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
17-
};
18-
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
19-
2011
internal static Command GetCommand(Action<int> setExitCode)
2112
{
2213
var command = new Command("clone", "Clones a repository in preparation for a dependabot job.")
2314
{
24-
JobPathOption,
25-
RepoContentsPathOption,
26-
ApiUrlOption,
27-
JobIdOption,
15+
SharedOptions.JobPathOption,
16+
SharedOptions.RepoContentsPathOption,
17+
SharedOptions.ApiUrlOption,
18+
SharedOptions.JobIdOption,
2819
};
2920

3021
command.TreatUnmatchedTokensAsErrors = true;
3122

3223
command.SetAction(async (parseResult, cancellationToken) =>
3324
{
34-
var jobPath = parseResult.GetValue(JobPathOption);
35-
var repoContentsPath = parseResult.GetValue(RepoContentsPathOption);
36-
var apiUrl = parseResult.GetValue(ApiUrlOption);
37-
var jobId = parseResult.GetValue(JobIdOption);
25+
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
26+
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
27+
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
28+
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
3829

3930
var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
4031
var logger = new OpenTelemetryLogger();
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System.CommandLine;
2+
3+
using NuGetUpdater.Core;
4+
using NuGetUpdater.Core.Discover;
5+
using NuGetUpdater.Core.Graph;
6+
using NuGetUpdater.Core.Run;
7+
8+
namespace NuGetUpdater.Cli.Commands;
9+
10+
internal static class GraphCommand
11+
{
12+
internal static Command GetCommand(Action<int> setExitCode)
13+
{
14+
Command command = new("graph", "Generates a dependency graph for a repository.")
15+
{
16+
SharedOptions.JobPathOption,
17+
SharedOptions.RepoContentsPathOption,
18+
SharedOptions.CaseInsensitiveRepoContentsPathOption,
19+
SharedOptions.ApiUrlOption,
20+
SharedOptions.JobIdOption,
21+
SharedOptions.BaseCommitShaOption
22+
};
23+
24+
command.TreatUnmatchedTokensAsErrors = true;
25+
26+
command.SetAction(async (parseResult, cancellationToken) =>
27+
{
28+
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
29+
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
30+
var caseInsensitiveRepoContentsPath = parseResult.GetValue(SharedOptions.CaseInsensitiveRepoContentsPathOption);
31+
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
32+
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
33+
var baseCommitSha = parseResult.GetValue(SharedOptions.BaseCommitShaOption);
34+
35+
var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
36+
var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId!, jobPath!.FullName);
37+
var logger = new OpenTelemetryLogger();
38+
var discoverWorker = new DiscoveryWorker(jobId!, experimentsManager, logger);
39+
var worker = new GraphWorker(jobId!, apiHandler, discoverWorker, logger);
40+
var result = await worker.RunAsync(jobPath!, repoContentsPath!, caseInsensitiveRepoContentsPath, baseCommitSha!);
41+
setExitCode(result);
42+
return 0;
43+
});
44+
45+
return command;
46+
}
47+
}

nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,28 @@ namespace NuGetUpdater.Cli.Commands;
99

1010
internal static class RunCommand
1111
{
12-
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
13-
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
14-
internal static readonly Option<DirectoryInfo?> CaseInsensitiveRepoContentsPathOption = new("--case-insensitive-repo-contents-path") { Required = false };
15-
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
16-
{
17-
Required = true,
18-
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
19-
};
20-
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
21-
internal static readonly Option<string> BaseCommitShaOption = new("--base-commit-sha") { Required = true };
22-
2312
internal static Command GetCommand(Action<int> setExitCode)
2413
{
2514
Command command = new("run", "Runs a full dependabot job.")
2615
{
27-
JobPathOption,
28-
RepoContentsPathOption,
29-
CaseInsensitiveRepoContentsPathOption,
30-
ApiUrlOption,
31-
JobIdOption,
32-
BaseCommitShaOption
16+
SharedOptions.JobPathOption,
17+
SharedOptions.RepoContentsPathOption,
18+
SharedOptions.CaseInsensitiveRepoContentsPathOption,
19+
SharedOptions.ApiUrlOption,
20+
SharedOptions.JobIdOption,
21+
SharedOptions.BaseCommitShaOption
3322
};
3423

3524
command.TreatUnmatchedTokensAsErrors = true;
3625

3726
command.SetAction(async (parseResult, cancellationToken) =>
3827
{
39-
var jobPath = parseResult.GetValue(JobPathOption);
40-
var repoContentsPath = parseResult.GetValue(RepoContentsPathOption);
41-
var caseInsensitiveRepoContentsPath = parseResult.GetValue(CaseInsensitiveRepoContentsPathOption);
42-
var apiUrl = parseResult.GetValue(ApiUrlOption);
43-
var jobId = parseResult.GetValue(JobIdOption);
44-
var baseCommitSha = parseResult.GetValue(BaseCommitShaOption);
28+
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
29+
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
30+
var caseInsensitiveRepoContentsPath = parseResult.GetValue(SharedOptions.CaseInsensitiveRepoContentsPathOption);
31+
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
32+
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
33+
var baseCommitSha = parseResult.GetValue(SharedOptions.BaseCommitShaOption);
4534

4635
var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
4736
var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId!, jobPath!.FullName);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.CommandLine;
2+
3+
namespace NuGetUpdater.Cli.Commands;
4+
5+
internal static class SharedOptions
6+
{
7+
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
8+
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
9+
internal static readonly Option<DirectoryInfo?> CaseInsensitiveRepoContentsPathOption = new("--case-insensitive-repo-contents-path") { Required = false };
10+
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
11+
{
12+
Required = true,
13+
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
14+
};
15+
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
16+
internal static readonly Option<string> BaseCommitShaOption = new("--base-commit-sha") { Required = true };
17+
}

0 commit comments

Comments
 (0)