/
main.go
198 lines (175 loc) · 5.43 KB
/
main.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"os/user"
"path"
"path/filepath"
"strings"
"github.com/google/go-github/v42/github"
"golang.org/x/oauth2"
)
const (
branchName = "discogen"
commitTitle = "feat(all): auto-regenerate discovery clients"
owner = "googleapis"
repo = "google-api-go-client"
)
func main() {
ctx := context.Background()
githubAccessToken := flag.String("github-access-token", os.Getenv("GITHUB_ACCESS_TOKEN"), "The token used to open pull requests. Required.")
githubUsername := flag.String("github-username", os.Getenv("GITHUB_USERNAME"), "The GitHub user name for the author. Required.")
githubName := flag.String("github-name", os.Getenv("GITHUB_NAME"), "The name of the author for git commits. Required.")
githubEmail := flag.String("github-email", os.Getenv("GITHUB_EMAIL"), "The email address of the author. Required.")
discoDir := flag.String("discovery-dir", os.Getenv("DISCOVERY_DIR"), "Directory where sources of googleapis/google-api-go-client resides. Required.")
flag.Parse()
if *githubAccessToken == "" || *githubUsername == "" || *githubName == "" || *githubEmail == "" || *discoDir == "" {
log.Fatal("all required flags not set")
}
if err := setGitCreds(*githubName, *githubEmail, *githubUsername, *githubAccessToken); err != nil {
log.Fatalf("unable to set git credentials: %v", err)
}
if prIsOpen, err := isPROpen(ctx, *githubAccessToken, *githubUsername); err != nil || prIsOpen {
if err != nil {
log.Fatalf("unable to check PR status: %v", err)
}
log.Println("a regen PR is already open, nothing to do here")
os.Exit(0)
}
if err := generate(*discoDir); err != nil {
log.Fatalf("unable to generate discovery clients: %v", err)
}
if hasChanges, err := hasChanges(*discoDir); err != nil || !hasChanges {
if err != nil {
log.Fatalf("unable to check git status: %v", err)
}
log.Println("no local changes, exiting")
os.Exit(0)
}
if err := makePR(ctx, *githubAccessToken, *discoDir); err != nil {
log.Fatalf("unable to make regen PR: %v", err)
}
}
// setGitCreds configures credentials for GitHub.
func setGitCreds(githubName, githubEmail, githubUsername, accessToken string) error {
u, err := user.Current()
if err != nil {
return err
}
gitCredentials := []byte(fmt.Sprintf("https://%s:%s@github.com", githubUsername, accessToken))
if err := ioutil.WriteFile(path.Join(u.HomeDir, ".git-credentials"), gitCredentials, 0644); err != nil {
return err
}
c := exec.Command("git", "config", "--global", "user.name", githubName)
c.Env = []string{
fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
}
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if err := c.Run(); err != nil {
return err
}
c = exec.Command("git", "config", "--global", "user.email", githubEmail)
c.Env = []string{
fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
}
c.Stdout = os.Stdout
c.Stderr = os.Stderr
return c.Run()
}
// isPROpen checks if a regen PR is already open.
func isPROpen(ctx context.Context, accessToken, username string) (bool, error) {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: accessToken},
)
tc := oauth2.NewClient(ctx, ts)
githubClient := github.NewClient(tc)
opt := &github.PullRequestListOptions{
ListOptions: github.ListOptions{PerPage: 50},
State: "open",
}
prs, _, err := githubClient.PullRequests.List(ctx, owner, repo, opt)
if err != nil {
return false, err
}
for _, pr := range prs {
if !strings.Contains(pr.GetTitle(), "auto-regenerate") {
continue
}
if pr.GetUser().GetLogin() != username {
continue
}
return true, nil
}
return false, nil
}
// generate regenerates the whole project.
func generate(dir string) error {
fp := filepath.Join(dir, "google-api-go-generator")
cmd := exec.Command("make", "all")
cmd.Dir = fp
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
return cmd.Run()
}
// hasChanges reports if any files have been updated.
func hasChanges(dir string) (bool, error) {
c := exec.Command("git", "status", "--short")
c.Dir = dir
b, err := c.Output()
return len(b) > 0, err
}
// makePR commits local changes and makes a regen PR.
func makePR(ctx context.Context, accessToken, dir string) error {
log.Println("creating commit and pushing")
c := exec.Command("/bin/bash", "-c", `
set -ex
git config credential.helper store
git branch -D $BRANCH_NAME || true
git push -d origin $BRANCH_NAME || true
git add -A
git checkout -b $BRANCH_NAME
git commit -m "$COMMIT_TITLE"
git push origin $BRANCH_NAME
`)
c.Env = []string{
fmt.Sprintf("COMMIT_TITLE=%s", commitTitle),
fmt.Sprintf("BRANCH_NAME=%s", branchName),
fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
}
c.Dir = dir
c.Stderr = os.Stderr
c.Stdout = os.Stdout
if err := c.Run(); err != nil {
return err
}
log.Println("creating pull request")
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: accessToken},
)
tc := oauth2.NewClient(ctx, ts)
githubClient := github.NewClient(tc)
head := owner + ":" + branchName
base := "main"
t := commitTitle
_, _, err := githubClient.PullRequests.Create(ctx, owner, repo, &github.NewPullRequest{
Title: &t,
Head: &head,
Base: &base,
})
if err != nil {
return err
}
return nil
}