diff --git a/cmd/notifyPendingCI.go b/cmd/notifyPendingCI.go new file mode 100644 index 0000000..8f8eceb --- /dev/null +++ b/cmd/notifyPendingCI.go @@ -0,0 +1,50 @@ +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" + github2 "github.com/chia-network/github-bot/internal/github" +) + +var notifyPendingCICmd = &cobra.Command{ + Use: "notify-pendingci", + Short: "Sends a Keybase message to a channel, alerting that a community PR is ready for CI to run", + 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") + var listPendingPRs []string + for { + log.Println("Checking for community PRs that are waiting for CI to run") + listPendingPRs, err = github2.CheckForPendingCI(client, cfg.InternalTeam, cfg.CheckStalePending) + if err != nil { + log.Printf("The following error occurred while obtaining a list of pending PRs: %s", err) + time.Sleep(loopDuration) + continue + } + log.Printf("Pending PRs ready for CI: %v\n", listPendingPRs) + + if !loop { + break + } + + log.Printf("Waiting %s for next iteration\n", loopDuration.String()) + time.Sleep(loopDuration) + } + }, +} + +func init() { + rootCmd.AddCommand(notifyPendingCICmd) +} diff --git a/cmd/notifyStale.go b/cmd/notifyStale.go new file mode 100644 index 0000000..10a66bb --- /dev/null +++ b/cmd/notifyStale.go @@ -0,0 +1,49 @@ +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" + github2 "github.com/chia-network/github-bot/internal/github" +) + +var notifyStaleCmd = &cobra.Command{ + Use: "notify-stale", + Short: "Sends a Keybase message to a channel, alerting that a community PR has not been updated in 7 days", + 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") + var listPendingPRs []string + for { + log.Println("Checking for community PRs that have no update in the last 7 days") + _, err = github2.CheckStalePRs(client, cfg.InternalTeam, cfg.CheckStalePending) + if err != nil { + log.Printf("The following error occurred while obtaining a list of stale PRs: %s", err) + time.Sleep(loopDuration) + continue + } + log.Printf("Stale PRs: %v\n", listPendingPRs) + if !loop { + break + } + + log.Printf("Waiting %s for next iteration\n", loopDuration.String()) + time.Sleep(loopDuration) + } + }, +} + +func init() { + rootCmd.AddCommand(notifyStaleCmd) +} diff --git a/go.mod b/go.mod index 992489e..b6917e5 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -28,5 +29,6 @@ require ( golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index d67f4e0..e91242c 100644 --- a/go.sum +++ b/go.sum @@ -20,12 +20,16 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -71,8 +75,8 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/config/config.go b/internal/config/config.go index 3f3d753..e02608b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,9 +2,10 @@ package config // Config defines the config for all aspects of the bot type Config struct { - GithubToken string `yaml:"github_token"` - InternalTeam string `yaml:"internal_team"` - LabelConfig `yaml:",inline"` + GithubToken string `yaml:"github_token"` + InternalTeam string `yaml:"internal_team"` + LabelConfig `yaml:",inline"` + CheckStalePending `yaml:",inline"` } // LabelConfig is the configuration options specific to labeling PRs @@ -21,3 +22,8 @@ type CheckRepo struct { Name string `yaml:"name"` MinimumNumber int `yaml:"minimum_number"` } + +// CheckStalePending are config settings when checking a repo +type CheckStalePending struct { + CheckStalePending []CheckRepo `yaml:"check_stale_pending_repos"` +} diff --git a/internal/github/checkPendingCI.go b/internal/github/checkPendingCI.go new file mode 100644 index 0000000..0bb4376 --- /dev/null +++ b/internal/github/checkPendingCI.go @@ -0,0 +1,128 @@ +package github + +import ( + "context" + "fmt" + "log" + "strconv" + "strings" + "time" + + "github.com/google/go-github/v60/github" // Ensure your go-github library version matches + + "github.com/chia-network/github-bot/internal/config" +) + +// CheckForPendingCI returns a list of PR URLs that are ready for CI to run but haven't started yet. +func CheckForPendingCI(githubClient *github.Client, internalTeam string, cfg config.CheckStalePending) ([]string, error) { + teamMembers, _ := GetTeamMemberList(githubClient, internalTeam) + var pendingPRs []string + + for _, fullRepo := range cfg.CheckStalePending { + log.Println("Checking repository:", fullRepo.Name) + parts := strings.Split(fullRepo.Name, "/") + if len(parts) != 2 { + log.Printf("invalid repository name - must contain owner and repository: %s", fullRepo.Name) + continue + } + owner, repo := parts[0], parts[1] + + // Fetch community PRs using the FindCommunityPRs function + communityPRs, err := FindCommunityPRs(cfg.CheckStalePending, teamMembers, githubClient) + if err != nil { + return nil, err + } + + for _, pr := range communityPRs { + // Dynamic cutoff time based on the last commit to the PR + lastCommitTime, err := getLastCommitTime(githubClient, owner, repo, pr.GetNumber()) + if err != nil { + log.Printf("Error retrieving last commit time for PR #%d in %s/%s: %v", pr.GetNumber(), owner, repo, err) + continue + } + cutoffTime := lastCommitTime.Add(2 * time.Hour) // 2 hours after the last commit + + if time.Now().Before(cutoffTime) { + log.Printf("Skipping PR #%d from %s/%s repo as it's still within the 2-hour window from the last commit.", pr.GetNumber(), owner, repo) + continue + } + + hasCIRuns, err := checkCIStatus(githubClient, owner, repo, pr.GetNumber()) + if err != nil { + log.Printf("Error checking CI status for PR #%d in %s/%s: %v", pr.GetNumber(), owner, repo, err) + continue + } + + teamMemberActivity, err := checkTeamMemberActivity(githubClient, owner, repo, pr.GetNumber(), teamMembers, lastCommitTime) + if err != nil { + log.Printf("Error checking team member activity for PR #%d in %s/%s: %v", pr.GetNumber(), owner, repo, err) + continue // or handle the error as needed + } + if !hasCIRuns || !teamMemberActivity { + log.Printf("PR #%d in %s/%s by %s is ready for CI since %v but no CI actions have started yet, or it requires re-approval.", pr.GetNumber(), owner, repo, pr.User.GetLogin(), pr.CreatedAt) + pendingPRs = append(pendingPRs, pr.GetHTMLURL()) + } + } + } + return pendingPRs, nil +} + +func getLastCommitTime(client *github.Client, owner, repo string, prNumber int) (time.Time, error) { + commits, _, err := client.PullRequests.ListCommits(context.Background(), owner, repo, prNumber, nil) + if err != nil { + return time.Time{}, err // Properly handle API errors + } + if len(commits) == 0 { + return time.Time{}, fmt.Errorf("no commits found for PR #%d", prNumber) // Handle case where no commits are found + } + // Requesting a list of commits will return the json list in descending order + lastCommit := commits[len(commits)-1] + commitDate := lastCommit.GetCommit().GetAuthor().GetDate() // commitDate is of type Timestamp + + // Since GetDate() returns a Timestamp (not *Timestamp), use the address to call GetTime() + commitTime := commitDate.GetTime() // Correctly accessing GetTime(), which returns *time.Time + + if commitTime == nil { + return time.Time{}, fmt.Errorf("commit time is nil for PR #%d", prNumber) + } + return *commitTime, nil // Safely dereference *time.Time to get time.Time +} + +func checkCIStatus(client *github.Client, owner, repo string, prNumber int) (bool, error) { + checks, _, err := client.Checks.ListCheckRunsForRef(context.Background(), owner, repo, strconv.Itoa(prNumber), &github.ListCheckRunsOptions{}) + if err != nil { + return false, err + } + return checks.GetTotal() > 0, nil +} + +func checkTeamMemberActivity(client *github.Client, owner, repo string, prNumber int, teamMembers map[string]bool, lastCommitTime time.Time) (bool, error) { + comments, _, err := client.Issues.ListComments(context.Background(), owner, repo, prNumber, nil) + if err != nil { + return false, fmt.Errorf("failed to fetch comments: %w", err) + } + + for _, comment := range comments { + if _, ok := teamMembers[comment.User.GetLogin()]; ok && comment.CreatedAt.After(lastCommitTime) { + // Check if the comment is after the last commit + return true, nil // Active and relevant participation + } + } + + reviews, _, err := client.PullRequests.ListReviews(context.Background(), owner, repo, prNumber, nil) + if err != nil { + return false, fmt.Errorf("failed to fetch reviews: %w", err) + } + + for _, review := range reviews { + if _, ok := teamMembers[review.User.GetLogin()]; ok && review.SubmittedAt.After(lastCommitTime) { + switch review.GetState() { + case "DISMISSED", "CHANGES_REQUESTED", "COMMENTED": + // Check if the review is after the last commit and is in one of the specified states + return true, nil + } + } + } + + return false, nil // No recent relevant activity from team members +} diff --git a/internal/github/checkStalePRs.go b/internal/github/checkStalePRs.go new file mode 100644 index 0000000..7a2f729 --- /dev/null +++ b/internal/github/checkStalePRs.go @@ -0,0 +1,69 @@ +package github + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/google/go-github/v60/github" + + "github.com/chia-network/github-bot/internal/config" +) + +// CheckStalePRs will return a list of PR URLs that have not been updated in the last 7 days by internal team members. +func CheckStalePRs(githubClient *github.Client, internalTeam string, cfg config.CheckStalePending) ([]string, error) { + var stalePRUrls []string + cutoffDate := time.Now().Add(7 * 24 * time.Hour) // 7 days ago + teamMembers, err := GetTeamMemberList(githubClient, internalTeam) + if err != nil { + return nil, err + } + communityPRs, err := FindCommunityPRs(cfg.CheckStalePending, teamMembers, githubClient) + if err != nil { + return nil, err + } + + for _, pr := range communityPRs { + repoName := pr.GetBase().GetRepo().GetFullName() // Get the full name of the repository + stale, err := isStale(githubClient, pr, teamMembers, cutoffDate) // Handle both returned values + if err != nil { + log.Printf("Error checking if PR in repo %s is stale: %v", repoName, err) + continue // Skip this PR or handle the error appropriately + } + if stale { + stalePRUrls = append(stalePRUrls, pr.GetHTMLURL()) // Append if PR is confirmed stale + } + } + return stalePRUrls, nil +} + +// Checks if a PR is stale based on the last update from team members +func isStale(githubClient *github.Client, pr *github.PullRequest, teamMembers map[string]bool, cutoffDate time.Time) (bool, error) { + listOptions := &github.ListOptions{PerPage: 100} + for { + // Create a context for each request + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) // 30 seconds timeout for each request + + events, resp, err := githubClient.Issues.ListIssueTimeline(ctx, pr.Base.Repo.Owner.GetLogin(), pr.Base.Repo.GetName(), pr.GetNumber(), listOptions) + if err != nil { + cancel() // Explicitly cancel the context when an error occurs + return false, fmt.Errorf("failed to get timeline for PR #%d: %w", pr.GetNumber(), err) + } + for _, event := range events { + if event.Event == nil || event.Actor == nil || event.Actor.Login == nil { + continue + } + if (*event.Event == "commented" || *event.Event == "reviewed") && teamMembers[*event.Actor.Login] && event.CreatedAt.After(cutoffDate) { + cancel() // Clean up the context when returning within the loop + return false, nil + } + } + cancel() // Clean up the context at the end of the loop iteration + if resp.NextPage == 0 { + break + } + listOptions.Page = resp.NextPage + } + return true, nil +} diff --git a/internal/github/pullRequests.go b/internal/github/pullRequests.go index 4befce8..313faa2 100644 --- a/internal/github/pullRequests.go +++ b/internal/github/pullRequests.go @@ -3,38 +3,58 @@ package github import ( "context" "fmt" + "log" + "strings" "github.com/google/go-github/v60/github" + + "github.com/chia-network/github-bot/internal/config" ) -// FindCommunityPRs obtains non-teammember PRs -func FindCommunityPRs(owner string, repo string, teamMembers map[string]bool, githubClient *github.Client) ([]*github.PullRequest, error) { +// FindCommunityPRs obtains PRs based on provided filters +func FindCommunityPRs(repos []config.CheckRepo, 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, + Page: 1, 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 _, fullRepo := range repos { + log.Println("Checking repository:", fullRepo.Name) + parts := strings.Split(fullRepo.Name, "/") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid repository name - must contain owner and repository: %s", fullRepo.Name) } + owner, repo := parts[0], parts[1] - for _, pullRequest := range pullRequests { - user := *pullRequest.User.Login - if !teamMembers[user] { - finalPRs = append(finalPRs, pullRequest) + for { + pullRequests, resp, err := githubClient.PullRequests.List(context.TODO(), owner, repo, opts) + if err != nil { + return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", owner, repo, err) } - } - if resp.NextPage == 0 { - break + for _, pullRequest := range pullRequests { + if *pullRequest.Number < fullRepo.MinimumNumber { + break + } + if *pullRequest.Draft { + continue + } + user := *pullRequest.User.Login + if !teamMembers[user] { + finalPRs = append(finalPRs, pullRequest) + } + } + + if resp.NextPage == 0 { + break // Exit the loop if there are no more pages + } + opts.Page = resp.NextPage // Set next page number } } return finalPRs, nil diff --git a/internal/label/pullrequests.go b/internal/label/pullrequests.go index 25d08d0..25e159e 100644 --- a/internal/label/pullrequests.go +++ b/internal/label/pullrequests.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "strings" "github.com/google/go-github/v60/github" @@ -13,60 +12,48 @@ 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, _ := github2.GetTeamMemberList(githubClient, internalTeam) - 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) + teamMembers, err := github2.GetTeamMemberList(githubClient, internalTeam) + if err != nil { + return fmt.Errorf("error getting team members: %w", err) // Properly handle and return error if team member list fetch fails + } + + pullRequests, err := github2.FindCommunityPRs(cfg.LabelCheckRepos, teamMembers, githubClient) + if err != nil { + return fmt.Errorf("error finding community PRs: %w", err) // Handle error from finding community PRs + } + + for _, pullRequest := range pullRequests { + user := *pullRequest.User.Login + if cfg.LabelSkipMap[user] { + continue } - owner := parts[0] - repo := parts[1] - pullRequests, err := github2.FindCommunityPRs(owner, repo, teamMembers, githubClient) - if err != nil { - return err + var label string + if teamMembers[user] { + label = cfg.LabelInternal + } else { + label = cfg.LabelExternal } - for _, pullRequest := range pullRequests { - if *pullRequest.Number < fullRepo.MinimumNumber { - continue - } - 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(), *pullRequest.Base.Repo.Owner.Login, *pullRequest.Base.Repo.Name, *pullRequest.Number, allLabels) + if err != nil { + return fmt.Errorf("error adding labels to pull request %d: %w", *pullRequest.Number, err) // Ensure error from label adding is handled } } } diff --git a/k8s/label-prs.yml.j2 b/k8s/label-prs.yml.j2 index 61ee1f4..bef6d15 100644 --- a/k8s/label-prs.yml.j2 +++ b/k8s/label-prs.yml.j2 @@ -29,4 +29,3 @@ secretFile: label_skip_users: - "dependabot[bot]" - "github-actions[bot]" - diff --git a/k8s/notify_stale_pending_prs.yml.j2 b/k8s/notify_stale_pending_prs.yml.j2 new file mode 100644 index 0000000..10d84db --- /dev/null +++ b/k8s/notify_stale_pending_prs.yml.j2 @@ -0,0 +1,23 @@ +replicaCount: 1 +image: + repository: ghcr.io/chia-network/github-bot + tag: {{ DOCKER_TAG }} + +deployment: + args: + - notify-pendingci + - notify-stale + - --loop + +# Creates a secret with the following values, and mounts as a file into the main deployment container +secretFile: + mountPath: "/config" + stringValues: + config.yml: | + github_token: "{{ BOT_GITHUB_TOKEN }}" + internal_team: "{{ INTERNAL_TEAM_NAME }}" + check_stale_pending_repos: + - name: "Chia-Network/chia-blockchain" + minimum_number: 17788 + - name: "Chia-Network/chia-blockchain-gui" + minimum_number: 2300