Skip to content

Commit 7d68022

Browse files
author
Andrew Mason
authored
Merge pull request #78 from vitessio/sync-with-github
Sync with GitHub
2 parents 6db45ca + ba1df19 commit 7d68022

File tree

2 files changed

+148
-43
lines changed

2 files changed

+148
-43
lines changed

go/cobradocs_sync.go

+113-37
Original file line numberDiff line numberDiff line change
@@ -19,79 +19,155 @@ package main
1919
import (
2020
"context"
2121
"fmt"
22+
"net/http"
2223

2324
"github.com/google/go-github/v53/github"
2425
"github.com/pkg/errors"
26+
"github.com/rs/zerolog"
27+
2528
"github.com/vitess.io/vitess-bot/go/git"
2629
"github.com/vitess.io/vitess-bot/go/shell"
2730
)
2831

2932
// synchronize cobradocs from main and release branches
30-
func synchronizeCobraDocs(
33+
func (h *PullRequestHandler) synchronizeCobraDocs(
3134
ctx context.Context,
3235
client *github.Client,
3336
vitess *git.Repo,
3437
website *git.Repo,
3538
pr *github.PullRequest,
3639
prInfo prInformation,
3740
) (*github.PullRequest, error) {
41+
logger := zerolog.Ctx(ctx)
3842
op := "update cobradocs"
3943
branch := "prod"
40-
newBranch := fmt.Sprintf("synchronize-cobradocs-for-%d", pr.GetNumber())
44+
headBranch := fmt.Sprintf("synchronize-cobradocs-for-%d", pr.GetNumber())
45+
headRef := fmt.Sprintf("refs/heads/%s", headBranch)
46+
47+
prodBranch, _, err := client.Repositories.GetBranch(ctx, website.Owner, website.Name, branch, false)
48+
if err != nil {
49+
return nil, errors.Wrapf(err, "Failed get production branch on %s/%s to update cobradocs on Pull Request %d", website.Owner, website.Name, pr.GetNumber())
50+
}
51+
52+
baseTree := prodBranch.GetCommit().Commit.Tree.GetSHA()
53+
parent := prodBranch.GetCommit().GetSHA()
54+
var openPR *github.PullRequest
4155

42-
if err := createAndCheckoutBranch(ctx, client, website, branch, newBranch, fmt.Sprintf("%s on Pull Request %d", op, pr.GetNumber())); err != nil {
56+
if err := createAndCheckoutBranch(ctx, client, website, branch, headBranch, fmt.Sprintf("%s on Pull Request %d", op, pr.GetNumber())); err != nil {
4357
return nil, err
4458
}
4559

4660
if err := setupRepo(ctx, vitess, fmt.Sprintf("%s on Pull Request %d", op, prInfo.num)); err != nil {
4761
return nil, err
4862
}
4963

64+
prs, err := website.FindPRs(ctx, client, github.PullRequestListOptions{
65+
State: "open",
66+
Head: fmt.Sprintf("%s:%s", website.Owner, headBranch),
67+
Base: branch,
68+
Sort: "created",
69+
Direction: "desc",
70+
}, func(pr *github.PullRequest) bool {
71+
return pr.GetUser().GetLogin() == h.botLogin
72+
}, 1)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
if len(prs) != 0 {
78+
openPR = prs[0]
79+
baseRepo := openPR.GetBase().GetRepo()
80+
logger.Debug().Msgf("Using existing PR #%d (%s/%s:%s)", openPR.GetNumber(), baseRepo.GetOwner().GetLogin(), baseRepo.GetName(), headBranch)
81+
82+
// If branch already existed, hard reset to `prod`.
83+
if err := website.ResetHard(ctx, branch); err != nil {
84+
return nil, errors.Wrapf(err, "Failed to reset %s to %s to %s for %s", headBranch, branch, op, pr.GetHTMLURL())
85+
}
86+
}
87+
5088
if err := vitess.FetchRef(ctx, "origin", "--tags"); err != nil {
5189
return nil, errors.Wrapf(err, "Failed to fetch tags in repository %s/%s to %s on Pull Request %d", vitess.Owner, vitess.Name, op, prInfo.num)
5290
}
5391

54-
// Run the sync script (which authors the commit already).
92+
// Run the sync script (which authors the commit locally but not with GitHub auth ctx).
5593
if _, err := shell.NewContext(ctx, "./tools/sync_cobradocs.sh").InDir(website.LocalDir).WithExtraEnv(
5694
fmt.Sprintf("VITESS_DIR=%s", vitess.LocalDir),
5795
"COBRADOCS_SYNC_PERSIST=yes",
5896
).Output(); err != nil {
59-
return nil, errors.Wrapf(err, "Failed to run cobradoc sync script in repository %s/%s to %s on Pull Request %d", website.Owner, website.Name, newBranch, prInfo.num)
60-
}
61-
62-
// Amend the commit to change the author to the bot.
63-
if err := website.Commit(ctx, "", git.CommitOpts{
64-
Author: botCommitAuthor,
65-
Amend: true,
66-
NoEdit: true,
67-
}); err != nil {
68-
return nil, errors.Wrapf(err, "Failed to amend commit author to %s on Pull Request %d", op, prInfo.num)
69-
}
70-
71-
// Push the branch
72-
if err := website.Push(ctx, git.PushOpts{
73-
Remote: "origin",
74-
Refs: []string{newBranch},
75-
Force: true,
76-
}); err != nil {
77-
return nil, errors.Wrapf(err, "Failed to push %s to %s on Pull Request %d", newBranch, op, prInfo.num)
78-
}
79-
80-
// Create a Pull Request for the new branch
81-
newPR := &github.NewPullRequest{
82-
Title: github.String(fmt.Sprintf("[cobradocs] synchronize with %s (vitess#%d)", pr.GetTitle(), pr.GetNumber())),
83-
Head: github.String(newBranch),
84-
Base: github.String(branch),
85-
Body: github.String(fmt.Sprintf("## Description\nThis is an automated PR to synchronize the cobradocs with %s", pr.GetHTMLURL())),
86-
MaintainerCanModify: github.Bool(true),
87-
}
88-
newPRCreated, _, err := client.PullRequests.Create(ctx, website.Owner, website.Name, newPR)
97+
return nil, errors.Wrapf(err, "Failed to run cobradoc sync script in repository %s/%s to %s on Pull Request %d", website.Owner, website.Name, op, prInfo.num)
98+
}
99+
100+
// Create a tree of the commit above using the GitHub API and then commit it.
101+
_, commit, err := h.writeAndCommitTree(
102+
ctx,
103+
client,
104+
website,
105+
pr,
106+
branch,
107+
"HEAD",
108+
baseTree,
109+
parent,
110+
fmt.Sprintf("synchronize cobradocs with %s/%s#%d", vitess.Owner, vitess.Name, pr.GetNumber()),
111+
op,
112+
)
89113
if err != nil {
90-
return nil, errors.Wrapf(err, "Failed to create Pull Request using branch %s on %s/%s", newBranch, website.Owner, website.Name)
114+
return nil, err
91115
}
92116

93-
return newPRCreated, nil
94-
117+
// Push the branch.
118+
if _, _, err := client.Git.UpdateRef(ctx, website.Owner, website.Name, &github.Reference{
119+
Ref: &headRef,
120+
Object: &github.GitObject{SHA: commit.SHA},
121+
}, true); err != nil {
122+
return nil, errors.Wrapf(err, "Failed to force-push %s to %s on Pull Request %s", headBranch, op, pr.GetHTMLURL())
123+
}
124+
125+
switch openPR {
126+
case nil:
127+
// Create a Pull Request for the new branch.
128+
newPR := &github.NewPullRequest{
129+
Title: github.String(fmt.Sprintf("[cobradocs] synchronize with %s (vitess#%d)", pr.GetTitle(), pr.GetNumber())),
130+
Head: github.String(headBranch),
131+
Base: github.String(branch),
132+
Body: github.String(fmt.Sprintf("## Description\nThis is an automated PR to synchronize the cobradocs with %s", pr.GetHTMLURL())),
133+
MaintainerCanModify: github.Bool(true),
134+
}
135+
newPRCreated, _, err := client.PullRequests.Create(ctx, website.Owner, website.Name, newPR)
136+
if err != nil {
137+
return nil, errors.Wrapf(err, "Failed to create Pull Request using branch %s on %s/%s", headBranch, website.Owner, website.Name)
138+
}
139+
140+
return newPRCreated, nil
141+
default:
142+
// Edit the title and body to take us out of preview-mode.
143+
if _, _, err := client.PullRequests.Edit(ctx, website.Owner, website.Name, openPR.GetNumber(), &github.PullRequest{
144+
Title: github.String(fmt.Sprintf("[cobradocs] synchronize with %s (vitess#%d)", pr.GetTitle(), pr.GetNumber())),
145+
Body: github.String(fmt.Sprintf("## Description\nThis is an automated PR to synchronize the cobradocs with %s", pr.GetHTMLURL())),
146+
}); err != nil {
147+
return nil, errors.Wrapf(err, "Failed to edit PR title/body on %s", openPR.GetHTMLURL())
148+
}
149+
150+
if _, _, err := client.Issues.CreateComment(ctx, website.Owner, website.Name, openPR.GetNumber(), &github.IssueComment{
151+
Body: github.String(fmt.Sprintf("PR was force-pushed to resync changes after merge of vitess PR %s. Removing do-not-merge label.", pr.GetHTMLURL())),
152+
}); err != nil {
153+
return nil, errors.Wrapf(err, "Failed to add PR comment on %s", openPR.GetHTMLURL())
154+
}
155+
156+
// Remove the doNotMerge label.
157+
if resp, err := client.Issues.RemoveLabelForIssue(ctx, website.Owner, website.Name, openPR.GetNumber(), doNotMergeLabel); err != nil {
158+
// We get a 404 if the label was already removed.
159+
if resp.StatusCode != http.StatusNotFound {
160+
161+
return nil, errors.Wrapf(err, "Failed to remove %s label to %s", doNotMergeLabel, openPR.GetHTMLURL())
162+
}
163+
}
164+
165+
// Propagate the UpdateRef call back to our PR object (which we fetched before pushing).
166+
// This saves us another round trip to the API.
167+
openPR.Head.SHA = commit.SHA
168+
169+
return openPR, nil
170+
}
95171
}
96172

97173
func createAndCheckoutBranch(ctx context.Context, client *github.Client, repo *git.Repo, baseBranch string, newBranch string, op string) error {

go/pull_request.go

+35-6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/palantir/go-githubapp/githubapp"
3333
"github.com/pkg/errors"
3434
"github.com/rs/zerolog"
35+
3536
"github.com/vitess.io/vitess-bot/go/git"
3637
"github.com/vitess.io/vitess-bot/go/shell"
3738
)
@@ -536,7 +537,7 @@ func (h *PullRequestHandler) createCobraDocsPreviewPR(
536537
// 1. Find an existing PR and switch to its branch, or create a new branch
537538
// based on `prod`.
538539
branch := "prod"
539-
headBranch := fmt.Sprintf("cobradocs-preview-for-%d", prInfo.num)
540+
headBranch := fmt.Sprintf("synchronize-cobradocs-for-%d", prInfo.num)
540541
headRef := fmt.Sprintf("refs/heads/%s", headBranch)
541542

542543
prodBranch, _, err := client.Repositories.GetBranch(ctx, website.Owner, website.Name, branch, false)
@@ -693,10 +694,12 @@ func (h *PullRequestHandler) createCobraDocsPreviewPR(
693694
}
694695

695696
// 6. Force push.
696-
client.Git.UpdateRef(ctx, website.Owner, website.Name, &github.Reference{
697+
if _, _, err := client.Git.UpdateRef(ctx, website.Owner, website.Name, &github.Reference{
697698
Ref: &headRef,
698699
Object: &github.GitObject{SHA: commit.SHA},
699-
}, true)
700+
}, true); err != nil {
701+
return nil, errors.Wrapf(err, "Failed to force-push %s to %s on Pull Request %s", headBranch, op, pr.GetHTMLURL())
702+
}
700703

701704
switch openPR {
702705
case nil:
@@ -805,6 +808,8 @@ func (h *PullRequestHandler) updateDocs(ctx context.Context, event github.PullRe
805808
// - PR contains changes to either `go/cmd/**/*.go` OR `go/flags/endtoend/*.txt` (TODO)
806809
if prInfo.base.GetRef() != "main" {
807810
logger.Debug().Msgf("PR %d is merged to %s, not main, skipping website cobradocs sync", prInfo.num, prInfo.base.GetRef())
811+
// TODO: close any potentially open PR against website.
812+
// (see https://github.com/vitessio/vitess-bot/issues/76).
808813
return nil
809814
}
810815

@@ -826,10 +831,34 @@ func (h *PullRequestHandler) updateDocs(ctx context.Context, event github.PullRe
826831
website := git.NewRepo(
827832
prInfo.repoOwner,
828833
"website",
829-
).WithLocalDir(filepath.Join(h.Workdir(), "website"))
834+
).WithDefaultBranch("prod").WithLocalDir(
835+
filepath.Join(h.Workdir(), "website"),
836+
)
830837

831-
_, err = synchronizeCobraDocs(ctx, client, vitess, website, event.GetPullRequest(), prInfo)
832-
return err
838+
pr, err := h.synchronizeCobraDocs(ctx, client, vitess, website, event.GetPullRequest(), prInfo)
839+
if err != nil {
840+
return err
841+
}
842+
843+
if pr != nil {
844+
_, _, err = client.PullRequests.Merge(
845+
ctx,
846+
website.Owner,
847+
website.Name,
848+
pr.GetNumber(),
849+
"", // Default to the standard automatic commit message.
850+
&github.PullRequestOptions{
851+
SHA: pr.GetHead().GetSHA(), // Fail if the branch has changed out from under us.
852+
MergeMethod: "squash",
853+
},
854+
)
855+
856+
if err != nil {
857+
return errors.Wrapf(err, "Failed to merge Pull Request %s", pr.GetHTMLURL())
858+
}
859+
}
860+
861+
return nil
833862
}
834863

835864
func detectCobraDocChanges(ctx context.Context, vitess *git.Repo, client *github.Client, prInfo prInformation) (bool, error) {

0 commit comments

Comments
 (0)