Skip to content

Commit

Permalink
Merge pull request #78 from vitessio/sync-with-github
Browse files Browse the repository at this point in the history
Sync with GitHub
  • Loading branch information
Andrew Mason authored Jan 29, 2024
2 parents 6db45ca + ba1df19 commit 7d68022
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 43 deletions.
150 changes: 113 additions & 37 deletions go/cobradocs_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,79 +19,155 @@ package main
import (
"context"
"fmt"
"net/http"

"github.com/google/go-github/v53/github"
"github.com/pkg/errors"
"github.com/rs/zerolog"

"github.com/vitess.io/vitess-bot/go/git"
"github.com/vitess.io/vitess-bot/go/shell"
)

// synchronize cobradocs from main and release branches
func synchronizeCobraDocs(
func (h *PullRequestHandler) synchronizeCobraDocs(
ctx context.Context,
client *github.Client,
vitess *git.Repo,
website *git.Repo,
pr *github.PullRequest,
prInfo prInformation,
) (*github.PullRequest, error) {
logger := zerolog.Ctx(ctx)
op := "update cobradocs"
branch := "prod"
newBranch := fmt.Sprintf("synchronize-cobradocs-for-%d", pr.GetNumber())
headBranch := fmt.Sprintf("synchronize-cobradocs-for-%d", pr.GetNumber())
headRef := fmt.Sprintf("refs/heads/%s", headBranch)

prodBranch, _, err := client.Repositories.GetBranch(ctx, website.Owner, website.Name, branch, false)
if err != nil {
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())
}

baseTree := prodBranch.GetCommit().Commit.Tree.GetSHA()
parent := prodBranch.GetCommit().GetSHA()
var openPR *github.PullRequest

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

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

prs, err := website.FindPRs(ctx, client, github.PullRequestListOptions{
State: "open",
Head: fmt.Sprintf("%s:%s", website.Owner, headBranch),
Base: branch,
Sort: "created",
Direction: "desc",
}, func(pr *github.PullRequest) bool {
return pr.GetUser().GetLogin() == h.botLogin
}, 1)
if err != nil {
return nil, err
}

if len(prs) != 0 {
openPR = prs[0]
baseRepo := openPR.GetBase().GetRepo()
logger.Debug().Msgf("Using existing PR #%d (%s/%s:%s)", openPR.GetNumber(), baseRepo.GetOwner().GetLogin(), baseRepo.GetName(), headBranch)

// If branch already existed, hard reset to `prod`.
if err := website.ResetHard(ctx, branch); err != nil {
return nil, errors.Wrapf(err, "Failed to reset %s to %s to %s for %s", headBranch, branch, op, pr.GetHTMLURL())
}
}

if err := vitess.FetchRef(ctx, "origin", "--tags"); err != nil {
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)
}

// Run the sync script (which authors the commit already).
// Run the sync script (which authors the commit locally but not with GitHub auth ctx).
if _, err := shell.NewContext(ctx, "./tools/sync_cobradocs.sh").InDir(website.LocalDir).WithExtraEnv(
fmt.Sprintf("VITESS_DIR=%s", vitess.LocalDir),
"COBRADOCS_SYNC_PERSIST=yes",
).Output(); err != nil {
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)
}

// Amend the commit to change the author to the bot.
if err := website.Commit(ctx, "", git.CommitOpts{
Author: botCommitAuthor,
Amend: true,
NoEdit: true,
}); err != nil {
return nil, errors.Wrapf(err, "Failed to amend commit author to %s on Pull Request %d", op, prInfo.num)
}

// Push the branch
if err := website.Push(ctx, git.PushOpts{
Remote: "origin",
Refs: []string{newBranch},
Force: true,
}); err != nil {
return nil, errors.Wrapf(err, "Failed to push %s to %s on Pull Request %d", newBranch, op, prInfo.num)
}

// Create a Pull Request for the new branch
newPR := &github.NewPullRequest{
Title: github.String(fmt.Sprintf("[cobradocs] synchronize with %s (vitess#%d)", pr.GetTitle(), pr.GetNumber())),
Head: github.String(newBranch),
Base: github.String(branch),
Body: github.String(fmt.Sprintf("## Description\nThis is an automated PR to synchronize the cobradocs with %s", pr.GetHTMLURL())),
MaintainerCanModify: github.Bool(true),
}
newPRCreated, _, err := client.PullRequests.Create(ctx, website.Owner, website.Name, newPR)
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)
}

// Create a tree of the commit above using the GitHub API and then commit it.
_, commit, err := h.writeAndCommitTree(
ctx,
client,
website,
pr,
branch,
"HEAD",
baseTree,
parent,
fmt.Sprintf("synchronize cobradocs with %s/%s#%d", vitess.Owner, vitess.Name, pr.GetNumber()),
op,
)
if err != nil {
return nil, errors.Wrapf(err, "Failed to create Pull Request using branch %s on %s/%s", newBranch, website.Owner, website.Name)
return nil, err
}

