git.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package utils
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "net/url"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "github.com/docker/docker/pkg/symlink"
  12. "github.com/docker/docker/pkg/urlutil"
  13. )
  14. func GitClone(remoteURL string) (string, error) {
  15. if !urlutil.IsGitTransport(remoteURL) {
  16. remoteURL = "https://" + remoteURL
  17. }
  18. root, err := ioutil.TempDir("", "docker-build-git")
  19. if err != nil {
  20. return "", err
  21. }
  22. u, err := url.Parse(remoteURL)
  23. if err != nil {
  24. return "", err
  25. }
  26. fragment := u.Fragment
  27. clone := cloneArgs(u, root)
  28. if output, err := git(clone...); err != nil {
  29. return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
  30. }
  31. return checkoutGit(fragment, root)
  32. }
  33. func cloneArgs(remoteURL *url.URL, root string) []string {
  34. args := []string{"clone", "--recursive"}
  35. shallow := len(remoteURL.Fragment) == 0
  36. if shallow && strings.HasPrefix(remoteURL.Scheme, "http") {
  37. res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL))
  38. if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" {
  39. shallow = false
  40. }
  41. }
  42. if shallow {
  43. args = append(args, "--depth", "1")
  44. }
  45. if remoteURL.Fragment != "" {
  46. remoteURL.Fragment = ""
  47. }
  48. return append(args, remoteURL.String(), root)
  49. }
  50. func checkoutGit(fragment, root string) (string, error) {
  51. refAndDir := strings.SplitN(fragment, ":", 2)
  52. if len(refAndDir[0]) != 0 {
  53. if output, err := gitWithinDir(root, "checkout", refAndDir[0]); err != nil {
  54. return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
  55. }
  56. }
  57. if len(refAndDir) > 1 && len(refAndDir[1]) != 0 {
  58. newCtx, err := symlink.FollowSymlinkInScope(filepath.Join(root, refAndDir[1]), root)
  59. if err != nil {
  60. return "", fmt.Errorf("Error setting git context, %q not within git root: %s", refAndDir[1], err)
  61. }
  62. fi, err := os.Stat(newCtx)
  63. if err != nil {
  64. return "", err
  65. }
  66. if !fi.IsDir() {
  67. return "", fmt.Errorf("Error setting git context, not a directory: %s", newCtx)
  68. }
  69. root = newCtx
  70. }
  71. return root, nil
  72. }
  73. func gitWithinDir(dir string, args ...string) ([]byte, error) {
  74. a := []string{"--work-tree", dir, "--git-dir", filepath.Join(dir, ".git")}
  75. return git(append(a, args...)...)
  76. }
  77. func git(args ...string) ([]byte, error) {
  78. return exec.Command("git", args...).CombinedOutput()
  79. }