// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !SOURCE_BUILD using Microsoft.DotNet.VersionTools.Automation; using Microsoft.DotNet.VersionTools.Automation.GitHubApi; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; namespace Microsoft.DotNet.Cli.Build { // This code is a slightly modified port of GitHubVersionsRepoUpdater from // https://raw.githubusercontent.com/dotnet/buildtools/master/src/Microsoft.DotNet.VersionTools/Automation/GitHubVersionsRepoUpdater.cs internal class GitHubWriteVersionUpdater: VersionsRepoUpdater { private const int MaxTries = 10; private const int RetryMillisecondsDelay = 5000; private GitHubAuth _gitHubAuth; private GitHubProject _project; public GitHubWriteVersionUpdater( GitHubAuth gitHubAuth, string versionsRepoOwner = null, string versionsRepo = null) : this( gitHubAuth, new GitHubProject(versionsRepo ?? "versions", versionsRepoOwner)) { } public GitHubWriteVersionUpdater(GitHubAuth gitHubAuth, GitHubProject project) { if (gitHubAuth == null) { throw new ArgumentNullException(nameof(gitHubAuth)); } _gitHubAuth = gitHubAuth; if (project == null) { throw new ArgumentNullException(nameof(project)); } _project = project; } /// If true, updates Latest.txt with a prerelease moniker. If there isn't one, makes the file empty. /// If true, updates Latest_Packages.txt. public async Task UpdateBuildInfoAsync( string versionIdentifier, string version, string versionsRepoPath, bool updateLatestPackageList = true, bool updateLatestVersion = true) { if (versionIdentifier == null) { throw new ArgumentNullException(nameof(versionIdentifier)); } if (version == null) { throw new ArgumentNullException(nameof(version)); } if (versionsRepoPath == null) { throw new ArgumentNullException(nameof(versionsRepoPath)); } using (GitHubClient client = new GitHubClient(_gitHubAuth)) { for (int i = 0; i < MaxTries; i++) { try { // Master commit to use as new commit's parent. string masterRef = "heads/master"; GitReference currentMaster = await client.GetReferenceAsync(_project, masterRef); string masterSha = currentMaster.Object.Sha; List objects = new List(); if (updateLatestVersion) { objects.Add(new GitObject { Path = $"{versionsRepoPath}/Latest.txt", Type = GitObject.TypeBlob, Mode = GitObject.ModeFile, Content = version }); } if (updateLatestPackageList) { objects.Add(new GitObject { Path = $"{versionsRepoPath}/Latest_Packages.txt", Type = GitObject.TypeBlob, Mode = GitObject.ModeFile, Content = $"{versionIdentifier} {version}{Environment.NewLine}" }); } string message = $"Updating {versionsRepoPath}"; GitTree tree = await client.PostTreeAsync(_project, masterSha, objects.ToArray()); GitCommit commit = await client.PostCommitAsync(_project, message, tree.Sha, new[] { masterSha }); // Only fast-forward. Don't overwrite other changes: throw exception instead. await client.PatchReferenceAsync(_project, masterRef, commit.Sha, force: false); Trace.TraceInformation($"Committed build-info update on attempt {i + 1}."); break; } catch (HttpRequestException ex) { int nextTry = i + 1; if (nextTry < MaxTries) { Trace.TraceInformation($"Encountered exception committing build-info update: {ex.Message}"); Trace.TraceInformation($"Trying again in {RetryMillisecondsDelay}ms. {MaxTries - nextTry} tries left."); await Task.Delay(RetryMillisecondsDelay); } else { Trace.TraceInformation("Encountered exception committing build-info update."); throw; } } } } } } } #endif