return newPRCreated, nil

// Push the branch.
if _, _, err := client.Git.UpdateRef(ctx, website.Owner, website.Name, &github.Reference{
Ref: &headRef,
Object: &github.GitObject{SHA: commit.SHA},
}, true); err != nil {
return nil, errors.Wrapf(err, "Failed to force-push %s to %s on Pull Request %s", headBranch, op, pr.GetHTMLURL())
}

switch openPR {
case nil:
// Create a Pull Request for the new branch.
newPR := &github.NewPullRequest{
Title: github.String(fmt.Sprintf("[cobradocs] synchronize with %s (vitess#%d)", pr.GetTitle(), pr.GetNumber())),
Head: github.String(headBranch),
Base: github.String(branch),
Body: github.String(fmt.Sprintf("## Description\nThis is an automated PR to synchronize the cobradocs with %s", pr.GetHTMLURL())),
MaintainerCanModify: github.Bool(true),
}
newPRCreated, _, err := client.PullRequests.Create(ctx, website.Owner, website.Name, newPR)
if err != nil {
return nil, errors.Wrapf(err, "Failed to create Pull Request using branch %s on %s/%s", headBranch, website.Owner, website.Name)
}

return newPRCreated, nil
default:
// Edit the title and body to take us out of preview-mode.
if _, _, err := client.PullRequests.Edit(ctx, website.Owner, website.Name, openPR.GetNumber(), &github.PullRequest{
Title: github.String(fmt.Sprintf("[cobradocs] synchronize with %s (vitess#%d)", pr.GetTitle(), pr.GetNumber())),
Body: github.String(fmt.Sprintf("## Description\nThis is an automated PR to synchronize the cobradocs with %s", pr.GetHTMLURL())),
}); err != nil {
return nil, errors.Wrapf(err, "Failed to edit PR title/body on %s", openPR.GetHTMLURL())
}

if _, _, err := client.Issues.CreateComment(ctx, website.Owner, website.Name, openPR.GetNumber(), &github.IssueComment{
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())),
}); err != nil {
return nil, errors.Wrapf(err, "Failed to add PR comment on %s", openPR.GetHTMLURL())
}

// Remove the doNotMerge label.
if resp, err := client.Issues.RemoveLabelForIssue(ctx, website.Owner, website.Name, openPR.GetNumber(), doNotMergeLabel); err != nil {
// We get a 404 if the label was already removed.
if resp.StatusCode != http.StatusNotFound {

return nil, errors.Wrapf(err, "Failed to remove %s label to %s", doNotMergeLabel, openPR.GetHTMLURL())
}
}

// Propagate the UpdateRef call back to our PR object (which we fetched before pushing).
// This saves us another round trip to the API.
openPR.Head.SHA = commit.SHA

return openPR, nil
}
}

func createAndCheckoutBranch(ctx context.Context, client *github.Client, repo *git.Repo, baseBranch string, newBranch string, op string) error {
Expand Down
41 changes: 35 additions & 6 deletions go/pull_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/palantir/go-githubapp/githubapp"
"github.com/pkg/errors"
"github.com/rs/zerolog"

"github.com/vitess.io/vitess-bot/go/git"
"github.com/vitess.io/vitess-bot/go/shell"
)
Expand Down Expand Up @@ -536,7 +537,7 @@ func (h *PullRequestHandler) createCobraDocsPreviewPR(
// 1. Find an existing PR and switch to its branch, or create a new branch
// based on `prod`.
branch := "prod"
headBranch := fmt.Sprintf("cobradocs-preview-for-%d", prInfo.num)
headBranch := fmt.Sprintf("synchronize-cobradocs-for-%d", prInfo.num)
headRef := fmt.Sprintf("refs/heads/%s", headBranch)

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

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

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

Expand All @@ -826,10 +831,34 @@ func (h *PullRequestHandler) updateDocs(ctx context.Context, event github.PullRe
website := git.NewRepo(
prInfo.repoOwner,
"website",
).WithLocalDir(filepath.Join(h.Workdir(), "website"))
).WithDefaultBranch("prod").WithLocalDir(
filepath.Join(h.Workdir(), "website"),
)

_, err = synchronizeCobraDocs(ctx, client, vitess, website, event.GetPullRequest(), prInfo)
return err
pr, err := h.synchronizeCobraDocs(ctx, client, vitess, website, event.GetPullRequest(), prInfo)
if err != nil {
return err
}

if pr != nil {
_, _, err = client.PullRequests.Merge(
ctx,
website.Owner,
website.Name,
pr.GetNumber(),
"", // Default to the standard automatic commit message.
&github.PullRequestOptions{
SHA: pr.GetHead().GetSHA(), // Fail if the branch has changed out from under us.
MergeMethod: "squash",
},
)

if err != nil {
return errors.Wrapf(err, "Failed to merge Pull Request %s", pr.GetHTMLURL())
}
}

return nil
}

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

0 comments on commit 7d68022

Please sign in to comment.