Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add new regen code #1403

Merged
merged 3 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions internal/kokoro/discogen/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module google.golang.org/api/internal/kokoro/discogen

go 1.17

require (
github.com/google/go-github/v42 v42.0.0
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
)

require (
github.com/golang/protobuf v1.3.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
google.golang.org/appengine v1.6.7 // indirect
)
32 changes: 32 additions & 0 deletions internal/kokoro/discogen/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
github.com/bradleyfalzon/ghinstallation/v2 v2.0.3/go.mod h1:tlgi+JWCXnKFx/Y4WtnDbZEINo31N5bcvnCoqieefmk=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v39 v39.0.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
github.com/google/go-github/v42 v42.0.0 h1:YNT0FwjPrEysRkLIiKuEfSvBPCGKphW5aS5PxwaoLec=
github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
178 changes: 178 additions & 0 deletions internal/kokoro/discogen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
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, *githubUsername, *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)
if err := c.Run(); err != nil {
return err
}

c = exec.Command("git", "config", "--global", "user.email", githubEmail)
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, "googleapis", 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 {
path := filepath.Join(dir, "google-api-go-generator")
cmd := exec.Command("make", "all")
cmd.Dir = path
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, username, dir string) error {
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("PATH=%s", os.Getenv("PATH")), // TODO(deklerk): Why do we need to do this? Doesn't seem to be necessary in other exec.Commands.
fmt.Sprintf("HOME=%s", os.Getenv("HOME")), // TODO(deklerk): Why do we need to do this? Doesn't seem to be necessary in other exec.Commands.
fmt.Sprintf("COMMIT_TITLE=%s", commitTitle),
fmt.Sprintf("BRANCH_NAME=%s", branchName),
}
c.Dir = dir
if err := c.Run(); err != nil {
return err
}

ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: accessToken},
)
tc := oauth2.NewClient(ctx, ts)
githubClient := github.NewClient(tc)
head := "googleapis:" + branchName
base := "main"
t := commitTitle
_, _, err := githubClient.PullRequests.Create(ctx, "googleapis", "google-api-go-client", &github.NewPullRequest{
Title: &t,
Head: &head,
Base: &base,
})
if err != nil {
return err
}
return nil
}