private async Task <string?> GetCorrelatedQueueNotificationUrlAsync() { // In the case where the publish build was queued by AutoBuilder, this finds the GitHub issue associated // with that queued build. GitHubClient gitHubClient = new(new ProductHeaderValue("dotnet")); Credentials token = new(Options.GitOptions.AuthToken); RepositoryIssueRequest issueRequest = new() { Filter = IssueFilter.All, Since = DateTimeOffset.Now - TimeSpan.FromDays(2) }; issueRequest.Labels.Add(NotificationLabels.AutoBuilder); issueRequest.Labels.Add(NotificationLabels.GetRepoLocationLabel(Options.SourceRepo, Options.SourceBranch)); gitHubClient.Credentials = token; IReadOnlyList <Octokit.Issue> issues = await gitHubClient.Issue.GetAllForRepository( Options.GitOptions.Owner, Options.GitOptions.Repo, issueRequest); foreach (Octokit.Issue issue in issues) { // Get the metadata embedded within the issue body, if any QueueInfo?queueInfo = NotificationHelper.GetNotificationMetadata <QueueInfo>(issue.Body); if (queueInfo?.BuildId == Options.BuildId) { return(issue.HtmlUrl); } } return(null); }
private async Task LogAndNotifyResultsAsync( Subscription subscription, IEnumerable <string> pathsToRebuild, WebApi.Build?queuedBuild, Exception?exception, IEnumerable <string>?inProgressBuilds, IEnumerable <string>?recentFailedBuilds) { StringBuilder notificationMarkdown = new(); notificationMarkdown.AppendLine($"Subscription: {subscription}"); notificationMarkdown.AppendLine("Paths to rebuild:"); notificationMarkdown.AppendLine(); foreach (string path in pathsToRebuild.OrderBy(path => path)) { notificationMarkdown.AppendLine($"* `{path}`"); } notificationMarkdown.AppendLine(); string?category = null; if (queuedBuild is not null) { category = "Queued"; string webLink = queuedBuild.GetWebLink(); _loggerService.WriteMessage($"Queued build {webLink}"); notificationMarkdown.AppendLine($"[Build Link]({webLink})"); } else if (recentFailedBuilds is not null) { category = "Failed"; StringBuilder builder = new(); builder.AppendLine( $"Due to recent failures of the following builds, a build will not be queued again for subscription '{subscription}':"); builder.AppendLine(); foreach (string buildUri in recentFailedBuilds) { builder.AppendLine($"* {buildUri}"); } builder.AppendLine(); builder.AppendLine( $"Please investigate the cause of the failures, resolve the issue, and manually queue a build for the Dockerfile paths listed above. You must manually tag the build with a tag named '{AzdoTags.AutoBuilder}' in order for AutoBuilder to recognize that a successful build has occurred."); string message = builder.ToString(); _loggerService.WriteMessage(message); notificationMarkdown.AppendLine(message); } else if (inProgressBuilds is not null) { category = "Skipped"; StringBuilder builder = new(); builder.AppendLine($"The following in-progress builds were detected on the pipeline for subscription '{subscription}':"); foreach (string buildUri in inProgressBuilds) { builder.AppendLine(buildUri); } builder.AppendLine(); builder.AppendLine("Queueing the build will be skipped."); string message = builder.ToString(); _loggerService.WriteMessage(message); notificationMarkdown.AppendLine(message); } else if (exception != null) { category = "Failed"; notificationMarkdown.AppendLine("An exception was thrown when attempting to queue the build:"); notificationMarkdown.AppendLine(); notificationMarkdown.AppendLine("```"); notificationMarkdown.AppendLine(exception.ToString()); notificationMarkdown.AppendLine("```"); } else { throw new NotSupportedException("Unknown state"); } string header = $"AutoBuilder - {category}"; notificationMarkdown.Insert(0, $"# {header}{Environment.NewLine}{Environment.NewLine}"); // Add metadata to the issue so it can be used programmatically QueueInfo queueInfo = new() { BuildId = queuedBuild?.Id }; notificationMarkdown.AppendLine(); notificationMarkdown.AppendLine(NotificationHelper.FormatNotificationMetadata(queueInfo)); if (Options.GitOptions.AuthToken == string.Empty || Options.GitOptions.Owner == string.Empty || Options.GitOptions.Repo == string.Empty) { _loggerService.WriteMessage( "Skipping posting of notification because GitHub auth token, owner, and repo options were not provided."); } else { await _notificationService.PostAsync($"{header} - {subscription}", notificationMarkdown.ToString(), new string[] { NotificationLabels.AutoBuilder, NotificationLabels.GetRepoLocationLabel(subscription.Manifest.Repo, subscription.Manifest.Branch) }.AppendIf(NotificationLabels.Failure, () => exception is not null), Options.GitOptions.GetRepoUrl().ToString(), Options.GitOptions.AuthToken, Options.IsDryRun); } }
public override async Task ExecuteAsync() { StringBuilder notificationMarkdown = new(); string buildUrl = string.Empty; Dictionary <string, TaskResult?> taskResults = Options.TaskNames .ToDictionary(name => name, name => (TaskResult?)null); Dictionary <string, string> buildParameters = new(); BuildResult overallResult = BuildResult.Succeeded; BuildReason buildReason = BuildReason.None; string? correlatedQueueNotificationUrl = null; if (!Options.IsDryRun) { (Uri baseUrl, VssCredentials credentials) = Options.AzdoOptions.GetConnectionDetails(); using (IVssConnection connection = _connectionFactory.Create(baseUrl, credentials)) using (IProjectHttpClient projectHttpClient = connection.GetProjectHttpClient()) using (IBuildHttpClient buildClient = connection.GetBuildHttpClient()) { TeamProject project = await projectHttpClient.GetProjectAsync(Options.AzdoOptions.Project); TeamFoundation.Build.WebApi.Build build = await buildClient.GetBuildAsync(project.Id, Options.BuildId); buildUrl = build.GetWebLink(); buildReason = build.Reason; // Get the build's queue-time parameters if (build.Parameters is not null) { JObject parametersJson = JsonConvert.DeserializeObject <JObject>(build.Parameters); foreach (KeyValuePair <string, JToken?> pair in parametersJson) { buildParameters.Add(pair.Key, pair.Value?.ToString() ?? string.Empty); } } overallResult = await GetBuildTaskResultsAsync(taskResults, buildClient, project); correlatedQueueNotificationUrl = await GetCorrelatedQueueNotificationUrlAsync(); } } notificationMarkdown.AppendLine($"# Publish Results"); notificationMarkdown.AppendLine(); WriteSummaryMarkdown(notificationMarkdown, buildUrl, overallResult, buildReason, correlatedQueueNotificationUrl); notificationMarkdown.AppendLine(); WriteTaskStatusesMarkdown(taskResults, notificationMarkdown); notificationMarkdown.AppendLine(); WriteBuildParameters(buildParameters, notificationMarkdown); notificationMarkdown.AppendLine(); WriteImagesMarkdown(notificationMarkdown); await _notificationService.PostAsync( $"Publish Result - {Options.SourceRepo}/{Options.SourceBranch}", notificationMarkdown.ToString(), new string[] { NotificationLabels.Publish, NotificationLabels.GetRepoLocationLabel(Options.SourceRepo, Options.SourceBranch) }.AppendIf(NotificationLabels.Failure, () => overallResult == BuildResult.Failed), Options.GitOptions.GetRepoUrl().ToString(), Options.GitOptions.AuthToken, Options.IsDryRun); }