LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2010 The Go Authors. All rights reserved. | 1 // Copyright 2010 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Download remote packages. | 5 // Download remote packages. |
6 | 6 |
7 package main | 7 package main |
8 | 8 |
9 import ( | 9 import ( |
10 "bytes" | 10 "bytes" |
| 11 "errors" |
11 "exec" | 12 "exec" |
12 "fmt" | 13 "fmt" |
13 "http" | 14 "http" |
14 "json" | 15 "json" |
15 "os" | 16 "os" |
16 "path/filepath" | 17 "path/filepath" |
17 "regexp" | 18 "regexp" |
18 "runtime" | 19 "runtime" |
19 "strconv" | 20 "strconv" |
20 "strings" | 21 "strings" |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
113 tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"), | 114 tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"), |
114 check: "info", | 115 check: "info", |
115 protocols: []string{"https", "http", "bzr"}, | 116 protocols: []string{"https", "http", "bzr"}, |
116 suffix: ".bzr", | 117 suffix: ".bzr", |
117 } | 118 } |
118 | 119 |
119 var vcsList = []*vcs{&git, &hg, &bzr, &svn} | 120 var vcsList = []*vcs{&git, &hg, &bzr, &svn} |
120 | 121 |
121 type host struct { | 122 type host struct { |
122 pattern *regexp.Regexp | 123 pattern *regexp.Regexp |
123 » getVcs func(repo, path string) (*vcsMatch, os.Error) | 124 » getVcs func(repo, path string) (*vcsMatch, error) |
124 } | 125 } |
125 | 126 |
126 var knownHosts = []host{ | 127 var knownHosts = []host{ |
127 { | 128 { |
128 regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|git|hg))
(/[a-z0-9A-Z_.\-/]*)?$`), | 129 regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|git|hg))
(/[a-z0-9A-Z_.\-/]*)?$`), |
129 googleVcs, | 130 googleVcs, |
130 }, | 131 }, |
131 { | 132 { |
132 regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\
-]+)(/[a-z0-9A-Z_.\-/]*)?$`), | 133 regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\
-]+)(/[a-z0-9A-Z_.\-/]*)?$`), |
133 githubVcs, | 134 githubVcs, |
134 }, | 135 }, |
135 { | 136 { |
136 regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z
_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), | 137 regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z
_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), |
137 bitbucketVcs, | 138 bitbucketVcs, |
138 }, | 139 }, |
139 { | 140 { |
140 regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A
-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0
-9A-Z_.\-/]+)?$`), | 141 regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A
-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0
-9A-Z_.\-/]+)?$`), |
141 launchpadVcs, | 142 launchpadVcs, |
142 }, | 143 }, |
143 } | 144 } |
144 | 145 |
145 type vcsMatch struct { | 146 type vcsMatch struct { |
146 *vcs | 147 *vcs |
147 prefix, repo string | 148 prefix, repo string |
148 } | 149 } |
149 | 150 |
150 func googleVcs(repo, path string) (*vcsMatch, os.Error) { | 151 func googleVcs(repo, path string) (*vcsMatch, error) { |
151 parts := strings.SplitN(repo, "/", 2) | 152 parts := strings.SplitN(repo, "/", 2) |
152 url := "https://" + repo | 153 url := "https://" + repo |
153 switch parts[1] { | 154 switch parts[1] { |
154 case "svn": | 155 case "svn": |
155 return &vcsMatch{&svn, repo, url}, nil | 156 return &vcsMatch{&svn, repo, url}, nil |
156 case "git": | 157 case "git": |
157 return &vcsMatch{&git, repo, url}, nil | 158 return &vcsMatch{&git, repo, url}, nil |
158 case "hg": | 159 case "hg": |
159 return &vcsMatch{&hg, repo, url}, nil | 160 return &vcsMatch{&hg, repo, url}, nil |
160 } | 161 } |
161 » return nil, os.NewError("unsupported googlecode vcs: " + parts[1]) | 162 » return nil, errors.New("unsupported googlecode vcs: " + parts[1]) |
162 } | 163 } |
163 | 164 |
164 func githubVcs(repo, path string) (*vcsMatch, os.Error) { | 165 func githubVcs(repo, path string) (*vcsMatch, error) { |
165 if strings.HasSuffix(repo, ".git") { | 166 if strings.HasSuffix(repo, ".git") { |
166 » » return nil, os.NewError("path must not include .git suffix") | 167 » » return nil, errors.New("path must not include .git suffix") |
167 } | 168 } |
168 return &vcsMatch{&git, repo, "http://" + repo + ".git"}, nil | 169 return &vcsMatch{&git, repo, "http://" + repo + ".git"}, nil |
169 } | 170 } |
170 | 171 |
171 func bitbucketVcs(repo, path string) (*vcsMatch, os.Error) { | 172 func bitbucketVcs(repo, path string) (*vcsMatch, error) { |
172 const bitbucketApiUrl = "https://api.bitbucket.org/1.0/repositories/" | 173 const bitbucketApiUrl = "https://api.bitbucket.org/1.0/repositories/" |
173 | 174 |
174 if strings.HasSuffix(repo, ".git") { | 175 if strings.HasSuffix(repo, ".git") { |
175 » » return nil, os.NewError("path must not include .git suffix") | 176 » » return nil, errors.New("path must not include .git suffix") |
176 } | 177 } |
177 | 178 |
178 parts := strings.SplitN(repo, "/", 2) | 179 parts := strings.SplitN(repo, "/", 2) |
179 | 180 |
180 // Ask the bitbucket API what kind of repository this is. | 181 // Ask the bitbucket API what kind of repository this is. |
181 r, err := http.Get(bitbucketApiUrl + parts[1]) | 182 r, err := http.Get(bitbucketApiUrl + parts[1]) |
182 if err != nil { | 183 if err != nil { |
183 return nil, fmt.Errorf("error querying BitBucket API: %v", err) | 184 return nil, fmt.Errorf("error querying BitBucket API: %v", err) |
184 } | 185 } |
185 defer r.Body.Close() | 186 defer r.Body.Close() |
(...skipping 12 matching lines...) Expand all Loading... |
198 } | 199 } |
199 | 200 |
200 // Now we should be able to construct a vcsMatch structure | 201 // Now we should be able to construct a vcsMatch structure |
201 switch response.Vcs { | 202 switch response.Vcs { |
202 case "git": | 203 case "git": |
203 return &vcsMatch{&git, repo, "http://" + repo + ".git"}, nil | 204 return &vcsMatch{&git, repo, "http://" + repo + ".git"}, nil |
204 case "hg": | 205 case "hg": |
205 return &vcsMatch{&hg, repo, "http://" + repo}, nil | 206 return &vcsMatch{&hg, repo, "http://" + repo}, nil |
206 } | 207 } |
207 | 208 |
208 » return nil, os.NewError("unsupported bitbucket vcs: " + response.Vcs) | 209 » return nil, errors.New("unsupported bitbucket vcs: " + response.Vcs) |
209 } | 210 } |
210 | 211 |
211 func launchpadVcs(repo, path string) (*vcsMatch, os.Error) { | 212 func launchpadVcs(repo, path string) (*vcsMatch, error) { |
212 return &vcsMatch{&bzr, repo, "https://" + repo}, nil | 213 return &vcsMatch{&bzr, repo, "https://" + repo}, nil |
213 } | 214 } |
214 | 215 |
215 // findPublicRepo checks whether pkg is located at one of | 216 // findPublicRepo checks whether pkg is located at one of |
216 // the supported code hosting sites and, if so, returns a match. | 217 // the supported code hosting sites and, if so, returns a match. |
217 func findPublicRepo(pkg string) (*vcsMatch, os.Error) { | 218 func findPublicRepo(pkg string) (*vcsMatch, error) { |
218 for _, host := range knownHosts { | 219 for _, host := range knownHosts { |
219 if hm := host.pattern.FindStringSubmatch(pkg); hm != nil { | 220 if hm := host.pattern.FindStringSubmatch(pkg); hm != nil { |
220 return host.getVcs(hm[1], hm[2]) | 221 return host.getVcs(hm[1], hm[2]) |
221 } | 222 } |
222 } | 223 } |
223 return nil, nil | 224 return nil, nil |
224 } | 225 } |
225 | 226 |
226 // findAnyRepo looks for a vcs suffix in pkg (.git, etc) and returns a match. | 227 // findAnyRepo looks for a vcs suffix in pkg (.git, etc) and returns a match. |
227 func findAnyRepo(pkg string) (*vcsMatch, os.Error) { | 228 func findAnyRepo(pkg string) (*vcsMatch, error) { |
228 for _, v := range vcsList { | 229 for _, v := range vcsList { |
229 i := strings.Index(pkg+"/", v.suffix+"/") | 230 i := strings.Index(pkg+"/", v.suffix+"/") |
230 if i < 0 { | 231 if i < 0 { |
231 continue | 232 continue |
232 } | 233 } |
233 if !strings.Contains(pkg[:i], "/") { | 234 if !strings.Contains(pkg[:i], "/") { |
234 continue // don't match vcs suffix in the host name | 235 continue // don't match vcs suffix in the host name |
235 } | 236 } |
236 if m := v.find(pkg[:i]); m != nil { | 237 if m := v.find(pkg[:i]); m != nil { |
237 return m, nil | 238 return m, nil |
(...skipping 27 matching lines...) Expand all Loading... |
265 return false | 266 return false |
266 } | 267 } |
267 parts = strings.Split(parts[0], ".") | 268 parts = strings.Split(parts[0], ".") |
268 if len(parts) < 2 || len(parts[len(parts)-1]) < 2 { | 269 if len(parts) < 2 || len(parts[len(parts)-1]) < 2 { |
269 return false | 270 return false |
270 } | 271 } |
271 return true | 272 return true |
272 } | 273 } |
273 | 274 |
274 // download checks out or updates pkg from the remote server. | 275 // download checks out or updates pkg from the remote server. |
275 func download(pkg, srcDir string) (public bool, err os.Error) { | 276 func download(pkg, srcDir string) (public bool, err error) { |
276 if strings.Contains(pkg, "..") { | 277 if strings.Contains(pkg, "..") { |
277 » » err = os.NewError("invalid path (contains ..)") | 278 » » err = errors.New("invalid path (contains ..)") |
278 return | 279 return |
279 } | 280 } |
280 m, err := findPublicRepo(pkg) | 281 m, err := findPublicRepo(pkg) |
281 if err != nil { | 282 if err != nil { |
282 return | 283 return |
283 } | 284 } |
284 if m != nil { | 285 if m != nil { |
285 public = true | 286 public = true |
286 } else { | 287 } else { |
287 m, err = findAnyRepo(pkg) | 288 m, err = findAnyRepo(pkg) |
288 if err != nil { | 289 if err != nil { |
289 return | 290 return |
290 } | 291 } |
291 } | 292 } |
292 if m == nil { | 293 if m == nil { |
293 » » err = os.NewError("cannot download: " + pkg) | 294 » » err = errors.New("cannot download: " + pkg) |
294 return | 295 return |
295 } | 296 } |
296 err = m.checkoutRepo(srcDir, m.prefix, m.repo) | 297 err = m.checkoutRepo(srcDir, m.prefix, m.repo) |
297 return | 298 return |
298 } | 299 } |
299 | 300 |
300 // updateRepo gets a list of tags in the repository and | 301 // updateRepo gets a list of tags in the repository and |
301 // checks out the tag closest to the current runtime.Version. | 302 // checks out the tag closest to the current runtime.Version. |
302 // If no matching tag is found, it just updates to tip. | 303 // If no matching tag is found, it just updates to tip. |
303 func (v *vcs) updateRepo(dst string) os.Error { | 304 func (v *vcs) updateRepo(dst string) error { |
304 if v.tagList == "" || v.tagListRe == nil { | 305 if v.tagList == "" || v.tagListRe == nil { |
305 // TODO(adg): fix for svn | 306 // TODO(adg): fix for svn |
306 return run(dst, nil, v.cmd, v.update) | 307 return run(dst, nil, v.cmd, v.update) |
307 } | 308 } |
308 | 309 |
309 // Get tag list. | 310 // Get tag list. |
310 stderr := new(bytes.Buffer) | 311 stderr := new(bytes.Buffer) |
311 cmd := exec.Command(v.cmd, v.tagList) | 312 cmd := exec.Command(v.cmd, v.tagList) |
312 cmd.Dir = dst | 313 cmd.Dir = dst |
313 cmd.Stderr = stderr | 314 cmd.Stderr = stderr |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 } | 376 } |
376 } | 377 } |
377 return match | 378 return match |
378 } | 379 } |
379 | 380 |
380 // checkoutRepo checks out repo into dst using vcs. | 381 // checkoutRepo checks out repo into dst using vcs. |
381 // It tries to check out (or update, if the dst already | 382 // It tries to check out (or update, if the dst already |
382 // exists and -u was specified on the command line) | 383 // exists and -u was specified on the command line) |
383 // the repository at tag/branch "release". If there is no | 384 // the repository at tag/branch "release". If there is no |
384 // such tag or branch, it falls back to the repository tip. | 385 // such tag or branch, it falls back to the repository tip. |
385 func (vcs *vcs) checkoutRepo(srcDir, pkgprefix, repo string) os.Error { | 386 func (vcs *vcs) checkoutRepo(srcDir, pkgprefix, repo string) error { |
386 dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix)) | 387 dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix)) |
387 dir, err := os.Stat(filepath.Join(dst, vcs.metadir)) | 388 dir, err := os.Stat(filepath.Join(dst, vcs.metadir)) |
388 if err == nil && !dir.IsDirectory() { | 389 if err == nil && !dir.IsDirectory() { |
389 » » return os.NewError("not a directory: " + dst) | 390 » » return errors.New("not a directory: " + dst) |
390 } | 391 } |
391 if err != nil { | 392 if err != nil { |
392 parent, _ := filepath.Split(dst) | 393 parent, _ := filepath.Split(dst) |
393 if err = os.MkdirAll(parent, 0777); err != nil { | 394 if err = os.MkdirAll(parent, 0777); err != nil { |
394 return err | 395 return err |
395 } | 396 } |
396 if err = run(string(filepath.Separator), nil, vcs.cmd, vcs.clone
, repo, dst); err != nil { | 397 if err = run(string(filepath.Separator), nil, vcs.cmd, vcs.clone
, repo, dst); err != nil { |
397 return err | 398 return err |
398 } | 399 } |
399 return vcs.updateRepo(dst) | 400 return vcs.updateRepo(dst) |
400 } | 401 } |
401 if *update { | 402 if *update { |
402 // Retrieve new revisions from the remote branch, if the VCS | 403 // Retrieve new revisions from the remote branch, if the VCS |
403 // supports this operation independently (e.g. svn doesn't) | 404 // supports this operation independently (e.g. svn doesn't) |
404 if vcs.pull != "" { | 405 if vcs.pull != "" { |
405 if vcs.pullForceFlag != "" { | 406 if vcs.pullForceFlag != "" { |
406 if err = run(dst, nil, vcs.cmd, vcs.pull, vcs.pu
llForceFlag); err != nil { | 407 if err = run(dst, nil, vcs.cmd, vcs.pull, vcs.pu
llForceFlag); err != nil { |
407 return err | 408 return err |
408 } | 409 } |
409 } else if err = run(dst, nil, vcs.cmd, vcs.pull); err !=
nil { | 410 } else if err = run(dst, nil, vcs.cmd, vcs.pull); err !=
nil { |
410 return err | 411 return err |
411 } | 412 } |
412 } | 413 } |
413 // Update to release or latest revision | 414 // Update to release or latest revision |
414 return vcs.updateRepo(dst) | 415 return vcs.updateRepo(dst) |
415 } | 416 } |
416 return nil | 417 return nil |
417 } | 418 } |
LEFT | RIGHT |