Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nuget/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,8 @@ RUN chmod +x $DEPENDABOT_HOME/dependabot-updater/bin/run
# .NET install targeting packs
RUN pwsh $DEPENDABOT_HOME/dependabot-updater/bin/install-targeting-packs.ps1

# Extract the Dependabot gem version for runtime use
RUN sed -n 's/.*VERSION = "\(.*\)"/\1/p' $DEPENDABOT_HOME/common/lib/dependabot.rb > $DEPENDABOT_HOME/.dependabot-version

# Enable MSBuild operations with a shallow clone for repos that use the Nerdbank.GitVersioning package
ENV NBGV_GitEngine=Disabled
1 change: 1 addition & 0 deletions nuget/helpers/lib/NuGetUpdater/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<PackageVersion Include="OpenTelemetry" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
<PackageVersion Include="packageurl-dotnet" Version="2.0.0" />
<PackageVersion Include="Semver" Version="3.0.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.3" />
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.3" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Text;
using System.Text.Json;

using NuGetUpdater.Core.Run;
using NuGetUpdater.Core.Run.ApiModel;
using NuGetUpdater.Core.Test;
using NuGetUpdater.Core.Test.Update;

using Xunit;

namespace NuGetUpdater.Cli.Test;

using TestFile = (string Path, string Content);

