Skip to content

Commit 57dda0b

Browse files
authoredNov 23, 2020
Publish to internal versions repo (#698)
1 parent 4b8b5fb commit 57dda0b

17 files changed

+526
-186
lines changed
 

‎.editorconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ dotnet_code_quality.ca1802.api_surface = private, internal
158158
dotnet_code_quality.ca2208.api_surface = public
159159

160160
# License header
161-
file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.\n
161+
file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.
162162

163163
# Xml project files
164164
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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.CommandLine;
6+
using Microsoft.VisualStudio.Services.Common;
7+
8+
namespace Microsoft.DotNet.ImageBuilder.Commands
9+
{
10+
public class AzdoOptions
11+
{
12+
public string AccessToken { get; set; }
13+
14+
public string Organization { get; set; }
15+
16+
public string Project { get; set; }
17+
18+
public string Repo { get; set; }
19+
20+
public string Branch { get; set; }
21+
22+
public string Path { get; set; }
23+
24+
public void DefineParameters(ArgumentSyntax syntax)
25+
{
26+
string accessToken = null;
27+
syntax.DefineParameter(
28+
"azdo-pat",
29+
ref accessToken,
30+
"Azure DevOps PAT");
31+
AccessToken = accessToken;
32+
33+
string organization = null;
34+
syntax.DefineParameter(
35+
"azdo-org",
36+
ref organization,
37+
"Azure DevOps organization");
38+
Organization = organization;
39+
40+
string project = null;
41+
syntax.DefineParameter(
42+
"azdo-project",
43+
ref project,
44+
"Azure DevOps project");
45+
Project = project;
46+
}
47+
48+
public void DefineOptions(ArgumentSyntax syntax)
49+
{
50+
string repo = null;
51+
syntax.DefineOption(
52+
"azdo-repo",
53+
ref repo,
54+
"Azure DevOps repo");
55+
Repo = repo;
56+
57+
string branch = "master";
58+
syntax.DefineOption(
59+
"azdo-branch",
60+
ref branch,
61+
"Azure DevOps branch (default: master)");
62+
Branch = branch;
63+
64+
string path = null;
65+
syntax.DefineOption(
66+
"azdo-path",
67+
ref path,
68+
"Azure DevOps path");
69+
Path = path;
70+
}
71+
72+
public (Uri BaseUrl, VssCredentials Credentials) GetConnectionDetails() =>
73+
(new Uri($"https://dev.azure.com/{Organization}"), new VssBasicCredential(string.Empty, AccessToken));
74+
}
75+
}

‎src/Microsoft.DotNet.ImageBuilder/src/Commands/PublishImageInfoCommand.cs

+66-25
Original file line numberDiff line numberDiff line change
@@ -10,63 +10,109 @@
1010
using System.Net.Http;
1111
using System.Threading.Tasks;
1212
using Microsoft.DotNet.ImageBuilder.Models.Image;
13+
using Microsoft.DotNet.ImageBuilder.Services;
1314
using Microsoft.DotNet.ImageBuilder.ViewModel;
1415
using Microsoft.DotNet.VersionTools.Automation.GitHubApi;
16+
using Microsoft.TeamFoundation.SourceControl.WebApi;
17+
using Microsoft.VisualStudio.Services.Common;
1518

19+
#nullable enable
1620
namespace Microsoft.DotNet.ImageBuilder.Commands
1721
{
1822
[Export(typeof(ICommand))]
1923
public class PublishImageInfoCommand : ManifestCommand<PublishImageInfoOptions>
2024
{
2125
private readonly IGitHubClientFactory _gitHubClientFactory;
2226
private readonly ILoggerService _loggerService;
27+
private readonly IAzdoGitHttpClientFactory _azdoGitHttpClientFactory;
2328
private readonly HttpClient _httpClient;
29+
private const string CommitMessage = "Merging Docker image info updates from build";
2430

2531
[ImportingConstructor]
26-
public PublishImageInfoCommand(IGitHubClientFactory gitHubClientFactory, ILoggerService loggerService, IHttpClientProvider httpClientFactory)
32+
public PublishImageInfoCommand(IGitHubClientFactory gitHubClientFactory, ILoggerService loggerService, IHttpClientProvider httpClientProvider,
33+
IAzdoGitHttpClientFactory azdoGitHttpClientFactory)
2734
{
28-
if (httpClientFactory is null)
35+
if (httpClientProvider is null)
2936
{
30-
throw new ArgumentNullException(nameof(httpClientFactory));
37+
throw new ArgumentNullException(nameof(httpClientProvider));
3138
}
3239

3340
_gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
3441
_loggerService = loggerService ?? throw new ArgumentNullException(nameof(loggerService));
42+
_azdoGitHttpClientFactory = azdoGitHttpClientFactory ?? throw new ArgumentNullException(nameof(azdoGitHttpClientFactory));
3543

36-
_httpClient = httpClientFactory.GetClient();
44+
_httpClient = httpClientProvider.GetClient();
3745
}
3846

3947
public override async Task ExecuteAsync()
4048
{
4149
Uri imageInfoPathIdentifier = GitHelper.GetBlobUrl(Options.GitOptions);
42-
GitObject imageInfoGitObject = await GetUpdatedImageInfoGitObjectAsync();
4350

44-
if (imageInfoGitObject is null)
51+
string? imageInfoContent = await GetUpdatedImageInfoAsync();
52+
53+
if (imageInfoContent is null)
4554
{
4655
_loggerService.WriteMessage($"No changes to the '{imageInfoPathIdentifier}' file were needed.");
4756
return;
4857
}
4958

5059
_loggerService.WriteMessage(
5160
$"The '{imageInfoPathIdentifier}' file has been updated with the following content:" +
52-
Environment.NewLine + imageInfoGitObject.Content + Environment.NewLine);
61+
Environment.NewLine + imageInfoContent + Environment.NewLine);
5362

5463
if (!Options.IsDryRun)
5564
{
56-
using IGitHubClient gitHubClient = _gitHubClientFactory.GetClient(Options.GitOptions.ToGitHubAuth(), Options.IsDryRun);
57-
await GitHelper.ExecuteGitOperationsWithRetryAsync(async () =>
58-
{
59-
GitReference gitRef = await GitHelper.PushChangesAsync(
60-
gitHubClient, Options, "Merging image info updates from build.",
61-
branch => Task.FromResult<IEnumerable<GitObject>>(new GitObject[] { imageInfoGitObject }));
62-
63-
Uri commitUrl = GitHelper.GetCommitUrl(Options.GitOptions, gitRef.Object.Sha);
64-
_loggerService.WriteMessage($"The '{imageInfoPathIdentifier}' file was updated ({commitUrl}).");
65-
});
65+
await UpdateGitHubAsync(imageInfoContent, imageInfoPathIdentifier);
66+
await UpdateAzdoAsync(imageInfoContent, imageInfoPathIdentifier);
6667
}
6768
}
6869

69-
private async Task<GitObject> GetUpdatedImageInfoGitObjectAsync()
70+
private async Task UpdateAzdoAsync(string imageInfoContent, Uri imageInfoPathIdentifier)
71+
{
72+
(Uri baseUrl, VssCredentials credentials) = Options.AzdoOptions.GetConnectionDetails();
73+
74+
using IAzdoGitHttpClient gitHttpClient = _azdoGitHttpClientFactory.GetClient(baseUrl, credentials);
75+
76+
GitRepository repo = (await gitHttpClient.GetRepositoriesAsync())
77+
.First(repo => repo.Name == Options.AzdoOptions.Repo);
78+
GitRef branchRef = (await gitHttpClient.GetBranchRefsAsync(repo.Id))
79+
.First(branch => branch.Name == $"refs/heads/{Options.AzdoOptions.Branch}");
80+
81+
GitPush push = await gitHttpClient.PushChangesAsync(CommitMessage, repo.Id, branchRef, new Dictionary<string, string>
82+
{
83+
{ Options.AzdoOptions.Path, imageInfoContent }
84+
});
85+
86+
TeamFoundation.SourceControl.WebApi.GitCommit commit =
87+
await gitHttpClient.GetCommitAsync(push.Commits.First().CommitId, repo.Id);
88+
89+
_loggerService.WriteMessage($"The '{imageInfoPathIdentifier}' file was updated ({commit.RemoteUrl}).");
90+
}
91+
92+
private async Task UpdateGitHubAsync(string imageInfoContent, Uri imageInfoPathIdentifier)
93+
{
94+
IGitHubClient gitHubClient = _gitHubClientFactory.GetClient(Options.GitOptions.ToGitHubAuth(), Options.IsDryRun);
95+
await GitHelper.ExecuteGitOperationsWithRetryAsync(async () =>
96+
{
97+
VersionTools.Automation.GitHubApi.GitObject imageInfoGitObject = new VersionTools.Automation.GitHubApi.GitObject
98+
{
99+
Path = Options.GitOptions.Path,
100+
Type = VersionTools.Automation.GitHubApi.GitObject.TypeBlob,
101+
Mode = VersionTools.Automation.GitHubApi.GitObject.ModeFile,
102+
Content = imageInfoContent
103+
};
104+
105+
GitReference gitRef = await GitHelper.PushChangesAsync(
106+
gitHubClient, Options, CommitMessage,
107+
branch => Task.FromResult<IEnumerable<VersionTools.Automation.GitHubApi.GitObject>>(
108+
new VersionTools.Automation.GitHubApi.GitObject[] { imageInfoGitObject }));
109+
110+
Uri commitUrl = GitHelper.GetCommitUrl(Options.GitOptions, gitRef.Object.Sha);
111+
_loggerService.WriteMessage($"The '{imageInfoPathIdentifier}' file was updated ({commitUrl}).");
112+
});
113+
}
114+
115+
private async Task<string?> GetUpdatedImageInfoAsync()
70116
{
71117
ImageArtifactDetails srcImageArtifactDetails = ImageInfoHelper.LoadFromFile(Options.ImageInfoPath, Manifest);
72118

@@ -106,13 +152,7 @@ private async Task<GitObject> GetUpdatedImageInfoGitObjectAsync()
106152

107153
if (originalTargetImageInfoContents != newTargetImageInfoContents)
108154
{
109-
return new GitObject
110-
{
111-
Path = Options.GitOptions.Path,
112-
Type = GitObject.TypeBlob,
113-
Mode = GitObject.ModeFile,
114-
Content = newTargetImageInfoContents
115-
};
155+
return newTargetImageInfoContents;
116156
}
117157
else
118158
{
@@ -178,3 +218,4 @@ private void RemoveOutOfDateContent(ImageArtifactDetails imageArtifactDetails)
178218
}
179219
}
180220
}
221+
#nullable disable

‎src/Microsoft.DotNet.ImageBuilder/src/Commands/PublishImageInfoOptions.cs

+3
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,22 @@ public class PublishImageInfoOptions : ImageInfoOptions, IGitOptionsHost
1111
protected override string CommandHelp => "Publishes a build's merged image info.";
1212

1313
public GitOptions GitOptions { get; set; } = new GitOptions();
14+
public AzdoOptions AzdoOptions { get; set; } = new AzdoOptions();
1415

1516
public override void DefineOptions(ArgumentSyntax syntax)
1617
{
1718
base.DefineOptions(syntax);
1819

1920
GitOptions.DefineOptions(syntax);
21+
AzdoOptions.DefineOptions(syntax);
2022
}
2123

2224
public override void DefineParameters(ArgumentSyntax syntax)
2325
{
2426
base.DefineParameters(syntax);
2527

2628
GitOptions.DefineParameters(syntax);
29+
AzdoOptions.DefineParameters(syntax);
2730
}
2831
}
2932
}

