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-----`