/// <param name="updateLatestVersion">If true, updates Latest.txt with a prerelease moniker. If there isn't one, makes the file empty.</param> /// <param name="updateLatestPackageList">If true, updates Latest_Packages.txt.</param> /// <param name="updateLastBuildPackageList">If true, updates Last_Build_Packages.txt, and enables keeping old packages in Latest_Packages.txt.</param> public async Task UpdateBuildInfoAsync( IEnumerable<string> packagePaths, string versionsRepoPath, bool updateLatestPackageList = true, bool updateLatestVersion = true, bool updateLastBuildPackageList = true) { if (packagePaths == null) { throw new ArgumentNullException(nameof(packagePaths)); } if (versionsRepoPath == null) { throw new ArgumentNullException(nameof(versionsRepoPath)); } NupkgNameInfo[] packages = packagePaths .Select(path => new NupkgNameInfo(path)) // Ignore symbol packages. .Where(t => !t.SymbolPackage) .ToArray(); string prereleaseVersion = packages .Select(t => t.Prerelease) .FirstOrDefault(prerelease => !string.IsNullOrEmpty(prerelease)) ?? ""; Dictionary<string, string> packageDictionary = packages.ToDictionary( t => t.Id, t => t.Version); 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<GitObject> objects = new List<GitObject>(); if (updateLastBuildPackageList) { objects.Add(new GitObject { Path = $"{versionsRepoPath}/Last_Build_Packages.txt", Type = GitObject.TypeBlob, Mode = GitObject.ModeFile, Content = CreatePackageListFile(packageDictionary) }); } if (updateLatestPackageList) { string latestPackagesPath = $"{versionsRepoPath}/Latest_Packages.txt"; var allPackages = new Dictionary<string, string>(packageDictionary); if (updateLastBuildPackageList) { Dictionary<string, string> existingPackages = await GetPackagesAsync(client, latestPackagesPath); // Add each existing package if there isn't a new package with the same id. foreach (var package in existingPackages) { if (!allPackages.ContainsKey(package.Key)) { allPackages[package.Key] = package.Value; } } } objects.Add(new GitObject { Path = latestPackagesPath, Type = GitObject.TypeBlob, Mode = GitObject.ModeFile, Content = CreatePackageListFile(allPackages) }); } if (updateLatestVersion) { objects.Add(new GitObject { Path = $"{versionsRepoPath}/Latest.txt", Type = GitObject.TypeBlob, Mode = GitObject.ModeFile, Content = prereleaseVersion }); } string message = $"Updating {versionsRepoPath}"; if (string.IsNullOrEmpty(prereleaseVersion)) { message += ". No prerelease versions published."; } else { message += $" for {prereleaseVersion}"; } 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; } } } } }
protected override void TraceListenedExecute() { // Use the commit sha of versions repo master (not just "master") for stable upgrade. var gitHubAuth = new GitHubAuth(GitHubAuthToken, GitHubUser, GitHubEmail); var client = new GitHubClient(gitHubAuth); string masterSha = client .GetReferenceAsync(new GitHubProject("versions", "dotnet"), "heads/master") .Result.Object.Sha; foreach (ITaskItem item in DependencyBuildInfo) { if (!string.IsNullOrEmpty(item.GetMetadata(CurrentRefMetadataName))) { item.SetMetadata(CurrentRefMetadataName, masterSha); } string autoUpgradeBranch = item.GetMetadata(AutoUpgradeBranchMetadataName); if (!string.IsNullOrEmpty(autoUpgradeBranch)) { item.SetMetadata(CurrentBranchMetadataName, autoUpgradeBranch); } } DependencyUpdateResults updateResults = DependencyUpdateUtils.Update( CreateUpdaters().ToArray(), CreateBuildInfoDependencies().ToArray()); // Update CurrentRef and CurrentBranch for each applicable build info used. if (!string.IsNullOrEmpty(CurrentRefXmlPath)) { foreach (BuildInfo info in updateResults.UsedBuildInfos) { ITaskItem infoItem = FindDependencyBuildInfo(info.Name); if (!string.IsNullOrEmpty(infoItem.GetMetadata(CurrentRefMetadataName))) { UpdateProperty( CurrentRefXmlPath, $"{info.Name}{CurrentRefMetadataName}", masterSha); } string autoUpgradeBranch = infoItem.GetMetadata(AutoUpgradeBranchMetadataName); if (!string.IsNullOrEmpty(autoUpgradeBranch)) { UpdateProperty( CurrentRefXmlPath, $"{info.Name}{CurrentBranchMetadataName}", autoUpgradeBranch); } } } if (updateResults.ChangesDetected()) { var origin = new GitHubProject(ProjectRepoName, GitHubUser); var upstreamBranch = new GitHubBranch( ProjectRepoBranch, new GitHubProject(ProjectRepoName, ProjectRepoOwner)); string suggestedMessage = updateResults.GetSuggestedCommitMessage(); string body = string.Empty; if (NotifyGitHubUsers != null) { body += PullRequestCreator.NotificationString(NotifyGitHubUsers.Select(item => item.ItemSpec)); } var prCreator = new PullRequestCreator(gitHubAuth, origin, upstreamBranch, GitHubAuthor); prCreator.CreateOrUpdateAsync( suggestedMessage, suggestedMessage + $" ({ProjectRepoBranch})", body, forceCreate: AlwaysCreateNewPullRequest).Wait(); } else { Log.LogMessage("No update required: no changes detected."); } }
private async Task<Dictionary<string, string>> GetPackagesAsync(GitHubClient client, string path) { string latestPackages = await client.GetGitHubFileContentsAsync( path, new GitHubBranch("master", _project)); using (var reader = new StringReader(latestPackages)) { return await BuildInfo.ReadPackageListAsync(reader); } }
protected override void TraceListenedExecute() { // Use the commit sha of versions repo master (not just "master") for stable upgrade. var gitHubAuth = new GitHubAuth(GitHubAuthToken, GitHubUser, GitHubEmail); var client = new GitHubClient(gitHubAuth); string masterSha = client .GetReferenceAsync(new GitHubProject("versions", "dotnet"), "heads/master") .Result.Object.Sha; foreach (ITaskItem item in DependencyBuildInfo) { if (!string.IsNullOrEmpty(item.GetMetadata(s_currentRef))) { item.SetMetadata(s_currentRef, masterSha); } } DependencyUpdateResults updateResults = DependencyUpdateUtils.Update( CreateUpdaters().ToArray(), CreateBuildInfoDependencies().ToArray()); if (!string.IsNullOrEmpty(CurrentRefXmlPath)) { // Update the build info commit sha for each applicable build info used. foreach (BuildInfo info in updateResults.UsedBuildInfos) { ITaskItem infoItem = FindDependencyBuildInfo(info.Name); if (string.IsNullOrEmpty(infoItem.GetMetadata(s_currentRef))) { continue; } Regex upgrader = CreateXmlUpdateRegex($"{info.Name}{s_currentRef}", s_currentRef); Action replace = FileUtils.ReplaceFileContents( CurrentRefXmlPath, contents => { Match m = upgrader.Match(contents); Group g = m.Groups[s_currentRef]; return contents .Remove(g.Index, g.Length) .Insert(g.Index, masterSha); }); replace(); } } if (updateResults.ChangesDetected()) { var origin = new GitHubProject(ProjectRepoName, GitHubUser); var upstreamBranch = new GitHubBranch( ProjectRepoBranch, new GitHubProject(ProjectRepoName, ProjectRepoOwner)); string suggestedMessage = updateResults.GetSuggestedCommitMessage(); string body = string.Empty; if (NotifyGitHubUsers != null) { body += PullRequestCreator.NotificationString(NotifyGitHubUsers.Select(item => item.ItemSpec)); } var prCreator = new PullRequestCreator(gitHubAuth, origin, upstreamBranch, GitHubAuthor); prCreator.CreateOrUpdateAsync( suggestedMessage, suggestedMessage + $" ({ProjectRepoBranch})", body, forceCreate: AlwaysCreateNewPullRequest).Wait(); } else { Log.LogMessage("No update required: no changes detected."); } }
private async Task UpdateGitHubFileAsync(string path, string newFileContent, string commitMessage) { using (GitHubClient client = new GitHubClient(_gitHubAuth)) { string fileUrl = $"https://api.github.com/repos/{_project.Segments}/contents/{path}"; await client.PutGitHubFileAsync(fileUrl, commitMessage, newFileContent); } }
public async Task CreateOrUpdateAsync( string commitMessage, string title, string description, bool forceCreate = false) { var upstream = UpstreamBranch.Project; using (var client = new GitHubClient(_auth)) { GitHubBranch originBranch = null; GitHubPullRequest pullRequestToUpdate = null; string upgradeBranchPrefix = _namingStrategy.Prefix(UpstreamBranch.Name); if (!forceCreate) { pullRequestToUpdate = await client.SearchPullRequestsAsync( upstream, upgradeBranchPrefix, _auth.User); if (pullRequestToUpdate == null) { Trace.TraceInformation($"No existing pull request found."); } else { Trace.TraceInformation( $"Pull request already exists for {upgradeBranchPrefix} in {upstream.Segments}. " + $"#{pullRequestToUpdate.Number}, '{pullRequestToUpdate.Title}'"); string blockedReason = GetUpdateBlockedReason(client, pullRequestToUpdate, upgradeBranchPrefix); if (blockedReason == null) { originBranch = new GitHubBranch( pullRequestToUpdate.Head.Ref, Origin); } else { string comment = $"Couldn't update this pull request: {blockedReason}\n" + $"Would have applied '{commitMessage}'"; await client.PostCommentAsync(upstream, pullRequestToUpdate.Number, comment); return; } } } // No existing branch to update: push to a new one. if (originBranch == null) { string newBranchName = _namingStrategy.Prefix(UpstreamBranch.Name) + _namingStrategy.CreateFreshBranchNameSuffix(UpstreamBranch.Name); originBranch = new GitHubBranch(newBranchName, Origin); } PushNewCommit(originBranch, commitMessage); if (pullRequestToUpdate != null) { await client.UpdateGitHubPullRequestAsync(upstream, pullRequestToUpdate.Number, title, description); } else { await client.PostGitHubPullRequestAsync(title, description, originBranch, UpstreamBranch); } } }
private string GetUpdateBlockedReason( GitHubClient client, GitHubPullRequest pullRequest, string upgradeBranchPrefix) { if (pullRequest.Head.User.Login != Origin.Owner) { return $"Owner of head repo '{pullRequest.Head.User.Login}' is not '{Origin.Owner}'"; } if (!pullRequest.Head.Ref.StartsWith(upgradeBranchPrefix)) { return $"Ref name '{pullRequest.Head.Ref}' does not start with '{upgradeBranchPrefix}'"; } GitCommit commit = client.GetCommitAsync(Origin, pullRequest.Head.Sha).Result; if (commit.Author.Name != GitAuthorName) { return $"Head commit author '{commit.Author.Name}' is not '{GitAuthorName}'"; } if (commit.Committer.Name != GitAuthorName) { return $"Head commit committer '{commit.Committer.Name}' is not '{GitAuthorName}'"; } return null; }