Skip to content

Commit

Permalink
add flag for rebasing branch off master
Browse files Browse the repository at this point in the history
  • Loading branch information
sstarcher committed Dec 5, 2018
1 parent 284fa2e commit 42fdc6a
Show file tree
Hide file tree
Showing 11 changed files with 54 additions and 31 deletions.
5 changes: 3 additions & 2 deletions server/events/command_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type CommandContext struct {
HeadRepo models.Repo
Pull models.PullRequest
// User is the user that triggered this command.
User models.User
Log *logging.SimpleLogger
User models.User
Log *logging.SimpleLogger
RebaseRepo bool
}
23 changes: 13 additions & 10 deletions server/events/command_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,19 @@ type DefaultCommandRunner struct {
AllowForkPRsFlag string
ProjectCommandBuilder ProjectCommandBuilder
ProjectCommandRunner ProjectCommandRunner
RebaseRepo bool
}

// RunAutoplanCommand runs plan when a pull request is opened or updated.
func (c *DefaultCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo models.Repo, pull models.PullRequest, user models.User) {
log := c.buildLogger(baseRepo.FullName, pull.Num)
ctx := &CommandContext{
User: user,
Log: log,
Pull: pull,
HeadRepo: headRepo,
BaseRepo: baseRepo,
User: user,
Log: log,
Pull: pull,
HeadRepo: headRepo,
BaseRepo: baseRepo,
RebaseRepo: c.RebaseRepo,
}
defer c.logPanics(ctx)
if !c.validateCtxAndComment(ctx) {
Expand Down Expand Up @@ -138,11 +140,12 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead
return
}
ctx := &CommandContext{
User: user,
Log: log,
Pull: pull,
HeadRepo: headRepo,
BaseRepo: baseRepo,
User: user,
Log: log,
Pull: pull,
HeadRepo: headRepo,
BaseRepo: baseRepo,
RebaseRepo: c.RebaseRepo,
}
defer c.logPanics(ctx)
if !c.validateCtxAndComment(ctx) {
Expand Down
4 changes: 2 additions & 2 deletions server/events/mocks/mock_working_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ func NewMockWorkingDir() *MockWorkingDir {
return &MockWorkingDir{fail: pegomock.GlobalFailHandler}
}

func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, error) {
params := []pegomock.Param{log, baseRepo, headRepo, p, workspace}
func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, rebase bool, workspace string) (string, error) {
params := []pegomock.Param{log, baseRepo, headRepo, p, rebase, workspace}
result := pegomock.GetGenericMockFrom(mock).Invoke("Clone", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 string
var ret1 error
Expand Down
2 changes: 2 additions & 0 deletions server/events/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ type ProjectCommandContext struct {
// ApplyCmd is the command that users should run to apply this plan. If
// this is an apply then this will be empty.
ApplyCmd string

RebaseRepo bool
}

// SplitRepoFullName splits a repo full name up into its owner and repo name
Expand Down
4 changes: 2 additions & 2 deletions server/events/project_command_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext,
ctx.Log.Debug("got workspace lock")
defer unlockFn()

repoDir, err := p.WorkingDir.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, workspace)
repoDir, err := p.WorkingDir.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.RebaseRepo, workspace)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -193,7 +193,7 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *CommandConte
defer unlockFn()

ctx.Log.Debug("cloning repository")
repoDir, err := p.WorkingDir.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, workspace)
repoDir, err := p.WorkingDir.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.RebaseRepo, workspace)
if err != nil {
return pcc, err
}
Expand Down
9 changes: 7 additions & 2 deletions server/events/project_command_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ projects:
pull := models.PullRequest{}
logger := logging.NewNoopLogger()
workingDir := mocks.NewMockWorkingDir()
When(workingDir.Clone(logger, baseRepo, headRepo, pull, "default")).ThenReturn(tmpDir, nil)
When(workingDir.Clone(logger, baseRepo, headRepo, pull, false, "default")).ThenReturn(tmpDir, nil)
if c.AtlantisYAML != "" {
err := ioutil.WriteFile(filepath.Join(tmpDir, yaml.AtlantisYAMLFilename), []byte(c.AtlantisYAML), 0600)
Ok(t, err)
Expand Down Expand Up @@ -405,7 +405,7 @@ projects:
expWorkspace = "default"
}
if cmdName == events.PlanCommand {
When(workingDir.Clone(logger, baseRepo, headRepo, pull, expWorkspace)).ThenReturn(tmpDir, nil)
When(workingDir.Clone(logger, baseRepo, headRepo, pull, false, expWorkspace)).ThenReturn(tmpDir, nil)
} else {
When(workingDir.GetWorkingDir(baseRepo, pull, expWorkspace)).ThenReturn(tmpDir, nil)
}
Expand Down Expand Up @@ -487,6 +487,7 @@ func TestDefaultProjectCommandBuilder_BuildMultiPlanNoAtlantisYAML(t *testing.T)
matchers.AnyModelsRepo(),
matchers.AnyModelsRepo(),
matchers.AnyModelsPullRequest(),
AnyBool(),
AnyString())).ThenReturn(tmpDir, nil)
vcsClient := vcsmocks.NewMockClientProxy()
When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"project1/main.tf", "project2/main.tf"}, nil)
Expand Down Expand Up @@ -540,6 +541,7 @@ func TestDefaultProjectCommandBuilder_BuildMultiPlanNoAtlantisYAMLNoModified(t *
matchers.AnyModelsRepo(),
matchers.AnyModelsRepo(),
matchers.AnyModelsPullRequest(),
AnyBool(),
AnyString())).ThenReturn(tmpDir, nil)
vcsClient := vcsmocks.NewMockClientProxy()
When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{}, nil)
Expand Down Expand Up @@ -610,6 +612,7 @@ projects:
matchers.AnyModelsRepo(),
matchers.AnyModelsRepo(),
matchers.AnyModelsPullRequest(),
AnyBool(),
AnyString())).ThenReturn(tmpDir, nil)
vcsClient := vcsmocks.NewMockClientProxy()
When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{
Expand Down Expand Up @@ -674,6 +677,7 @@ projects:
matchers.AnyModelsRepo(),
matchers.AnyModelsRepo(),
matchers.AnyModelsPullRequest(),
AnyBool(),
AnyString())).ThenReturn(tmpDir, nil)
vcsClient := vcsmocks.NewMockClientProxy()
When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"main.tf"}, nil)
Expand Down Expand Up @@ -809,6 +813,7 @@ func TestDefaultProjectCommandBuilder_RepoConfigDisabled(t *testing.T) {
matchers.AnyModelsRepo(),
matchers.AnyModelsRepo(),
matchers.AnyModelsPullRequest(),
AnyBool(),
AnyString())).ThenReturn(repoDir, nil)
When(workingDir.GetWorkingDir(
matchers.AnyModelsRepo(),
Expand Down
2 changes: 1 addition & 1 deletion server/events/project_command_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (p *DefaultProjectCommandRunner) doPlan(ctx models.ProjectCommandContext) (
defer unlockFn()

// Clone is idempotent so okay to run even if the repo was already cloned.
repoDir, cloneErr := p.WorkingDir.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Workspace)
repoDir, cloneErr := p.WorkingDir.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.RebaseRepo, ctx.Workspace)
if cloneErr != nil {
if unlockErr := lockAttempt.UnlockFn(); unlockErr != nil {
ctx.Log.Err("error unlocking state after plan error: %v", unlockErr)
Expand Down
1 change: 1 addition & 0 deletions server/events/project_command_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ func TestDefaultProjectCommandRunner_Plan(t *testing.T) {
matchers.AnyModelsRepo(),
matchers.AnyModelsRepo(),
matchers.AnyModelsPullRequest(),
AnyBool(),
AnyString(),
)).ThenReturn(repoDir, nil)
When(mockLocker.TryLock(
Expand Down
12 changes: 4 additions & 8 deletions server/events/vcs/github_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,12 @@ func (g *GithubClient) CreateComment(repo models.Repo, pullNum int, comment stri

// PullIsApproved returns true if the pull request was approved.
func (g *GithubClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
reviews, _, err := g.client.PullRequests.ListReviews(g.ctx, repo.Owner, repo.Name, pull.Num, nil)
request, err := g.GetPullRequest(repo, pull.Num)
if err != nil {
return false, errors.Wrap(err, "getting reviews")
return false, err
}
for _, review := range reviews {
if review != nil && review.GetState() == "APPROVED" {
return true, nil
}
}
return false, nil

return *request.Mergeable, nil
}

// GetPullRequest returns the pull request.
Expand Down
21 changes: 17 additions & 4 deletions server/events/working_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const workingDirPrefix = "repos"
type WorkingDir interface {
// Clone git clones headRepo, checks out the branch and then returns the
// absolute path to the root of the cloned repo.
Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, error)
Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, rebase bool, workspace string) (string, error)
// GetWorkingDir returns the path to the workspace for this repo and pull.
// If workspace does not exist on disk, error will be of type os.IsNotExist.
GetWorkingDir(r models.Repo, p models.PullRequest, workspace string) (string, error)
Expand All @@ -60,6 +60,7 @@ func (w *FileWorkspace) Clone(
baseRepo models.Repo,
headRepo models.Repo,
p models.PullRequest,
rebase bool,
workspace string) (string, error) {
cloneDir := w.cloneDir(baseRepo, p, workspace)

Expand All @@ -72,7 +73,7 @@ func (w *FileWorkspace) Clone(
output, err := revParseCmd.CombinedOutput()
if err != nil {
log.Err("will re-clone repo, could not determine if was at correct commit: git rev-parse HEAD: %s: %s", err, string(output))
return w.forceClone(log, cloneDir, headRepo, p)
return w.forceClone(log, cloneDir, headRepo, p, rebase)
}
currCommit := strings.Trim(string(output), "\n")
// We're prefix matching here because BitBucket doesn't give us the full
Expand All @@ -86,13 +87,14 @@ func (w *FileWorkspace) Clone(
}

// Otherwise we clone the repo.
return w.forceClone(log, cloneDir, headRepo, p)
return w.forceClone(log, cloneDir, headRepo, p, rebase)
}

func (w *FileWorkspace) forceClone(log *logging.SimpleLogger,
cloneDir string,
headRepo models.Repo,
p models.PullRequest) (string, error) {
p models.PullRequest,
rebase bool) (string, error) {

err := os.RemoveAll(cloneDir)
if err != nil {
Expand Down Expand Up @@ -122,6 +124,17 @@ func (w *FileWorkspace) forceClone(log *logging.SimpleLogger,
if err := checkoutCmd.Run(); err != nil {
return "", errors.Wrapf(err, "checking out branch %s", p.Branch)
}

if rebase {
// Rebase branch
log.Info("rebase branch onto master")
rebaseCmd := exec.Command("git", "rebase", "origin/master") // #nosec
rebaseCmd.Dir = cloneDir
if err := rebaseCmd.Run(); err != nil {
return "", errors.Wrapf(err, "unable to rebase %s onto master", p.Branch)
}
}

return cloneDir, nil
}

Expand Down
2 changes: 2 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type UserConfig struct {
GitlabWebhookSecret string `mapstructure:"gitlab-webhook-secret"`
LogLevel string `mapstructure:"log-level"`
Port int `mapstructure:"port"`
RebaseRepo bool `mapstructure:"rebase-repo"`
RepoWhitelist string `mapstructure:"repo-whitelist"`
// RequireApproval is whether to require pull request approval before
// allowing terraform apply's to be run.
Expand Down Expand Up @@ -263,6 +264,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
Logger: logger,
AllowForkPRs: userConfig.AllowForkPRs,
AllowForkPRsFlag: config.AllowForkPRsFlag,
RebaseRepo: userConfig.RebaseRepo,
ProjectCommandBuilder: &events.DefaultProjectCommandBuilder{
ParserValidator: &yaml.ParserValidator{},
ProjectFinder: &events.DefaultProjectFinder{},
Expand Down

0 comments on commit 42fdc6a

Please sign in to comment.