github.com/alloyci/alloy-runner@v1.0.1-0.20180222164613-925503ccafd6/executors/shell/executor_shell_test.go (about)

     1  package shell_test
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  
    15  	"gitlab.com/gitlab-org/gitlab-runner/common"
    16  	"gitlab.com/gitlab-org/gitlab-runner/helpers"
    17  )
    18  
    19  func gitInDir(dir string, args ...string) ([]byte, error) {
    20  	cmd := exec.Command("git", args...)
    21  	cmd.Dir = dir
    22  
    23  	return cmd.Output()
    24  }
    25  
    26  func skipOnGit17x(t *testing.T) bool {
    27  	out, err := gitInDir("", "version")
    28  	if err != nil {
    29  		t.Fatal("Can't detect git version", err)
    30  		return true
    31  	}
    32  
    33  	version := string(out)
    34  	if strings.Contains(version, "git version 1.7.") {
    35  		t.Skip("Git 1.7.x detected", version)
    36  		return true
    37  	}
    38  
    39  	return false
    40  }
    41  
    42  func onEachShell(t *testing.T, f func(t *testing.T, shell string)) {
    43  	t.Run("bash", func(t *testing.T) {
    44  		if helpers.SkipIntegrationTests(t, "bash") {
    45  			t.Skip()
    46  		}
    47  
    48  		f(t, "bash")
    49  	})
    50  
    51  	t.Run("cmd.exe", func(t *testing.T) {
    52  		if helpers.SkipIntegrationTests(t, "cmd.exe") {
    53  			t.Skip()
    54  		}
    55  
    56  		f(t, "cmd")
    57  	})
    58  
    59  	t.Run("powershell.exe", func(t *testing.T) {
    60  		if helpers.SkipIntegrationTests(t, "powershell.exe") {
    61  			t.Skip()
    62  		}
    63  
    64  		f(t, "powershell")
    65  	})
    66  }
    67  
    68  func runBuildWithTrace(t *testing.T, build *common.Build, trace *common.Trace) error {
    69  	timeoutTimer := time.AfterFunc(10*time.Second, func() {
    70  		t.Log("Timed out")
    71  		t.FailNow()
    72  	})
    73  	defer timeoutTimer.Stop()
    74  
    75  	return build.Run(&common.Config{}, trace)
    76  }
    77  
    78  func runBuild(t *testing.T, build *common.Build) error {
    79  	err := runBuildWithTrace(t, build, &common.Trace{Writer: os.Stdout})
    80  	assert.True(t, build.IsSharedEnv())
    81  	return err
    82  }
    83  
    84  func runBuildReturningOutput(t *testing.T, build *common.Build) (string, error) {
    85  	buf := bytes.NewBuffer(nil)
    86  	err := runBuildWithTrace(t, build, &common.Trace{Writer: buf})
    87  	output := buf.String()
    88  	t.Log(output)
    89  
    90  	return output, err
    91  }
    92  
    93  func newBuild(t *testing.T, getBuildResponse common.JobResponse, shell string) (*common.Build, func()) {
    94  	dir, err := ioutil.TempDir("", "gitlab-runner-shell-executor-test")
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	t.Log("Build directory:", dir)
   100  
   101  	build := &common.Build{
   102  		JobResponse: getBuildResponse,
   103  		Runner: &common.RunnerConfig{
   104  			RunnerSettings: common.RunnerSettings{
   105  				BuildsDir: dir,
   106  				Executor:  "shell",
   107  				Shell:     shell,
   108  			},
   109  		},
   110  		SystemInterrupt: make(chan os.Signal, 1),
   111  	}
   112  
   113  	cleanup := func() {
   114  		os.RemoveAll(dir)
   115  	}
   116  
   117  	return build, cleanup
   118  }
   119  
   120  func TestBuildSuccess(t *testing.T) {
   121  	onEachShell(t, func(t *testing.T, shell string) {
   122  		successfulBuild, err := common.GetSuccessfulBuild()
   123  		assert.NoError(t, err)
   124  		build, cleanup := newBuild(t, successfulBuild, shell)
   125  		defer cleanup()
   126  
   127  		err = runBuild(t, build)
   128  		assert.NoError(t, err)
   129  	})
   130  }
   131  
   132  func TestBuildAbort(t *testing.T) {
   133  	onEachShell(t, func(t *testing.T, shell string) {
   134  		longRunningBuild, err := common.GetLongRunningBuild()
   135  		assert.NoError(t, err)
   136  		build, cleanup := newBuild(t, longRunningBuild, shell)
   137  		defer cleanup()
   138  
   139  		abortTimer := time.AfterFunc(time.Second, func() {
   140  			t.Log("Interrupt")
   141  			build.SystemInterrupt <- os.Interrupt
   142  		})
   143  		defer abortTimer.Stop()
   144  
   145  		err = runBuild(t, build)
   146  		assert.EqualError(t, err, "aborted: interrupt")
   147  	})
   148  }
   149  
   150  func TestBuildCancel(t *testing.T) {
   151  	onEachShell(t, func(t *testing.T, shell string) {
   152  		longRunningBuild, err := common.GetLongRunningBuild()
   153  		assert.NoError(t, err)
   154  		build, cleanup := newBuild(t, longRunningBuild, shell)
   155  		defer cleanup()
   156  
   157  		trace := &common.Trace{Writer: os.Stdout}
   158  
   159  		cancelTimer := time.AfterFunc(time.Second, func() {
   160  			t.Log("Cancel")
   161  			trace.CancelFunc()
   162  		})
   163  		defer cancelTimer.Stop()
   164  
   165  		err = runBuildWithTrace(t, build, trace)
   166  		assert.EqualError(t, err, "canceled")
   167  		assert.IsType(t, err, &common.BuildError{})
   168  	})
   169  }
   170  
   171  func TestBuildWithIndexLock(t *testing.T) {
   172  	onEachShell(t, func(t *testing.T, shell string) {
   173  		successfulBuild, err := common.GetSuccessfulBuild()
   174  		assert.NoError(t, err)
   175  		build, cleanup := newBuild(t, successfulBuild, shell)
   176  		defer cleanup()
   177  
   178  		err = runBuild(t, build)
   179  		assert.NoError(t, err)
   180  
   181  		build.JobResponse.AllowGitFetch = true
   182  		ioutil.WriteFile(build.BuildDir+"/.git/index.lock", []byte{}, os.ModeSticky)
   183  
   184  		err = runBuild(t, build)
   185  		assert.NoError(t, err)
   186  	})
   187  }
   188  
   189  func TestBuildWithShallowLock(t *testing.T) {
   190  	onEachShell(t, func(t *testing.T, shell string) {
   191  		successfulBuild, err := common.GetSuccessfulBuild()
   192  		assert.NoError(t, err)
   193  		build, cleanup := newBuild(t, successfulBuild, shell)
   194  		defer cleanup()
   195  
   196  		build.Variables = append(build.Variables, []common.JobVariable{
   197  			common.JobVariable{Key: "GIT_DEPTH", Value: "1"},
   198  			common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"}}...)
   199  
   200  		err = runBuild(t, build)
   201  		assert.NoError(t, err)
   202  
   203  		ioutil.WriteFile(build.BuildDir+"/.git/shallow.lock", []byte{}, os.ModeSticky)
   204  
   205  		err = runBuild(t, build)
   206  		assert.NoError(t, err)
   207  	})
   208  }
   209  
   210  func TestBuildPullReferences(t *testing.T) {
   211  	onEachShell(t, func(t *testing.T, shell string) {
   212  		successfulBuild, err := common.GetSuccessfulBuild()
   213  		assert.NoError(t, err)
   214  		build, cleanup := newBuild(t, successfulBuild, shell)
   215  		defer cleanup()
   216  
   217  		build.Runner.PreCloneScript = "echo pre-clone-script"
   218  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   219  		build.Variables = append(build.Variables, common.JobVariable{Key: "CI_DEBUG_TRACE", Value: "true"})
   220  
   221  		out, err := runBuildReturningOutput(t, build)
   222  		assert.NoError(t, err)
   223  		assert.Contains(t, out, "Cloning repository")
   224  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   225  
   226  		out, err = runBuildReturningOutput(t, build)
   227  		assert.NoError(t, err)
   228  		assert.Contains(t, out, "Fetching changes")
   229  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   230  		assert.Contains(t, out, "pre-clone-script")
   231  		assert.Contains(t, out, "git fetch origin --prune '+refs/heads/*:refs/remotes/origin/*' '+refs/tags/*:refs/tags/*' '+refs/pull/*:refs/remotes/origin/*'")
   232  	})
   233  }
   234  
   235  func TestBuildWithHeadLock(t *testing.T) {
   236  	onEachShell(t, func(t *testing.T, shell string) {
   237  		successfulBuild, err := common.GetSuccessfulBuild()
   238  		assert.NoError(t, err)
   239  		build, cleanup := newBuild(t, successfulBuild, shell)
   240  		defer cleanup()
   241  
   242  		err = runBuild(t, build)
   243  		assert.NoError(t, err)
   244  
   245  		build.JobResponse.AllowGitFetch = true
   246  		ioutil.WriteFile(build.BuildDir+"/.git/HEAD.lock", []byte{}, os.ModeSticky)
   247  
   248  		err = runBuild(t, build)
   249  		assert.NoError(t, err)
   250  	})
   251  }
   252  
   253  func TestBuildWithGitLFSHook(t *testing.T) {
   254  	onEachShell(t, func(t *testing.T, shell string) {
   255  		successfulBuild, err := common.GetSuccessfulBuild()
   256  		assert.NoError(t, err)
   257  		build, cleanup := newBuild(t, successfulBuild, shell)
   258  		defer cleanup()
   259  
   260  		err = runBuild(t, build)
   261  		assert.NoError(t, err)
   262  
   263  		gitLFSPostCheckoutHook := "#!/bin/sh\necho 'running git lfs hook' >&2\nexit 2\n"
   264  
   265  		os.MkdirAll(build.BuildDir+"/.git/hooks/", 0755)
   266  		ioutil.WriteFile(build.BuildDir+"/.git/hooks/post-checkout", []byte(gitLFSPostCheckoutHook), 0777)
   267  		build.JobResponse.AllowGitFetch = true
   268  
   269  		err = runBuild(t, build)
   270  		assert.NoError(t, err)
   271  	})
   272  }
   273  
   274  func TestBuildWithGitStrategyNone(t *testing.T) {
   275  	onEachShell(t, func(t *testing.T, shell string) {
   276  		successfulBuild, err := common.GetSuccessfulBuild()
   277  		assert.NoError(t, err)
   278  		build, cleanup := newBuild(t, successfulBuild, shell)
   279  		defer cleanup()
   280  
   281  		build.Runner.PreCloneScript = "echo pre-clone-script"
   282  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   283  
   284  		out, err := runBuildReturningOutput(t, build)
   285  		assert.NoError(t, err)
   286  		assert.NotContains(t, out, "pre-clone-script")
   287  		assert.NotContains(t, out, "Cloning repository")
   288  		assert.NotContains(t, out, "Fetching changes")
   289  		assert.Contains(t, out, "Skipping Git repository setup")
   290  	})
   291  }
   292  
   293  func TestBuildWithGitStrategyFetch(t *testing.T) {
   294  	onEachShell(t, func(t *testing.T, shell string) {
   295  		successfulBuild, err := common.GetSuccessfulBuild()
   296  		assert.NoError(t, err)
   297  		build, cleanup := newBuild(t, successfulBuild, shell)
   298  		defer cleanup()
   299  
   300  		build.Runner.PreCloneScript = "echo pre-clone-script"
   301  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   302  
   303  		out, err := runBuildReturningOutput(t, build)
   304  		assert.NoError(t, err)
   305  		assert.Contains(t, out, "Cloning repository")
   306  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   307  
   308  		out, err = runBuildReturningOutput(t, build)
   309  		assert.NoError(t, err)
   310  		assert.Contains(t, out, "Fetching changes")
   311  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   312  		assert.Contains(t, out, "pre-clone-script")
   313  	})
   314  }
   315  
   316  func TestBuildWithGitStrategyFetchNoCheckout(t *testing.T) {
   317  	onEachShell(t, func(t *testing.T, shell string) {
   318  		successfulBuild, err := common.GetSuccessfulBuild()
   319  		assert.NoError(t, err)
   320  		build, cleanup := newBuild(t, successfulBuild, shell)
   321  		defer cleanup()
   322  
   323  		build.Runner.PreCloneScript = "echo pre-clone-script"
   324  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   325  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   326  
   327  		out, err := runBuildReturningOutput(t, build)
   328  		assert.NoError(t, err)
   329  		assert.Contains(t, out, "Cloning repository")
   330  		assert.Contains(t, out, "Skippping Git checkout")
   331  
   332  		out, err = runBuildReturningOutput(t, build)
   333  		assert.NoError(t, err)
   334  		assert.Contains(t, out, "Fetching changes")
   335  		assert.Contains(t, out, "Skippping Git checkout")
   336  		assert.Contains(t, out, "pre-clone-script")
   337  	})
   338  }
   339  
   340  func TestBuildWithGitStrategyClone(t *testing.T) {
   341  	onEachShell(t, func(t *testing.T, shell string) {
   342  		successfulBuild, err := common.GetSuccessfulBuild()
   343  		assert.NoError(t, err)
   344  		build, cleanup := newBuild(t, successfulBuild, shell)
   345  		defer cleanup()
   346  
   347  		build.Runner.PreCloneScript = "echo pre-clone-script"
   348  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   349  
   350  		out, err := runBuildReturningOutput(t, build)
   351  		assert.NoError(t, err)
   352  		assert.Contains(t, out, "Cloning repository")
   353  
   354  		out, err = runBuildReturningOutput(t, build)
   355  		assert.NoError(t, err)
   356  		assert.Contains(t, out, "Cloning repository")
   357  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   358  		assert.Contains(t, out, "pre-clone-script")
   359  	})
   360  }
   361  
   362  func TestBuildWithGitStrategyCloneNoCheckout(t *testing.T) {
   363  	onEachShell(t, func(t *testing.T, shell string) {
   364  		successfulBuild, err := common.GetSuccessfulBuild()
   365  		assert.NoError(t, err)
   366  		build, cleanup := newBuild(t, successfulBuild, shell)
   367  		defer cleanup()
   368  
   369  		build.Runner.PreCloneScript = "echo pre-clone-script"
   370  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   371  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   372  
   373  		out, err := runBuildReturningOutput(t, build)
   374  		assert.NoError(t, err)
   375  		assert.Contains(t, out, "Cloning repository")
   376  
   377  		out, err = runBuildReturningOutput(t, build)
   378  		assert.NoError(t, err)
   379  		assert.Contains(t, out, "Cloning repository")
   380  		assert.Contains(t, out, "Skippping Git checkout")
   381  		assert.Contains(t, out, "pre-clone-script")
   382  	})
   383  }
   384  
   385  func TestBuildWithGitSubmoduleStrategyNone(t *testing.T) {
   386  	for _, strategy := range []string{"none", ""} {
   387  		t.Run("strategy "+strategy, func(t *testing.T) {
   388  			onEachShell(t, func(t *testing.T, shell string) {
   389  				successfulBuild, err := common.GetSuccessfulBuild()
   390  				assert.NoError(t, err)
   391  				build, cleanup := newBuild(t, successfulBuild, shell)
   392  				defer cleanup()
   393  
   394  				build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "none"})
   395  
   396  				out, err := runBuildReturningOutput(t, build)
   397  				assert.NoError(t, err)
   398  				assert.Contains(t, out, "Skipping Git submodules setup")
   399  				assert.NotContains(t, out, "Updating/initializing submodules...")
   400  				assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   401  
   402  				_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   403  				assert.Error(t, err, "Submodule not should have been initialized")
   404  
   405  				_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   406  				assert.Error(t, err, "The submodule's submodule should not have been initialized")
   407  			})
   408  		})
   409  	}
   410  }
   411  
   412  func TestBuildWithGitSubmoduleStrategyNormal(t *testing.T) {
   413  	onEachShell(t, func(t *testing.T, shell string) {
   414  		successfulBuild, err := common.GetSuccessfulBuild()
   415  		assert.NoError(t, err)
   416  		build, cleanup := newBuild(t, successfulBuild, shell)
   417  		defer cleanup()
   418  
   419  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   420  
   421  		out, err := runBuildReturningOutput(t, build)
   422  		assert.NoError(t, err)
   423  		assert.NotContains(t, out, "Skipping Git submodules setup")
   424  		assert.Contains(t, out, "Updating/initializing submodules...")
   425  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   426  
   427  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   428  		assert.NoError(t, err, "Submodule should have been initialized")
   429  
   430  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   431  		assert.Error(t, err, "The submodule's submodule should not have been initialized")
   432  	})
   433  }
   434  
   435  func TestBuildWithGitSubmoduleStrategyRecursive(t *testing.T) {
   436  	if skipOnGit17x(t) {
   437  		return
   438  	}
   439  
   440  	onEachShell(t, func(t *testing.T, shell string) {
   441  		successfulBuild, err := common.GetSuccessfulBuild()
   442  		assert.NoError(t, err)
   443  		build, cleanup := newBuild(t, successfulBuild, shell)
   444  		defer cleanup()
   445  
   446  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "recursive"})
   447  
   448  		out, err := runBuildReturningOutput(t, build)
   449  		assert.NoError(t, err)
   450  		assert.NotContains(t, out, "Skipping Git submodules setup")
   451  		assert.NotContains(t, out, "Updating/initializing submodules...")
   452  		assert.Contains(t, out, "Updating/initializing submodules recursively...")
   453  
   454  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   455  		assert.NoError(t, err, "Submodule should have been initialized")
   456  
   457  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   458  		assert.NoError(t, err, "The submodule's submodule should have been initialized")
   459  	})
   460  }
   461  
   462  func TestBuildWithGitSubmoduleStrategyInvalid(t *testing.T) {
   463  	onEachShell(t, func(t *testing.T, shell string) {
   464  		successfulBuild, err := common.GetSuccessfulBuild()
   465  		assert.NoError(t, err)
   466  		build, cleanup := newBuild(t, successfulBuild, shell)
   467  		defer cleanup()
   468  
   469  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "invalid"})
   470  
   471  		out, err := runBuildReturningOutput(t, build)
   472  		assert.EqualError(t, err, "unknown GIT_SUBMODULE_STRATEGY")
   473  		assert.NotContains(t, out, "Skipping Git submodules setup")
   474  		assert.NotContains(t, out, "Updating/initializing submodules...")
   475  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   476  	})
   477  }
   478  
   479  func TestBuildWithGitSubmoduleStrategyRecursiveAndGitStrategyNone(t *testing.T) {
   480  	onEachShell(t, func(t *testing.T, shell string) {
   481  		successfulBuild, err := common.GetSuccessfulBuild()
   482  		assert.NoError(t, err)
   483  		build, cleanup := newBuild(t, successfulBuild, shell)
   484  		defer cleanup()
   485  
   486  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   487  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "recursive"})
   488  
   489  		out, err := runBuildReturningOutput(t, build)
   490  		assert.NoError(t, err)
   491  		assert.NotContains(t, out, "Cloning repository")
   492  		assert.NotContains(t, out, "Fetching changes")
   493  		assert.Contains(t, out, "Skipping Git repository setup")
   494  		assert.NotContains(t, out, "Updating/initializing submodules...")
   495  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   496  		assert.Contains(t, out, "Skipping Git submodules setup")
   497  	})
   498  }
   499  
   500  func TestBuildWithGitSubmoduleModified(t *testing.T) {
   501  	onEachShell(t, func(t *testing.T, shell string) {
   502  		successfulBuild, err := common.GetSuccessfulBuild()
   503  		assert.NoError(t, err)
   504  		build, cleanup := newBuild(t, successfulBuild, shell)
   505  		defer cleanup()
   506  
   507  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   508  
   509  		out, err := runBuildReturningOutput(t, build)
   510  		assert.NoError(t, err)
   511  		assert.Contains(t, out, "Updating/initializing submodules...")
   512  
   513  		submoduleDir := filepath.Join(build.BuildDir, "gitlab-grack")
   514  		submoduleReadme := filepath.Join(submoduleDir, "README.md")
   515  
   516  		// modify submodule and commit
   517  		modifySubmoduleBeforeCommit := "commited change"
   518  		ioutil.WriteFile(submoduleReadme, []byte(modifySubmoduleBeforeCommit), os.ModeSticky)
   519  		_, err = gitInDir(submoduleDir, "add", "README.md")
   520  		assert.NoError(t, err)
   521  		_, err = gitInDir(submoduleDir, "config", "user.name", "test")
   522  		assert.NoError(t, err)
   523  		_, err = gitInDir(submoduleDir, "config", "user.email", "test@example.org")
   524  		assert.NoError(t, err)
   525  		_, err = gitInDir(submoduleDir, "commit", "-m", "modify submodule")
   526  		assert.NoError(t, err)
   527  
   528  		_, err = gitInDir(build.BuildDir, "add", "gitlab-grack")
   529  		assert.NoError(t, err)
   530  		_, err = gitInDir(build.BuildDir, "config", "user.name", "test")
   531  		assert.NoError(t, err)
   532  		_, err = gitInDir(build.BuildDir, "config", "user.email", "test@example.org")
   533  		assert.NoError(t, err)
   534  		_, err = gitInDir(build.BuildDir, "commit", "-m", "modify submodule")
   535  		assert.NoError(t, err)
   536  
   537  		// modify submodule without commit before second build
   538  		modifySubmoduleAfterCommit := "not commited change"
   539  		ioutil.WriteFile(submoduleReadme, []byte(modifySubmoduleAfterCommit), os.ModeSticky)
   540  
   541  		build.JobResponse.AllowGitFetch = true
   542  		out, err = runBuildReturningOutput(t, build)
   543  		assert.NoError(t, err)
   544  		assert.NotContains(t, out, "Your local changes to the following files would be overwritten by checkout")
   545  		assert.NotContains(t, out, "Please commit your changes or stash them before you switch branches")
   546  		assert.NotContains(t, out, "Aborting")
   547  		assert.Contains(t, out, "Updating/initializing submodules...")
   548  	})
   549  }
   550  
   551  func TestBuildWithoutDebugTrace(t *testing.T) {
   552  	onEachShell(t, func(t *testing.T, shell string) {
   553  		successfulBuild, err := common.GetSuccessfulBuild()
   554  		assert.NoError(t, err)
   555  		build, cleanup := newBuild(t, successfulBuild, shell)
   556  		defer cleanup()
   557  
   558  		// The default build shouldn't have debug tracing enabled
   559  		out, err := runBuildReturningOutput(t, build)
   560  		assert.NoError(t, err)
   561  		assert.NotRegexp(t, `[^$] echo Hello World`, out)
   562  	})
   563  }
   564  func TestBuildWithDebugTrace(t *testing.T) {
   565  	onEachShell(t, func(t *testing.T, shell string) {
   566  		successfulBuild, err := common.GetSuccessfulBuild()
   567  		assert.NoError(t, err)
   568  		build, cleanup := newBuild(t, successfulBuild, shell)
   569  		defer cleanup()
   570  
   571  		build.Variables = append(build.Variables, common.JobVariable{Key: "CI_DEBUG_TRACE", Value: "true"})
   572  
   573  		out, err := runBuildReturningOutput(t, build)
   574  		assert.NoError(t, err)
   575  		assert.Regexp(t, `[^$] echo Hello World`, out)
   576  	})
   577  }
   578  
   579  func TestBuildMultilineCommand(t *testing.T) {
   580  	multilineBuild, err := common.GetMultilineBashBuild()
   581  	assert.NoError(t, err)
   582  	build, cleanup := newBuild(t, multilineBuild, "bash")
   583  	defer cleanup()
   584  
   585  	// The default build shouldn't have debug tracing enabled
   586  	out, err := runBuildReturningOutput(t, build)
   587  	assert.NoError(t, err)
   588  	assert.NotContains(t, out, "bash")
   589  	assert.Contains(t, out, "Hello World")
   590  	assert.Contains(t, out, "collapsed multi-line command")
   591  }
   592  
   593  func TestBuildWithBrokenGitSSLCAInfo(t *testing.T) {
   594  	if skipOnGit17x(t) {
   595  		return
   596  	}
   597  
   598  	onEachShell(t, func(t *testing.T, shell string) {
   599  		successfulBuild, err := common.GetRemoteBrokenTLSBuild()
   600  		assert.NoError(t, err)
   601  		build, cleanup := newBuild(t, successfulBuild, shell)
   602  		defer cleanup()
   603  
   604  		build.Runner.URL = "https://gitlab.com"
   605  
   606  		out, err := runBuildReturningOutput(t, build)
   607  		assert.Error(t, err)
   608  		assert.Contains(t, out, "Cloning repository")
   609  		assert.NotContains(t, out, "Updating/initializing submodules")
   610  	})
   611  }
   612  
   613  func TestBuildWithGoodGitSSLCAInfo(t *testing.T) {
   614  	onEachShell(t, func(t *testing.T, shell string) {
   615  		successfulBuild, err := common.GetRemoteGitLabComTLSBuild()
   616  		assert.NoError(t, err)
   617  		build, cleanup := newBuild(t, successfulBuild, shell)
   618  		defer cleanup()
   619  
   620  		build.Runner.URL = "https://gitlab.com"
   621  
   622  		out, err := runBuildReturningOutput(t, build)
   623  		assert.NoError(t, err)
   624  		assert.Contains(t, out, "Cloning repository")
   625  		assert.Contains(t, out, "Updating/initializing submodules")
   626  	})
   627  }
   628  
   629  // TestBuildWithGitSSLAndStrategyFetch describes issue https://gitlab.com/gitlab-org/gitlab-runner/issues/2991
   630  func TestBuildWithGitSSLAndStrategyFetch(t *testing.T) {
   631  	onEachShell(t, func(t *testing.T, shell string) {
   632  		successfulBuild, err := common.GetRemoteGitLabComTLSBuild()
   633  		assert.NoError(t, err)
   634  		build, cleanup := newBuild(t, successfulBuild, shell)
   635  		defer cleanup()
   636  
   637  		build.Runner.PreCloneScript = "echo pre-clone-script"
   638  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   639  
   640  		out, err := runBuildReturningOutput(t, build)
   641  		assert.NoError(t, err)
   642  		assert.Contains(t, out, "Cloning repository")
   643  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   644  
   645  		out, err = runBuildReturningOutput(t, build)
   646  		assert.NoError(t, err)
   647  		assert.Contains(t, out, "Fetching changes")
   648  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   649  		assert.Contains(t, out, "pre-clone-script")
   650  	})
   651  }