github.com/remind101/go-getter@v0.0.0-20180809191950-4bda8fa99001/get_git_test.go (about)

     1  package getter
     2  
     3  import (
     4  	"encoding/base64"
     5  	"io/ioutil"
     6  	"net/url"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"testing"
    13  )
    14  
    15  var testHasGit bool
    16  
    17  func init() {
    18  	if _, err := exec.LookPath("git"); err == nil {
    19  		testHasGit = true
    20  	}
    21  }
    22  
    23  func TestGitGetter_impl(t *testing.T) {
    24  	var _ Getter = new(GitGetter)
    25  }
    26  
    27  func TestGitGetter(t *testing.T) {
    28  	if !testHasGit {
    29  		t.Log("git not found, skipping")
    30  		t.Skip()
    31  	}
    32  
    33  	g := new(GitGetter)
    34  	dst := tempDir(t)
    35  
    36  	repo := testGitRepo(t, "basic")
    37  	repo.commitFile("foo.txt", "hello")
    38  
    39  	// With a dir that doesn't exist
    40  	if err := g.Get(dst, repo.url); err != nil {
    41  		t.Fatalf("err: %s", err)
    42  	}
    43  
    44  	// Verify the main file exists
    45  	mainPath := filepath.Join(dst, "foo.txt")
    46  	if _, err := os.Stat(mainPath); err != nil {
    47  		t.Fatalf("err: %s", err)
    48  	}
    49  }
    50  
    51  func TestGitGetter_branch(t *testing.T) {
    52  	if !testHasGit {
    53  		t.Log("git not found, skipping")
    54  		t.Skip()
    55  	}
    56  
    57  	g := new(GitGetter)
    58  	dst := tempDir(t)
    59  
    60  	repo := testGitRepo(t, "branch")
    61  	repo.git("checkout", "-b", "test-branch")
    62  	repo.commitFile("branch.txt", "branch")
    63  
    64  	q := repo.url.Query()
    65  	q.Add("ref", "test-branch")
    66  	repo.url.RawQuery = q.Encode()
    67  
    68  	if err := g.Get(dst, repo.url); err != nil {
    69  		t.Fatalf("err: %s", err)
    70  	}
    71  
    72  	// Verify the main file exists
    73  	mainPath := filepath.Join(dst, "branch.txt")
    74  	if _, err := os.Stat(mainPath); err != nil {
    75  		t.Fatalf("err: %s", err)
    76  	}
    77  
    78  	// Get again should work
    79  	if err := g.Get(dst, repo.url); err != nil {
    80  		t.Fatalf("err: %s", err)
    81  	}
    82  
    83  	// Verify the main file exists
    84  	mainPath = filepath.Join(dst, "branch.txt")
    85  	if _, err := os.Stat(mainPath); err != nil {
    86  		t.Fatalf("err: %s", err)
    87  	}
    88  }
    89  
    90  func TestGitGetter_branchUpdate(t *testing.T) {
    91  	if !testHasGit {
    92  		t.Log("git not found, skipping")
    93  		t.Skip()
    94  	}
    95  
    96  	g := new(GitGetter)
    97  	dst := tempDir(t)
    98  
    99  	// First setup the state with a fresh branch
   100  	repo := testGitRepo(t, "branch-update")
   101  	repo.git("checkout", "-b", "test-branch")
   102  	repo.commitFile("branch.txt", "branch")
   103  
   104  	// Get the "test-branch" branch
   105  	q := repo.url.Query()
   106  	q.Add("ref", "test-branch")
   107  	repo.url.RawQuery = q.Encode()
   108  	if err := g.Get(dst, repo.url); err != nil {
   109  		t.Fatalf("err: %s", err)
   110  	}
   111  
   112  	// Verify the main file exists
   113  	mainPath := filepath.Join(dst, "branch.txt")
   114  	if _, err := os.Stat(mainPath); err != nil {
   115  		t.Fatalf("err: %s", err)
   116  	}
   117  
   118  	// Commit an update to the branch
   119  	repo.commitFile("branch-update.txt", "branch-update")
   120  
   121  	// Get again should work
   122  	if err := g.Get(dst, repo.url); err != nil {
   123  		t.Fatalf("err: %s", err)
   124  	}
   125  
   126  	// Verify the main file exists
   127  	mainPath = filepath.Join(dst, "branch-update.txt")
   128  	if _, err := os.Stat(mainPath); err != nil {
   129  		t.Fatalf("err: %s", err)
   130  	}
   131  }
   132  
   133  func TestGitGetter_tag(t *testing.T) {
   134  	if !testHasGit {
   135  		t.Log("git not found, skipping")
   136  		t.Skip()
   137  	}
   138  
   139  	g := new(GitGetter)
   140  	dst := tempDir(t)
   141  
   142  	repo := testGitRepo(t, "tag")
   143  	repo.commitFile("tag.txt", "tag")
   144  	repo.git("tag", "v1.0")
   145  
   146  	q := repo.url.Query()
   147  	q.Add("ref", "v1.0")
   148  	repo.url.RawQuery = q.Encode()
   149  
   150  	if err := g.Get(dst, repo.url); err != nil {
   151  		t.Fatalf("err: %s", err)
   152  	}
   153  
   154  	// Verify the main file exists
   155  	mainPath := filepath.Join(dst, "tag.txt")
   156  	if _, err := os.Stat(mainPath); err != nil {
   157  		t.Fatalf("err: %s", err)
   158  	}
   159  
   160  	// Get again should work
   161  	if err := g.Get(dst, repo.url); err != nil {
   162  		t.Fatalf("err: %s", err)
   163  	}
   164  
   165  	// Verify the main file exists
   166  	mainPath = filepath.Join(dst, "tag.txt")
   167  	if _, err := os.Stat(mainPath); err != nil {
   168  		t.Fatalf("err: %s", err)
   169  	}
   170  }
   171  
   172  func TestGitGetter_GetFile(t *testing.T) {
   173  	if !testHasGit {
   174  		t.Log("git not found, skipping")
   175  		t.Skip()
   176  	}
   177  
   178  	g := new(GitGetter)
   179  	dst := tempFile(t)
   180  
   181  	repo := testGitRepo(t, "file")
   182  	repo.commitFile("file.txt", "hello")
   183  
   184  	// Download the file
   185  	repo.url.Path = filepath.Join(repo.url.Path, "file.txt")
   186  	if err := g.GetFile(dst, repo.url); err != nil {
   187  		t.Fatalf("err: %s", err)
   188  	}
   189  
   190  	// Verify the main file exists
   191  	if _, err := os.Stat(dst); err != nil {
   192  		t.Fatalf("err: %s", err)
   193  	}
   194  	assertContents(t, dst, "hello")
   195  }
   196  
   197  func TestGitGetter_gitVersion(t *testing.T) {
   198  	dir, err := ioutil.TempDir("", "go-getter")
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	defer os.RemoveAll(dir)
   203  
   204  	script := filepath.Join(dir, "git")
   205  	err = ioutil.WriteFile(
   206  		script,
   207  		[]byte("#!/bin/sh\necho \"git version 2.0 (Some Metadata Here)\n\""),
   208  		0700)
   209  	if err != nil {
   210  		t.Fatal(err)
   211  	}
   212  
   213  	defer func(v string) {
   214  		os.Setenv("PATH", v)
   215  	}(os.Getenv("PATH"))
   216  
   217  	os.Setenv("PATH", dir)
   218  
   219  	// Asking for a higher version throws an error
   220  	if err := checkGitVersion("2.3"); err == nil {
   221  		t.Fatal("expect git version error")
   222  	}
   223  
   224  	// Passes when version is satisfied
   225  	if err := checkGitVersion("1.9"); err != nil {
   226  		t.Fatal(err)
   227  	}
   228  }
   229  
   230  func TestGitGetter_sshKey(t *testing.T) {
   231  	if !testHasGit {
   232  		t.Log("git not found, skipping")
   233  		t.Skip()
   234  	}
   235  
   236  	g := new(GitGetter)
   237  	dst := tempDir(t)
   238  
   239  	encodedKey := base64.StdEncoding.EncodeToString([]byte(testGitToken))
   240  
   241  	u, err := url.Parse("ssh://git@github.com/hashicorp/test-private-repo" +
   242  		"?sshkey=" + encodedKey)
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  
   247  	if err := g.Get(dst, u); err != nil {
   248  		t.Fatalf("err: %s", err)
   249  	}
   250  
   251  	readmePath := filepath.Join(dst, "README.md")
   252  	if _, err := os.Stat(readmePath); err != nil {
   253  		t.Fatalf("err: %s", err)
   254  	}
   255  }
   256  
   257  func TestGitGetter_submodule(t *testing.T) {
   258  	if !testHasGit {
   259  		t.Log("git not found, skipping")
   260  		t.Skip()
   261  	}
   262  
   263  	g := new(GitGetter)
   264  	dst := tempDir(t)
   265  
   266  	// Set up the grandchild
   267  	gc := testGitRepo(t, "grandchild")
   268  	gc.commitFile("grandchild.txt", "grandchild")
   269  
   270  	// Set up the child
   271  	c := testGitRepo(t, "child")
   272  	c.commitFile("child.txt", "child")
   273  	c.git("submodule", "add", gc.dir)
   274  	c.git("commit", "-m", "Add grandchild submodule")
   275  
   276  	// Set up the parent
   277  	p := testGitRepo(t, "parent")
   278  	p.commitFile("parent.txt", "parent")
   279  	p.git("submodule", "add", c.dir)
   280  	p.git("commit", "-m", "Add child submodule")
   281  
   282  	// Clone the root repository
   283  	if err := g.Get(dst, p.url); err != nil {
   284  		t.Fatalf("err: %s", err)
   285  	}
   286  
   287  	// Check that the files exist
   288  	for _, path := range []string{
   289  		filepath.Join(dst, "parent.txt"),
   290  		filepath.Join(dst, "child", "child.txt"),
   291  		filepath.Join(dst, "child", "grandchild", "grandchild.txt"),
   292  	} {
   293  		if _, err := os.Stat(path); err != nil {
   294  			t.Fatalf("err: %s", err)
   295  		}
   296  	}
   297  }
   298  
   299  func TestGitGetter_setupGitEnv_sshKey(t *testing.T) {
   300  	if runtime.GOOS == "windows" {
   301  		t.Skipf("skipping on windows since the test requires sh")
   302  		return
   303  	}
   304  
   305  	cmd := exec.Command("/bin/sh", "-c", "echo $GIT_SSH_COMMAND")
   306  	setupGitEnv(cmd, "/tmp/foo.pem")
   307  	out, err := cmd.Output()
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  
   312  	actual := strings.TrimSpace(string(out))
   313  	if actual != "ssh -i /tmp/foo.pem" {
   314  		t.Fatalf("unexpected GIT_SSH_COMMAND: %q", actual)
   315  	}
   316  }
   317  
   318  func TestGitGetter_setupGitEnvWithExisting_sshKey(t *testing.T) {
   319  	if runtime.GOOS == "windows" {
   320  		t.Skipf("skipping on windows since the test requires sh")
   321  		return
   322  	}
   323  
   324  	// start with an existing ssh command configuration
   325  	os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no")
   326  	defer os.Setenv("GIT_SSH_COMMAND", "")
   327  
   328  	cmd := exec.Command("/bin/sh", "-c", "echo $GIT_SSH_COMMAND")
   329  	setupGitEnv(cmd, "/tmp/foo.pem")
   330  	out, err := cmd.Output()
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  
   335  	actual := strings.TrimSpace(string(out))
   336  	if actual != "ssh -o StrictHostKeyChecking=no -i /tmp/foo.pem" {
   337  		t.Fatalf("unexpected GIT_SSH_COMMAND: %q", actual)
   338  	}
   339  }
   340  
   341  // gitRepo is a helper struct which controls a single temp git repo.
   342  type gitRepo struct {
   343  	t   *testing.T
   344  	url *url.URL
   345  	dir string
   346  }
   347  
   348  // testGitRepo creates a new test git repository.
   349  func testGitRepo(t *testing.T, name string) *gitRepo {
   350  	dir, err := ioutil.TempDir("", "go-getter")
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  	dir = filepath.Join(dir, name)
   355  	if err := os.Mkdir(dir, 0700); err != nil {
   356  		t.Fatal(err)
   357  	}
   358  
   359  	r := &gitRepo{
   360  		t:   t,
   361  		dir: dir,
   362  	}
   363  
   364  	url, err := url.Parse("file://" + r.dir)
   365  	if err != nil {
   366  		t.Fatal(err)
   367  	}
   368  	r.url = url
   369  
   370  	r.git("init")
   371  	r.git("config", "user.name", "go-getter")
   372  	r.git("config", "user.email", "go-getter@hashicorp.com")
   373  
   374  	return r
   375  }
   376  
   377  // git runs a git command against the repo.
   378  func (r *gitRepo) git(args ...string) {
   379  	cmd := exec.Command("git", args...)
   380  	cmd.Dir = r.dir
   381  	if err := cmd.Run(); err != nil {
   382  		r.t.Fatal(err)
   383  	}
   384  }
   385  
   386  // commitFile writes and commits a text file to the repo.
   387  func (r *gitRepo) commitFile(file, content string) {
   388  	path := filepath.Join(r.dir, file)
   389  	if err := ioutil.WriteFile(path, []byte(content), 0600); err != nil {
   390  		r.t.Fatal(err)
   391  	}
   392  	r.git("add", file)
   393  	r.git("commit", "-m", "Adding "+file)
   394  }
   395  
   396  // This is a read-only deploy key for an empty test repository.
   397  // Note: This is split over multiple lines to avoid being disabled by key
   398  // scanners automatically.
   399  var testGitToken = `-----BEGIN RSA PRIVATE KEY-----
   400  MIIEpAIBAAKCAQEA9cHsxCl3Jjgu9DHpwvmfFOl1XEdY+ShHDR/cMnzJ5ddk5/oV
   401  Wy6EWatvyHZfRSZMwzv4PtKeUPm6iXjqWp4xdWU9khlPzozyj+U9Fq70TRVUW9E5
   402  T1XdQVwJE421yffr4VMMwu60wBqjI1epapH2i2inYvw9Zl9X2MXq0+jTvFvDerbT
   403  mDtfStDPljenELAIZtWVETSvbI46gALwbxbM2292ZUIL4D6jRz0aZMmyy/twYv8r
   404  9WGJLwmYzU518Ie7zqKW/mCTdTrV0WRiDj0MeRaPgrGY9amuHE4r9iG/cJkwpKAO
   405  Ccz0Hs6i89u9vZnTqZU9V7weJqRAQcMjXXR6yQIDAQABAoIBAQDBzICKnGxiTlHw
   406  rd+6qqChnAy5jWYDbZjCJ8q8YZ3RS08+g/8NXZxvHftTqM0uOaq1FviHig3gq15H
   407  hHvCpBc6jXDFYoKFzq6FfO/0kFkE5HoWweIgxwRow0xBCDJAJ+ryUEyy+Ay/pQHb
   408  IAjwilRS0V+WdnVw4mTjBAhPvb4jPOo97Yfy3PYUyx2F3newkqXOZy+zx3G/ANoa
   409  ncypfMGyy76sfCWKqw4J1gVkVQLwbB6gQkXUFGYwY9sRrxbG93kQw76Flc/E/s52
   410  62j4v1IM0fq0t/St+Y/+s6Lkw` + `aqt3ft1nsqWcRaVDdqvMfkzgJGXlw0bGzJG5MEQ
   411  AIBq3dHRAoGBAP8OeG/DKG2Z1VmSfzuz1pas1fbZ+F7venOBrjez3sKlb3Pyl2aH
   412  mt2wjaTUi5v10VrHgYtOEdqyhQeUSYydWXIBKNMag0NLLrfFUKZK+57wrHWFdFjn
   413  VgpsdkLSNTOZpC8gA5OaJ+36IcOPfGqyyP9wuuRoaYnVT1KEzqLa9FEFAoGBAPaq
   414  pglwhil2rxjJE4zq0afQLNpAfi7Xqcrepij+xvJIcIj7nawxXuPxqRFxONE/h3yX
   415  zkybO8wLdbHX9Iw/wc1j50Uf1Z5gHdLf7/hQJoWKpz1RnkWRy6CYON8v1tpVp0tb
   416  OAajR/kZnzebq2mfa7pyy5zDCX++2kp/dcFwHf31AoGAE8oupBVTZLWj7TBFuP8q
   417  LkS40U92Sv9v09iDCQVmylmFvUxcXPM2m+7f/qMTNgWrucxzC7kB/6MMWVszHbrz
   418  vrnCTibnemgx9sZTjKOSxHFOIEw7i85fSa3Cu0qOIDPSnmlwfZpfcMKQrhjLAYhf
   419  uhooFiLX1X78iZ2OXup4PHUCgYEAsmBrm83sp1V1gAYBBlnVbXakyNv0pCk/Vz61
   420  iFXeRt1NzDGxLxGw3kQnED8BaIh5kQcyn8Fud7sdzJMv/LAqlT4Ww60mzNYTGyjo
   421  H3jOsqm3ESfRvduWFreeAQBWbiOczGjV1i8D4EbAFfWT+tjXjchwKBf+6Yt5zn/o
   422  Bw/uEHUCgYAFs+JPOR25oRyBs7ujrMo/OY1z/eXTVVgZxY+tYGe1FJqDeFyR7ytK
   423  +JBB1MuDwQKGm2wSIXdCzTNoIx2B9zTseiPTwT8G7vqNFhXoIaTBp4P2xIQb45mJ
   424  7GkTsMBHwpSMOXgX9Weq3v5xOJ2WxVtjENmd6qzxcYCO5lP15O17hA==
   425  -----END RSA PRIVATE KEY-----`