mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 23:58:19 +00:00
Detects if master branch has diverged and warn to pull new commits in comment
This commit is contained in:
committed by
Luke Kysow
parent
54d677aed0
commit
a824ad4d23
@@ -228,14 +228,17 @@ var multiProjectApplyTmpl = template.Must(template.New("").Funcs(sprig.TxtFuncMa
|
||||
var planSuccessUnwrappedTmpl = template.Must(template.New("").Parse(
|
||||
"```diff\n" +
|
||||
"{{.TerraformOutput}}\n" +
|
||||
"```\n\n" + planNextSteps))
|
||||
"```\n\n" + planNextSteps +
|
||||
"{{ if .HasDiverged }}\n\n:warning: Master branch is ahead, it is recommended to pull new commits first.{{end}}"))
|
||||
|
||||
var planSuccessWrappedTmpl = template.Must(template.New("").Parse(
|
||||
"<details><summary>Show Output</summary>\n\n" +
|
||||
"```diff\n" +
|
||||
"{{.TerraformOutput}}\n" +
|
||||
"```\n\n" +
|
||||
planNextSteps + "\n" +
|
||||
"</details>"))
|
||||
"</details>" +
|
||||
"{{ if .HasDiverged }}\n\n:warning: Master branch is ahead, it is recommended to pull new commits first.{{end}}"))
|
||||
|
||||
// planNextSteps are instructions appended after successful plans as to what
|
||||
// to do next.
|
||||
|
||||
@@ -156,6 +156,42 @@ $$$
|
||||
* :repeat: To **plan** this project again, comment:
|
||||
* $atlantis plan -d path -w workspace$
|
||||
|
||||
---
|
||||
* :fast_forward: To **apply** all unapplied plans from this pull request, comment:
|
||||
* $atlantis apply$
|
||||
`,
|
||||
},
|
||||
{
|
||||
"single successful plan with master ahead",
|
||||
models.PlanCommand,
|
||||
[]models.ProjectResult{
|
||||
{
|
||||
PlanSuccess: &models.PlanSuccess{
|
||||
TerraformOutput: "terraform-output",
|
||||
LockURL: "lock-url",
|
||||
RePlanCmd: "atlantis plan -d path -w workspace",
|
||||
ApplyCmd: "atlantis apply -d path -w workspace",
|
||||
HasDiverged: true,
|
||||
},
|
||||
Workspace: "workspace",
|
||||
RepoRelDir: "path",
|
||||
},
|
||||
},
|
||||
models.Github,
|
||||
`Ran Plan for dir: $path$ workspace: $workspace$
|
||||
|
||||
$$$diff
|
||||
terraform-output
|
||||
$$$
|
||||
|
||||
* :arrow_forward: To **apply** this plan, comment:
|
||||
* $atlantis apply -d path -w workspace$
|
||||
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
|
||||
* :repeat: To **plan** this project again, comment:
|
||||
* $atlantis plan -d path -w workspace$
|
||||
|
||||
:warning: Master branch is ahead, it is recommended to pull new commits first.
|
||||
|
||||
---
|
||||
* :fast_forward: To **apply** all unapplied plans from this pull request, comment:
|
||||
* $atlantis apply$
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
logging "github.com/runatlantis/atlantis/server/logging"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MockWorkingDir struct {
|
||||
@@ -26,7 +27,7 @@ func NewMockWorkingDir(options ...pegomock.Option) *MockWorkingDir {
|
||||
func (mock *MockWorkingDir) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
|
||||
func (mock *MockWorkingDir) FailHandler() pegomock.FailHandler { return mock.fail }
|
||||
|
||||
func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, error) {
|
||||
func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, bool, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockWorkingDir().")
|
||||
}
|
||||
@@ -42,7 +43,7 @@ func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Rep
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
return ret0, false, ret1
|
||||
}
|
||||
|
||||
func (mock *MockWorkingDir) GetWorkingDir(r models.Repo, p models.PullRequest, workspace string) (string, error) {
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
logging "github.com/runatlantis/atlantis/server/logging"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MockWorkingDir struct {
|
||||
@@ -26,7 +27,7 @@ func NewMockWorkingDir(options ...pegomock.Option) *MockWorkingDir {
|
||||
func (mock *MockWorkingDir) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
|
||||
func (mock *MockWorkingDir) FailHandler() pegomock.FailHandler { return mock.fail }
|
||||
|
||||
func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, error) {
|
||||
func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, bool, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockWorkingDir().")
|
||||
}
|
||||
@@ -42,7 +43,7 @@ func (mock *MockWorkingDir) Clone(log *logging.SimpleLogger, baseRepo models.Rep
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
return ret0, false, ret1
|
||||
}
|
||||
|
||||
func (mock *MockWorkingDir) GetWorkingDir(r models.Repo, p models.PullRequest, workspace string) (string, error) {
|
||||
|
||||
@@ -431,6 +431,8 @@ type PlanSuccess struct {
|
||||
RePlanCmd string
|
||||
// ApplyCmd is the command that users should run to apply this plan.
|
||||
ApplyCmd string
|
||||
// Indicates if remote master has diverged
|
||||
HasDiverged bool
|
||||
}
|
||||
|
||||
// PullStatus is the current status of a pull request that is in progress.
|
||||
|
||||
@@ -113,7 +113,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext,
|
||||
}
|
||||
ctx.Log.Debug("%d files were modified in this pull request", len(modifiedFiles))
|
||||
|
||||
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, workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -176,7 +176,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, workspace)
|
||||
if err != nil {
|
||||
return pcc, err
|
||||
}
|
||||
|
||||
@@ -151,7 +151,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, hasDiverged, cloneErr := p.WorkingDir.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Workspace)
|
||||
if cloneErr != nil {
|
||||
if unlockErr := lockAttempt.UnlockFn(); unlockErr != nil {
|
||||
ctx.Log.Err("error unlocking state after plan error: %v", unlockErr)
|
||||
@@ -176,6 +176,7 @@ func (p *DefaultProjectCommandRunner) doPlan(ctx models.ProjectCommandContext) (
|
||||
TerraformOutput: strings.Join(outputs, "\n"),
|
||||
RePlanCmd: ctx.RePlanCmd,
|
||||
ApplyCmd: ctx.ApplyCmd,
|
||||
HasDiverged: hasDiverged,
|
||||
}, "", nil
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/runatlantis/atlantis/server/events/runtime"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/runatlantis/atlantis/server/events/runtime"
|
||||
|
||||
. "github.com/petergtz/pegomock"
|
||||
"github.com/runatlantis/atlantis/server/events"
|
||||
"github.com/runatlantis/atlantis/server/events/mocks"
|
||||
|
||||
@@ -34,8 +34,8 @@ const workingDirPrefix = "repos"
|
||||
// WorkingDir handles the workspace on disk for running commands.
|
||||
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)
|
||||
// absolute path to the root of the cloned repo as well as if master branch has diverged.
|
||||
Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, bool, 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)
|
||||
@@ -70,7 +70,7 @@ func (w *FileWorkspace) Clone(
|
||||
baseRepo models.Repo,
|
||||
headRepo models.Repo,
|
||||
p models.PullRequest,
|
||||
workspace string) (string, error) {
|
||||
workspace string) (string, bool, error) {
|
||||
cloneDir := w.cloneDir(baseRepo, p, workspace)
|
||||
|
||||
// If the directory already exists, check if it's at the right commit.
|
||||
@@ -89,17 +89,41 @@ func (w *FileWorkspace) Clone(
|
||||
}
|
||||
revParseCmd := exec.Command("git", "rev-parse", pullHead) // #nosec
|
||||
revParseCmd.Dir = cloneDir
|
||||
output, err := revParseCmd.CombinedOutput()
|
||||
outputRevParseCmd, err := revParseCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Warn("will re-clone repo, could not determine if was at correct commit: %s: %s: %s", strings.Join(revParseCmd.Args, " "), err, string(output))
|
||||
log.Warn("will re-clone repo, could not determine if was at correct commit: %s: %s: %s", strings.Join(revParseCmd.Args, " "), err, string(outputRevParseCmd))
|
||||
return w.forceClone(log, cloneDir, headRepo, p)
|
||||
}
|
||||
currCommit := strings.Trim(string(output), "\n")
|
||||
currCommit := strings.Trim(string(outputRevParseCmd), "\n")
|
||||
|
||||
// Bring our remote refs up to date.
|
||||
remoteUpdateCmd := exec.Command("git", "remote", "update")
|
||||
remoteUpdateCmd.Dir = cloneDir
|
||||
outputRemoteUpdate, err := remoteUpdateCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Warn("getting remote update failed: %s", string(outputRemoteUpdate))
|
||||
}
|
||||
|
||||
// Check if remote master branch has diverged.
|
||||
statusUnoCmd := exec.Command("git", "status", "--untracked-files=no")
|
||||
statusUnoCmd.Dir = cloneDir
|
||||
outputStatusUno, err := statusUnoCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Warn("getting repo status has failed: %s", string(outputStatusUno))
|
||||
}
|
||||
status := strings.Trim(string(outputStatusUno), "\n")
|
||||
hasDiverged := strings.Contains(status, "have diverged")
|
||||
if hasDiverged {
|
||||
log.Info("remote master branch is ahead and thereby has new commits, it is recommended to pull new commits")
|
||||
} else {
|
||||
log.Debug("remote master branch has no new commits")
|
||||
}
|
||||
|
||||
// We're prefix matching here because BitBucket doesn't give us the full
|
||||
// commit, only a 12 character prefix.
|
||||
if strings.HasPrefix(currCommit, p.HeadCommit) {
|
||||
log.Debug("repo is at correct commit %q so will not re-clone", p.HeadCommit)
|
||||
return cloneDir, nil
|
||||
return cloneDir, hasDiverged, nil
|
||||
}
|
||||
log.Debug("repo was already cloned but is not at correct commit, wanted %q got %q", p.HeadCommit, currCommit)
|
||||
// We'll fall through to re-clone.
|
||||
@@ -112,17 +136,17 @@ func (w *FileWorkspace) Clone(
|
||||
func (w *FileWorkspace) forceClone(log *logging.SimpleLogger,
|
||||
cloneDir string,
|
||||
headRepo models.Repo,
|
||||
p models.PullRequest) (string, error) {
|
||||
p models.PullRequest) (string, bool, error) {
|
||||
|
||||
err := os.RemoveAll(cloneDir)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "deleting dir %q before cloning", cloneDir)
|
||||
return "", false, errors.Wrapf(err, "deleting dir %q before cloning", cloneDir)
|
||||
}
|
||||
|
||||
// Create the directory and parents if necessary.
|
||||
log.Info("creating dir %q", cloneDir)
|
||||
if err := os.MkdirAll(cloneDir, 0700); err != nil {
|
||||
return "", errors.Wrap(err, "creating new workspace")
|
||||
return "", false, errors.Wrap(err, "creating new workspace")
|
||||
}
|
||||
|
||||
// During testing, we mock some of this out.
|
||||
@@ -184,11 +208,11 @@ func (w *FileWorkspace) forceClone(log *logging.SimpleLogger,
|
||||
sanitizedOutput := w.sanitizeGitCredentials(string(output), p.BaseRepo, headRepo)
|
||||
if err != nil {
|
||||
sanitizedErrMsg := w.sanitizeGitCredentials(err.Error(), p.BaseRepo, headRepo)
|
||||
return "", fmt.Errorf("running %s: %s: %s", cmdStr, sanitizedOutput, sanitizedErrMsg)
|
||||
return "", false, fmt.Errorf("running %s: %s: %s", cmdStr, sanitizedOutput, sanitizedErrMsg)
|
||||
}
|
||||
log.Debug("ran: %s. Output: %s", cmdStr, strings.TrimSuffix(sanitizedOutput, "\n"))
|
||||
}
|
||||
return cloneDir, nil
|
||||
return cloneDir, false, nil
|
||||
}
|
||||
|
||||
// GetWorkingDir returns the path to the workspace for this repo and pull.
|
||||
|
||||
@@ -27,11 +27,14 @@ func TestClone_NoneExisting(t *testing.T) {
|
||||
TestingOverrideHeadCloneURL: fmt.Sprintf("file://%s", repoDir),
|
||||
}
|
||||
|
||||
cloneDir, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
cloneDir, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Use rev-parse to verify at correct commit.
|
||||
actCommit := runCmd(t, cloneDir, "git", "rev-parse", "HEAD")
|
||||
Equals(t, expCommit, actCommit)
|
||||
@@ -76,12 +79,15 @@ func TestClone_CheckoutMergeNoneExisting(t *testing.T) {
|
||||
TestingOverrideBaseCloneURL: overrideURL,
|
||||
}
|
||||
|
||||
cloneDir, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
cloneDir, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
BaseBranch: "master",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Check the commits.
|
||||
actBaseCommit := runCmd(t, cloneDir, "git", "rev-parse", "HEAD~1")
|
||||
actHeadCommit := runCmd(t, cloneDir, "git", "rev-parse", "HEAD^2")
|
||||
@@ -123,22 +129,28 @@ func TestClone_CheckoutMergeNoReclone(t *testing.T) {
|
||||
TestingOverrideBaseCloneURL: overrideURL,
|
||||
}
|
||||
|
||||
_, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
_, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
BaseBranch: "master",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Create a file that we can use to check if the repo was recloned.
|
||||
runCmd(t, dataDir, "touch", "repos/0/default/proof")
|
||||
|
||||
// Now run the clone again.
|
||||
cloneDir, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
cloneDir, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
BaseBranch: "master",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Check that our proof file is still there, proving that we didn't reclone.
|
||||
_, err = os.Stat(filepath.Join(cloneDir, "proof"))
|
||||
Ok(t, err)
|
||||
@@ -169,22 +181,28 @@ func TestClone_CheckoutMergeNoRecloneFastForward(t *testing.T) {
|
||||
TestingOverrideBaseCloneURL: overrideURL,
|
||||
}
|
||||
|
||||
_, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
_, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
BaseBranch: "master",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Create a file that we can use to check if the repo was recloned.
|
||||
runCmd(t, dataDir, "touch", "repos/0/default/proof")
|
||||
|
||||
// Now run the clone again.
|
||||
cloneDir, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
cloneDir, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
BaseBranch: "master",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Check that our proof file is still there, proving that we didn't reclone.
|
||||
_, err = os.Stat(filepath.Join(cloneDir, "proof"))
|
||||
Ok(t, err)
|
||||
@@ -220,11 +238,14 @@ func TestClone_CheckoutMergeConflict(t *testing.T) {
|
||||
TestingOverrideBaseCloneURL: overrideURL,
|
||||
}
|
||||
|
||||
_, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
_, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
BaseBranch: "master",
|
||||
}, "default")
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
ErrContains(t, "running git merge -q --no-ff -m atlantis-merge FETCH_HEAD", err)
|
||||
ErrContains(t, "Auto-merging file", err)
|
||||
ErrContains(t, "CONFLICT (add/add)", err)
|
||||
@@ -250,11 +271,14 @@ func TestClone_NoReclone(t *testing.T) {
|
||||
CheckoutMerge: false,
|
||||
TestingOverrideHeadCloneURL: fmt.Sprintf("file://%s", repoDir),
|
||||
}
|
||||
cloneDir, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
cloneDir, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Check that our proof file is still there.
|
||||
_, err = os.Stat(filepath.Join(cloneDir, "proof"))
|
||||
Ok(t, err)
|
||||
@@ -284,17 +308,83 @@ func TestClone_RecloneWrongCommit(t *testing.T) {
|
||||
CheckoutMerge: false,
|
||||
TestingOverrideHeadCloneURL: fmt.Sprintf("file://%s", repoDir),
|
||||
}
|
||||
cloneDir, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
cloneDir, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "branch",
|
||||
HeadCommit: expCommit,
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo has not diverged
|
||||
Equals(t, hasDiverged, false)
|
||||
|
||||
// Use rev-parse to verify at correct commit.
|
||||
actCommit := runCmd(t, cloneDir, "git", "rev-parse", "HEAD")
|
||||
Equals(t, expCommit, actCommit)
|
||||
}
|
||||
|
||||
// Test that if master (remote) repo has diverged, see issue 804
|
||||
func TestClone_MasterHasDiverged(t *testing.T) {
|
||||
// Initialize the git repo.
|
||||
repoDir, cleanup := initRepo(t)
|
||||
defer cleanup()
|
||||
|
||||
// Simulate first PR.
|
||||
runCmd(t, repoDir, "git", "checkout", "-b", "first-pr")
|
||||
runCmd(t, repoDir, "touch", "file1")
|
||||
runCmd(t, repoDir, "git", "add", "file1")
|
||||
runCmd(t, repoDir, "git", "commit", "-m", "file1")
|
||||
|
||||
// Atlantis checkout first PR.
|
||||
firstPRDir := repoDir + "/first-pr"
|
||||
runCmd(t, repoDir, "mkdir", "-p", "first-pr")
|
||||
runCmd(t, firstPRDir, "git", "clone", "--branch", "master", "--single-branch", repoDir, ".")
|
||||
runCmd(t, firstPRDir, "git", "remote", "add", "head", repoDir)
|
||||
runCmd(t, firstPRDir, "git", "fetch", "head", "+refs/heads/first-pr")
|
||||
runCmd(t, firstPRDir, "git", "config", "--local", "user.email", "atlantisbot@runatlantis.io")
|
||||
runCmd(t, firstPRDir, "git", "config", "--local", "user.name", "atlantisbot")
|
||||
runCmd(t, firstPRDir, "git", "merge", "-q", "--no-ff", "-m", "atlantis-merge", "FETCH_HEAD")
|
||||
|
||||
// Simulate second PR.
|
||||
runCmd(t, repoDir, "git", "checkout", "master")
|
||||
runCmd(t, repoDir, "git", "checkout", "-b", "second-pr")
|
||||
runCmd(t, repoDir, "touch", "file2")
|
||||
runCmd(t, repoDir, "git", "add", "file2")
|
||||
runCmd(t, repoDir, "git", "commit", "-m", "file2")
|
||||
|
||||
// Atlantis checkout second PR.
|
||||
secondPRDir := repoDir + "/second-pr"
|
||||
runCmd(t, repoDir, "mkdir", "-p", "second-pr")
|
||||
runCmd(t, secondPRDir, "git", "clone", "--branch", "master", "--single-branch", repoDir, ".")
|
||||
runCmd(t, secondPRDir, "git", "remote", "add", "head", repoDir)
|
||||
runCmd(t, secondPRDir, "git", "fetch", "head", "+refs/heads/second-pr")
|
||||
runCmd(t, secondPRDir, "git", "config", "--local", "user.email", "atlantisbot@runatlantis.io")
|
||||
runCmd(t, secondPRDir, "git", "config", "--local", "user.name", "atlantisbot")
|
||||
runCmd(t, secondPRDir, "git", "merge", "-q", "--no-ff", "-m", "atlantis-merge", "FETCH_HEAD")
|
||||
|
||||
// Merge first PR
|
||||
runCmd(t, repoDir, "git", "checkout", "master")
|
||||
runCmd(t, repoDir, "git", "merge", "first-pr")
|
||||
|
||||
// Copy the second-pr repo to our data dir which has diverged remote master
|
||||
runCmd(t, repoDir, "mkdir", "-p", "repos/0/")
|
||||
runCmd(t, repoDir, "cp", "-R", secondPRDir, "repos/0/default")
|
||||
|
||||
// Run the clone.
|
||||
wd := &events.FileWorkspace{
|
||||
DataDir: repoDir,
|
||||
CheckoutMerge: true,
|
||||
}
|
||||
|
||||
_, hasDiverged, err := wd.Clone(nil, models.Repo{}, models.Repo{}, models.PullRequest{
|
||||
HeadBranch: "second-pr",
|
||||
BaseBranch: "master",
|
||||
}, "default")
|
||||
Ok(t, err)
|
||||
|
||||
// Check if master repo is ahead and has diverged
|
||||
Equals(t, hasDiverged, true)
|
||||
}
|
||||
|
||||
func initRepo(t *testing.T) (string, func()) {
|
||||
repoDir, cleanup := TempDir(t)
|
||||
runCmd(t, repoDir, "git", "init")
|
||||
|
||||
Reference in New Issue
Block a user