From cd631d074774ccf7345bc5bd065050dbf62e2a3b Mon Sep 17 00:00:00 2001 From: Patrick Maslana Date: Tue, 30 Apr 2024 17:09:27 -0700 Subject: [PATCH 1/3] First steps to splitting out functions --- cmd/notifyPendingCI.go | 0 cmd/notifyStale.go | 47 ++++++++++++ internal/github/pullRequests.go | 40 +++++++++++ internal/label/pullrequests.go | 102 ++++++++++++-------------- internal/message/sendMessage.go | 124 ++++++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+), 56 deletions(-) create mode 100644 cmd/notifyPendingCI.go create mode 100644 cmd/notifyStale.go create mode 100644 internal/github/pullRequests.go create mode 100644 internal/message/sendMessage.go diff --git a/cmd/notifyPendingCI.go b/cmd/notifyPendingCI.go new file mode 100644 index 0000000..e69de29 diff --git a/cmd/notifyStale.go b/cmd/notifyStale.go new file mode 100644 index 0000000..0aa3eff --- /dev/null +++ b/cmd/notifyStale.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "log" + "time" + + "github.com/google/go-github/v60/github" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/chia-network/github-bot/internal/config" + "github.com/chia-network/github-bot/internal/label" +) + +// labelPRsCmd represents the labelPRs command +var sendKeybaseMessage = &cobra.Command{ + Use: "sendKeybaseMessage", + Short: "Sends a message to Keybase informing users of stale community PRs", + Run: func(cmd *cobra.Command, args []string) { + cfg, err := config.LoadConfig(viper.GetString("config")) + if err != nil { + log.Fatalf("error loading config: %s\n", err.Error()) + } + client := github.NewClient(nil).WithAuthToken(cfg.GithubToken) + + loop := viper.GetBool("loop") + loopDuration := viper.GetDuration("loop-time") + for { + log.Println("Labeling Pull Requests") + err = label.PullRequests(client, cfg.InternalTeam, cfg.LabelConfig) + if err != nil { + log.Fatalln(err.Error()) + } + + if !loop { + break + } + + log.Printf("Waiting %s for next iteration\n", loopDuration.String()) + time.Sleep(loopDuration) + } + }, +} + +func init() { + rootCmd.AddCommand(sendKeybaseMessage) +} diff --git a/internal/github/pullRequests.go b/internal/github/pullRequests.go new file mode 100644 index 0000000..872de9e --- /dev/null +++ b/internal/github/pullRequests.go @@ -0,0 +1,40 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v60/github" +) + +func FindCommunityPRs(owner string, repo string, teamMembers map[string]bool, githubClient *github.Client) ([]*github.PullRequest, error) { + var finalPRs []*github.PullRequest + opts := &github.PullRequestListOptions{ + State: "open", + Sort: "created", + Direction: "desc", + ListOptions: github.ListOptions{ + Page: 0, + PerPage: 100, + }, + } + for { + opts.ListOptions.Page++ + pullRequests, resp, err := githubClient.PullRequests.List(context.TODO(), owner, repo, opts) + if err != nil { + return finalPRs, fmt.Errorf("error listing pull requests: %w", err) + } + + for _, pullRequest := range pullRequests { + user := *pullRequest.User.Login + if !teamMembers[user] { + finalPRs = append(finalPRs, pullRequest) + } + } + + if resp.NextPage == 0 { + break + } + } + return finalPRs, nil +} diff --git a/internal/label/pullrequests.go b/internal/label/pullrequests.go index af7c207..c1f83d0 100644 --- a/internal/label/pullrequests.go +++ b/internal/label/pullrequests.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-github/v60/github" "github.com/chia-network/github-bot/internal/config" + github2 "github.com/chia-network/github-bot/internal/github" ) // PullRequests applies internal or community labels to pull requests @@ -51,72 +52,61 @@ func PullRequests(githubClient *github.Client, internalTeam string, cfg config.L if len(parts) != 2 { return fmt.Errorf("invalid repository name - must contain owner and repository: %s", fullRepo.Name) } - opts := &github.PullRequestListOptions{ - State: "open", - Sort: "created", - Direction: "desc", - ListOptions: github.ListOptions{ - Page: 0, - PerPage: 100, - }, + owner := parts[0] + repo := parts[1] + pullRequests, err := github2.FindCommunityPRs(owner, repo, teamMembers, githubClient) + if err != nil { + return err } - for { - lowestNumber := 0 - opts.ListOptions.Page++ - owner := parts[0] - repo := parts[1] - pullRequests, resp, err := githubClient.PullRequests.List(context.TODO(), owner, repo, opts) - if err != nil { - return fmt.Errorf("error listing pull requests: %w", err) + + lowestNumber := 0 + + for _, pullRequest := range pullRequests { + lowestNumber = *pullRequest.Number + if *pullRequest.Number < fullRepo.MinimumNumber { + // Break, not continue, since our order ensures PR numbers are getting smaller + break + } + if *pullRequest.Draft { + continue + } + user := *pullRequest.User.Login + if cfg.LabelSkipMap[user] { + continue + } + var label string + if teamMembers[user] { + label = cfg.LabelInternal + } else { + label = cfg.LabelExternal } - for _, pullRequest := range pullRequests { - lowestNumber = *pullRequest.Number - if *pullRequest.Number < fullRepo.MinimumNumber { - // Break, not continue, since our order ensures PR numbers are getting smaller - break - } - if *pullRequest.Draft { - continue - } - user := *pullRequest.User.Login - if cfg.LabelSkipMap[user] { - continue - } - var label string - if teamMembers[user] { - label = cfg.LabelInternal - } else { - label = cfg.LabelExternal + if label != "" { + log.Printf("Pull Request %d by %s will be labeled %s\n", *pullRequest.Number, user, label) + hasLabel := false + for _, existingLabel := range pullRequest.Labels { + if *existingLabel.Name == label { + log.Println(" Already labeled, skipping...") + hasLabel = true + break + } } - if label != "" { - log.Printf("Pull Request %d by %s will be labeled %s\n", *pullRequest.Number, user, label) - hasLabel := false - for _, existingLabel := range pullRequest.Labels { - if *existingLabel.Name == label { - log.Println(" Already labeled, skipping...") - hasLabel = true - break - } + if !hasLabel { + allLabels := []string{label} + for _, labelP := range pullRequest.Labels { + allLabels = append(allLabels, *labelP.Name) } - - if !hasLabel { - allLabels := []string{label} - for _, labelP := range pullRequest.Labels { - allLabels = append(allLabels, *labelP.Name) - } - _, _, err := githubClient.Issues.AddLabelsToIssue(context.TODO(), owner, repo, *pullRequest.Number, allLabels) - if err != nil { - return fmt.Errorf("error adding labels to pull request %d: %w", *pullRequest.Number, err) - } + _, _, err := githubClient.Issues.AddLabelsToIssue(context.TODO(), owner, repo, *pullRequest.Number, allLabels) + if err != nil { + return fmt.Errorf("error adding labels to pull request %d: %w", *pullRequest.Number, err) } } } + } - if resp.NextPage == 0 || lowestNumber <= fullRepo.MinimumNumber { - break - } + if lowestNumber <= fullRepo.MinimumNumber { + break } } diff --git a/internal/message/sendMessage.go b/internal/message/sendMessage.go new file mode 100644 index 0000000..c594f51 --- /dev/null +++ b/internal/message/sendMessage.go @@ -0,0 +1,124 @@ +package sendMessage + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/google/go-github/v60/github" + + "github.com/chia-network/github-bot/internal/config" +) + +// PullRequests applies internal or community labels to pull requests +// Internal is determined by checking if the PR author is a member of the specified internalTeam +func PullRequests(githubClient *github.Client, internalTeam string, cfg config.LabelConfig) error { + teamMembers := map[string]bool{} + + teamParts := strings.Split(internalTeam, "/") + if len(teamParts) != 2 { + return fmt.Errorf("invalid team name - must contain org and team : %s", internalTeam) + } + + teamOpts := &github.TeamListTeamMembersOptions{ + Role: "all", + ListOptions: github.ListOptions{ + Page: 0, + PerPage: 100, + }, + } + + for { + teamOpts.ListOptions.Page++ + members, resp, err := githubClient.Teams.ListTeamMembersBySlug(context.TODO(), teamParts[0], teamParts[1], teamOpts) + if err != nil { + return fmt.Errorf("error getting team %s: %w", internalTeam, err) + } + + for _, member := range members { + teamMembers[*member.Login] = true + } + + if resp.NextPage == 0 { + break + } + } + + for _, fullRepo := range cfg.LabelCheckRepos { + log.Println("checking repos") + parts := strings.Split(fullRepo.Name, "/") + if len(parts) != 2 { + return fmt.Errorf("invalid repository name - must contain owner and repository: %s", fullRepo.Name) + } + opts := &github.PullRequestListOptions{ + State: "open", + Sort: "created", + Direction: "desc", + ListOptions: github.ListOptions{ + Page: 0, + PerPage: 100, + }, + } + for { + lowestNumber := 0 + opts.ListOptions.Page++ + owner := parts[0] + repo := parts[1] + pullRequests, resp, err := githubClient.PullRequests.List(context.TODO(), owner, repo, opts) + if err != nil { + return fmt.Errorf("error listing pull requests: %w", err) + } + + for _, pullRequest := range pullRequests { + lowestNumber = *pullRequest.Number + if *pullRequest.Number < fullRepo.MinimumNumber { + // Break, not continue, since our order ensures PR numbers are getting smaller + break + } + if *pullRequest.Draft { + continue + } + user := *pullRequest.User.Login + if cfg.LabelSkipMap[user] { + continue + } + var label string + if teamMembers[user] { + label = cfg.LabelInternal + } else { + label = cfg.LabelExternal + } + + if label != "" { + log.Printf("Pull Request %d by %s will be labeled %s\n", *pullRequest.Number, user, label) + hasLabel := false + for _, existingLabel := range pullRequest.Labels { + if *existingLabel.Name == label { + log.Println(" Already labeled, skipping...") + hasLabel = true + break + } + } + + if !hasLabel { + allLabels := []string{label} + for _, labelP := range pullRequest.Labels { + allLabels = append(allLabels, *labelP.Name) + } + _, _, err := githubClient.Issues.AddLabelsToIssue(context.TODO(), owner, repo, *pullRequest.Number, allLabels) + if err != nil { + return fmt.Errorf("error adding labels to pull request %d: %w", *pullRequest.Number, err) + } + } + } + } + + if resp.NextPage == 0 || lowestNumber <= fullRepo.MinimumNumber { + break + } + } + } + + return nil +} From 7ecf1fbe83cbbfc35aa449242caaf215a70dc4d3 Mon Sep 17 00:00:00 2001 From: Patrick Maslana Date: Tue, 30 Apr 2024 17:10:22 -0700 Subject: [PATCH 2/3] Add another file for team members --- internal/github/teamMemList.go | 1 + 1 file changed, 1 insertion(+) create mode 100644 internal/github/teamMemList.go diff --git a/internal/github/teamMemList.go b/internal/github/teamMemList.go new file mode 100644 index 0000000..d2e73c2 --- /dev/null +++ b/internal/github/teamMemList.go @@ -0,0 +1 @@ +package github From 590ca92fdee5825e88cf654c3d2ee469b02893d7 Mon Sep 17 00:00:00 2001 From: Patrick Maslana Date: Thu, 2 May 2024 11:49:55 -0700 Subject: [PATCH 3/3] Split out code from label/pullrequests.go into helper functions --- cmd/notifyPendingCI.go | 3 + cmd/notifyStale.go | 90 +++++++++++------------ cmd/root.go | 5 +- internal/github/pullRequests.go | 1 + internal/github/teamMemList.go | 43 +++++++++++ internal/label/pullrequests.go | 32 +------- internal/message/sendMessage.go | 125 +------------------------------- 7 files changed, 99 insertions(+), 200 deletions(-) diff --git a/cmd/notifyPendingCI.go b/cmd/notifyPendingCI.go index e69de29..03ec6b5 100644 --- a/cmd/notifyPendingCI.go +++ b/cmd/notifyPendingCI.go @@ -0,0 +1,3 @@ +package cmd + +// For future use diff --git a/cmd/notifyStale.go b/cmd/notifyStale.go index 0aa3eff..4e07001 100644 --- a/cmd/notifyStale.go +++ b/cmd/notifyStale.go @@ -1,47 +1,47 @@ package cmd -import ( - "log" - "time" - - "github.com/google/go-github/v60/github" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/chia-network/github-bot/internal/config" - "github.com/chia-network/github-bot/internal/label" -) - -// labelPRsCmd represents the labelPRs command -var sendKeybaseMessage = &cobra.Command{ - Use: "sendKeybaseMessage", - Short: "Sends a message to Keybase informing users of stale community PRs", - Run: func(cmd *cobra.Command, args []string) { - cfg, err := config.LoadConfig(viper.GetString("config")) - if err != nil { - log.Fatalf("error loading config: %s\n", err.Error()) - } - client := github.NewClient(nil).WithAuthToken(cfg.GithubToken) - - loop := viper.GetBool("loop") - loopDuration := viper.GetDuration("loop-time") - for { - log.Println("Labeling Pull Requests") - err = label.PullRequests(client, cfg.InternalTeam, cfg.LabelConfig) - if err != nil { - log.Fatalln(err.Error()) - } - - if !loop { - break - } - - log.Printf("Waiting %s for next iteration\n", loopDuration.String()) - time.Sleep(loopDuration) - } - }, -} - -func init() { - rootCmd.AddCommand(sendKeybaseMessage) -} +//import ( +// "log" +// "time" +// +// "github.com/google/go-github/v60/github" +// "github.com/spf13/cobra" +// "github.com/spf13/viper" +// +// "github.com/chia-network/github-bot/internal/config" +// "github.com/chia-network/github-bot/internal/label" +//) +// +//labelPRsCmd represents the labelPRs command +//var sendKeybaseMessage = &cobra.Command{ +// Use: "sendKeybaseMessage", +// Short: "Sends a message to Keybase informing users of stale community PRs", +// Run: func(cmd *cobra.Command, args []string) { +// cfg, err := config.LoadConfig(viper.GetString("config")) +// if err != nil { +// log.Fatalf("error loading config: %s\n", err.Error()) +// } +// client := github.NewClient(nil).WithAuthToken(cfg.GithubToken) +// +// loop := viper.GetBool("loop") +// loopDuration := viper.GetDuration("loop-time") +// for { +// log.Println("Labeling Pull Requests") +// err = label.PullRequests(client, cfg.InternalTeam, cfg.LabelConfig) +// if err != nil { +// log.Fatalln(err.Error()) +// } +// +// if !loop { +// break +// } +// +// log.Printf("Waiting %s for next iteration\n", loopDuration.String()) +// time.Sleep(loopDuration) +// } +// }, +//} +// +//func init() { +// rootCmd.AddCommand(sendKeybaseMessage) +//} diff --git a/cmd/root.go b/cmd/root.go index e12a607..ea739e9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -60,6 +60,9 @@ func initConfig() { // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { - fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + _, err := fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + if err != nil { + return + } } } diff --git a/internal/github/pullRequests.go b/internal/github/pullRequests.go index 872de9e..4befce8 100644 --- a/internal/github/pullRequests.go +++ b/internal/github/pullRequests.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-github/v60/github" ) +// FindCommunityPRs obtains non-teammember PRs func FindCommunityPRs(owner string, repo string, teamMembers map[string]bool, githubClient *github.Client) ([]*github.PullRequest, error) { var finalPRs []*github.PullRequest opts := &github.PullRequestListOptions{ diff --git a/internal/github/teamMemList.go b/internal/github/teamMemList.go index d2e73c2..8e65bb2 100644 --- a/internal/github/teamMemList.go +++ b/internal/github/teamMemList.go @@ -1 +1,44 @@ package github + +import ( + "context" + "fmt" + "strings" + + "github.com/google/go-github/v60/github" +) + +// GetTeamMemList obtains a list of teammembers +func GetTeamMemList(githubClient *github.Client, internalTeam string) (map[string]bool, error) { + teamMembers := make(map[string]bool) + + teamParts := strings.Split(internalTeam, "/") + if len(teamParts) != 2 { + return nil, fmt.Errorf("invalid team name - must contain org and team : %s", internalTeam) + } + + teamOpts := &github.TeamListTeamMembersOptions{ + Role: "all", + ListOptions: github.ListOptions{ + Page: 0, + PerPage: 100, + }, + } + + for { + teamOpts.ListOptions.Page++ + members, resp, err := githubClient.Teams.ListTeamMembersBySlug(context.TODO(), teamParts[0], teamParts[1], teamOpts) + if err != nil { + return nil, fmt.Errorf("error getting team %s: %w", internalTeam, err) + } + + for _, member := range members { + teamMembers[*member.Login] = true + } + + if resp.NextPage == 0 { + break + } + } + return teamMembers, nil +} diff --git a/internal/label/pullrequests.go b/internal/label/pullrequests.go index c1f83d0..c493eeb 100644 --- a/internal/label/pullrequests.go +++ b/internal/label/pullrequests.go @@ -15,37 +15,6 @@ import ( // PullRequests applies internal or community labels to pull requests // Internal is determined by checking if the PR author is a member of the specified internalTeam func PullRequests(githubClient *github.Client, internalTeam string, cfg config.LabelConfig) error { - teamMembers := map[string]bool{} - - teamParts := strings.Split(internalTeam, "/") - if len(teamParts) != 2 { - return fmt.Errorf("invalid team name - must contain org and team : %s", internalTeam) - } - - teamOpts := &github.TeamListTeamMembersOptions{ - Role: "all", - ListOptions: github.ListOptions{ - Page: 0, - PerPage: 100, - }, - } - - for { - teamOpts.ListOptions.Page++ - members, resp, err := githubClient.Teams.ListTeamMembersBySlug(context.TODO(), teamParts[0], teamParts[1], teamOpts) - if err != nil { - return fmt.Errorf("error getting team %s: %w", internalTeam, err) - } - - for _, member := range members { - teamMembers[*member.Login] = true - } - - if resp.NextPage == 0 { - break - } - } - for _, fullRepo := range cfg.LabelCheckRepos { log.Println("checking repos") parts := strings.Split(fullRepo.Name, "/") @@ -54,6 +23,7 @@ func PullRequests(githubClient *github.Client, internalTeam string, cfg config.L } owner := parts[0] repo := parts[1] + teamMembers, _ := github2.GetTeamMemList(githubClient, internalTeam) pullRequests, err := github2.FindCommunityPRs(owner, repo, teamMembers, githubClient) if err != nil { return err diff --git a/internal/message/sendMessage.go b/internal/message/sendMessage.go index c594f51..748a11c 100644 --- a/internal/message/sendMessage.go +++ b/internal/message/sendMessage.go @@ -1,124 +1,3 @@ -package sendMessage +package message -import ( - "context" - "fmt" - "log" - "strings" - - "github.com/google/go-github/v60/github" - - "github.com/chia-network/github-bot/internal/config" -) - -// PullRequests applies internal or community labels to pull requests -// Internal is determined by checking if the PR author is a member of the specified internalTeam -func PullRequests(githubClient *github.Client, internalTeam string, cfg config.LabelConfig) error { - teamMembers := map[string]bool{} - - teamParts := strings.Split(internalTeam, "/") - if len(teamParts) != 2 { - return fmt.Errorf("invalid team name - must contain org and team : %s", internalTeam) - } - - teamOpts := &github.TeamListTeamMembersOptions{ - Role: "all", - ListOptions: github.ListOptions{ - Page: 0, - PerPage: 100, - }, - } - - for { - teamOpts.ListOptions.Page++ - members, resp, err := githubClient.Teams.ListTeamMembersBySlug(context.TODO(), teamParts[0], teamParts[1], teamOpts) - if err != nil { - return fmt.Errorf("error getting team %s: %w", internalTeam, err) - } - - for _, member := range members { - teamMembers[*member.Login] = true - } - - if resp.NextPage == 0 { - break - } - } - - for _, fullRepo := range cfg.LabelCheckRepos { - log.Println("checking repos") - parts := strings.Split(fullRepo.Name, "/") - if len(parts) != 2 { - return fmt.Errorf("invalid repository name - must contain owner and repository: %s", fullRepo.Name) - } - opts := &github.PullRequestListOptions{ - State: "open", - Sort: "created", - Direction: "desc", - ListOptions: github.ListOptions{ - Page: 0, - PerPage: 100, - }, - } - for { - lowestNumber := 0 - opts.ListOptions.Page++ - owner := parts[0] - repo := parts[1] - pullRequests, resp, err := githubClient.PullRequests.List(context.TODO(), owner, repo, opts) - if err != nil { - return fmt.Errorf("error listing pull requests: %w", err) - } - - for _, pullRequest := range pullRequests { - lowestNumber = *pullRequest.Number - if *pullRequest.Number < fullRepo.MinimumNumber { - // Break, not continue, since our order ensures PR numbers are getting smaller - break - } - if *pullRequest.Draft { - continue - } - user := *pullRequest.User.Login - if cfg.LabelSkipMap[user] { - continue - } - var label string - if teamMembers[user] { - label = cfg.LabelInternal - } else { - label = cfg.LabelExternal - } - - if label != "" { - log.Printf("Pull Request %d by %s will be labeled %s\n", *pullRequest.Number, user, label) - hasLabel := false - for _, existingLabel := range pullRequest.Labels { - if *existingLabel.Name == label { - log.Println(" Already labeled, skipping...") - hasLabel = true - break - } - } - - if !hasLabel { - allLabels := []string{label} - for _, labelP := range pullRequest.Labels { - allLabels = append(allLabels, *labelP.Name) - } - _, _, err := githubClient.Issues.AddLabelsToIssue(context.TODO(), owner, repo, *pullRequest.Number, allLabels) - if err != nil { - return fmt.Errorf("error adding labels to pull request %d: %w", *pullRequest.Number, err) - } - } - } - } - - if resp.NextPage == 0 || lowestNumber <= fullRepo.MinimumNumber { - break - } - } - } - - return nil -} +// For Future Use