internal static class EntryPointTestHelper
{
internal static async Task RunAsync(
string commandName,
TestFile[] files,
Job job,
string[] expectedUrls,
MockNuGetPackage[]? packages = null,
string? repoContentsPath = null,
int expectedExitCode = 0)
{
using var tempDirectory = new TemporaryDirectory();

// write test files
foreach (var testFile in files)
{
var fullPath = Path.Join(tempDirectory.DirectoryPath, testFile.Path);
var directory = Path.GetDirectoryName(fullPath)!;
Directory.CreateDirectory(directory);
await File.WriteAllTextAsync(fullPath, testFile.Content);
}

// write job file
var jobPath = Path.Combine(tempDirectory.DirectoryPath, "job.json");
await File.WriteAllTextAsync(jobPath, JsonSerializer.Serialize(new { Job = job }, RunWorker.SerializerOptions));

// save packages
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDirectory.DirectoryPath);

var actualUrls = new List<string>();
using var http = TestHttpServer.CreateTestStringServer((method, url) =>
{
actualUrls.Add($"{method} {new Uri(url).PathAndQuery}");
return (200, "ok");
});
var args = new List<string>()
{
commandName,
"--job-path",
jobPath,
"--repo-contents-path",
repoContentsPath ?? tempDirectory.DirectoryPath,
"--api-url",
http.BaseUrl,
"--job-id",
"TEST-ID",
"--base-commit-sha",
"BASE-COMMIT-SHA"
};

var output = new StringBuilder();
// redirect stdout
var originalOut = Console.Out;
Console.SetOut(new StringWriter(output));
int result = -1;
try
{
result = await Program.Main(args.ToArray());
}
catch
{
// restore stdout
Console.SetOut(originalOut);
throw;
}

Assert.True(result == expectedExitCode, $"Expected exit code {expectedExitCode} but got {result}.\nSTDOUT:\n" + output.ToString());
Assert.Equal(expectedUrls, actualUrls);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using NuGetUpdater.Core.Run.ApiModel;
using NuGetUpdater.Core.Test;

using Xunit;

namespace NuGetUpdater.Cli.Test;

using TestFile = (string Path, string Content);

public partial class EntryPointTests
{
public class Graph
{
[Fact]
public async Task Graph_Simple()
{
// verify we can pass command line arguments for graph command
await RunAsync(
packages:
[
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
],
files:
[
("Directory.Build.props", "<Project />"),
("Directory.Build.targets", "<Project />"),
("Directory.Packages.props", """
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>
"""),
("src/project.csproj", """
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Some.Package" Version="1.0.0" />
</ItemGroup>
</Project>
""")
],
job: new Job()
{
Source = new()
{
Provider = "github",
Repo = "test",
Directory = "src",
}
},
expectedUrls:
[
"POST /update_jobs/TEST-ID/create_dependency_submission",
"PATCH /update_jobs/TEST-ID/mark_as_processed",
]
);
}

private static Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
=> EntryPointTestHelper.RunAsync("graph", files, job, expectedUrls, packages, repoContentsPath, expectedExitCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,65 +143,7 @@ await RunAsync(
);
}

private static async Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
{
using var tempDirectory = new TemporaryDirectory();

// write test files
foreach (var testFile in files)
{
var fullPath = Path.Join(tempDirectory.DirectoryPath, testFile.Path);
var directory = Path.GetDirectoryName(fullPath)!;
Directory.CreateDirectory(directory);
await File.WriteAllTextAsync(fullPath, testFile.Content);
}

// write job file
var jobPath = Path.Combine(tempDirectory.DirectoryPath, "job.json");
await File.WriteAllTextAsync(jobPath, JsonSerializer.Serialize(new { Job = job }, RunWorker.SerializerOptions));

// save packages
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDirectory.DirectoryPath);

var actualUrls = new List<string>();
using var http = TestHttpServer.CreateTestStringServer((method, url) =>
{
actualUrls.Add($"{method} {new Uri(url).PathAndQuery}");
return (200, "ok");
});
var args = new List<string>()
{
"run",
"--job-path",
jobPath,
"--repo-contents-path",
repoContentsPath ?? tempDirectory.DirectoryPath,
"--api-url",
http.BaseUrl,
"--job-id",
"TEST-ID",
"--base-commit-sha",
"BASE-COMMIT-SHA"
};

var output = new StringBuilder();
// redirect stdout
var originalOut = Console.Out;
Console.SetOut(new StringWriter(output));
int result = -1;
try
{
result = await Program.Main(args.ToArray());
}
catch
{
// restore stdout
Console.SetOut(originalOut);
throw;
}

Assert.True(result == expectedExitCode, $"Expected exit code {expectedExitCode} but got {result}.\nSTDOUT:\n" + output.ToString());
Assert.Equal(expectedUrls, actualUrls);
}
private static Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
=> EntryPointTestHelper.RunAsync("run", files, job, expectedUrls, packages, repoContentsPath, expectedExitCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,24 @@ namespace NuGetUpdater.Cli.Commands;

internal static class CloneCommand
{
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
{
Required = true,
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
};
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };

internal static Command GetCommand(Action<int> setExitCode)
{
var command = new Command("clone", "Clones a repository in preparation for a dependabot job.")
{
JobPathOption,
RepoContentsPathOption,
ApiUrlOption,
JobIdOption,
SharedOptions.JobPathOption,
SharedOptions.RepoContentsPathOption,
SharedOptions.ApiUrlOption,
SharedOptions.JobIdOption,
};

command.TreatUnmatchedTokensAsErrors = true;

command.SetAction(async (parseResult, cancellationToken) =>
{
var jobPath = parseResult.GetValue(JobPathOption);
var repoContentsPath = parseResult.GetValue(RepoContentsPathOption);
var apiUrl = parseResult.GetValue(ApiUrlOption);
var jobId = parseResult.GetValue(JobIdOption);
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);

var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
var logger = new OpenTelemetryLogger();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.CommandLine;

using NuGetUpdater.Core;
using NuGetUpdater.Core.Discover;
using NuGetUpdater.Core.Graph;
using NuGetUpdater.Core.Run;

namespace NuGetUpdater.Cli.Commands;

internal static class GraphCommand
{
internal static Command GetCommand(Action<int> setExitCode)
{
Command command = new("graph", "Generates a dependency graph for a repository.")
{
SharedOptions.JobPathOption,
SharedOptions.RepoContentsPathOption,
SharedOptions.CaseInsensitiveRepoContentsPathOption,
SharedOptions.ApiUrlOption,
SharedOptions.JobIdOption,
SharedOptions.BaseCommitShaOption
};

command.TreatUnmatchedTokensAsErrors = true;

command.SetAction(async (parseResult, cancellationToken) =>
{
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
var caseInsensitiveRepoContentsPath = parseResult.GetValue(SharedOptions.CaseInsensitiveRepoContentsPathOption);
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
var baseCommitSha = parseResult.GetValue(SharedOptions.BaseCommitShaOption);

var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId!, jobPath!.FullName);
var logger = new OpenTelemetryLogger();
var discoverWorker = new DiscoveryWorker(jobId!, experimentsManager, logger);
var worker = new GraphWorker(jobId!, apiHandler, discoverWorker, logger);
var result = await worker.RunAsync(jobPath!, repoContentsPath!, caseInsensitiveRepoContentsPath, baseCommitSha!);
setExitCode(result);
return 0;
});

return command;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,28 @@ namespace NuGetUpdater.Cli.Commands;

internal static class RunCommand
{
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
internal static readonly Option<DirectoryInfo?> CaseInsensitiveRepoContentsPathOption = new("--case-insensitive-repo-contents-path") { Required = false };
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
{
Required = true,
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
};
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
internal static readonly Option<string> BaseCommitShaOption = new("--base-commit-sha") { Required = true };

internal static Command GetCommand(Action<int> setExitCode)
{
Command command = new("run", "Runs a full dependabot job.")
{
JobPathOption,
RepoContentsPathOption,
CaseInsensitiveRepoContentsPathOption,
ApiUrlOption,
JobIdOption,
BaseCommitShaOption
SharedOptions.JobPathOption,
SharedOptions.RepoContentsPathOption,
SharedOptions.CaseInsensitiveRepoContentsPathOption,
SharedOptions.ApiUrlOption,
SharedOptions.JobIdOption,
SharedOptions.BaseCommitShaOption
};

command.TreatUnmatchedTokensAsErrors = true;

command.SetAction(async (parseResult, cancellationToken) =>
{
var jobPath = parseResult.GetValue(JobPathOption);
var repoContentsPath = parseResult.GetValue(RepoContentsPathOption);
var caseInsensitiveRepoContentsPath = parseResult.GetValue(CaseInsensitiveRepoContentsPathOption);
var apiUrl = parseResult.GetValue(ApiUrlOption);
var jobId = parseResult.GetValue(JobIdOption);
var baseCommitSha = parseResult.GetValue(BaseCommitShaOption);
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
var caseInsensitiveRepoContentsPath = parseResult.GetValue(SharedOptions.CaseInsensitiveRepoContentsPathOption);
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
var baseCommitSha = parseResult.GetValue(SharedOptions.BaseCommitShaOption);

var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId!, jobPath!.FullName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.CommandLine;

namespace NuGetUpdater.Cli.Commands;

internal static class SharedOptions
{
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
internal static readonly Option<DirectoryInfo?> CaseInsensitiveRepoContentsPathOption = new("--case-insensitive-repo-contents-path") { Required = false };
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
{
Required = true,
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
};
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
internal static readonly Option<string> BaseCommitShaOption = new("--base-commit-sha") { Required = true };
}
Loading
Loading