-
Notifications
You must be signed in to change notification settings - Fork 46
/
github.go
116 lines (99 loc) · 3.52 KB
/
github.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package gh
import (
"context"
"fmt"
"log"
"net/http"
"time"
"github.com/google/go-github/v58/github"
)
const SecondsToSleepWhenRateLimited = 30
type GitHubOperations interface {
CheckExistingIssue(ctx context.Context, r *Issues) (string, error)
OpenIssue(ctx context.Context, r *Issues) (string, error)
OpenPullRequest(ctx context.Context, pr *NewPullRequest) (string, error)
AddReactionIssue(ctx context.Context, i *Issues, number int, reaction string) error
HasExistingComment(ctx context.Context, r *Issues, issueNumber int, newComment string) (bool, error)
CommentIssue(ctx context.Context, r *Issues, number int) (string, error)
ListIssues(ctx context.Context, owner, repo, state string) ([]*github.Issue, error)
ListPullRequests(ctx context.Context, owner, repo, state string) ([]*github.PullRequest, error)
}
type BasePullRequest struct {
Owner string
RepoName string
Branch string
PullRequestBaseBranch string
}
type GitOptions struct {
GithubClient *github.Client
MaxRetries int
Logger *log.Logger
}
/*
Code modified from original. Credited to https://github.com/gruntwork-io/git-xargs/blob/f68178c5878108f32c63e1cb027eb1b5b93caaac/repository/repo-operations.go#L404
*/
func (o GitOptions) handleRateLimit(action func() (*github.Response, error)) error {
resp, err := action()
return o.handleGitHubResponse(resp, err, func() error {
return o.handleRateLimit(action)
})
}
func (o GitOptions) handleRateLimitList(action func(opt *github.ListOptions) (*github.Response, error)) error {
opt := &github.ListOptions{
PerPage: 30,
}
for {
resp, err := action(opt)
err = o.handleGitHubResponse(resp, err, func() error {
return o.handleRateLimitList(action)
})
if err != nil {
return err
}
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return nil
}
func (o GitOptions) handleGitHubResponse(resp *github.Response, err error, action func() error) error {
if resp.StatusCode == http.StatusUnauthorized {
return fmt.Errorf("failed to auth with GitHub, does your personal access token have the repo scope? https://github.com/settings/tokens/new?scopes=repo. status code: %d", resp.StatusCode)
}
if err != nil {
if githubErr := github.CheckResponse(resp.Response); githubErr != nil {
isRateLimited, delay := o.checkRateLimiting(githubErr)
if isRateLimited {
o.Logger.Printf("retrying again later with %v second delay due to secondary rate limiting.", delay.Seconds())
time.Sleep(delay)
return action()
}
return githubErr
}
return err
}
return nil
}
func (o GitOptions) checkRateLimiting(githubErr error) (bool, time.Duration) {
isRateLimited := false
delay := time.Duration(SecondsToSleepWhenRateLimited * int(time.Second))
// If GitHub returned an error of type RateLimitError, we can attempt to compute the next time to try the request again
// by reading its rate limit information
if rateLimitError, ok := githubErr.(*github.RateLimitError); ok {
isRateLimited = true
retryAfter := time.Until(rateLimitError.Rate.Reset.Time)
delay = retryAfter
o.Logger.Printf("parsed retryAfter %d from GitHub rate limit error's reset time", retryAfter)
}
// If GitHub returned a Retry-After header, use its value, otherwise use the default
if abuseRateLimitError, ok := githubErr.(*github.AbuseRateLimitError); ok {
isRateLimited = true
if abuseRateLimitError.RetryAfter != nil {
if abuseRateLimitError.RetryAfter.Seconds() > 0 {
delay = *abuseRateLimitError.RetryAfter
}
}
}
return isRateLimited, delay
}