diff --git a/config/module/get.go b/config/module/get.go index 270ca815a6..7c4761d6e9 100644 --- a/config/module/get.go +++ b/config/module/get.go @@ -1,8 +1,11 @@ package module import ( + "bytes" "fmt" "net/url" + "os/exec" + "syscall" ) // Getter defines the interface that schemes must implement to download @@ -24,6 +27,7 @@ var Getters map[string]Getter func init() { Getters = map[string]Getter{ "file": new(FileGetter), + "git": new(GitGetter), } } @@ -51,3 +55,27 @@ func Get(dst, src string) error { return err } + +// getRunCommand is a helper that will run a command and capture the output +// in the case an error happens. +func getRunCommand(cmd *exec.Cmd) error { + var buf bytes.Buffer + cmd.Stdout = &buf + cmd.Stderr = &buf + err := cmd.Run() + if err == nil { + return nil + } + if exiterr, ok := err.(*exec.ExitError); ok { + // The program has exited with an exit code != 0 + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + return fmt.Errorf( + "%s exited with %d: %s", + cmd.Path, + status.ExitStatus(), + buf.String()) + } + } + + return fmt.Errorf("error running %s: %s", cmd.Path, buf.String()) +} diff --git a/config/module/get_git.go b/config/module/get_git.go new file mode 100644 index 0000000000..132bd8e11c --- /dev/null +++ b/config/module/get_git.go @@ -0,0 +1,39 @@ +package module + +import ( + "fmt" + "net/url" + "os" + "os/exec" +) + +// GitGetter is a Getter implementation that will download a module from +// a git repository. +type GitGetter struct{} + +func (g *GitGetter) Get(dst string, u *url.URL) error { + if _, err := exec.LookPath("git"); err != nil { + return fmt.Errorf("git must be available and on the PATH") + } + + _, err := os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return err + } + if err == nil { + return g.update(dst, u) + } + + return g.clone(dst, u) +} + +func (g *GitGetter) clone(dst string, u *url.URL) error { + cmd := exec.Command("git", "clone", u.String(), dst) + return getRunCommand(cmd) +} + +func (g *GitGetter) update(dst string, u *url.URL) error { + cmd := exec.Command("git", "pull", "--ff-only") + cmd.Dir = dst + return getRunCommand(cmd) +}