diff options
Diffstat (limited to 'vendor/github.com/hashicorp/go-getter/get_git.go')
-rw-r--r-- | vendor/github.com/hashicorp/go-getter/get_git.go | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/go-getter/get_git.go b/vendor/github.com/hashicorp/go-getter/get_git.go new file mode 100644 index 0000000..0728139 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_git.go | |||
@@ -0,0 +1,225 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "encoding/base64" | ||
5 | "fmt" | ||
6 | "io/ioutil" | ||
7 | "net/url" | ||
8 | "os" | ||
9 | "os/exec" | ||
10 | "path/filepath" | ||
11 | "strings" | ||
12 | |||
13 | urlhelper "github.com/hashicorp/go-getter/helper/url" | ||
14 | "github.com/hashicorp/go-version" | ||
15 | ) | ||
16 | |||
17 | // GitGetter is a Getter implementation that will download a module from | ||
18 | // a git repository. | ||
19 | type GitGetter struct{} | ||
20 | |||
21 | func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) { | ||
22 | return ClientModeDir, nil | ||
23 | } | ||
24 | |||
25 | func (g *GitGetter) Get(dst string, u *url.URL) error { | ||
26 | if _, err := exec.LookPath("git"); err != nil { | ||
27 | return fmt.Errorf("git must be available and on the PATH") | ||
28 | } | ||
29 | |||
30 | // Extract some query parameters we use | ||
31 | var ref, sshKey string | ||
32 | q := u.Query() | ||
33 | if len(q) > 0 { | ||
34 | ref = q.Get("ref") | ||
35 | q.Del("ref") | ||
36 | |||
37 | sshKey = q.Get("sshkey") | ||
38 | q.Del("sshkey") | ||
39 | |||
40 | // Copy the URL | ||
41 | var newU url.URL = *u | ||
42 | u = &newU | ||
43 | u.RawQuery = q.Encode() | ||
44 | } | ||
45 | |||
46 | var sshKeyFile string | ||
47 | if sshKey != "" { | ||
48 | // Check that the git version is sufficiently new. | ||
49 | if err := checkGitVersion("2.3"); err != nil { | ||
50 | return fmt.Errorf("Error using ssh key: %v", err) | ||
51 | } | ||
52 | |||
53 | // We have an SSH key - decode it. | ||
54 | raw, err := base64.StdEncoding.DecodeString(sshKey) | ||
55 | if err != nil { | ||
56 | return err | ||
57 | } | ||
58 | |||
59 | // Create a temp file for the key and ensure it is removed. | ||
60 | fh, err := ioutil.TempFile("", "go-getter") | ||
61 | if err != nil { | ||
62 | return err | ||
63 | } | ||
64 | sshKeyFile = fh.Name() | ||
65 | defer os.Remove(sshKeyFile) | ||
66 | |||
67 | // Set the permissions prior to writing the key material. | ||
68 | if err := os.Chmod(sshKeyFile, 0600); err != nil { | ||
69 | return err | ||
70 | } | ||
71 | |||
72 | // Write the raw key into the temp file. | ||
73 | _, err = fh.Write(raw) | ||
74 | fh.Close() | ||
75 | if err != nil { | ||
76 | return err | ||
77 | } | ||
78 | } | ||
79 | |||
80 | // Clone or update the repository | ||
81 | _, err := os.Stat(dst) | ||
82 | if err != nil && !os.IsNotExist(err) { | ||
83 | return err | ||
84 | } | ||
85 | if err == nil { | ||
86 | err = g.update(dst, sshKeyFile, ref) | ||
87 | } else { | ||
88 | err = g.clone(dst, sshKeyFile, u) | ||
89 | } | ||
90 | if err != nil { | ||
91 | return err | ||
92 | } | ||
93 | |||
94 | // Next: check out the proper tag/branch if it is specified, and checkout | ||
95 | if ref != "" { | ||
96 | if err := g.checkout(dst, ref); err != nil { | ||
97 | return err | ||
98 | } | ||
99 | } | ||
100 | |||
101 | // Lastly, download any/all submodules. | ||
102 | return g.fetchSubmodules(dst, sshKeyFile) | ||
103 | } | ||
104 | |||
105 | // GetFile for Git doesn't support updating at this time. It will download | ||
106 | // the file every time. | ||
107 | func (g *GitGetter) GetFile(dst string, u *url.URL) error { | ||
108 | td, err := ioutil.TempDir("", "getter-git") | ||
109 | if err != nil { | ||
110 | return err | ||
111 | } | ||
112 | if err := os.RemoveAll(td); err != nil { | ||
113 | return err | ||
114 | } | ||
115 | |||
116 | // Get the filename, and strip the filename from the URL so we can | ||
117 | // just get the repository directly. | ||
118 | filename := filepath.Base(u.Path) | ||
119 | u.Path = filepath.Dir(u.Path) | ||
120 | |||
121 | // Get the full repository | ||
122 | if err := g.Get(td, u); err != nil { | ||
123 | return err | ||
124 | } | ||
125 | |||
126 | // Copy the single file | ||
127 | u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename))) | ||
128 | if err != nil { | ||
129 | return err | ||
130 | } | ||
131 | |||
132 | fg := &FileGetter{Copy: true} | ||
133 | return fg.GetFile(dst, u) | ||
134 | } | ||
135 | |||
136 | func (g *GitGetter) checkout(dst string, ref string) error { | ||
137 | cmd := exec.Command("git", "checkout", ref) | ||
138 | cmd.Dir = dst | ||
139 | return getRunCommand(cmd) | ||
140 | } | ||
141 | |||
142 | func (g *GitGetter) clone(dst, sshKeyFile string, u *url.URL) error { | ||
143 | cmd := exec.Command("git", "clone", u.String(), dst) | ||
144 | setupGitEnv(cmd, sshKeyFile) | ||
145 | return getRunCommand(cmd) | ||
146 | } | ||
147 | |||
148 | func (g *GitGetter) update(dst, sshKeyFile, ref string) error { | ||
149 | // Determine if we're a branch. If we're NOT a branch, then we just | ||
150 | // switch to master prior to checking out | ||
151 | cmd := exec.Command("git", "show-ref", "-q", "--verify", "refs/heads/"+ref) | ||
152 | cmd.Dir = dst | ||
153 | |||
154 | if getRunCommand(cmd) != nil { | ||
155 | // Not a branch, switch to master. This will also catch non-existent | ||
156 | // branches, in which case we want to switch to master and then | ||
157 | // checkout the proper branch later. | ||
158 | ref = "master" | ||
159 | } | ||
160 | |||
161 | // We have to be on a branch to pull | ||
162 | if err := g.checkout(dst, ref); err != nil { | ||
163 | return err | ||
164 | } | ||
165 | |||
166 | cmd = exec.Command("git", "pull", "--ff-only") | ||
167 | cmd.Dir = dst | ||
168 | setupGitEnv(cmd, sshKeyFile) | ||
169 | return getRunCommand(cmd) | ||
170 | } | ||
171 | |||
172 | // fetchSubmodules downloads any configured submodules recursively. | ||
173 | func (g *GitGetter) fetchSubmodules(dst, sshKeyFile string) error { | ||
174 | cmd := exec.Command("git", "submodule", "update", "--init", "--recursive") | ||
175 | cmd.Dir = dst | ||
176 | setupGitEnv(cmd, sshKeyFile) | ||
177 | return getRunCommand(cmd) | ||
178 | } | ||
179 | |||
180 | // setupGitEnv sets up the environment for the given command. This is used to | ||
181 | // pass configuration data to git and ssh and enables advanced cloning methods. | ||
182 | func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) { | ||
183 | var sshOpts []string | ||
184 | |||
185 | if sshKeyFile != "" { | ||
186 | // We have an SSH key temp file configured, tell ssh about this. | ||
187 | sshOpts = append(sshOpts, "-i", sshKeyFile) | ||
188 | } | ||
189 | |||
190 | cmd.Env = append(os.Environ(), | ||
191 | // Set the ssh command to use for clones. | ||
192 | "GIT_SSH_COMMAND=ssh "+strings.Join(sshOpts, " "), | ||
193 | ) | ||
194 | } | ||
195 | |||
196 | // checkGitVersion is used to check the version of git installed on the system | ||
197 | // against a known minimum version. Returns an error if the installed version | ||
198 | // is older than the given minimum. | ||
199 | func checkGitVersion(min string) error { | ||
200 | want, err := version.NewVersion(min) | ||
201 | if err != nil { | ||
202 | return err | ||
203 | } | ||
204 | |||
205 | out, err := exec.Command("git", "version").Output() | ||
206 | if err != nil { | ||
207 | return err | ||
208 | } | ||
209 | |||
210 | fields := strings.Fields(string(out)) | ||
211 | if len(fields) != 3 { | ||
212 | return fmt.Errorf("Unexpected 'git version' output: %q", string(out)) | ||
213 | } | ||
214 | |||
215 | have, err := version.NewVersion(fields[2]) | ||
216 | if err != nil { | ||
217 | return err | ||
218 | } | ||
219 | |||
220 | if have.LessThan(want) { | ||
221 | return fmt.Errorf("Required git version = %s, have %s", want, have) | ||
222 | } | ||
223 | |||
224 | return nil | ||
225 | } | ||