]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/go-getter/get_git.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / go-getter / get_git.go
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 }