gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+incompatible/executors/shell/executor_shell_test.go (about)

     1  package shell_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/gorilla/websocket"
    18  	"github.com/hashicorp/go-version"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  
    22  	"gitlab.com/gitlab-org/gitlab-runner/common"
    23  	"gitlab.com/gitlab-org/gitlab-runner/helpers"
    24  	"gitlab.com/gitlab-org/gitlab-runner/session"
    25  	"gitlab.com/gitlab-org/gitlab-runner/shells/shellstest"
    26  )
    27  
    28  const (
    29  	TestTimeout = 20 * time.Second
    30  )
    31  
    32  func gitInDir(dir string, args ...string) ([]byte, error) {
    33  	cmd := exec.Command("git", args...)
    34  	cmd.Dir = dir
    35  
    36  	return cmd.Output()
    37  }
    38  
    39  func skipOnGitWithMessage(t *testing.T, constraints string, message string) {
    40  	out, err := gitInDir("", "version")
    41  	if err != nil {
    42  		t.Fatal("Can't detect git version", err)
    43  		return
    44  	}
    45  
    46  	gitVersionOut := string(out)
    47  	split := strings.SplitN(gitVersionOut, " ", 3)
    48  	if len(split) < 3 {
    49  		t.Fatal("Can't extract git version from", gitVersionOut)
    50  		return
    51  	}
    52  
    53  	gitVersion, err := version.NewVersion(strings.TrimSpace(split[2]))
    54  	if err != nil {
    55  		t.Fatal("Can't detect git version", err)
    56  		return
    57  	}
    58  
    59  	rules, err := version.NewConstraint(constraints)
    60  	if err != nil {
    61  		t.Fatal("Invalid constraint", err)
    62  		return
    63  	}
    64  
    65  	shouldSkip := rules.Check(gitVersion)
    66  	if shouldSkip {
    67  		t.Skipf("Git %q found, skipping the test; %s", constraints, message)
    68  	}
    69  }
    70  
    71  func skipIfGitDoesNotSupportLFS(t *testing.T) {
    72  	skipOnGitWithMessage(t, "< 1.8.2", "available git version doesn't support LFS")
    73  }
    74  
    75  func skipOnGit(t *testing.T, constraints string) {
    76  	skipOnGitWithMessage(t, constraints, "")
    77  }
    78  
    79  func skipOnGit17x(t *testing.T) {
    80  	skipOnGit(t, "< 1.8")
    81  }
    82  
    83  func runBuildWithOptions(t *testing.T, build *common.Build, config *common.Config, trace *common.Trace) error {
    84  	timeoutTimer := time.AfterFunc(TestTimeout, func() {
    85  		t.Log("Timed out")
    86  		t.FailNow()
    87  	})
    88  	defer timeoutTimer.Stop()
    89  
    90  	return build.Run(config, trace)
    91  }
    92  
    93  func runBuildWithTrace(t *testing.T, build *common.Build, trace *common.Trace) error {
    94  	return runBuildWithOptions(t, build, &common.Config{}, trace)
    95  }
    96  
    97  func runBuild(t *testing.T, build *common.Build) error {
    98  	err := runBuildWithTrace(t, build, &common.Trace{Writer: os.Stdout})
    99  	assert.True(t, build.IsSharedEnv())
   100  	return err
   101  }
   102  
   103  func runBuildReturningOutput(t *testing.T, build *common.Build) (string, error) {
   104  	buf := bytes.NewBuffer(nil)
   105  	err := runBuildWithTrace(t, build, &common.Trace{Writer: buf})
   106  	output := buf.String()
   107  	t.Log(output)
   108  
   109  	return output, err
   110  }
   111  
   112  func newBuild(t *testing.T, getBuildResponse common.JobResponse, shell string) (*common.Build, func()) {
   113  	dir, err := ioutil.TempDir("", "gitlab-runner-shell-executor-test")
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	t.Log("Build directory:", dir)
   119  
   120  	build := &common.Build{
   121  		JobResponse: getBuildResponse,
   122  		Runner: &common.RunnerConfig{
   123  			RunnerSettings: common.RunnerSettings{
   124  				BuildsDir: dir,
   125  				Executor:  "shell",
   126  				Shell:     shell,
   127  			},
   128  		},
   129  		SystemInterrupt: make(chan os.Signal, 1),
   130  		Session: &session.Session{
   131  			DisconnectCh: make(chan error),
   132  			TimeoutCh:    make(chan error),
   133  		},
   134  	}
   135  
   136  	cleanup := func() {
   137  		os.RemoveAll(dir)
   138  	}
   139  
   140  	return build, cleanup
   141  }
   142  
   143  func TestBuildSuccess(t *testing.T) {
   144  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   145  		successfulBuild, err := common.GetSuccessfulBuild()
   146  		assert.NoError(t, err)
   147  		build, cleanup := newBuild(t, successfulBuild, shell)
   148  		defer cleanup()
   149  
   150  		err = runBuild(t, build)
   151  		assert.NoError(t, err)
   152  	})
   153  }
   154  
   155  func TestBuildAbort(t *testing.T) {
   156  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   157  		longRunningBuild, err := common.GetLongRunningBuild()
   158  		assert.NoError(t, err)
   159  		build, cleanup := newBuild(t, longRunningBuild, shell)
   160  		defer cleanup()
   161  
   162  		abortTimer := time.AfterFunc(time.Second, func() {
   163  			t.Log("Interrupt")
   164  			build.SystemInterrupt <- os.Interrupt
   165  		})
   166  		defer abortTimer.Stop()
   167  
   168  		err = runBuild(t, build)
   169  		assert.EqualError(t, err, "aborted: interrupt")
   170  	})
   171  }
   172  
   173  func TestBuildCancel(t *testing.T) {
   174  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   175  		longRunningBuild, err := common.GetLongRunningBuild()
   176  		assert.NoError(t, err)
   177  		build, cleanup := newBuild(t, longRunningBuild, shell)
   178  		defer cleanup()
   179  
   180  		trace := &common.Trace{Writer: os.Stdout}
   181  
   182  		cancelTimer := time.AfterFunc(time.Second, func() {
   183  			t.Log("Cancel")
   184  			trace.CancelFunc()
   185  		})
   186  		defer cancelTimer.Stop()
   187  
   188  		err = runBuildWithTrace(t, build, trace)
   189  		assert.EqualError(t, err, "canceled")
   190  		assert.IsType(t, err, &common.BuildError{})
   191  	})
   192  }
   193  
   194  func TestBuildWithIndexLock(t *testing.T) {
   195  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   196  		successfulBuild, err := common.GetSuccessfulBuild()
   197  		assert.NoError(t, err)
   198  		build, cleanup := newBuild(t, successfulBuild, shell)
   199  		defer cleanup()
   200  
   201  		err = runBuild(t, build)
   202  		assert.NoError(t, err)
   203  
   204  		build.JobResponse.AllowGitFetch = true
   205  		err = ioutil.WriteFile(build.BuildDir+"/.git/index.lock", []byte{}, os.ModeSticky)
   206  		require.NoError(t, err)
   207  
   208  		err = runBuild(t, build)
   209  		assert.NoError(t, err)
   210  	})
   211  }
   212  
   213  func TestBuildWithShallowLock(t *testing.T) {
   214  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   215  		successfulBuild, err := common.GetSuccessfulBuild()
   216  		assert.NoError(t, err)
   217  		build, cleanup := newBuild(t, successfulBuild, shell)
   218  		defer cleanup()
   219  
   220  		build.Variables = append(build.Variables,
   221  			common.JobVariable{Key: "GIT_DEPTH", Value: "1"},
   222  			common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   223  
   224  		err = runBuild(t, build)
   225  		assert.NoError(t, err)
   226  
   227  		err = ioutil.WriteFile(build.BuildDir+"/.git/shallow.lock", []byte{}, os.ModeSticky)
   228  		require.NoError(t, err)
   229  
   230  		err = runBuild(t, build)
   231  		assert.NoError(t, err)
   232  	})
   233  }
   234  
   235  func TestBuildWithHeadLock(t *testing.T) {
   236  	shellstest.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  		err = ioutil.WriteFile(build.BuildDir+"/.git/HEAD.lock", []byte{}, os.ModeSticky)
   247  		require.NoError(t, err)
   248  
   249  		err = runBuild(t, build)
   250  		assert.NoError(t, err)
   251  	})
   252  }
   253  
   254  func TestBuildWithGitLFSHook(t *testing.T) {
   255  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   256  		successfulBuild, err := common.GetSuccessfulBuild()
   257  		assert.NoError(t, err)
   258  		build, cleanup := newBuild(t, successfulBuild, shell)
   259  		defer cleanup()
   260  
   261  		err = runBuild(t, build)
   262  		assert.NoError(t, err)
   263  
   264  		gitLFSPostCheckoutHook := "#!/bin/sh\necho 'running git lfs hook' >&2\nexit 2\n"
   265  
   266  		err = os.MkdirAll(build.BuildDir+"/.git/hooks/", 0755)
   267  		require.NoError(t, err)
   268  		err = ioutil.WriteFile(build.BuildDir+"/.git/hooks/post-checkout", []byte(gitLFSPostCheckoutHook), 0777)
   269  		require.NoError(t, err)
   270  		build.JobResponse.AllowGitFetch = true
   271  
   272  		err = runBuild(t, build)
   273  		assert.NoError(t, err)
   274  	})
   275  }
   276  
   277  func assertLFSFileDownloaded(t *testing.T, build *common.Build) {
   278  	lfsFilePath := filepath.Join(build.FullProjectDir(), "files", "lfs", "file_1.lfs")
   279  	info, err := os.Stat(lfsFilePath)
   280  	require.NoError(t, err)
   281  	assert.Equal(t, common.FilesLFSFile1LFSsize, info.Size(), "invalid size of %q file", lfsFilePath)
   282  }
   283  
   284  func assertLFSFileNotDownloaded(t *testing.T, build *common.Build) {
   285  	lfsFilePath := filepath.Join(build.FullProjectDir(), "files", "lfs", "file_1.lfs")
   286  	info, err := os.Stat(lfsFilePath)
   287  	require.NoError(t, err)
   288  	assert.True(t, info.Size() < common.FilesLFSFile1LFSsize, "invalid size of %q file - expected to be less then downloaded LFS object", lfsFilePath)
   289  }
   290  
   291  func assertLFSFileNotPresent(t *testing.T, build *common.Build) {
   292  	lfsFilePath := filepath.Join(build.FullProjectDir(), "files", "lfs", "file_1.lfs")
   293  	_, err := os.Stat(lfsFilePath)
   294  	require.IsType(t, &os.PathError{}, err)
   295  	assert.Equal(t, lfsFilePath, err.(*os.PathError).Path)
   296  }
   297  
   298  func TestBuildWithGitStrategyNoneWithoutLFS(t *testing.T) {
   299  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   300  		successfulBuild, err := common.GetSuccessfulBuild()
   301  		assert.NoError(t, err)
   302  		build, cleanup := newBuild(t, successfulBuild, shell)
   303  		defer cleanup()
   304  
   305  		build.Runner.PreCloneScript = "echo pre-clone-script"
   306  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   307  
   308  		out, err := runBuildReturningOutput(t, build)
   309  		assert.NoError(t, err)
   310  		assert.NotContains(t, out, "pre-clone-script")
   311  		assert.NotContains(t, out, "Created fresh repository")
   312  		assert.NotContains(t, out, "Fetching changes")
   313  		assert.Contains(t, out, "Skipping Git repository setup")
   314  	})
   315  }
   316  
   317  func TestBuildWithGitStrategyNoneWithLFS(t *testing.T) {
   318  	skipIfGitDoesNotSupportLFS(t)
   319  
   320  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   321  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   322  		assert.NoError(t, err)
   323  		build, cleanup := newBuild(t, successfulBuild, shell)
   324  		defer cleanup()
   325  
   326  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   327  
   328  		out, err := runBuildReturningOutput(t, build)
   329  		assert.NoError(t, err)
   330  		assert.NotContains(t, out, "Created fresh repository")
   331  		assert.NotContains(t, out, "Fetching changes")
   332  		assert.Contains(t, out, "Skipping Git repository setup")
   333  		assertLFSFileNotPresent(t, build)
   334  	})
   335  }
   336  
   337  func TestBuildWithGitStrategyFetchWithoutLFS(t *testing.T) {
   338  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   339  		successfulBuild, err := common.GetSuccessfulBuild()
   340  		assert.NoError(t, err)
   341  		build, cleanup := newBuild(t, successfulBuild, shell)
   342  		defer cleanup()
   343  
   344  		build.Runner.PreCloneScript = "echo pre-clone-script"
   345  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   346  
   347  		out, err := runBuildReturningOutput(t, build)
   348  		assert.NoError(t, err)
   349  		assert.Contains(t, out, "Created fresh repository")
   350  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   351  
   352  		out, err = runBuildReturningOutput(t, build)
   353  		assert.NoError(t, err)
   354  		assert.Contains(t, out, "Fetching changes")
   355  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   356  		assert.Contains(t, out, "pre-clone-script")
   357  	})
   358  }
   359  
   360  func TestBuildWithGitStrategyFetchWithLFS(t *testing.T) {
   361  	skipIfGitDoesNotSupportLFS(t)
   362  
   363  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   364  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   365  		assert.NoError(t, err)
   366  		build, cleanup := newBuild(t, successfulBuild, shell)
   367  		defer cleanup()
   368  
   369  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   370  
   371  		out, err := runBuildReturningOutput(t, build)
   372  		assert.NoError(t, err)
   373  		assert.Contains(t, out, "Created fresh repository")
   374  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   375  		assertLFSFileNotPresent(t, build)
   376  
   377  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   378  
   379  		out, err = runBuildReturningOutput(t, build)
   380  		assert.NoError(t, err)
   381  		assert.Contains(t, out, "Fetching changes")
   382  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   383  		assertLFSFileDownloaded(t, build)
   384  	})
   385  }
   386  
   387  func TestBuildWithGitStrategyFetchWithUserDisabledLFS(t *testing.T) {
   388  	skipIfGitDoesNotSupportLFS(t)
   389  
   390  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   391  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   392  		assert.NoError(t, err)
   393  		build, cleanup := newBuild(t, successfulBuild, shell)
   394  		defer cleanup()
   395  
   396  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   397  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   398  
   399  		out, err := runBuildReturningOutput(t, build)
   400  		assert.NoError(t, err)
   401  		assert.Contains(t, out, "Created fresh repository")
   402  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   403  		assertLFSFileNotPresent(t, build)
   404  
   405  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   406  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   407  
   408  		out, err = runBuildReturningOutput(t, build)
   409  		assert.NoError(t, err)
   410  		assert.Contains(t, out, "Fetching changes")
   411  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   412  		assertLFSFileNotDownloaded(t, build)
   413  	})
   414  }
   415  
   416  func TestBuildWithGitStrategyFetchNoCheckoutWithoutLFS(t *testing.T) {
   417  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   418  		successfulBuild, err := common.GetSuccessfulBuild()
   419  		assert.NoError(t, err)
   420  		build, cleanup := newBuild(t, successfulBuild, shell)
   421  		defer cleanup()
   422  
   423  		build.Runner.PreCloneScript = "echo pre-clone-script"
   424  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   425  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   426  
   427  		out, err := runBuildReturningOutput(t, build)
   428  		assert.NoError(t, err)
   429  		assert.Contains(t, out, "Created fresh repository")
   430  		assert.Contains(t, out, "Skipping Git checkout")
   431  
   432  		out, err = runBuildReturningOutput(t, build)
   433  		assert.NoError(t, err)
   434  		assert.Contains(t, out, "Fetching changes")
   435  		assert.Contains(t, out, "Skipping Git checkout")
   436  		assert.Contains(t, out, "pre-clone-script")
   437  	})
   438  }
   439  
   440  func TestBuildWithGitStrategyFetchNoCheckoutWithLFS(t *testing.T) {
   441  	skipIfGitDoesNotSupportLFS(t)
   442  
   443  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   444  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   445  		assert.NoError(t, err)
   446  		build, cleanup := newBuild(t, successfulBuild, shell)
   447  		defer cleanup()
   448  
   449  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   450  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   451  
   452  		out, err := runBuildReturningOutput(t, build)
   453  		assert.NoError(t, err)
   454  		assert.Contains(t, out, "Created fresh repository")
   455  		assert.Contains(t, out, "Skipping Git checkout")
   456  		assertLFSFileNotPresent(t, build)
   457  
   458  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   459  
   460  		out, err = runBuildReturningOutput(t, build)
   461  		assert.NoError(t, err)
   462  		assert.Contains(t, out, "Fetching changes")
   463  		assert.Contains(t, out, "Skipping Git checkout")
   464  		assertLFSFileNotPresent(t, build)
   465  	})
   466  }
   467  
   468  func TestBuildWithGitStrategyCloneWithoutLFS(t *testing.T) {
   469  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   470  		successfulBuild, err := common.GetSuccessfulBuild()
   471  		assert.NoError(t, err)
   472  		build, cleanup := newBuild(t, successfulBuild, shell)
   473  		defer cleanup()
   474  
   475  		build.Runner.PreCloneScript = "echo pre-clone-script"
   476  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   477  
   478  		out, err := runBuildReturningOutput(t, build)
   479  		assert.NoError(t, err)
   480  		assert.Contains(t, out, "Created fresh repository")
   481  
   482  		out, err = runBuildReturningOutput(t, build)
   483  		assert.NoError(t, err)
   484  		assert.Contains(t, out, "Created fresh repository")
   485  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   486  		assert.Contains(t, out, "pre-clone-script")
   487  	})
   488  }
   489  
   490  func TestBuildWithGitStrategyCloneWithLFS(t *testing.T) {
   491  	skipIfGitDoesNotSupportLFS(t)
   492  
   493  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   494  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   495  		assert.NoError(t, err)
   496  		build, cleanup := newBuild(t, successfulBuild, shell)
   497  		defer cleanup()
   498  
   499  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   500  
   501  		out, err := runBuildReturningOutput(t, build)
   502  		assert.NoError(t, err)
   503  		assert.Contains(t, out, "Created fresh repository")
   504  		assertLFSFileDownloaded(t, build)
   505  	})
   506  }
   507  
   508  func TestBuildWithGitStrategyCloneWithUserDisabledLFS(t *testing.T) {
   509  	skipIfGitDoesNotSupportLFS(t)
   510  
   511  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   512  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   513  		assert.NoError(t, err)
   514  		build, cleanup := newBuild(t, successfulBuild, shell)
   515  		defer cleanup()
   516  
   517  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   518  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   519  
   520  		out, err := runBuildReturningOutput(t, build)
   521  		assert.NoError(t, err)
   522  		assert.Contains(t, out, "Created fresh repository")
   523  		assertLFSFileNotDownloaded(t, build)
   524  	})
   525  }
   526  
   527  func TestBuildWithGitStrategyCloneNoCheckoutWithoutLFS(t *testing.T) {
   528  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   529  		successfulBuild, err := common.GetSuccessfulBuild()
   530  		assert.NoError(t, err)
   531  		build, cleanup := newBuild(t, successfulBuild, shell)
   532  		defer cleanup()
   533  
   534  		build.Runner.PreCloneScript = "echo pre-clone-script"
   535  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   536  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   537  
   538  		out, err := runBuildReturningOutput(t, build)
   539  		assert.NoError(t, err)
   540  		assert.Contains(t, out, "Created fresh repository")
   541  
   542  		out, err = runBuildReturningOutput(t, build)
   543  		assert.NoError(t, err)
   544  		assert.Contains(t, out, "Created fresh repository")
   545  		assert.Contains(t, out, "Skipping Git checkout")
   546  		assert.Contains(t, out, "pre-clone-script")
   547  	})
   548  }
   549  
   550  func TestBuildWithGitStrategyCloneNoCheckoutWithLFS(t *testing.T) {
   551  	skipIfGitDoesNotSupportLFS(t)
   552  
   553  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   554  		successfulBuild, err := common.GetRemoteSuccessfulLFSBuild()
   555  		assert.NoError(t, err)
   556  		build, cleanup := newBuild(t, successfulBuild, shell)
   557  		defer cleanup()
   558  
   559  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "clone"})
   560  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_CHECKOUT", Value: "false"})
   561  
   562  		out, err := runBuildReturningOutput(t, build)
   563  		assert.NoError(t, err)
   564  		assert.Contains(t, out, "Created fresh repository")
   565  		assert.Contains(t, out, "Skipping Git checkout")
   566  		assertLFSFileNotPresent(t, build)
   567  	})
   568  }
   569  
   570  func TestBuildWithSubmoduleLFSPullsLFSObject(t *testing.T) {
   571  	skipIfGitDoesNotSupportLFS(t)
   572  
   573  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   574  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   575  		assert.NoError(t, err)
   576  		build, cleanup := newBuild(t, successfulBuild, shell)
   577  		defer cleanup()
   578  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   579  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   580  		build.GitInfo = common.GetSubmoduleLFSGitInfo(build.GitInfo.RepoURL)
   581  
   582  		out, err := runBuildReturningOutput(t, build)
   583  		assert.NoError(t, err)
   584  		assert.Contains(t, out, "Created fresh repository")
   585  
   586  		f, err := os.Stat(filepath.Join(build.FullProjectDir(), "lfs", "1.lfs"))
   587  		require.NoError(t, err)
   588  		assert.Equal(t, common.FilesLFSFile1LFSsize, f.Size())
   589  	})
   590  }
   591  
   592  func TestBuildWithSubmoduleLFSDisabledSmudging(t *testing.T) {
   593  	skipIfGitDoesNotSupportLFS(t)
   594  
   595  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   596  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   597  		assert.NoError(t, err)
   598  		build, cleanup := newBuild(t, successfulBuild, shell)
   599  		defer cleanup()
   600  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   601  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   602  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_LFS_SKIP_SMUDGE", Value: "1", Public: true})
   603  		build.GitInfo = common.GetSubmoduleLFSGitInfo(build.GitInfo.RepoURL)
   604  
   605  		out, err := runBuildReturningOutput(t, build)
   606  		assert.NoError(t, err)
   607  		assert.Contains(t, out, "Created fresh repository")
   608  
   609  		f, err := os.Stat(filepath.Join(build.FullProjectDir(), "lfs", "1.lfs"))
   610  		require.NoError(t, err)
   611  		assert.True(t, f.Size() < common.FilesLFSFile1LFSsize)
   612  	})
   613  }
   614  
   615  func TestBuildWithGitSubmoduleStrategyNone(t *testing.T) {
   616  	for _, strategy := range []string{"none", ""} {
   617  		t.Run("strategy "+strategy, func(t *testing.T) {
   618  			shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   619  				successfulBuild, err := common.GetSuccessfulBuild()
   620  				assert.NoError(t, err)
   621  				build, cleanup := newBuild(t, successfulBuild, shell)
   622  				defer cleanup()
   623  
   624  				build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "none"})
   625  
   626  				out, err := runBuildReturningOutput(t, build)
   627  				assert.NoError(t, err)
   628  				assert.Contains(t, out, "Skipping Git submodules setup")
   629  				assert.NotContains(t, out, "Updating/initializing submodules...")
   630  				assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   631  
   632  				_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   633  				assert.Error(t, err, "Submodule not should have been initialized")
   634  
   635  				_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   636  				assert.Error(t, err, "The submodule's submodule should not have been initialized")
   637  			})
   638  		})
   639  	}
   640  }
   641  
   642  func TestBuildWithGitSubmoduleStrategyNormal(t *testing.T) {
   643  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   644  		successfulBuild, err := common.GetSuccessfulBuild()
   645  		assert.NoError(t, err)
   646  		build, cleanup := newBuild(t, successfulBuild, shell)
   647  		defer cleanup()
   648  
   649  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   650  
   651  		out, err := runBuildReturningOutput(t, build)
   652  		assert.NoError(t, err)
   653  		assert.NotContains(t, out, "Skipping Git submodules setup")
   654  		assert.Contains(t, out, "Updating/initializing submodules...")
   655  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   656  
   657  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   658  		assert.NoError(t, err, "Submodule should have been initialized")
   659  
   660  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   661  		assert.Error(t, err, "The submodule's submodule should not have been initialized")
   662  	})
   663  }
   664  
   665  func TestBuildWithGitSubmoduleStrategyRecursive(t *testing.T) {
   666  	skipOnGit17x(t)
   667  
   668  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   669  		successfulBuild, err := common.GetSuccessfulBuild()
   670  		assert.NoError(t, err)
   671  		build, cleanup := newBuild(t, successfulBuild, shell)
   672  		defer cleanup()
   673  
   674  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "recursive"})
   675  
   676  		out, err := runBuildReturningOutput(t, build)
   677  		assert.NoError(t, err)
   678  		assert.NotContains(t, out, "Skipping Git submodules setup")
   679  		assert.NotContains(t, out, "Updating/initializing submodules...")
   680  		assert.Contains(t, out, "Updating/initializing submodules recursively...")
   681  
   682  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", ".git"))
   683  		assert.NoError(t, err, "Submodule should have been initialized")
   684  
   685  		_, err = os.Stat(filepath.Join(build.BuildDir, "gitlab-grack", "tests", "example", ".git"))
   686  		assert.NoError(t, err, "The submodule's submodule should have been initialized")
   687  	})
   688  }
   689  
   690  func TestBuildWithGitSubmoduleStrategyInvalid(t *testing.T) {
   691  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   692  		successfulBuild, err := common.GetSuccessfulBuild()
   693  		assert.NoError(t, err)
   694  		build, cleanup := newBuild(t, successfulBuild, shell)
   695  		defer cleanup()
   696  
   697  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "invalid"})
   698  
   699  		out, err := runBuildReturningOutput(t, build)
   700  		assert.EqualError(t, err, "unknown GIT_SUBMODULE_STRATEGY")
   701  		assert.NotContains(t, out, "Skipping Git submodules setup")
   702  		assert.NotContains(t, out, "Updating/initializing submodules...")
   703  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   704  	})
   705  }
   706  
   707  func TestBuildWithGitSubmoduleStrategyRecursiveAndGitStrategyNone(t *testing.T) {
   708  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   709  		successfulBuild, err := common.GetSuccessfulBuild()
   710  		assert.NoError(t, err)
   711  		build, cleanup := newBuild(t, successfulBuild, shell)
   712  		defer cleanup()
   713  
   714  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "none"})
   715  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "recursive"})
   716  
   717  		out, err := runBuildReturningOutput(t, build)
   718  		assert.NoError(t, err)
   719  		assert.NotContains(t, out, "Created fresh repository")
   720  		assert.NotContains(t, out, "Fetching changes")
   721  		assert.Contains(t, out, "Skipping Git repository setup")
   722  		assert.NotContains(t, out, "Updating/initializing submodules...")
   723  		assert.NotContains(t, out, "Updating/initializing submodules recursively...")
   724  		assert.Contains(t, out, "Skipping Git submodules setup")
   725  	})
   726  }
   727  
   728  func TestBuildWithGitSubmoduleModified(t *testing.T) {
   729  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   730  		successfulBuild, err := common.GetSuccessfulBuild()
   731  		assert.NoError(t, err)
   732  		build, cleanup := newBuild(t, successfulBuild, shell)
   733  		defer cleanup()
   734  
   735  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_SUBMODULE_STRATEGY", Value: "normal"})
   736  
   737  		out, err := runBuildReturningOutput(t, build)
   738  		assert.NoError(t, err)
   739  		assert.Contains(t, out, "Updating/initializing submodules...")
   740  
   741  		submoduleDir := filepath.Join(build.BuildDir, "gitlab-grack")
   742  		submoduleReadme := filepath.Join(submoduleDir, "README.md")
   743  
   744  		// modify submodule and commit
   745  		modifySubmoduleBeforeCommit := "commited change"
   746  		err = ioutil.WriteFile(submoduleReadme, []byte(modifySubmoduleBeforeCommit), os.ModeSticky)
   747  		require.NoError(t, err)
   748  		_, err = gitInDir(submoduleDir, "add", "README.md")
   749  		assert.NoError(t, err)
   750  		_, err = gitInDir(submoduleDir, "config", "user.name", "test")
   751  		assert.NoError(t, err)
   752  		_, err = gitInDir(submoduleDir, "config", "user.email", "test@example.org")
   753  		assert.NoError(t, err)
   754  		_, err = gitInDir(submoduleDir, "commit", "-m", "modify submodule")
   755  		assert.NoError(t, err)
   756  
   757  		_, err = gitInDir(build.BuildDir, "add", "gitlab-grack")
   758  		assert.NoError(t, err)
   759  		_, err = gitInDir(build.BuildDir, "config", "user.name", "test")
   760  		assert.NoError(t, err)
   761  		_, err = gitInDir(build.BuildDir, "config", "user.email", "test@example.org")
   762  		assert.NoError(t, err)
   763  		_, err = gitInDir(build.BuildDir, "commit", "-m", "modify submodule")
   764  		assert.NoError(t, err)
   765  
   766  		// modify submodule without commit before second build
   767  		modifySubmoduleAfterCommit := "not committed change"
   768  		err = ioutil.WriteFile(submoduleReadme, []byte(modifySubmoduleAfterCommit), os.ModeSticky)
   769  		require.NoError(t, err)
   770  
   771  		build.JobResponse.AllowGitFetch = true
   772  		out, err = runBuildReturningOutput(t, build)
   773  		assert.NoError(t, err)
   774  		assert.NotContains(t, out, "Your local changes to the following files would be overwritten by checkout")
   775  		assert.NotContains(t, out, "Please commit your changes or stash them before you switch branches")
   776  		assert.NotContains(t, out, "Aborting")
   777  		assert.Contains(t, out, "Updating/initializing submodules...")
   778  	})
   779  }
   780  
   781  func TestBuildWithoutDebugTrace(t *testing.T) {
   782  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   783  		successfulBuild, err := common.GetSuccessfulBuild()
   784  		assert.NoError(t, err)
   785  		build, cleanup := newBuild(t, successfulBuild, shell)
   786  		defer cleanup()
   787  
   788  		// The default build shouldn't have debug tracing enabled
   789  		out, err := runBuildReturningOutput(t, build)
   790  		assert.NoError(t, err)
   791  		assert.NotRegexp(t, `[^$] echo Hello World`, out)
   792  	})
   793  }
   794  func TestBuildWithDebugTrace(t *testing.T) {
   795  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   796  		successfulBuild, err := common.GetSuccessfulBuild()
   797  		assert.NoError(t, err)
   798  		build, cleanup := newBuild(t, successfulBuild, shell)
   799  		defer cleanup()
   800  
   801  		build.Variables = append(build.Variables, common.JobVariable{Key: "CI_DEBUG_TRACE", Value: "true"})
   802  
   803  		out, err := runBuildReturningOutput(t, build)
   804  		assert.NoError(t, err)
   805  		assert.Regexp(t, `[^$] echo Hello World`, out)
   806  	})
   807  }
   808  
   809  func TestBuildMultilineCommand(t *testing.T) {
   810  	multilineBuild, err := common.GetMultilineBashBuild()
   811  	assert.NoError(t, err)
   812  	build, cleanup := newBuild(t, multilineBuild, "bash")
   813  	defer cleanup()
   814  
   815  	// The default build shouldn't have debug tracing enabled
   816  	out, err := runBuildReturningOutput(t, build)
   817  	assert.NoError(t, err)
   818  	assert.NotContains(t, out, "bash")
   819  	assert.Contains(t, out, "Hello World")
   820  	assert.Contains(t, out, "collapsed multi-line command")
   821  }
   822  
   823  func TestBuildWithBrokenGitSSLCAInfo(t *testing.T) {
   824  	skipOnGit17x(t)
   825  	skipOnGit(t, ">= 2.10.2")
   826  
   827  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   828  		successfulBuild, err := common.GetRemoteBrokenTLSBuild()
   829  		assert.NoError(t, err)
   830  		build, cleanup := newBuild(t, successfulBuild, shell)
   831  		defer cleanup()
   832  
   833  		build.Runner.URL = "https://gitlab.com"
   834  
   835  		out, err := runBuildReturningOutput(t, build)
   836  		assert.Error(t, err)
   837  		assert.Contains(t, out, "Created fresh repository")
   838  		assert.NotContains(t, out, "Updating/initializing submodules")
   839  	})
   840  }
   841  
   842  func TestBuildWithGoodGitSSLCAInfo(t *testing.T) {
   843  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   844  		successfulBuild, err := common.GetRemoteGitLabComTLSBuild()
   845  		assert.NoError(t, err)
   846  		build, cleanup := newBuild(t, successfulBuild, shell)
   847  		defer cleanup()
   848  
   849  		build.Runner.URL = "https://gitlab.com"
   850  
   851  		out, err := runBuildReturningOutput(t, build)
   852  		assert.NoError(t, err)
   853  		assert.Contains(t, out, "Created fresh repository")
   854  		assert.Contains(t, out, "Updating/initializing submodules")
   855  	})
   856  }
   857  
   858  // TestBuildWithGitSSLAndStrategyFetch describes issue https://gitlab.com/gitlab-org/gitlab-runner/issues/2991
   859  func TestBuildWithGitSSLAndStrategyFetch(t *testing.T) {
   860  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   861  		successfulBuild, err := common.GetRemoteGitLabComTLSBuild()
   862  		assert.NoError(t, err)
   863  		build, cleanup := newBuild(t, successfulBuild, shell)
   864  		defer cleanup()
   865  
   866  		build.Runner.PreCloneScript = "echo pre-clone-script"
   867  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   868  
   869  		out, err := runBuildReturningOutput(t, build)
   870  		assert.NoError(t, err)
   871  		assert.Contains(t, out, "Created fresh repository")
   872  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   873  
   874  		out, err = runBuildReturningOutput(t, build)
   875  		assert.NoError(t, err)
   876  		assert.Contains(t, out, "Fetching changes")
   877  		assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   878  		assert.Contains(t, out, "pre-clone-script")
   879  	})
   880  }
   881  
   882  func TestBuildWithUntrackedDirFromPreviousBuild(t *testing.T) {
   883  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   884  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   885  		assert.NoError(t, err)
   886  		build, cleanup := newBuild(t, successfulBuild, shell)
   887  		defer cleanup()
   888  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   889  
   890  		out, err := runBuildReturningOutput(t, build)
   891  		assert.NoError(t, err)
   892  		assert.Contains(t, out, "Created fresh repository")
   893  
   894  		err = os.MkdirAll(fmt.Sprintf("%s/.test", build.FullProjectDir()), 0644)
   895  		require.NoError(t, err)
   896  
   897  		out, err = runBuildReturningOutput(t, build)
   898  		assert.NoError(t, err)
   899  		assert.Contains(t, out, "Removing .test/")
   900  	})
   901  }
   902  
   903  func TestBuildChangesBranchesWhenFetchingRepo(t *testing.T) {
   904  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
   905  		successfulBuild, err := common.GetRemoteSuccessfulBuild()
   906  		assert.NoError(t, err)
   907  		build, cleanup := newBuild(t, successfulBuild, shell)
   908  		defer cleanup()
   909  		build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   910  
   911  		out, err := runBuildReturningOutput(t, build)
   912  		assert.NoError(t, err)
   913  		assert.Contains(t, out, "Created fresh repository")
   914  
   915  		// Another build using the same repo but different branch.
   916  		build.GitInfo = common.GetLFSGitInfo(build.GitInfo.RepoURL)
   917  		out, err = runBuildReturningOutput(t, build)
   918  		assert.NoError(t, err)
   919  		assert.Contains(t, out, "Checking out 2371dd05 as add-lfs-object...")
   920  	})
   921  }
   922  
   923  func TestBuildPowerShellCatchesExceptions(t *testing.T) {
   924  	if helpers.SkipIntegrationTests(t, "powershell") {
   925  		t.Skip()
   926  	}
   927  
   928  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   929  	assert.NoError(t, err)
   930  	build, cleanup := newBuild(t, successfulBuild, "powershell")
   931  	defer cleanup()
   932  	build.Variables = append(build.Variables, common.JobVariable{Key: "ErrorActionPreference", Value: "Stop"})
   933  	build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"})
   934  
   935  	out, err := runBuildReturningOutput(t, build)
   936  	assert.NoError(t, err)
   937  	assert.Contains(t, out, "Created fresh repository")
   938  
   939  	out, err = runBuildReturningOutput(t, build)
   940  	assert.NoError(t, err)
   941  	assert.NotContains(t, out, "Created fresh repository")
   942  	assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   943  
   944  	build.Variables = append(build.Variables, common.JobVariable{Key: "ErrorActionPreference", Value: "Continue"})
   945  	out, err = runBuildReturningOutput(t, build)
   946  	assert.NoError(t, err)
   947  	assert.NotContains(t, out, "Created fresh repository")
   948  	assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   949  
   950  	build.Variables = append(build.Variables, common.JobVariable{Key: "ErrorActionPreference", Value: "SilentlyContinue"})
   951  	out, err = runBuildReturningOutput(t, build)
   952  	assert.NoError(t, err)
   953  	assert.NotContains(t, out, "Created fresh repository")
   954  	assert.Regexp(t, "Checking out [a-f0-9]+ as", out)
   955  }
   956  
   957  func TestInteractiveTerminal(t *testing.T) {
   958  	cases := []struct {
   959  		app                string
   960  		shell              string
   961  		command            string
   962  		expectedStatusCode int
   963  	}{
   964  		{
   965  			app:                "bash",
   966  			shell:              "bash",
   967  			command:            "sleep 5",
   968  			expectedStatusCode: http.StatusSwitchingProtocols,
   969  		},
   970  		{
   971  			app:                "cmd.exe",
   972  			shell:              "cmd",
   973  			command:            "timeout 2",
   974  			expectedStatusCode: http.StatusInternalServerError,
   975  		},
   976  		{
   977  			app:                "powershell.exe",
   978  			shell:              "powershell",
   979  			command:            "Start-Sleep -s 2",
   980  			expectedStatusCode: http.StatusInternalServerError,
   981  		},
   982  	}
   983  
   984  	for _, c := range cases {
   985  		t.Run(c.shell, func(t *testing.T) {
   986  			if helpers.SkipIntegrationTests(t, c.app) {
   987  				t.Skip()
   988  			}
   989  
   990  			successfulBuild, err := common.GetLocalBuildResponse(c.command)
   991  			require.NoError(t, err)
   992  			build, cleanup := newBuild(t, successfulBuild, c.shell)
   993  			defer cleanup()
   994  			sess, err := session.NewSession(nil)
   995  			build.Session = sess
   996  			require.NoError(t, err)
   997  
   998  			buildOut := make(chan string)
   999  
  1000  			go func() {
  1001  				buf := bytes.NewBuffer(nil)
  1002  				err := runBuildWithOptions(
  1003  					t,
  1004  					build,
  1005  					&common.Config{SessionServer: common.SessionServer{SessionTimeout: 2}},
  1006  					&common.Trace{Writer: buf},
  1007  				)
  1008  				require.NoError(t, err)
  1009  
  1010  				buildOut <- buf.String()
  1011  			}()
  1012  
  1013  			// Wait until the build starts.
  1014  			for build.Session.Mux() == nil {
  1015  				time.Sleep(10 * time.Millisecond)
  1016  			}
  1017  
  1018  			srv := httptest.NewServer(build.Session.Mux())
  1019  			defer srv.Close()
  1020  
  1021  			u := url.URL{
  1022  				Scheme: "ws",
  1023  				Host:   srv.Listener.Addr().String(),
  1024  				Path:   build.Session.Endpoint + "/exec",
  1025  			}
  1026  			headers := http.Header{
  1027  				"Authorization": []string{build.Session.Token},
  1028  			}
  1029  			conn, resp, err := websocket.DefaultDialer.Dial(u.String(), headers)
  1030  			assert.NoError(t, err)
  1031  			assert.Equal(t, c.expectedStatusCode, resp.StatusCode)
  1032  
  1033  			defer func() {
  1034  				if conn != nil {
  1035  					conn.Close()
  1036  				}
  1037  			}()
  1038  
  1039  			if c.expectedStatusCode == http.StatusSwitchingProtocols {
  1040  				_, message, err := conn.ReadMessage()
  1041  				assert.NoError(t, err)
  1042  				assert.NotEmpty(t, string(message))
  1043  
  1044  				out := <-buildOut
  1045  				t.Log(out)
  1046  				assert.Contains(t, out, "Terminal is connected, will time out in 2s...")
  1047  				return
  1048  			}
  1049  
  1050  			out := <-buildOut
  1051  			t.Log(out)
  1052  			assert.NotContains(t, out, "Terminal is connected, will time out in 2s...")
  1053  		})
  1054  	}
  1055  }
  1056  
  1057  func TestBuildWithGitCleanFlags(t *testing.T) {
  1058  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
  1059  		jobResponse, err := common.GetSuccessfulBuild()
  1060  		assert.NoError(t, err)
  1061  
  1062  		build, cleanup := newBuild(t, jobResponse, shell)
  1063  		defer cleanup()
  1064  
  1065  		build.Variables = append(build.Variables,
  1066  			common.JobVariable{Key: "GIT_STRATEGY", Value: "fetch"},
  1067  			common.JobVariable{Key: "GIT_CLEAN_FLAGS", Value: "-ffdx cleanup_file"})
  1068  
  1069  		// Run build and save file
  1070  		err = runBuild(t, build)
  1071  		require.NoError(t, err)
  1072  
  1073  		excludedFilePath := filepath.Join(build.BuildDir, "excluded_file")
  1074  		cleanUpFilePath := filepath.Join(build.BuildDir, "cleanup_file")
  1075  
  1076  		err = ioutil.WriteFile(excludedFilePath, []byte{}, os.ModePerm)
  1077  		require.NoError(t, err)
  1078  		err = ioutil.WriteFile(cleanUpFilePath, []byte{}, os.ModePerm)
  1079  		require.NoError(t, err)
  1080  
  1081  		// Re-run build and ensure that file still exists
  1082  		err = runBuild(t, build)
  1083  		require.NoError(t, err)
  1084  
  1085  		_, err = os.Stat(excludedFilePath)
  1086  		assert.NoError(t, err, "excluded_file does exist")
  1087  		_, err = os.Stat(cleanUpFilePath)
  1088  		assert.Error(t, err, "cleanup_file does not exist")
  1089  	})
  1090  }
  1091  
  1092  // TODO: Remove in 12.0
  1093  func TestBuildNoRefspecs(t *testing.T) {
  1094  	strategies := []string{"clone", "fetch", "none"}
  1095  	expectedOutputs := map[string]string{
  1096  		"clone": "Cloning repository...",
  1097  		"fetch": "Fetching changes...",
  1098  		"none":  "Skipping Git repository setup",
  1099  	}
  1100  
  1101  	shellstest.OnEachShell(t, func(t *testing.T, shell string) {
  1102  		for _, strategy := range strategies {
  1103  			t.Run(fmt.Sprintf("GIT_STRATEGY %s", strategy), func(t *testing.T) {
  1104  				apiBuild, err := common.GetSuccessfulBuild()
  1105  				assert.NoError(t, err)
  1106  
  1107  				apiBuild.GitInfo.Refspecs = []string{}
  1108  				build, cleanup := newBuild(t, apiBuild, shell)
  1109  				defer cleanup()
  1110  
  1111  				build.Variables = append(build.Variables, common.JobVariable{Key: "GIT_STRATEGY", Value: strategy})
  1112  
  1113  				expectedOutput, ok := expectedOutputs[strategy]
  1114  				require.True(t, ok, "missing expectancies for %s strategy", strategy)
  1115  
  1116  				err = runBuild(t, build)
  1117  				require.NoError(t, err)
  1118  
  1119  				// run twice because script behavior changes based on build directory existence
  1120  				out, err := runBuildReturningOutput(t, build)
  1121  				require.NoError(t, err)
  1122  				require.Contains(t, out, expectedOutput)
  1123  
  1124  				for s, notExpected := range expectedOutputs {
  1125  					if s == strategy {
  1126  						continue
  1127  					}
  1128  
  1129  					require.NotContains(t, out, notExpected)
  1130  				}
  1131  			})
  1132  		}
  1133  	})
  1134  }