Skip to content

Commit 09ec946

Browse files
authored
Add from-build command to Update Dependencies tool (#6424)
1 parent b029b2b commit 09ec946

5 files changed

Lines changed: 172 additions & 68 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
using Microsoft.DotNet.DarcLib;
9+
using Microsoft.DotNet.ProductConstructionService.Client.Models;
10+
using Microsoft.Extensions.Logging;
11+
12+
namespace Dotnet.Docker;
13+
14+
internal interface IBuildUpdaterService
15+
{
16+
Task<int> UpdateFrom(Build build, CreatePullRequestOptions pullRequestOptions);
17+
}
18+
19+
internal class BuildUpdaterService(
20+
IBuildAssetService buildAssetService,
21+
IBasicBarClient barClient,
22+
ILogger<BuildUpdaterService> logger) : IBuildUpdaterService
23+
{
24+
private readonly IBuildAssetService _buildAssetService = buildAssetService;
25+
private readonly IBasicBarClient _barClient = barClient;
26+
private readonly ILogger<BuildUpdaterService> _logger = logger;
27+
28+
/// <summary>
29+
/// Updates product versions according to a specific BAR build of the VMR. This will update the
30+
/// manifest.versions.json file, generate Dockerfiles and Readmes from the templates, and if
31+
/// credentials are provided, submit a pull request.
32+
/// </summary>
33+
/// <param name="build">
34+
/// A build of the VMR repo (dotnet/dotnet)
35+
/// </param>
36+
/// <param name="pullRequestOptions">
37+
/// Options for creating a pull request. If credentials are provided, a pull request will be created.
38+
/// </param>
39+
/// <returns>Exit code (0 for success)</returns>
40+
public async Task<int> UpdateFrom(Build build, CreatePullRequestOptions pullRequestOptions)
41+
{
42+
_logger.LogInformation("Updating to build {build.Id} with commit {options.Repo}@{build.Commit}",
43+
build.Id, build.AzureDevOpsRepository ?? build.GitHubRepository, build.Commit);
44+
45+
if (!IsVmrBuild(build))
46+
{
47+
throw new InvalidOperationException(
48+
"Expected a build of the VMR, but got a build of " +
49+
$"{build.AzureDevOpsRepository ?? build.GitHubRepository} instead.");
50+
}
51+
52+
IEnumerable<Asset> assets = await _barClient.GetAssetsAsync(buildId: build.Id);
53+
54+
Asset productCommitsAsset = assets.FirstOrDefault(a => ProductCommits.SdkAssetRegex.IsMatch(a.Name))
55+
?? throw new InvalidOperationException($"Could not find product version commit in assets.");
56+
57+
string productCommitsJson = await _buildAssetService.GetAssetTextContentsAsync(productCommitsAsset);
58+
ProductCommits productCommits = ProductCommits.FromJson(productCommitsJson);
59+
60+
Version dockerfileVersion = ResolveMajorMinorVersion(productCommits.Sdk.Version);
61+
62+
// Run old update-dependencies command using the resolved versions
63+
var updateDependencies = new SpecificCommand();
64+
var updateDependenciesOptions = new SpecificCommandOptions()
65+
{
66+
DockerfileVersion = dockerfileVersion.ToString(),
67+
ProductVersions = new Dictionary<string, string?>()
68+
{
69+
// "dotnet" version is also required. It sets the "dotnet|*|product-version"
70+
// variable which is used for runtime-deps, runtime, and aspnet tags.
71+
{ "dotnet", productCommits.Runtime.Version },
72+
{ "runtime", productCommits.Runtime.Version },
73+
{ "aspnet", productCommits.AspNetCore.Version },
74+
{ "aspnet-composite", productCommits.AspNetCore.Version },
75+
{ "sdk", productCommits.Sdk.Version },
76+
},
77+
78+
// Pass through all properties of CreatePullRequestOptions
79+
User = pullRequestOptions.User,
80+
Email = pullRequestOptions.Email,
81+
Password = pullRequestOptions.Password,
82+
AzdoOrganization = pullRequestOptions.AzdoOrganization,
83+
AzdoProject = pullRequestOptions.AzdoProject,
84+
AzdoRepo = pullRequestOptions.AzdoRepo,
85+
VersionSourceName = pullRequestOptions.VersionSourceName,
86+
SourceBranch = pullRequestOptions.SourceBranch,
87+
TargetBranch = pullRequestOptions.TargetBranch,
88+
};
89+
90+
return await updateDependencies.ExecuteAsync(updateDependenciesOptions);
91+
}
92+
93+
private static Version ResolveMajorMinorVersion(string versionString)
94+
{
95+
string[] versionParts = versionString.Split('.');
96+
if (versionParts.Length < 2)
97+
{
98+
throw new InvalidOperationException($"Could not parse major-minor version from '{versionString}'.");
99+
}
100+
101+
return new Version(major: int.Parse(versionParts[0]), minor: int.Parse(versionParts[1]));
102+
}
103+
104+
private static bool IsVmrBuild(Build build)
105+
{
106+
string repo = build.GitHubRepository ?? build.AzureDevOpsRepository;
107+
return repo == "https://github.com/dotnet/dotnet"
108+
|| repo == "https://dev.azure.com/dnceng/internal/_git/dotnet-dotnet";
109+
}
110+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
4+
using System.Threading.Tasks;
5+
using Microsoft.DotNet.DarcLib;
6+
using Microsoft.DotNet.ProductConstructionService.Client.Models;
7+
using Microsoft.Extensions.Logging;
8+
9+
namespace Dotnet.Docker;
10+
11+
internal class FromBuildCommand(
12+
IBasicBarClient barClient,
13+
IBuildUpdaterService buildUpdaterService,
14+
ILogger<FromBuildCommand> logger)
15+
: BaseCommand<FromBuildOptions>
16+
{
17+
private readonly IBasicBarClient _barClient = barClient;
18+
private readonly IBuildUpdaterService _buildUpdaterService = buildUpdaterService;
19+
private readonly ILogger<FromBuildCommand> _logger = logger;
20+
21+
public override async Task<int> ExecuteAsync(FromBuildOptions options)
22+
{
23+
_logger.LogInformation("Getting BAR build with ID {options.Id}", options.Id);
24+
Build build = await _barClient.GetBuildAsync(options.Id);
25+
return await _buildUpdaterService.UpdateFrom(build, options);
26+
}
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
4+
using System.Collections.Generic;
5+
using System.CommandLine;
6+
7+
namespace Dotnet.Docker;
8+
9+
internal class FromBuildOptions : CreatePullRequestOptions, IOptions
10+
{
11+
public required int Id { get; init; }
12+
13+
public static new List<Argument> Arguments { get; } =
14+
[
15+
new Argument<int>("id")
16+
{
17+
Arity = ArgumentArity.ExactlyOne,
18+
Description = "The BAR build ID to use as a source for the update (see https://aka.ms/bar)"
19+
},
20+
..CreatePullRequestOptions.Arguments,
21+
];
22+
23+
public static new List<Option> Options { get; } =
24+
[
25+
..CreatePullRequestOptions.Options,
26+
];
27+
}
Lines changed: 3 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.Collections.Generic;
64
using System.Linq;
75
using System.Threading.Tasks;
86
using Microsoft.DotNet.DarcLib;
@@ -12,13 +10,13 @@
1210
namespace Dotnet.Docker;
1311

1412
internal class FromChannelCommand(
15-
IBuildAssetService buildAssetService,
1613
IBasicBarClient barClient,
14+
IBuildUpdaterService buildUpdaterService,
1715
ILogger<FromChannelCommand> logger)
1816
: BaseCommand<FromChannelOptions>
1917
{
20-
private readonly IBuildAssetService _buildAssetService = buildAssetService;
2118
private readonly IBasicBarClient _barClient = barClient;
19+
private readonly IBuildUpdaterService _buildUpdaterService = buildUpdaterService;
2220
private readonly ILogger<FromChannelCommand> _logger = logger;
2321

2422
public override async Task<int> ExecuteAsync(FromChannelOptions options)
@@ -31,70 +29,7 @@ public override async Task<int> ExecuteAsync(FromChannelOptions options)
3129

3230
_logger.LogInformation("Channel {options.Channel} is '{channel.Name}'",
3331
options.Channel, latestBuild.Channels.FirstOrDefault(c => c.Id == options.Channel)?.Name);
34-
_logger.LogInformation("Got latest build {latestBuild.Id} with commit {options.Repo}@{latestBuild.Commit}",
35-
latestBuild.Id, latestBuild.AzureDevOpsRepository ?? latestBuild.GitHubRepository, latestBuild.Commit);
3632

37-
if (!IsVmrBuild(latestBuild))
38-
{
39-
throw new InvalidOperationException(
40-
"Expected a build of the VMR, but got a build of " +
41-
$"{latestBuild.AzureDevOpsRepository ?? latestBuild.GitHubRepository} instead.");
42-
}
43-
44-
IEnumerable<Asset> assets = await _barClient.GetAssetsAsync(buildId: latestBuild.Id);
45-
46-
Asset productCommitsAsset = assets.FirstOrDefault(a => ProductCommits.SdkAssetRegex.IsMatch(a.Name))
47-
?? throw new InvalidOperationException($"Could not find product version commit in assets.");
48-
49-
string productCommitsJson = await _buildAssetService.GetAssetTextContentsAsync(productCommitsAsset);
50-
ProductCommits productCommits = ProductCommits.FromJson(productCommitsJson);
51-
52-
Version dockerfileVersion = ResolveMajorMinorVersion(productCommits.Sdk.Version);
53-
54-
// Run old update-dependencies command using the resolved versions
55-
var updateDependencies = new SpecificCommand();
56-
var updateDependenciesOptions = new SpecificCommandOptions()
57-
{
58-
DockerfileVersion = dockerfileVersion.ToString(),
59-
ProductVersions = new Dictionary<string, string?>()
60-
{
61-
// In the VMR, runtime and aspnetcore versions are coupled
62-
{ "runtime", productCommits.Runtime.Version },
63-
{ "aspnet", productCommits.AspNetCore.Version },
64-
{ "aspnet-composite", productCommits.AspNetCore.Version },
65-
{ "sdk", productCommits.Sdk.Version },
66-
},
67-
68-
// Pass through all properties of CreatePullRequestOptions
69-
User = options.User,
70-
Email = options.Email,
71-
Password = options.Password,
72-
AzdoOrganization = options.AzdoOrganization,
73-
AzdoProject = options.AzdoProject,
74-
AzdoRepo = options.AzdoRepo,
75-
VersionSourceName = options.VersionSourceName,
76-
SourceBranch = options.SourceBranch,
77-
TargetBranch = options.TargetBranch,
78-
};
79-
80-
return await updateDependencies.ExecuteAsync(updateDependenciesOptions);
81-
}
82-
83-
private static Version ResolveMajorMinorVersion(string versionString)
84-
{
85-
string[] versionParts = versionString.Split('.');
86-
if (versionParts.Length < 2)
87-
{
88-
throw new InvalidOperationException($"Could not parse major-minor version from '{versionString}'.");
89-
}
90-
91-
return new Version(major: int.Parse(versionParts[0]), minor: int.Parse(versionParts[1]));
92-
}
93-
94-
private static bool IsVmrBuild(Build build)
95-
{
96-
string repo = build.GitHubRepository ?? build.AzureDevOpsRepository;
97-
return repo == "https://github.com/dotnet/dotnet"
98-
|| repo == "https://dev.azure.com/dnceng/internal/_git/dotnet-dotnet";
33+
return await _buildUpdaterService.UpdateFrom(latestBuild, options);
9934
}
10035
}

eng/update-dependencies/Program.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
var rootCommand = new RootCommand()
1616
{
17+
FromBuildCommand.Create(
18+
name: "from-build",
19+
description: "Update dependencies using a specific BAR build"),
1720
FromChannelCommand.Create(
1821
name: "from-channel",
1922
description: "Update dependencies using the latest build from a channel"),
@@ -40,11 +43,13 @@
4043
},
4144
configureHost: host => host.ConfigureServices(services =>
4245
{
46+
services.AddSingleton<IBuildUpdaterService, BuildUpdaterService>();
4347
services.AddSingleton<IBasicBarClient>(_ =>
4448
new BarApiClient(null, null, disableInteractiveAuth: true));
4549
services.AddSingleton<IBuildAssetService, BuildAssetService>();
4650
services.AddSingleton<HttpClient>();
4751

52+
FromBuildCommand.Register<FromBuildCommand>(services);
4853
FromChannelCommand.Register<FromChannelCommand>(services);
4954
SpecificCommand.Register<SpecificCommand>(services);
5055
})

0 commit comments

Comments
 (0)