‎src/Microsoft.DotNet.ImageBuilder/src/Commands/QueueBuildCommand.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ private async Task QueueBuildForStaleImages(Subscription subscription, IEnumerab
9393
return;
9494
}
9595

96-
using (IVssConnection connection = _connectionFactory.Create(
97-
new Uri($"https://dev.azure.com/{Options.BuildOrganization}"),
98-
new VssBasicCredential(string.Empty, Options.BuildPersonalAccessToken)))
96+
(Uri baseUrl, VssCredentials credentials) = Options.AzdoOptions.GetConnectionDetails();
97+
98+
using (IVssConnection connection = _connectionFactory.Create(baseUrl, credentials))
9999
using (IProjectHttpClient projectHttpClient = connection.GetProjectHttpClient())
100100
using (IBuildHttpClient client = connection.GetBuildHttpClient())
101101
{
102-
TeamProject project = await projectHttpClient.GetProjectAsync(Options.BuildProject);
102+
TeamProject project = await projectHttpClient.GetProjectAsync(Options.AzdoOptions.Project);
103103

104104
Build build = new Build
105105
{

‎src/Microsoft.DotNet.ImageBuilder/src/Commands/QueueBuildOptions.cs

+3-24
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@ public class QueueBuildOptions : Options
1313
protected override string CommandHelp => "Queues builds to update images";
1414

1515
public string SubscriptionsPath { get; set; }
16-
public string BuildPersonalAccessToken { get; set; }
17-
public string BuildOrganization { get; set; }
18-
public string BuildProject { get; set; }
16+
public AzdoOptions AzdoOptions { get; set; } = new AzdoOptions();
1917
public IEnumerable<string> AllSubscriptionImagePaths { get; set; }
2018

2119
public override void DefineOptions(ArgumentSyntax syntax)
2220
{
2321
base.DefineOptions(syntax);
22+
AzdoOptions.DefineOptions(syntax);
2423

2524
const string DefaultSubscriptionsPath = "subscriptions.json";
2625
string subscriptionsPath = DefaultSubscriptionsPath;
@@ -41,27 +40,7 @@ public override void DefineOptions(ArgumentSyntax syntax)
4140
public override void DefineParameters(ArgumentSyntax syntax)
4241
{
4342
base.DefineParameters(syntax);
44-
45-
string buildPersonalAccessToken = null;
46-
syntax.DefineParameter(
47-
"build-pat",
48-
ref buildPersonalAccessToken,
49-
"Azure DevOps PAT for queuing builds");
50-
BuildPersonalAccessToken = buildPersonalAccessToken;
51-
52-
string buildOrganization = null;
53-
syntax.DefineParameter(
54-
"build-organization",
55-
ref buildOrganization,
56-
"Azure DevOps organization for queuing builds");
57-
BuildOrganization = buildOrganization;
58-
59-
string buildProject = null;
60-
syntax.DefineParameter(
61-
"build-project",
62-
ref buildProject,
63-
"Azure DevOps project for queuing builds");
64-
BuildProject = buildProject;
43+
AzdoOptions.DefineParameters(syntax);
6544
}
6645
}
6746
}

‎src/Microsoft.DotNet.ImageBuilder/src/Microsoft.DotNet.ImageBuilder.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="1.22.0" />
1515
<PackageReference Include="Microsoft.DotNet.VersionTools" Version="1.0.0-beta.19426.12" />
1616
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.0" />
17-
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="16.143.1" />
17+
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="16.153.0" />
1818
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.11" />
1919
<PackageReference Include="Polly" Version="7.2.0" />
2020
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.0" />

0 commit comments

Comments
 (0)
Please sign in to comment.