From ef97e68c04c72f1d182ba1161ce1badc28d609fa Mon Sep 17 00:00:00 2001 From: James Suplizio Date: Fri, 8 Mar 2024 10:39:06 -0800 Subject: [PATCH] Enable the new InitialIssueTriage Rule that uses AzureSdkOwners (#7847) --- .../Static/IssueProcessingTests.cs | 1 - .../EventProcessing/IssueProcessing.cs | 404 +++++++++--------- 2 files changed, 201 insertions(+), 204 deletions(-) diff --git a/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor.Tests/Static/IssueProcessingTests.cs b/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor.Tests/Static/IssueProcessingTests.cs index 5319127f7d2..c078fb3ed4b 100644 --- a/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor.Tests/Static/IssueProcessingTests.cs +++ b/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor.Tests/Static/IssueProcessingTests.cs @@ -60,7 +60,6 @@ public class IssueProcessingTests : ProcessingTestBase /// Whether or not to expect a codeowners entry for the labels returned. /// Whether or not the owner that created the issue is a member of Azure /// Whether or not the owner that created the issue has write or admin - [Ignore("Ignore until the InitialIssueTriage rule is re-enabled")] [Category("static")] [NonParallelizable] // All the tests use the same CODEOWNERS file // Scenario: Everything turned off, nothing should process diff --git a/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor/EventProcessing/IssueProcessing.cs b/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor/EventProcessing/IssueProcessing.cs index 257f5900264..3d9d323554c 100644 --- a/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor/EventProcessing/IssueProcessing.cs +++ b/tools/github-event-processor/Azure.Sdk.Tools.GitHubEventProcessor/EventProcessing/IssueProcessing.cs @@ -43,209 +43,45 @@ public static async Task ProcessIssueEvent(GitHubEventClient gitHubEventClient, } - // TODO-START - // When the CODEOWNERS files are updated with the AzureSdkOwners, select everything in between the TODO-START and - // TODO-END tags, in VS, and do a ctrl-k, ctrl-u to uncomment the function and delete the TODO comments. The - // in the test project, in IssueProcessingTests.cs, remove the Ignore attribute that's TestInitialIssueTriage tests - ///// - ///// Initial Issue Triage - ///// Trigger: issue opened - ///// Conditions: Issue has no labels - ///// Issue has no assignee - ///// Resulting Actions: - ///// Query AI label service for label suggestions: - ///// IF labels were predicted: - ///// - Assign returned labels to the issue - ///// IF service and category labels have AzureSdkOwners (in CODEOWNERS): - ///// IF a single AzureSdkOwner: - ///// - Assign the AzureSdkOwner issue - ///// ELSE - ///// - Assign a random AzureSdkOwner from the set to the issue - ///// - Create the following comment, mentioning all AzureSdkOwners from the set - ///// "@{person1} @{person2}...${personX}" - ///// - Create the following comment - ///// "Thank you for your feedback. Tagging and routing to the team member best able to assist." - ///// - ///// # Note: No valid AzureSdkOwners means there were no CODEOWNERS entries for the ServiceLabel OR no - ///// # CODEOWNERS entries for the ServiceLabel with AzureSdkOwners OR there is a CODEOWNERS entry with - ///// # AzureSdkOwners but none of them have permissions to be assigned to an issue for the repository. - ///// IF there are no valid AzureSdkOwners, but there are ServiceOwners, and the ServiceAttention rule is enabled - ///// - Add "Service Attention" label to the issue and apply the logic from the "Service Attention" rule - ///// ELSE - ///// - Add "needs-team-triage" (at this point it owners cannot be determined for this issue) - ///// - ///// IF "needs-team-triage" is not being added to the issue - ///// - Add "needs-team-attention" label to the issue - ///// - ///// - ///// Evaluate the user that created the issue: - ///// IF the user is NOT a member of the Azure Org - ///// IF the user does not have Admin or Write Collaborator permission - ///// - Add "customer-reported" label - ///// - Add "question" label - ///// - ///// Authenticated GitHubEventClient - ///// IssueEventGitHubPayload deserialized from the json event payload - //public static async Task InitialIssueTriage(GitHubEventClient gitHubEventClient, IssueEventGitHubPayload issueEventPayload) - //{ - // if (gitHubEventClient.RulesConfiguration.RuleEnabled(RulesConstants.InitialIssueTriage)) - // { - // if (issueEventPayload.Action == ActionConstants.Opened) - // { - // // If there are no labels and no assignees - // if ((issueEventPayload.Issue.Labels.Count == 0) && (issueEventPayload.Issue.Assignee == null)) - // { - // List labelSuggestions = await gitHubEventClient.QueryAILabelService(issueEventPayload); - // if (labelSuggestions.Count > 0) - // { - // // If labels were predicted, add them to the issue - // foreach (string label in labelSuggestions) - // { - // gitHubEventClient.AddLabel(label); - // } - - // // needs-team-attention needs to be added if it can be determined who this issue actually - // // belongs to. - // bool addNeedsTeamAttention = true; - - // CodeownersEntry codeownersEntry = CodeOwnerUtils.GetCodeownersEntryForLabelList(labelSuggestions); - // bool hasValidAssignee = false; - // if (codeownersEntry.AzureSdkOwners.Count > 0) - // { - // // If there's only a single owner, - // if (codeownersEntry.AzureSdkOwners.Count == 1) - // { - // if (await gitHubEventClient.OwnerCanBeAssignedToIssuesInRepo( - // issueEventPayload.Repository.Owner.Login, - // issueEventPayload.Repository.Name, - // codeownersEntry.AzureSdkOwners[0])) - // { - // hasValidAssignee = true; - // gitHubEventClient.AssignOwnerToIssue( - // issueEventPayload.Repository.Owner.Login, - // issueEventPayload.Repository.Name, - // codeownersEntry.AzureSdkOwners[0]); - // } - // // Output something into the logs pointing out that AzureSdkOwners has a user that cannot - // // be assigned to an issue - // else - // { - // Console.WriteLine($"{codeownersEntry.AzureSdkOwners[0]} is the only owner in the AzureSdkOwners for service label(s), {string.Join(",", labelSuggestions)}, but cannot be assigned as an issue owner in this repository."); - // } - // } - // // else there are multiple owners and a random one needs to be assigned - // else - // { - // // Create a list of AzureSdkOwners that has been randomized. The reason - // // the entire list is being randomed is because each person has to be - // // checked to see if they can be assigned to an issue in the repository. - // // and having the entire list being random simplifies processing if a given - // // owner cannot be assigned. - // var rnd = new Random(); - // var randomAzureSdkOwners = codeownersEntry.AzureSdkOwners.OrderBy(item => rnd.Next(0, codeownersEntry.AzureSdkOwners.Count)); - // foreach (string azureSdkOwner in randomAzureSdkOwners) - // { - // if (await gitHubEventClient.OwnerCanBeAssignedToIssuesInRepo( - // issueEventPayload.Repository.Owner.Login, - // issueEventPayload.Repository.Name, - // azureSdkOwner)) - // { - // hasValidAssignee = true; - // gitHubEventClient.AssignOwnerToIssue(issueEventPayload.Repository.Owner.Login, - // issueEventPayload.Repository.Name, - // azureSdkOwner); - // // As soon as there's a valid assignee, add the comment mentioning everyone - // // in the AzureSdkOwners and exit. The @ mention is only necessary if there - // // are multiple AzureSdkOwners. - // string azureSdkOwnersAtMention = CodeOwnerUtils.CreateAtMentionForOwnerList(codeownersEntry.AzureSdkOwners); - // gitHubEventClient.CreateComment(issueEventPayload.Repository.Id, - // issueEventPayload.Issue.Number, - // azureSdkOwnersAtMention); - // break; - // } - // else - // { - // Console.WriteLine($"{azureSdkOwner} is an AzureSdkOwner for service labels {string.Join(",", labelSuggestions)} but cannot be assigned as an issue owner in this repository."); - // } - // } - // } - // // If the issue had a valid assignee add the comment - // if (hasValidAssignee) - // { - // string issueComment = "Thank you for your feedback. Tagging and routing to the team member best able to assist."; - // gitHubEventClient.CreateComment(issueEventPayload.Repository.Id, - // issueEventPayload.Issue.Number, - // issueComment); - // } - // else - // { - // // Output a message indicating every owner in the AzureSdkOwners, for the AI label suggestions. The lines immediately - // // above this output will contain the messages for each user checked. - // Console.WriteLine($"AzureSdkOwners for service labels {string.Join(",", labelSuggestions)} has no owners that can be assigned to issues in this repository."); - // } - // } - - // // If there's no valid AzureSdkOwner to assign the issue to (this means that there's either - // // no AzureSdkOwners or none of them have permissions to be assigned to an issue) - // if (!hasValidAssignee) - // { - // // Check to see if there are ServiceOwners and the ServiceAttention rule is turned on. If - // // both are true then add the ServiceAttention label and run ServiceAttention processing - // if (codeownersEntry.ServiceOwners.Count > 0 - // && gitHubEventClient.RulesConfiguration.RuleEnabled(RulesConstants.ServiceAttention, - // false /* don't output log messages for this check*/)) - - // { - // gitHubEventClient.AddLabel(TriageLabelConstants.ServiceAttention); - // Common_ProcessServiceAttentionForLabels(gitHubEventClient, - // issueEventPayload.Issue, - // issueEventPayload.Repository.Id, - // labelSuggestions); - // } - // // At this point, it cannot be determined who this issue belongs to. Add - // // the needs-team-triage label instead of the needs-team-attention label - // else - // { - // gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTeamTriage); - // addNeedsTeamAttention = false; - // } - // } - - // // The needs-team-attention label is only added when it can be determined - // // who this issue belongs to. - // if (addNeedsTeamAttention) - // { - // gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTeamAttention); - // } - // gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTeamTriage); - - // } - // // If there are no labels predicted add NeedsTriage to the issue - // else - // { - // gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTriage); - // } - - // // If the user is not a member of the Azure Org AND the user does not have write or admin collaborator permission. - // // This piece is executed for every issue created that doesn't have labels or owners on it at the time of creation. - // bool isMemberOfOrg = await gitHubEventClient.IsUserMemberOfOrg(OrgConstants.Azure, issueEventPayload.Issue.User.Login); - // if (!isMemberOfOrg) - // { - // bool hasAdminOrWritePermission = await gitHubEventClient.DoesUserHaveAdminOrWritePermission(issueEventPayload.Repository.Id, issueEventPayload.Issue.User.Login); - // if (!hasAdminOrWritePermission) - // { - // gitHubEventClient.AddLabel(TriageLabelConstants.CustomerReported); - // gitHubEventClient.AddLabel(TriageLabelConstants.Question); - // } - // } - // } - // } - // } - //} - // TODO-END - - // This is the IntitialIssueTriage as it exists today. It's the old rule that needs to be replaced by the commented out - // function above. + /// + /// Initial Issue Triage + /// Trigger: issue opened + /// Conditions: Issue has no labels + /// Issue has no assignee + /// Resulting Actions: + /// Query AI label service for label suggestions: + /// IF labels were predicted: + /// - Assign returned labels to the issue + /// IF service and category labels have AzureSdkOwners (in CODEOWNERS): + /// IF a single AzureSdkOwner: + /// - Assign the AzureSdkOwner issue + /// ELSE + /// - Assign a random AzureSdkOwner from the set to the issue + /// - Create the following comment, mentioning all AzureSdkOwners from the set + /// "@{person1} @{person2}...${personX}" + /// - Create the following comment + /// "Thank you for your feedback. Tagging and routing to the team member best able to assist." + /// + /// # Note: No valid AzureSdkOwners means there were no CODEOWNERS entries for the ServiceLabel OR no + /// # CODEOWNERS entries for the ServiceLabel with AzureSdkOwners OR there is a CODEOWNERS entry with + /// # AzureSdkOwners but none of them have permissions to be assigned to an issue for the repository. + /// IF there are no valid AzureSdkOwners, but there are ServiceOwners, and the ServiceAttention rule is enabled + /// - Add "Service Attention" label to the issue and apply the logic from the "Service Attention" rule + /// ELSE + /// - Add "needs-team-triage" (at this point it owners cannot be determined for this issue) + /// + /// IF "needs-team-triage" is not being added to the issue + /// - Add "needs-team-attention" label to the issue + /// + /// + /// Evaluate the user that created the issue: + /// IF the user is NOT a member of the Azure Org + /// IF the user does not have Admin or Write Collaborator permission + /// - Add "customer-reported" label + /// - Add "question" label + /// + /// Authenticated GitHubEventClient + /// IssueEventGitHubPayload deserialized from the json event payload public static async Task InitialIssueTriage(GitHubEventClient gitHubEventClient, IssueEventGitHubPayload issueEventPayload) { if (gitHubEventClient.RulesConfiguration.RuleEnabled(RulesConstants.InitialIssueTriage)) @@ -264,6 +100,119 @@ public static async Task InitialIssueTriage(GitHubEventClient gitHubEventClient, gitHubEventClient.AddLabel(label); } + // needs-team-attention needs to be added if it can be determined who this issue actually + // belongs to. + bool addNeedsTeamAttention = true; + + CodeownersEntry codeownersEntry = CodeOwnerUtils.GetCodeownersEntryForLabelList(labelSuggestions); + bool hasValidAssignee = false; + if (codeownersEntry.AzureSdkOwners.Count > 0) + { + // If there's only a single owner, + if (codeownersEntry.AzureSdkOwners.Count == 1) + { + if (await gitHubEventClient.OwnerCanBeAssignedToIssuesInRepo( + issueEventPayload.Repository.Owner.Login, + issueEventPayload.Repository.Name, + codeownersEntry.AzureSdkOwners[0])) + { + hasValidAssignee = true; + gitHubEventClient.AssignOwnerToIssue( + issueEventPayload.Repository.Owner.Login, + issueEventPayload.Repository.Name, + codeownersEntry.AzureSdkOwners[0]); + } + // Output something into the logs pointing out that AzureSdkOwners has a user that cannot + // be assigned to an issue + else + { + Console.WriteLine($"{codeownersEntry.AzureSdkOwners[0]} is the only owner in the AzureSdkOwners for service label(s), {string.Join(",", labelSuggestions)}, but cannot be assigned as an issue owner in this repository."); + } + } + // else there are multiple owners and a random one needs to be assigned + else + { + // Create a list of AzureSdkOwners that has been randomized. The reason + // the entire list is being randomed is because each person has to be + // checked to see if they can be assigned to an issue in the repository. + // and having the entire list being random simplifies processing if a given + // owner cannot be assigned. + var rnd = new Random(); + var randomAzureSdkOwners = codeownersEntry.AzureSdkOwners.OrderBy(item => rnd.Next(0, codeownersEntry.AzureSdkOwners.Count)); + foreach (string azureSdkOwner in randomAzureSdkOwners) + { + if (await gitHubEventClient.OwnerCanBeAssignedToIssuesInRepo( + issueEventPayload.Repository.Owner.Login, + issueEventPayload.Repository.Name, + azureSdkOwner)) + { + hasValidAssignee = true; + gitHubEventClient.AssignOwnerToIssue(issueEventPayload.Repository.Owner.Login, + issueEventPayload.Repository.Name, + azureSdkOwner); + // As soon as there's a valid assignee, add the comment mentioning everyone + // in the AzureSdkOwners and exit. The @ mention is only necessary if there + // are multiple AzureSdkOwners. + string azureSdkOwnersAtMention = CodeOwnerUtils.CreateAtMentionForOwnerList(codeownersEntry.AzureSdkOwners); + gitHubEventClient.CreateComment(issueEventPayload.Repository.Id, + issueEventPayload.Issue.Number, + azureSdkOwnersAtMention); + break; + } + else + { + Console.WriteLine($"{azureSdkOwner} is an AzureSdkOwner for service labels {string.Join(",", labelSuggestions)} but cannot be assigned as an issue owner in this repository."); + } + } + } + // If the issue had a valid assignee add the comment + if (hasValidAssignee) + { + string issueComment = "Thank you for your feedback. Tagging and routing to the team member best able to assist."; + gitHubEventClient.CreateComment(issueEventPayload.Repository.Id, + issueEventPayload.Issue.Number, + issueComment); + } + else + { + // Output a message indicating every owner in the AzureSdkOwners, for the AI label suggestions. The lines immediately + // above this output will contain the messages for each user checked. + Console.WriteLine($"AzureSdkOwners for service labels {string.Join(",", labelSuggestions)} has no owners that can be assigned to issues in this repository."); + } + } + + // If there's no valid AzureSdkOwner to assign the issue to (this means that there's either + // no AzureSdkOwners or none of them have permissions to be assigned to an issue) + if (!hasValidAssignee) + { + // Check to see if there are ServiceOwners and the ServiceAttention rule is turned on. If + // both are true then add the ServiceAttention label and run ServiceAttention processing + if (codeownersEntry.ServiceOwners.Count > 0 + && gitHubEventClient.RulesConfiguration.RuleEnabled(RulesConstants.ServiceAttention, + false /* don't output log messages for this check*/)) + + { + gitHubEventClient.AddLabel(TriageLabelConstants.ServiceAttention); + Common_ProcessServiceAttentionForLabels(gitHubEventClient, + issueEventPayload.Issue, + issueEventPayload.Repository.Id, + labelSuggestions); + } + // At this point, it cannot be determined who this issue belongs to. Add + // the needs-team-triage label instead of the needs-team-attention label + else + { + gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTeamTriage); + addNeedsTeamAttention = false; + } + } + + // The needs-team-attention label is only added when it can be determined + // who this issue belongs to. + if (addNeedsTeamAttention) + { + gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTeamAttention); + } gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTeamTriage); } @@ -290,6 +239,55 @@ public static async Task InitialIssueTriage(GitHubEventClient gitHubEventClient, } } + + // TODO - Remove this when it's absolutely clear it's no longer needed. The only reason to keep this around, temporarily, + // is for "just in case" purposes. + //// This is the IntitialIssueTriage as it exists today. It's the old rule that needs to be replaced by the commented out + //// function above. + //public static async Task InitialIssueTriage(GitHubEventClient gitHubEventClient, IssueEventGitHubPayload issueEventPayload) + //{ + // if (gitHubEventClient.RulesConfiguration.RuleEnabled(RulesConstants.InitialIssueTriage)) + // { + // if (issueEventPayload.Action == ActionConstants.Opened) + // { + // // If there are no labels and no assignees + // if ((issueEventPayload.Issue.Labels.Count == 0) && (issueEventPayload.Issue.Assignee == null)) + // { + // List labelSuggestions = await gitHubEventClient.QueryAILabelService(issueEventPayload); + // if (labelSuggestions.Count > 0) + // { + // // If labels were predicted, add them to the issue + // foreach (string label in labelSuggestions) + // { + // gitHubEventClient.AddLabel(label); + // } + + // gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTeamTriage); + + // } + // // If there are no labels predicted add NeedsTriage to the issue + // else + // { + // gitHubEventClient.AddLabel(TriageLabelConstants.NeedsTriage); + // } + + // // If the user is not a member of the Azure Org AND the user does not have write or admin collaborator permission. + // // This piece is executed for every issue created that doesn't have labels or owners on it at the time of creation. + // bool isMemberOfOrg = await gitHubEventClient.IsUserMemberOfOrg(OrgConstants.Azure, issueEventPayload.Issue.User.Login); + // if (!isMemberOfOrg) + // { + // bool hasAdminOrWritePermission = await gitHubEventClient.DoesUserHaveAdminOrWritePermission(issueEventPayload.Repository.Id, issueEventPayload.Issue.User.Login); + // if (!hasAdminOrWritePermission) + // { + // gitHubEventClient.AddLabel(TriageLabelConstants.CustomerReported); + // gitHubEventClient.AddLabel(TriageLabelConstants.Question); + // } + // } + // } + // } + // } + //} + /// /// Manual Issue Triage /// Trigger: issue labeled