package triageissue import ( "net/http" "regexp" "strconv" "strings" "github.com/google/go-github/v47/github" "edge-infra.dev/pkg/f8n/devinfra/jack/constants" guestservices "edge-infra.dev/pkg/f8n/devinfra/jack/guest_services" "edge-infra.dev/pkg/f8n/devinfra/jack/plugin" ) var ( // LabelApplied is a check to see if triage/needs-issue is already present on the PR labelApplied bool check bool ) const ghURL = "https://github.com/" // checkForIssueInPR triggered when the PR is created. The function will scan the body and determine // if the triage/needs-issue needs to be included or removed func checkForIssueInPR(hp plugin.HandlerParams, event github.PullRequestEvent) error { // if a bot added a label we don't want to handle it twice if event.GetSender().GetType() == constants.Bot { return nil } body := event.GetPullRequest().GetBody() linkNum, _ := guestservices.ParsePRIssueLinkBody(body) labelCheck := labelCheck(body, linkNum) switch { case labelCheck: hp.Log.Info("adding needs-issue label") newLabels := []string{constants.TriageIssueLabel} labelApplied = true _, _, err := hp.Client.Issues().AddLabelsToIssue(hp.Ctx, hp.Org, hp.Repo, event.PullRequest.GetNumber(), newLabels) if err != nil { return err } case !labelCheck: _, err := http.Get(ghURL + constants.Address + linkNum) if err != nil { return err } hp.Log.Info("removing needs issue label") labelApplied = false err = removeTriageLabel(hp, event) if err != nil { return err } err = addIssueNumberToTitle(hp, event, linkNum, event.GetPullRequest().GetTitle()) if err != nil { return err } } return nil } // labelCheck returns true when anything in the relevant issue is incorrect func labelCheck(body, linkNum string) bool { _, err := strconv.Atoi(linkNum) if body == "" || linkNum == "" || linkNum == "ISSUE_NUMBER_HERE" || linkNum[0:1] == "0" || err != nil { check = true } else { check = false } return check } // addLabel is triggered whenever a user manually adds the triage/needs-issue label func addLabel(hp plugin.HandlerParams, event github.PullRequestEvent) error { // if a bot added a label we don't want to handle it twice if event.GetSender().GetType() == constants.Bot { return nil } hp.Log.Info("add triage/needs_issue") body := event.GetPullRequest().GetBody() linkNum, _ := guestservices.ParsePRIssueLinkBody(body) if !labelCheck(body, linkNum) { hp.Log.Info("Link exists. Removing label again.") return removeLabel(hp, event) } if labelCheck(body, linkNum) { hp.Log.Info("adding needs-issue label") newLabels := []string{constants.TriageIssueLabel} labelApplied = true _, _, err := hp.Client.Issues().AddLabelsToIssue(hp.Ctx, hp.Org, hp.Repo, event.PullRequest.GetNumber(), newLabels) if err != nil { return err } } return nil } // removeLabel is triggered whenever the user manually removes the triage/needs-issue label func removeLabel(hp plugin.HandlerParams, event github.PullRequestEvent) error { // if a bot added a label we don't want to handle it twice if event.GetSender().GetType() == constants.Bot { return nil } hp.Log.Info("remove triage/needs_issue") body := event.GetPullRequest().GetBody() linkNum, _ := guestservices.ParsePRIssueLinkBody(body) if labelCheck(body, linkNum) { hp.Log.Info("Link does not exist. Adding label back") return addLabel(hp, event) } _, err := http.Get(ghURL + constants.Address + linkNum) if err != nil { return err } hp.Log.Info("removing needs-issue label") labelApplied = false err = removeTriageLabel(hp, event) if err != nil { return err } return nil } // editedPRBody is called whenever the main PR comment body is altered. // It runs through checks to determine how the body was changed and then applies/removes the label. func editedPRBody(hp plugin.HandlerParams, event github.PullRequestEvent) error { // if a bot added a label we don't want to handle it twice if event.GetSender().GetType() == constants.Bot { return nil } body := event.GetPullRequest().GetBody() linkNum, _ := guestservices.ParsePRIssueLinkBody(body) doesNotHaveIssue := labelCheck(body, linkNum) // LabelApplied returns true if the label is already present on the PR // doesNotHaveIssue returns true if there isn't a correct issue in the PR switch { case labelApplied && !doesNotHaveIssue: _, err := http.Get(ghURL + constants.Address + linkNum) if err != nil { return err } labelApplied = false hp.Log.Info("removing needs-issue label") err = removeTriageLabel(hp, event) if err != nil { return err } if !isIssueNumberInTitle(event.GetPullRequest().GetTitle()) { err = addIssueNumberToTitle(hp, event, linkNum, event.GetPullRequest().GetTitle()) if err != nil { return err } } case !labelApplied && doesNotHaveIssue: if labelCheck(body, linkNum) { newLabels := []string{constants.TriageIssueLabel} labelApplied = true hp.Log.Info("adding needs-issue label") _, _, err := hp.Client.Issues().AddLabelsToIssue(hp.Ctx, hp.Org, hp.Repo, event.PullRequest.GetNumber(), newLabels) if err != nil { return err } } case !doesNotHaveIssue: // compare the issue number to the title if same no change if different, update if compareTitleNumberToBody(event.GetPullRequest().GetTitle(), linkNum) != 0 { err := updateIssueNumberInTitle(hp, event, linkNum, event.GetPullRequest().GetTitle()) if err != nil { return err } } } return nil } // remove the triage issue label if it exists in the prs labels func removeTriageLabel(hp plugin.HandlerParams, pre github.PullRequestEvent) error { for _, x := range pre.PullRequest.Labels { if x.GetName() == constants.TriageIssueLabel { _, err := hp.Client.Issues().RemoveLabelForIssue(hp.Ctx, hp.Org, hp.Repo, pre.PullRequest.GetNumber(), constants.TriageIssueLabel) if err != nil { return err } break } } return nil } // append issue number to title func addIssueNumberToTitle(hp plugin.HandlerParams, pre github.PullRequestEvent, linkNum string, title string) error { githubIssueBody := &github.IssueRequest{Title: github.String(linkNum + ": " + title)} _, _, err := hp.Client.Issues().Edit(hp.Ctx, hp.Org, hp.Repo, pre.PullRequest.GetNumber(), githubIssueBody) if err != nil { return err } return nil } // append issue number to title func updateIssueNumberInTitle(hp plugin.HandlerParams, pre github.PullRequestEvent, linkNum string, title string) error { r := regexp.MustCompile(`(\d+):\s*(.*)`) matches := r.FindStringSubmatch(title) githubIssueBody := &github.IssueRequest{Title: github.String(linkNum + ": " + matches[2])} _, _, err := hp.Client.Issues().Edit(hp.Ctx, hp.Org, hp.Repo, pre.PullRequest.GetNumber(), githubIssueBody) if err != nil { return err } return nil } // checks if an issue number already exists within the title func isIssueNumberInTitle(title string) bool { r := regexp.MustCompile(`[0-9]+:`) return len(r.FindStringSubmatch(title)) > 0 } func compareTitleNumberToBody(title string, linkNum string) int { r := regexp.MustCompile(`(\d+):\s*(.*)`) matches := r.FindStringSubmatch(title) return strings.Compare(matches[1], linkNum) }