Skip to content

Commit 4651916

Browse files
committed
fix(pkg): fallback at using git client when github API files limits is reached.
Github API has an hard limit of 3000 files returned for push/pull request events. Workaround this when checking pre/post submit jobs to be triggered, by manually diffing using the git client. Signed-off-by: Federico Di Pierro <[email protected]>
1 parent c5e374d commit 4651916

File tree

8 files changed

+75
-25
lines changed

8 files changed

+75
-25
lines changed

cmd/status-reconciler/main.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ func main() {
127127
logrus.WithError(err).Fatal("Error getting GitHub client.")
128128
}
129129

130+
gitClient, err := o.github.GitClientFactory("", &o.config.InRepoConfigCacheDirBase, o.dryRun, false)
131+
if err != nil {
132+
logrus.WithError(err).Fatal("Error getting Git client.")
133+
}
134+
130135
prowJobClient, err := o.kubernetes.ProwJobClient(configAgent.Config().ProwJobNamespace, o.dryRun)
131136
if err != nil {
132137
logrus.WithError(err).Fatal("Error getting kube client.")
@@ -139,7 +144,7 @@ func main() {
139144
logrus.WithError(err).Fatal("Cannot create opener")
140145
}
141146

142-
c := statusreconciler.NewController(o.continueOnError, o.getDenyList(), o.getDenyListAll(), opener, o.config, o.statusURI, prowJobClient, githubClient, pluginAgent)
147+
c := statusreconciler.NewController(o.continueOnError, o.getDenyList(), o.getDenyListAll(), opener, o.config, o.statusURI, prowJobClient, gitClient, githubClient, pluginAgent)
143148
interrupts.Run(func(ctx context.Context) {
144149
c.Run(ctx)
145150
})

pkg/config/jobs.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"net/url"
2323
"regexp"
24+
"sigs.k8s.io/prow/pkg/git/v2"
2425
"strings"
2526
"time"
2627

@@ -525,7 +526,8 @@ type githubClient interface {
525526
// NewGitHubDeferredChangedFilesProvider uses a closure to lazily retrieve the file changes only if they are needed.
526527
// We only have to fetch the changes if there is at least one RunIfChanged/SkipIfOnlyChanged job that is not being
527528
// force run (due to a `/retest` after a failure or because it is explicitly triggered with `/test foo`).
528-
func NewGitHubDeferredChangedFilesProvider(client githubClient, org, repo string, num int) ChangedFilesProvider {
529+
func NewGitHubDeferredChangedFilesProvider(gc git.ClientFactory, client githubClient, org, repo string, num int,
530+
baseSHA, headSHA string) ChangedFilesProvider {
529531
var changedFiles []string
530532
return func() ([]string, error) {
531533
// Fetch the changed files from github at most once.
@@ -534,8 +536,25 @@ func NewGitHubDeferredChangedFilesProvider(client githubClient, org, repo string
534536
if err != nil {
535537
return nil, fmt.Errorf("error getting pull request changes: %w", err)
536538
}
537-
for _, change := range changes {
538-
changedFiles = append(changedFiles, change.Filename)
539+
540+
// Fallback to use gitClient since github API truncated the response
541+
if len(changes) == github.ChangesFilesLimit && gc != nil {
542+
repoClient, err := gc.ClientFor(org, repo)
543+
if err == nil {
544+
// Use git client since Github PushEvent is limited to 3000 keys:
545+
// https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests-files
546+
files, err := repoClient.Diff(headSHA, baseSHA)
547+
if err == nil {
548+
changedFiles = files
549+
}
550+
}
551+
}
552+
553+
// in all failure cases, return truncated files
554+
if len(changedFiles) == 0 {
555+
for _, change := range changes {
556+
changedFiles = append(changedFiles, change.Filename)
557+
}
539558
}
540559
}
541560
return changedFiles, nil

pkg/github/types.go

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ const (
5151

5252
// DefaultGraphQLEndpoint is the default GitHub GraphQL API endpoint.
5353
DefaultGraphQLEndpoint = "https://api.github.com/graphql"
54+
55+
// ChangesFilesLimit is the limit to the files changed pushed by the github rest API.
56+
// See https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests-files.
57+
ChangesFilesLimit = 3000
5458
)
5559

5660
var (

pkg/plugins/skip/skip.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func handle(gc githubClient, log *logrus.Entry, e *github.GenericCommentEvent, c
114114
}
115115
statuses := combinedStatus.Statuses
116116

117-
filteredPresubmits, err := trigger.FilterPresubmits(honorOkToTest, gc, e.Body, pr, presubmits, log)
117+
filteredPresubmits, err := trigger.FilterPresubmits(honorOkToTest, gitClient, gc, e.Body, pr, presubmits, log)
118118
if err != nil {
119119
resp := fmt.Sprintf("Cannot get combined status for PR #%d in %s/%s: %v", number, org, repo, err)
120120
log.Warn(resp)

pkg/plugins/trigger/generic-comment.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package trigger
1818

1919
import (
2020
"fmt"
21+
"sigs.k8s.io/prow/pkg/git/v2"
2122

2223
"github.com/sirupsen/logrus"
2324
"sigs.k8s.io/prow/pkg/kube"
@@ -126,12 +127,12 @@ func handleGenericComment(c Client, trigger plugins.Trigger, gc github.GenericCo
126127
return err
127128
}
128129

129-
toTest, err := FilterPresubmits(HonorOkToTest(trigger), c.GitHubClient, gc.Body, pr, presubmits, c.Logger)
130+
toTest, err := FilterPresubmits(HonorOkToTest(trigger), c.GitClient, c.GitHubClient, gc.Body, pr, presubmits, c.Logger)
130131
if err != nil {
131132
return err
132133
}
133134
if needsHelp, note := pjutil.ShouldRespondWithHelp(gc.Body, len(toTest)); needsHelp {
134-
return addHelpComment(c.GitHubClient, gc.Body, org, repo, pr.Base.Ref, pr.Number, presubmits, gc.HTMLURL, commentAuthor, note, c.Logger)
135+
return addHelpComment(c.GitClient, c.GitHubClient, gc.Body, org, repo, pr, presubmits, gc.HTMLURL, commentAuthor, note, c.Logger)
135136
}
136137
// we want to be able to track re-tests separately from the general body of tests
137138
additionalLabels := map[string]string{}
@@ -195,7 +196,7 @@ type GitHubClient interface {
195196
// If a comment that we get matches more than one of the above patterns, we
196197
// consider the set of matching presubmits the union of the results from the
197198
// matching cases.
198-
func FilterPresubmits(honorOkToTest bool, gitHubClient GitHubClient, body string, pr *github.PullRequest, presubmits []config.Presubmit, logger *logrus.Entry) ([]config.Presubmit, error) {
199+
func FilterPresubmits(honorOkToTest bool, gc git.ClientFactory, gitHubClient GitHubClient, body string, pr *github.PullRequest, presubmits []config.Presubmit, logger *logrus.Entry) ([]config.Presubmit, error) {
199200
org, repo, sha := pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Head.SHA
200201

201202
contextGetter := func() (sets.Set[string], sets.Set[string], error) {
@@ -212,8 +213,8 @@ func FilterPresubmits(honorOkToTest bool, gitHubClient GitHubClient, body string
212213
return nil, err
213214
}
214215

215-
number, branch := pr.Number, pr.Base.Ref
216-
changes := config.NewGitHubDeferredChangedFilesProvider(gitHubClient, org, repo, number)
216+
number, branch, baseSHA, headSHA := pr.Number, pr.Base.Ref, pr.Base.SHA, pr.Head.SHA
217+
changes := config.NewGitHubDeferredChangedFilesProvider(gc, gitHubClient, org, repo, number, baseSHA, headSHA)
217218
return pjutil.FilterPresubmits(filter, changes, branch, presubmits, logger)
218219
}
219220

@@ -231,8 +232,9 @@ func getContexts(combinedStatus *github.CombinedStatus) (sets.Set[string], sets.
231232
return failedContexts, allContexts
232233
}
233234

234-
func addHelpComment(githubClient githubClient, body, org, repo, branch string, number int, presubmits []config.Presubmit, HTMLURL, user, note string, logger *logrus.Entry) error {
235-
changes := config.NewGitHubDeferredChangedFilesProvider(githubClient, org, repo, number)
235+
func addHelpComment(gc git.ClientFactory, githubClient githubClient, body, org, repo string, pr *github.PullRequest, presubmits []config.Presubmit, HTMLURL, user, note string, logger *logrus.Entry) error {
236+
number, branch, baseSHA, headSHA := pr.Number, pr.Base.Ref, pr.Base.SHA, pr.Head.SHA
237+
changes := config.NewGitHubDeferredChangedFilesProvider(gc, githubClient, org, repo, number, baseSHA, headSHA)
236238
testAllNames, optionalJobsCommands, requiredJobsCommands, err := pjutil.AvailablePresubmits(changes, branch, presubmits, logger)
237239
if err != nil {
238240
return err

pkg/plugins/trigger/pull-request.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,8 @@ func buildAllButDrafts(c Client, pr *github.PullRequest, eventGUID string, baseS
354354

355355
// buildAll ensures that all builds that should run and will be required are built
356356
func buildAll(c Client, pr *github.PullRequest, eventGUID string, baseSHA string, presubmits []config.Presubmit) error {
357-
org, repo, number, branch := pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Number, pr.Base.Ref
358-
changes := config.NewGitHubDeferredChangedFilesProvider(c.GitHubClient, org, repo, number)
357+
org, repo, number, branch, headSHA := pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Number, pr.Base.Ref, pr.Head.SHA
358+
changes := config.NewGitHubDeferredChangedFilesProvider(c.GitClient, c.GitHubClient, org, repo, number, baseSHA, headSHA)
359359
toTest, err := pjutil.FilterPresubmits(pjutil.NewTestAllFilter(), changes, branch, presubmits, c.Logger)
360360
if err != nil {
361361
return err

pkg/plugins/trigger/push.go

+25-8
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,44 @@ import (
2121

2222
prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
2323
"sigs.k8s.io/prow/pkg/config"
24+
"sigs.k8s.io/prow/pkg/git/v2"
2425
"sigs.k8s.io/prow/pkg/github"
2526
"sigs.k8s.io/prow/pkg/pjutil"
2627
)
2728

28-
func listPushEventChanges(pe github.PushEvent) config.ChangedFilesProvider {
29+
func listPushEventChanges(gc git.ClientFactory, pe github.PushEvent) config.ChangedFilesProvider {
2930
return func() ([]string, error) {
30-
changed := make(map[string]bool)
31+
32+
// Fallback to use PushEvent
33+
changes := make(map[string]bool)
3134
for _, commit := range pe.Commits {
3235
for _, added := range commit.Added {
33-
changed[added] = true
36+
changes[added] = true
3437
}
3538
for _, removed := range commit.Removed {
36-
changed[removed] = true
39+
changes[removed] = true
3740
}
3841
for _, modified := range commit.Modified {
39-
changed[modified] = true
42+
changes[modified] = true
4043
}
4144
}
4245
var changedFiles []string
43-
for file := range changed {
44-
changedFiles = append(changedFiles, file)
46+
if len(changes) == github.ChangesFilesLimit && gc != nil {
47+
repoClient, err := gc.ClientFor(pe.Repo.Owner.Name, pe.Repo.Name)
48+
if err == nil {
49+
// Use git client since Github PushEvent is limited to 3000 keys:
50+
// https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests-files
51+
files, err := repoClient.Diff(pe.After, pe.Before)
52+
if err == nil {
53+
changedFiles = files
54+
}
55+
}
56+
}
57+
58+
if len(changedFiles) == 0 {
59+
for file := range changes {
60+
changedFiles = append(changedFiles, file)
61+
}
4562
}
4663
return changedFiles, nil
4764
}
@@ -74,7 +91,7 @@ func handlePE(c Client, pe github.PushEvent) error {
7491
postsubmits := getPostsubmits(c.Logger, c.GitClient, c.Config, org+"/"+repo, shaGetter)
7592

7693
for _, j := range postsubmits {
77-
if shouldRun, err := j.ShouldRun(pe.Branch(), listPushEventChanges(pe)); err != nil {
94+
if shouldRun, err := j.ShouldRun(pe.Branch(), listPushEventChanges(c.GitClient, pe)); err != nil {
7895
return err
7996
} else if !shouldRun {
8097
continue

pkg/statusreconciler/controller.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package statusreconciler
1919
import (
2020
"context"
2121
"fmt"
22+
"sigs.k8s.io/prow/pkg/git/v2"
2223
"strings"
2324
"time"
2425

@@ -38,7 +39,7 @@ import (
3839
)
3940

4041
// NewController constructs a new controller to reconcile stauses on config change
41-
func NewController(continueOnError bool, addedPresubmitDenylist, addedPresubmitDenylistAll sets.Set[string], opener io.Opener, configOpts configflagutil.ConfigOptions, statusURI string, prowJobClient prowv1.ProwJobInterface, githubClient github.Client, pluginAgent *plugins.ConfigAgent) *Controller {
42+
func NewController(continueOnError bool, addedPresubmitDenylist, addedPresubmitDenylistAll sets.Set[string], opener io.Opener, configOpts configflagutil.ConfigOptions, statusURI string, prowJobClient prowv1.ProwJobInterface, gc git.ClientFactory, githubClient github.Client, pluginAgent *plugins.ConfigAgent) *Controller {
4243
sc := &statusController{
4344
logger: logrus.WithField("client", "statusController"),
4445
opener: opener,
@@ -57,6 +58,7 @@ func NewController(continueOnError bool, addedPresubmitDenylist, addedPresubmitD
5758
pluginAgent: pluginAgent,
5859
},
5960
githubClient: githubClient,
61+
gitClient: gc,
6062
statusMigrator: &gitHubMigrator{
6163
githubClient: githubClient,
6264
continueOnError: continueOnError,
@@ -151,6 +153,7 @@ type Controller struct {
151153
addedPresubmitDenylistAll sets.Set[string]
152154
prowJobTriggerer prowJobTriggerer
153155
githubClient githubClient
156+
gitClient git.ClientFactory
154157
statusMigrator statusMigrator
155158
trustedChecker trustedChecker
156159
statusClient statusClient
@@ -247,8 +250,8 @@ func (c *Controller) triggerNewPresubmits(addedPresubmits map[string][]config.Pr
247250
filter := pjutil.NewArbitraryFilter(func(p config.Presubmit) (shouldRun bool, forcedToRun bool, defaultBehavior bool) {
248251
return true, false, true
249252
}, "inline-filter")
250-
org, repo, number, branch := pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Number, pr.Base.Ref
251-
changes := config.NewGitHubDeferredChangedFilesProvider(c.githubClient, org, repo, number)
253+
org, repo, number, branch, baseSHA, headSHA := pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Number, pr.Base.Ref, pr.Base.SHA, pr.Head.SHA
254+
changes := config.NewGitHubDeferredChangedFilesProvider(c.gitClient, c.githubClient, org, repo, number, baseSHA, headSHA)
252255
logger := log.WithFields(logrus.Fields{"org": org, "repo": repo, "number": number, "branch": branch})
253256
toTrigger, err := pjutil.FilterPresubmits(filter, changes, branch, presubmits, logger)
254257
if err != nil {

0 commit comments

Comments
 (0)