Skip to content

Commit 3e68f53

Browse files
committed
internal/telemetry/cmd/stacks: add GitHub client
Add a type for a GitHub client. The type holds the auth token, and its methods are the only way to access GitHub. The first of two CLs that will make it possible to test more of this program, by shunting changes to GitHub off to the side during testing. (See the following CL for details.) Change-Id: Ic487714fe75e19b016a132c0eeaaaf74d5c7cd42 Reviewed-on: https://go-review.googlesource.com/c/tools/+/643936 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 45227b6 commit 3e68f53

File tree

1 file changed

+26
-22
lines changed

1 file changed

+26
-22
lines changed

gopls/internal/telemetry/cmd/stacks/stacks.go

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ var (
9999
daysFlag = flag.Int("days", 7, "number of previous days of telemetry data to read")
100100

101101
dryRun = flag.Bool("n", false, "dry run, avoid updating issues")
102-
103-
authToken string // mandatory GitHub authentication token (for R/W issues access)
104102
)
105103

106104
// ProgramConfig is the configuration for processing reports for a specific
@@ -179,6 +177,8 @@ func main() {
179177
log.SetPrefix("stacks: ")
180178
flag.Parse()
181179

180+
var ghclient *githubClient
181+
182182
// Read GitHub authentication token from $HOME/.stacks.token.
183183
//
184184
// You can create one using the flow at: GitHub > You > Settings >
@@ -198,12 +198,9 @@ func main() {
198198
tokenFile := filepath.Join(home, ".stacks.token")
199199
content, err := os.ReadFile(tokenFile)
200200
if err != nil {
201-
if !os.IsNotExist(err) {
202-
log.Fatalf("cannot read GitHub authentication token: %v", err)
203-
}
204-
log.Fatalf("no file %s containing GitHub authentication token.", tokenFile)
201+
log.Fatalf("cannot read GitHub authentication token: %v", err)
205202
}
206-
authToken = string(bytes.TrimSpace(content))
203+
ghclient = &githubClient{authToken: string(bytes.TrimSpace(content))}
207204
}
208205

209206
pcfg, ok := programs[*programFlag]
@@ -217,7 +214,7 @@ func main() {
217214
log.Fatalf("Error reading reports: %v", err)
218215
}
219216

220-
issues, err := readIssues(pcfg)
217+
issues, err := readIssues(ghclient, pcfg)
221218
if err != nil {
222219
log.Fatalf("Error reading issues: %v", err)
223220
}
@@ -226,7 +223,7 @@ func main() {
226223
claimedBy := claimStacks(issues, stacks)
227224

228225
// Update existing issues that claimed new stacks.
229-
updateIssues(issues, stacks, stackToURL)
226+
updateIssues(ghclient, issues, stacks, stackToURL)
230227

231228
// For each stack, show existing issue or create a new one.
232229
// Aggregate stack IDs by issue summary.
@@ -392,9 +389,9 @@ func readReports(pcfg ProgramConfig, days int) (stacks map[string]map[Info]int64
392389

393390
// readIssues returns all existing issues for the given program and parses any
394391
// predicates.
395-
func readIssues(pcfg ProgramConfig) ([]*Issue, error) {
392+
func readIssues(cli *githubClient, pcfg ProgramConfig) ([]*Issue, error) {
396393
// Query GitHub for all existing GitHub issues with the report label.
397-
issues, err := searchIssues(pcfg.SearchLabel)
394+
issues, err := cli.searchIssues(pcfg.SearchLabel)
398395
if err != nil {
399396
// TODO(jba): return error instead of dying, or doc.
400397
log.Fatalf("GitHub issues label %q search failed: %v", pcfg.SearchLabel, err)
@@ -564,7 +561,7 @@ func claimStacks(issues []*Issue, stacks map[string]map[Info]int64) map[string]*
564561
}
565562

566563
// updateIssues updates existing issues that claimed new stacks by predicate.
567-
func updateIssues(issues []*Issue, stacks map[string]map[Info]int64, stackToURL map[string]string) {
564+
func updateIssues(cli *githubClient, issues []*Issue, stacks map[string]map[Info]int64, stackToURL map[string]string) {
568565
for _, issue := range issues {
569566
if len(issue.newStacks) == 0 {
570567
continue
@@ -580,7 +577,7 @@ func updateIssues(issues []*Issue, stacks map[string]map[Info]int64, stackToURL
580577
writeStackComment(comment, stack, id, stackToURL[stack], stacks[stack])
581578
}
582579

583-
if err := addIssueComment(issue.Number, comment.String()); err != nil {
580+
if err := cli.addIssueComment(issue.Number, comment.String()); err != nil {
584581
log.Println(err)
585582
continue
586583
}
@@ -593,7 +590,7 @@ func updateIssues(issues []*Issue, stacks map[string]map[Info]int64, stackToURL
593590
body += "\nDups:"
594591
}
595592
body += " " + strings.Join(newStackIDs, " ")
596-
if err := updateIssueBody(issue.Number, body); err != nil {
593+
if err := cli.updateIssueBody(issue.Number, body); err != nil {
597594
log.Printf("added comment to issue #%d but failed to update body: %v",
598595
issue.Number, err)
599596
continue
@@ -811,10 +808,17 @@ func frameURL(pclntab map[string]FileLine, info Info, frame string) string {
811808
return ""
812809
}
813810

811+
// -- GitHub client --
812+
813+
// A githubClient interacts with GitHub.
814+
type githubClient struct {
815+
authToken string // mandatory GitHub authentication token (for R/W issues access)
816+
}
817+
814818
// -- GitHub search --
815819

816820
// searchIssues queries the GitHub issue tracker.
817-
func searchIssues(label string) ([]*Issue, error) {
821+
func (cli *githubClient) searchIssues(label string) ([]*Issue, error) {
818822
label = url.QueryEscape(label)
819823

820824
// Slurp all issues with the telemetry label.
@@ -833,7 +837,7 @@ func searchIssues(label string) ([]*Issue, error) {
833837
if err != nil {
834838
return nil, err
835839
}
836-
req.Header.Add("Authorization", "Bearer "+authToken)
840+
req.Header.Add("Authorization", "Bearer "+cli.authToken)
837841
resp, err := http.DefaultClient.Do(req)
838842
if err != nil {
839843
return nil, err
@@ -869,7 +873,7 @@ func searchIssues(label string) ([]*Issue, error) {
869873
}
870874

871875
// updateIssueBody updates the body of the numbered issue.
872-
func updateIssueBody(number int, body string) error {
876+
func (cli *githubClient) updateIssueBody(number int, body string) error {
873877
// https://docs.github.com/en/rest/issues/comments#update-an-issue
874878
var payload struct {
875879
Body string `json:"body"`
@@ -881,14 +885,14 @@ func updateIssueBody(number int, body string) error {
881885
}
882886

883887
url := fmt.Sprintf("https://api.github.com/repos/golang/go/issues/%d", number)
884-
if err := requestChange("PATCH", url, data, http.StatusOK); err != nil {
888+
if err := cli.requestChange("PATCH", url, data, http.StatusOK); err != nil {
885889
return fmt.Errorf("updating issue: %v", err)
886890
}
887891
return nil
888892
}
889893

890894
// addIssueComment adds a markdown comment to the numbered issue.
891-
func addIssueComment(number int, comment string) error {
895+
func (cli *githubClient) addIssueComment(number int, comment string) error {
892896
// https://docs.github.com/en/rest/issues/comments#create-an-issue-comment
893897
var payload struct {
894898
Body string `json:"body"`
@@ -900,15 +904,15 @@ func addIssueComment(number int, comment string) error {
900904
}
901905

902906
url := fmt.Sprintf("https://api.github.com/repos/golang/go/issues/%d/comments", number)
903-
if err := requestChange("POST", url, data, http.StatusCreated); err != nil {
907+
if err := cli.requestChange("POST", url, data, http.StatusCreated); err != nil {
904908
return fmt.Errorf("creating issue comment: %v", err)
905909
}
906910
return nil
907911
}
908912

909913
// requestChange sends a request to url using method, which may change the state at the server.
910914
// The data is sent as the request body, and wantStatus is the expected response status code.
911-
func requestChange(method, url string, data []byte, wantStatus int) error {
915+
func (cli *githubClient) requestChange(method, url string, data []byte, wantStatus int) error {
912916
if *dryRun {
913917
log.Printf("DRY RUN: %s %s", method, url)
914918
return nil
@@ -917,7 +921,7 @@ func requestChange(method, url string, data []byte, wantStatus int) error {
917921
if err != nil {
918922
return err
919923
}
920-
req.Header.Add("Authorization", "Bearer "+authToken)
924+
req.Header.Add("Authorization", "Bearer "+cli.authToken)
921925
resp, err := http.DefaultClient.Do(req)
922926
if err != nil {
923927
return err

0 commit comments

Comments
 (0)