private async Task ProcessOpenedTriageIssue(IssuesHookData issuePayload, IList <TriageItem> triageItems, IGitHubClient gitHubClient) { var issue = await gitHubClient.Issue.Get(issuePayload.Repository.Id, issuePayload.Issue.Number); if (!issue.Labels.Any(l => l.Name == _markingLabelName)) { var update = issue.ToUpdate(); update.AddLabel(_markingLabelName); foreach (var label in _issueLabels.Except(issue.Labels.Select(l => l.Name))) { update.AddLabel(label); } await gitHubClient.Issue.Update(issuePayload.Repository.Id, issuePayload.Issue.Number, update); await AddToZenHubTopic(issuePayload, gitHubClient, issue); } foreach (var triageItem in triageItems) { triageItem.Url = issuePayload.Issue.HtmlUrl; _logger.LogInformation($"New triage item: {triageItem}"); } await IngestTriageItemsIntoKusto(triageItems); await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"Bot has updated the 'TimelineIssuesTriage' database.\n**PowerBI reports may take up to 24 hours to refresh**\n\nSee {_docLink} for more information."); }
private async Task UpdateExistingIssue(IssuesHookData issuePayload, IList <TriageItem> triageItems, Issue existingIssueToUpdate, IGitHubClient gitHubClient) { var existingIssueItems = GetTriageItems(existingIssueToUpdate.Body); // update existing issue body var existintIssueUpdate = new IssueUpdate { Body = UpdateExistingIssueBody(issuePayload, triageItems, existingIssueToUpdate, existingIssueItems), }; await gitHubClient.Issue.Update(issuePayload.Repository.Id, existingIssueToUpdate.Number, existintIssueUpdate); // insert TimelineIssuesTriage with existing issue URL var toBeUpdatedTriageItems = triageItems.Except(existingIssueItems).ToList(); foreach (var triageItem in toBeUpdatedTriageItems) { triageItem.Url = existingIssueToUpdate.HtmlUrl; _logger.LogInformation($"buildId: {triageItem.BuildId}, recordId: {triageItem.RecordId}, index: {triageItem.Index}, category: {triageItem.UpdatedCategory}, url: {triageItem.Url}"); } await IngestTriageItemsIntoKusto(toBeUpdatedTriageItems); await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, existingIssueToUpdate.Number, $"Bot has updated the issue and 'TimelineIssuesTriage' database by data from issue {issuePayload.Issue.HtmlUrl}.\n**PowerBI reports may take up to 24 hours to refresh**\n\nSee {_docLink} for more information."); // add comment to opened issue and close it await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"Existing issue was detected and additional builds was added to it.\n\nClosing as updated into {existingIssueToUpdate.HtmlUrl}\n\nFor more information see {_docLink}"); var issueUpdate = new IssueUpdate { State = ItemState.Closed, }; await gitHubClient.Issue.Update(issuePayload.Repository.Id, issuePayload.Issue.Number, issueUpdate); }
private bool IsDuplicate(IssuesHookData issuePayload, IList <TriageItem> triageItems, IReadOnlyList <Issue> existingTriageIssues, out Issue existingDuplicate) { existingDuplicate = existingTriageIssues.FirstOrDefault(e => issuePayload.Issue.Number != e.Number && IsDuplicate(triageItems, GetTriageItems(e.Body))); return(existingDuplicate != null); }
private bool ShallUpdateExistingIssue(IssuesHookData issuePayload, IList <TriageItem> triageItems, IReadOnlyList <Issue> existingTriageIssues, out Issue existingIssueToUpdate) { existingIssueToUpdate = existingTriageIssues.FirstOrDefault(e => issuePayload.Issue.Number != e.Number && ShallExistingIssueBeUpdated(triageItems, GetTriageItems(e.Body))); return(existingIssueToUpdate != null); }
private static async Task CloseAsDuplicate(IssuesHookData issuePayload, Issue existingDuplicateIssue, IGitHubClient gitHubClient) { await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"Duplicate issue was detected.\n\nClosing as duplicate of {existingDuplicateIssue.HtmlUrl}\n\nFor more information see {_docLink}"); var issueUpdate = new IssueUpdate { State = ItemState.Closed, }; await gitHubClient.Issue.Update(issuePayload.Repository.Id, issuePayload.Issue.Number, issueUpdate); }
public void IssueEventDeserialization_SameAsExpected() { var expected = new IssuesHookData { Action = "labeled", Issue = new IssuesHookIssue { Assignee = new IssuesHookUser { Login = "******", }, Labels = ImmutableArray.Create(new[] { new IssuesHookLabel { Name = "area-GC-coreclr" }, new IssuesHookLabel { Name = "area-Serialization" }, new IssuesHookLabel { Name = "area-cat" }, }), Number = 217, State = ItemState.Open, Title = "Intermittent serialization error in GC during build", Url = "https://api.github.com/repos/thatguy-int-tests/issue-notify-tests/issues/217", HtmlUrl = "https://github.com/thatguy-int-tests/issue-notify-tests/issues/217", Body = "This one is a mystery" }, Label = new IssuesHookLabel { Name = "area-cat" }, Repository = new IssuesHookRepository { Name = "issue-notify-tests", Owner = new IssuesHookUser { Login = "******" }, Id = 987654321, }, Sender = new IssuesHookUser { Login = "******", } }; var issueEvent = JsonSerializer.Deserialize <IssuesHookData>(GetTestPayload(), GitHubHookController.SerializerOptions()); issueEvent.Should().BeEquivalentTo(expected); }
public async Task ProcessIssueEvent(IssuesHookData issuePayload) { if (issuePayload.Action != "opened" && issuePayload.Action != "reopened" && issuePayload.Action != "closed") { _logger.LogInformation($"Received GitHub action '{issuePayload.Action}', no triage issue handling for on such action."); return; } var triageItems = GetTriageItems(issuePayload.Issue.Body); if (!triageItems.Any()) { _logger.LogInformation($"{issuePayload.Issue.Url} is not a time line triage type issue."); return; } IGitHubClient gitHubClient = await _gitHubApplicationClientFactory.CreateGitHubClientAsync(issuePayload.Repository.Owner.Login, issuePayload.Repository.Name); var existingTriageIssues = await ExistingTriageItems(issuePayload, gitHubClient); if (issuePayload.Action == "opened" || issuePayload.Action == "reopened") { if (IsDuplicate(issuePayload, triageItems, existingTriageIssues, out var existingDuplicateIssue)) { await CloseAsDuplicate(issuePayload, existingDuplicateIssue, gitHubClient); } else if (ShallUpdateExistingIssue(issuePayload, triageItems, existingTriageIssues, out var existingIssueToUpdate)) { await UpdateExistingIssue(issuePayload, triageItems, existingIssueToUpdate, gitHubClient); } else { await ProcessOpenedTriageIssue(issuePayload, triageItems, gitHubClient); } } else if (issuePayload.Action == "closed") { await RecatogorizeFailedBuildsIfRequested(issuePayload, triageItems, gitHubClient); } else { throw new InvalidOperationException($"Unexpected action {issuePayload.Action} during handling time line triage issue"); } }
private async Task <IReadOnlyList <Issue> > ExistingTriageItems(IssuesHookData issuePayload, IGitHubClient gitHubClient) { var openIssues = new RepositoryIssueRequest { Filter = IssueFilter.All, State = ItemStateFilter.Open, SortProperty = IssueSort.Created, SortDirection = SortDirection.Ascending, }; openIssues.Labels.Add(_markingLabelName); _logger.LogInformation("Getting open issues"); var existingTriageIssues = await gitHubClient.Issue.GetAllForRepository(issuePayload.Repository.Id, openIssues); _logger.LogInformation($"There are {existingTriageIssues.Count} open issues with the '{_markingLabelName}' label"); return(existingTriageIssues); }
private async Task AddToZenHubTopic(IssuesHookData issuePayload, IGitHubClient gitHubClient, Issue issue) { // add into notification epic -> currently 8/2020 it's First Response epic NotificationEpicOptions epic = _githubOptions.Value.NotificationEpic; if (epic != null) { var epicRepoData = await gitHubClient.Repository.Get(_githubOptions.Value.Organization, epic.Repository); _logger.LogInformation("Adding the issue to ZenHub Epic..."); await _zenHub.AddIssueToEpicAsync( new ZenHubClient.IssueIdentifier(issuePayload.Repository.Id, issue.Number), new ZenHubClient.IssueIdentifier(epicRepoData.Id, epic.IssueNumber) ); } else { _logger.LogInformation("No ZenHub epic configured, skipping..."); } }
private async Task RecatogorizeFailedBuildsIfRequested(IssuesHookData issuePayload, IList <TriageItem> triageItems, IGitHubClient gitHubClient) { if (issuePayload.Issue.Labels.All(l => l.Name != _markingLabelName)) { // do not handle issues which are not marked by label return; } IReadOnlyList <IssueComment> comments = gitHubClient.Issue.Comment.GetAllForIssue(issuePayload.Repository.Id, issuePayload.Issue.Number).Result; // find the latest comment with category command string updatedCategory = null; foreach (var comment in comments) { string category = GetTriageIssueProperty("category", comment.Body); if (!string.IsNullOrEmpty(category)) { updatedCategory = category; } } if (updatedCategory != null) { foreach (var triageItem in triageItems) { triageItem.UpdatedCategory = updatedCategory; } foreach (var triageItem in triageItems) { triageItem.Url = issuePayload.Issue.HtmlUrl; _logger.LogInformation($"Updated category of triage item: {triageItem}"); } await IngestTriageItemsIntoKusto(triageItems); await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"Bot has updated the 'TimelineIssuesTriage' database with new category '{updatedCategory}'.\n**PowerBI reports may take up to 24 hours to refresh**\n\nSee {_docLink} for more information."); } }
private string UpdateExistingIssueBody(IssuesHookData issuePayload, IList <TriageItem> triageItems, Issue existingIssue, IList <TriageItem> existingIssueItems) { return(_internal.UpdateExistingIssueBody(triageItems, issuePayload.Issue.Body, existingIssueItems, existingIssue.Body)); }
public async Task ProcessIssueEvent(IssuesHookData issuePayload) { if (issuePayload.Action != "opened" && issuePayload.Action != "reopened" && issuePayload.Action != "closed") { _logger.LogInformation($"Received github action '{issuePayload.Action}', nothing to do"); return; } // Determine identifiable information for triage items var triageItems = GetTriageItems(issuePayload.Issue.Body); if (!triageItems.Any()) { /* Item is not a triage item (does not contain identifiable information), do nothing */ _logger.LogInformation($"{issuePayload.Issue.Url} is not a triage type issue."); return; } IGitHubClient gitHubClient = await _gitHubApplicationClientFactory.CreateGitHubClientAsync(issuePayload.Repository.Owner.Login, issuePayload.Repository.Name); if (issuePayload.Action == "opened" || issuePayload.Action == "reopened") { // First, look for duplicate issues that are open var openIssues = new RepositoryIssueRequest { Filter = IssueFilter.All, State = ItemStateFilter.Open, SortProperty = IssueSort.Created, SortDirection = SortDirection.Ascending, }; openIssues.Labels.Add(_markingLabelName); _logger.LogInformation("Getting open issues"); var existingTriageIssues = await gitHubClient.Issue.GetAllForRepository(issuePayload.Repository.Id, openIssues); _logger.LogInformation($"There are {existingTriageIssues.Count} open issues with the '{_markingLabelName}' label"); foreach (var existingIssue in existingTriageIssues) { if (existingIssue.Number != issuePayload.Issue.Number) { var existingIssueItems = GetTriageItems(existingIssue.Body); if (IsDuplicate(triageItems, existingIssueItems)) { await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"Duplicate issue was detected.\n\nClosing as duplicate of {existingIssue.HtmlUrl}\n\nFor more information see {_docLink}"); var issueUpdate = new IssueUpdate { State = ItemState.Closed, }; await gitHubClient.Issue.Update(issuePayload.Repository.Id, issuePayload.Issue.Number, issueUpdate); return; } } } // No duplicates, add label and move issue to triage var issue = await gitHubClient.Issue.Get(issuePayload.Repository.Id, issuePayload.Issue.Number); if (!issue.Labels.Any(l => l.Name == _markingLabelName)) { var update = issue.ToUpdate(); update.AddLabel(_markingLabelName); foreach (var label in _issueLabels) { if (issue.Labels.All(l => l.Name != label)) { update.AddLabel(label); } } await gitHubClient.Issue.Update(issuePayload.Repository.Id, issuePayload.Issue.Number, update); await AddToZenHubTopic(issuePayload, gitHubClient, issue); } } if (issuePayload.Action == "closed") { IReadOnlyList <IssueComment> comments = gitHubClient.Issue.Comment.GetAllForIssue(issuePayload.Repository.Id, issuePayload.Issue.Number).Result; // find the latest comment with category command string updatedCategory = null; foreach (var comment in comments) { string category = GetTriageIssueProperty("category", comment.Body); if (!string.IsNullOrEmpty(category)) { updatedCategory = category; } } if (updatedCategory != null) { foreach (var triageItem in triageItems) { triageItem.UpdatedCategory = updatedCategory; } } } foreach (var triageItem in triageItems) { triageItem.Url = issuePayload.Issue.HtmlUrl; _logger.LogInformation($"buildId: {triageItem.BuildId}, recordId: {triageItem.RecordId}, index: {triageItem.Index}, category: {triageItem.UpdatedCategory}, url: {triageItem.Url}"); } await IngestTriageItemsIntoKusto(triageItems); await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"Bot has updated the 'TimelineIssuesTriage' database.\n**PowerBI reports may take up to 24 hours to refresh**\n\nSee {_docLink} for more information."); return; }