Merge ~dviererbe/ubuntu/+source/dh-make-golang:lp2046369 into ubuntu/+source/dh-make-golang:ubuntu/devel
- Git
- lp:~dviererbe/ubuntu/+source/dh-make-golang
- lp2046369
- Merge into ubuntu/devel
Proposed by
Dominik Viererbe
Status: | Merged | ||||
---|---|---|---|---|---|
Merge reported by: | Dominik Viererbe | ||||
Merged at revision: | 0b036a097be51a70929463d03e311e58b84ccd06 | ||||
Proposed branch: | ~dviererbe/ubuntu/+source/dh-make-golang:lp2046369 | ||||
Merge into: | ubuntu/+source/dh-make-golang:ubuntu/devel | ||||
Diff against target: |
1435 lines (+1309/-2) 11 files modified
debian/changelog (+12/-0) debian/control (+2/-1) debian/copyright (+4/-0) debian/go/src/golang.org/x/tools/go/vcs/discovery.go (+83/-0) debian/go/src/golang.org/x/tools/go/vcs/env.go (+39/-0) debian/go/src/golang.org/x/tools/go/vcs/go.mod (+7/-0) debian/go/src/golang.org/x/tools/go/vcs/http.go (+80/-0) debian/go/src/golang.org/x/tools/go/vcs/vcs.go (+764/-0) debian/go/src/golang.org/x/tools/go/vcs/vcs_test.go (+309/-0) debian/rules (+9/-0) dev/null (+0/-1) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Simon Chopin | Pending | ||
git-ubuntu import | Pending | ||
Review via email: mp+463019@code.launchpad.net |
Commit message
Description of the change
Fixes FTBFS of 0.6.0-2build1 (LP: #2046369) by backporting changes from 0.7.0-1 in debian sid.
Syncing 0.7.0-1 is currently undesirable, because it requires a sync of golang-
To post a comment you must log in.
Revision history for this message
Dominik Viererbe (dviererbe) wrote : | # |
Revision history for this message
Dominik Viererbe (dviererbe) wrote : | # |
Someone synced 0.7.0-1 and golang-
Note: I set the state to "merged" even though this MP never got merged, because there is no state to "reject" the merge.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/debian/changelog b/debian/changelog |
2 | index c231f11..77e461b 100644 |
3 | --- a/debian/changelog |
4 | +++ b/debian/changelog |
5 | @@ -1,3 +1,15 @@ |
6 | +dh-make-golang (0.6.0-2ubuntu1) noble; urgency=medium |
7 | + |
8 | + * Disable lto. |
9 | + * Include copy of golang.org/x/tools/go/vcs@v0.1.0-deprecated (LP: #2046369); |
10 | + backported from 0.7.0-1 in debian unstable (see debian bug: #1043070): |
11 | + - debian/go/src/golang.org/x/tools/go/vcs |
12 | + - debian/copyright: add copiright information |
13 | + - debian/rules: add execute_after_dh_auto_configure target to configure copy |
14 | + - debian/patches/*: removed |
15 | + |
16 | + -- Dominik Viererbe <dominik.viererbe@canonical.com> Mon, 25 Mar 2024 13:27:14 +0200 |
17 | + |
18 | dh-make-golang (0.6.0-2build1) mantic; urgency=medium |
19 | |
20 | * No-change rebuild with Go 1.21. |
21 | diff --git a/debian/control b/debian/control |
22 | index 553d6f6..bd69f19 100644 |
23 | --- a/debian/control |
24 | +++ b/debian/control |
25 | @@ -1,5 +1,6 @@ |
26 | Source: dh-make-golang |
27 | -Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org> |
28 | +Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
29 | +XSBC-Original-Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org> |
30 | Uploaders: Michael Stapelberg <stapelberg@debian.org>, |
31 | Dr. Tobias Quathamer <toddy@debian.org>, |
32 | Anthony Fok <foka@debian.org>, |
33 | diff --git a/debian/copyright b/debian/copyright |
34 | index d05ed52..a7307d5 100644 |
35 | --- a/debian/copyright |
36 | +++ b/debian/copyright |
37 | @@ -16,6 +16,10 @@ Copyright: |
38 | License: BSD-3-clause |
39 | Comment: Debian packaging is licensed under the same terms as upstream |
40 | |
41 | +Files: debian/go/src/golang.org/x/tools/go/vcs/* |
42 | +Copyright: 2009 The Go Authors |
43 | +License: BSD-3-clause |
44 | + |
45 | License: BSD-3-clause |
46 | Copyright © 2015, Michael Stapelberg, Google Inc. and contributors |
47 | All rights reserved. |
48 | diff --git a/debian/go/src/golang.org/x/tools/go/vcs/discovery.go b/debian/go/src/golang.org/x/tools/go/vcs/discovery.go |
49 | new file mode 100644 |
50 | index 0000000..7d179bc |
51 | --- /dev/null |
52 | +++ b/debian/go/src/golang.org/x/tools/go/vcs/discovery.go |
53 | @@ -0,0 +1,83 @@ |
54 | +// Copyright 2012 The Go Authors. All rights reserved. |
55 | +// Use of this source code is governed by a BSD-style |
56 | +// license that can be found in the LICENSE file. |
57 | + |
58 | +package vcs |
59 | + |
60 | +import ( |
61 | + "encoding/xml" |
62 | + "fmt" |
63 | + "io" |
64 | + "strings" |
65 | +) |
66 | + |
67 | +// charsetReader returns a reader for the given charset. Currently |
68 | +// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful |
69 | +// error which is printed by go get, so the user can find why the package |
70 | +// wasn't downloaded if the encoding is not supported. Note that, in |
71 | +// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters |
72 | +// greater than 0x7f are not rejected). |
73 | +func charsetReader(charset string, input io.Reader) (io.Reader, error) { |
74 | + switch strings.ToLower(charset) { |
75 | + case "ascii": |
76 | + return input, nil |
77 | + default: |
78 | + return nil, fmt.Errorf("can't decode XML document using charset %q", charset) |
79 | + } |
80 | +} |
81 | + |
82 | +// parseMetaGoImports returns meta imports from the HTML in r. |
83 | +// Parsing ends at the end of the <head> section or the beginning of the <body>. |
84 | +// |
85 | +// This copy of cmd/go/internal/vcs.parseMetaGoImports always operates |
86 | +// in IgnoreMod ModuleMode. |
87 | +func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { |
88 | + d := xml.NewDecoder(r) |
89 | + d.CharsetReader = charsetReader |
90 | + d.Strict = false |
91 | + var t xml.Token |
92 | + for { |
93 | + t, err = d.RawToken() |
94 | + if err != nil { |
95 | + if err == io.EOF || len(imports) > 0 { |
96 | + err = nil |
97 | + } |
98 | + return |
99 | + } |
100 | + if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { |
101 | + return |
102 | + } |
103 | + if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { |
104 | + return |
105 | + } |
106 | + e, ok := t.(xml.StartElement) |
107 | + if !ok || !strings.EqualFold(e.Name.Local, "meta") { |
108 | + continue |
109 | + } |
110 | + if attrValue(e.Attr, "name") != "go-import" { |
111 | + continue |
112 | + } |
113 | + if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { |
114 | + // Ignore VCS type "mod", which is applicable only in module mode. |
115 | + if f[1] == "mod" { |
116 | + continue |
117 | + } |
118 | + imports = append(imports, metaImport{ |
119 | + Prefix: f[0], |
120 | + VCS: f[1], |
121 | + RepoRoot: f[2], |
122 | + }) |
123 | + } |
124 | + } |
125 | +} |
126 | + |
127 | +// attrValue returns the attribute value for the case-insensitive key |
128 | +// `name', or the empty string if nothing is found. |
129 | +func attrValue(attrs []xml.Attr, name string) string { |
130 | + for _, a := range attrs { |
131 | + if strings.EqualFold(a.Name.Local, name) { |
132 | + return a.Value |
133 | + } |
134 | + } |
135 | + return "" |
136 | +} |
137 | diff --git a/debian/go/src/golang.org/x/tools/go/vcs/env.go b/debian/go/src/golang.org/x/tools/go/vcs/env.go |
138 | new file mode 100644 |
139 | index 0000000..189210c |
140 | --- /dev/null |
141 | +++ b/debian/go/src/golang.org/x/tools/go/vcs/env.go |
142 | @@ -0,0 +1,39 @@ |
143 | +// Copyright 2013 The Go Authors. All rights reserved. |
144 | +// Use of this source code is governed by a BSD-style |
145 | +// license that can be found in the LICENSE file. |
146 | + |
147 | +package vcs |
148 | + |
149 | +import ( |
150 | + "os" |
151 | + "strings" |
152 | +) |
153 | + |
154 | +// envForDir returns a copy of the environment |
155 | +// suitable for running in the given directory. |
156 | +// The environment is the current process's environment |
157 | +// but with an updated $PWD, so that an os.Getwd in the |
158 | +// child will be faster. |
159 | +func envForDir(dir string) []string { |
160 | + env := os.Environ() |
161 | + // Internally we only use rooted paths, so dir is rooted. |
162 | + // Even if dir is not rooted, no harm done. |
163 | + return mergeEnvLists([]string{"PWD=" + dir}, env) |
164 | +} |
165 | + |
166 | +// mergeEnvLists merges the two environment lists such that |
167 | +// variables with the same name in "in" replace those in "out". |
168 | +func mergeEnvLists(in, out []string) []string { |
169 | +NextVar: |
170 | + for _, inkv := range in { |
171 | + k := strings.SplitAfterN(inkv, "=", 2)[0] |
172 | + for i, outkv := range out { |
173 | + if strings.HasPrefix(outkv, k) { |
174 | + out[i] = inkv |
175 | + continue NextVar |
176 | + } |
177 | + } |
178 | + out = append(out, inkv) |
179 | + } |
180 | + return out |
181 | +} |
182 | diff --git a/debian/go/src/golang.org/x/tools/go/vcs/go.mod b/debian/go/src/golang.org/x/tools/go/vcs/go.mod |
183 | new file mode 100644 |
184 | index 0000000..74da6cb |
185 | --- /dev/null |
186 | +++ b/debian/go/src/golang.org/x/tools/go/vcs/go.mod |
187 | @@ -0,0 +1,7 @@ |
188 | +// Deprecated: This module contains one deprecated package. |
189 | +// See the package deprecation notice for more information. |
190 | +module golang.org/x/tools/go/vcs |
191 | + |
192 | +go 1.19 |
193 | + |
194 | +require golang.org/x/sys v0.9.0 |
195 | diff --git a/debian/go/src/golang.org/x/tools/go/vcs/http.go b/debian/go/src/golang.org/x/tools/go/vcs/http.go |
196 | new file mode 100644 |
197 | index 0000000..5836511 |
198 | --- /dev/null |
199 | +++ b/debian/go/src/golang.org/x/tools/go/vcs/http.go |
200 | @@ -0,0 +1,80 @@ |
201 | +// Copyright 2012 The Go Authors. All rights reserved. |
202 | +// Use of this source code is governed by a BSD-style |
203 | +// license that can be found in the LICENSE file. |
204 | + |
205 | +package vcs |
206 | + |
207 | +import ( |
208 | + "fmt" |
209 | + "io" |
210 | + "io/ioutil" |
211 | + "log" |
212 | + "net/http" |
213 | + "net/url" |
214 | +) |
215 | + |
216 | +// httpClient is the default HTTP client, but a variable so it can be |
217 | +// changed by tests, without modifying http.DefaultClient. |
218 | +var httpClient = http.DefaultClient |
219 | + |
220 | +// httpGET returns the data from an HTTP GET request for the given URL. |
221 | +func httpGET(url string) ([]byte, error) { |
222 | + resp, err := httpClient.Get(url) |
223 | + if err != nil { |
224 | + return nil, err |
225 | + } |
226 | + defer resp.Body.Close() |
227 | + if resp.StatusCode != 200 { |
228 | + return nil, fmt.Errorf("%s: %s", url, resp.Status) |
229 | + } |
230 | + b, err := ioutil.ReadAll(resp.Body) |
231 | + if err != nil { |
232 | + return nil, fmt.Errorf("%s: %v", url, err) |
233 | + } |
234 | + return b, nil |
235 | +} |
236 | + |
237 | +// httpsOrHTTP returns the body of either the importPath's |
238 | +// https resource or, if unavailable, the http resource. |
239 | +func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { |
240 | + fetch := func(scheme string) (urlStr string, res *http.Response, err error) { |
241 | + u, err := url.Parse(scheme + "://" + importPath) |
242 | + if err != nil { |
243 | + return "", nil, err |
244 | + } |
245 | + u.RawQuery = "go-get=1" |
246 | + urlStr = u.String() |
247 | + if Verbose { |
248 | + log.Printf("Fetching %s", urlStr) |
249 | + } |
250 | + res, err = httpClient.Get(urlStr) |
251 | + return |
252 | + } |
253 | + closeBody := func(res *http.Response) { |
254 | + if res != nil { |
255 | + res.Body.Close() |
256 | + } |
257 | + } |
258 | + urlStr, res, err := fetch("https") |
259 | + if err != nil || res.StatusCode != 200 { |
260 | + if Verbose { |
261 | + if err != nil { |
262 | + log.Printf("https fetch failed.") |
263 | + } else { |
264 | + log.Printf("ignoring https fetch with status code %d", res.StatusCode) |
265 | + } |
266 | + } |
267 | + closeBody(res) |
268 | + urlStr, res, err = fetch("http") |
269 | + } |
270 | + if err != nil { |
271 | + closeBody(res) |
272 | + return "", nil, err |
273 | + } |
274 | + // Note: accepting a non-200 OK here, so people can serve a |
275 | + // meta import in their http 404 page. |
276 | + if Verbose { |
277 | + log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode) |
278 | + } |
279 | + return urlStr, res.Body, nil |
280 | +} |
281 | diff --git a/debian/go/src/golang.org/x/tools/go/vcs/vcs.go b/debian/go/src/golang.org/x/tools/go/vcs/vcs.go |
282 | new file mode 100644 |
283 | index 0000000..232177d |
284 | --- /dev/null |
285 | +++ b/debian/go/src/golang.org/x/tools/go/vcs/vcs.go |
286 | @@ -0,0 +1,764 @@ |
287 | +// Copyright 2012 The Go Authors. All rights reserved. |
288 | +// Use of this source code is governed by a BSD-style |
289 | +// license that can be found in the LICENSE file. |
290 | + |
291 | +// Package vcs exposes functions for resolving import paths |
292 | +// and using version control systems, which can be used to |
293 | +// implement behavior similar to the standard "go get" command. |
294 | +// |
295 | +// Deprecated: Use the go list command with -json flag instead, |
296 | +// which implements up-to-date import path resolution behavior, |
297 | +// module support, and includes the latest security fixes. |
298 | +// |
299 | +// This package was a copy of internal code in package cmd/go/internal/get |
300 | +// before module support, modified to make the identifiers exported. |
301 | +// It was provided here for developers who wanted to write tools with similar semantics. |
302 | +// It needed to be manually kept in sync with upstream when changes were |
303 | +// made to cmd/go/internal/get, as tracked in go.dev/issue/11490. |
304 | +// By now, it has diverged significantly from upstream cmd/go/internal/get |
305 | +// behavior and will not receive any further updates. |
306 | +package vcs // import "golang.org/x/tools/go/vcs" |
307 | + |
308 | +import ( |
309 | + "bytes" |
310 | + "encoding/json" |
311 | + "errors" |
312 | + "fmt" |
313 | + exec "golang.org/x/sys/execabs" |
314 | + "log" |
315 | + "net/url" |
316 | + "os" |
317 | + "path/filepath" |
318 | + "regexp" |
319 | + "strconv" |
320 | + "strings" |
321 | +) |
322 | + |
323 | +// Verbose enables verbose operation logging. |
324 | +var Verbose bool |
325 | + |
326 | +// ShowCmd controls whether VCS commands are printed. |
327 | +var ShowCmd bool |
328 | + |
329 | +// A Cmd describes how to use a version control system |
330 | +// like Mercurial, Git, or Subversion. |
331 | +type Cmd struct { |
332 | + Name string |
333 | + Cmd string // name of binary to invoke command |
334 | + |
335 | + CreateCmd string // command to download a fresh copy of a repository |
336 | + DownloadCmd string // command to download updates into an existing repository |
337 | + |
338 | + TagCmd []TagCmd // commands to list tags |
339 | + TagLookupCmd []TagCmd // commands to lookup tags before running tagSyncCmd |
340 | + TagSyncCmd string // command to sync to specific tag |
341 | + TagSyncDefault string // command to sync to default tag |
342 | + |
343 | + LogCmd string // command to list repository changelogs in an XML format |
344 | + |
345 | + Scheme []string |
346 | + PingCmd string |
347 | +} |
348 | + |
349 | +// A TagCmd describes a command to list available tags |
350 | +// that can be passed to Cmd.TagSyncCmd. |
351 | +type TagCmd struct { |
352 | + Cmd string // command to list tags |
353 | + Pattern string // regexp to extract tags from list |
354 | +} |
355 | + |
356 | +// vcsList lists the known version control systems |
357 | +var vcsList = []*Cmd{ |
358 | + vcsHg, |
359 | + vcsGit, |
360 | + vcsSvn, |
361 | + vcsBzr, |
362 | +} |
363 | + |
364 | +// ByCmd returns the version control system for the given |
365 | +// command name (hg, git, svn, bzr). |
366 | +func ByCmd(cmd string) *Cmd { |
367 | + for _, vcs := range vcsList { |
368 | + if vcs.Cmd == cmd { |
369 | + return vcs |
370 | + } |
371 | + } |
372 | + return nil |
373 | +} |
374 | + |
375 | +// vcsHg describes how to use Mercurial. |
376 | +var vcsHg = &Cmd{ |
377 | + Name: "Mercurial", |
378 | + Cmd: "hg", |
379 | + |
380 | + CreateCmd: "clone -U {repo} {dir}", |
381 | + DownloadCmd: "pull", |
382 | + |
383 | + // We allow both tag and branch names as 'tags' |
384 | + // for selecting a version. This lets people have |
385 | + // a go.release.r60 branch and a go1 branch |
386 | + // and make changes in both, without constantly |
387 | + // editing .hgtags. |
388 | + TagCmd: []TagCmd{ |
389 | + {"tags", `^(\S+)`}, |
390 | + {"branches", `^(\S+)`}, |
391 | + }, |
392 | + TagSyncCmd: "update -r {tag}", |
393 | + TagSyncDefault: "update default", |
394 | + |
395 | + LogCmd: "log --encoding=utf-8 --limit={limit} --template={template}", |
396 | + |
397 | + Scheme: []string{"https", "http", "ssh"}, |
398 | + PingCmd: "identify {scheme}://{repo}", |
399 | +} |
400 | + |
401 | +// vcsGit describes how to use Git. |
402 | +var vcsGit = &Cmd{ |
403 | + Name: "Git", |
404 | + Cmd: "git", |
405 | + |
406 | + CreateCmd: "clone {repo} {dir}", |
407 | + DownloadCmd: "pull --ff-only", |
408 | + |
409 | + TagCmd: []TagCmd{ |
410 | + // tags/xxx matches a git tag named xxx |
411 | + // origin/xxx matches a git branch named xxx on the default remote repository |
412 | + {"show-ref", `(?:tags|origin)/(\S+)$`}, |
413 | + }, |
414 | + TagLookupCmd: []TagCmd{ |
415 | + {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, |
416 | + }, |
417 | + TagSyncCmd: "checkout {tag}", |
418 | + TagSyncDefault: "checkout master", |
419 | + |
420 | + Scheme: []string{"git", "https", "http", "git+ssh"}, |
421 | + PingCmd: "ls-remote {scheme}://{repo}", |
422 | +} |
423 | + |
424 | +// vcsBzr describes how to use Bazaar. |
425 | +var vcsBzr = &Cmd{ |
426 | + Name: "Bazaar", |
427 | + Cmd: "bzr", |
428 | + |
429 | + CreateCmd: "branch {repo} {dir}", |
430 | + |
431 | + // Without --overwrite bzr will not pull tags that changed. |
432 | + // Replace by --overwrite-tags after http://pad.lv/681792 goes in. |
433 | + DownloadCmd: "pull --overwrite", |
434 | + |
435 | + TagCmd: []TagCmd{{"tags", `^(\S+)`}}, |
436 | + TagSyncCmd: "update -r {tag}", |
437 | + TagSyncDefault: "update -r revno:-1", |
438 | + |
439 | + Scheme: []string{"https", "http", "bzr", "bzr+ssh"}, |
440 | + PingCmd: "info {scheme}://{repo}", |
441 | +} |
442 | + |
443 | +// vcsSvn describes how to use Subversion. |
444 | +var vcsSvn = &Cmd{ |
445 | + Name: "Subversion", |
446 | + Cmd: "svn", |
447 | + |
448 | + CreateCmd: "checkout {repo} {dir}", |
449 | + DownloadCmd: "update", |
450 | + |
451 | + // There is no tag command in subversion. |
452 | + // The branch information is all in the path names. |
453 | + |
454 | + LogCmd: "log --xml --limit={limit}", |
455 | + |
456 | + Scheme: []string{"https", "http", "svn", "svn+ssh"}, |
457 | + PingCmd: "info {scheme}://{repo}", |
458 | +} |
459 | + |
460 | +func (v *Cmd) String() string { |
461 | + return v.Name |
462 | +} |
463 | + |
464 | +// run runs the command line cmd in the given directory. |
465 | +// keyval is a list of key, value pairs. run expands |
466 | +// instances of {key} in cmd into value, but only after |
467 | +// splitting cmd into individual arguments. |
468 | +// If an error occurs, run prints the command line and the |
469 | +// command's combined stdout+stderr to standard error. |
470 | +// Otherwise run discards the command's output. |
471 | +func (v *Cmd) run(dir string, cmd string, keyval ...string) error { |
472 | + _, err := v.run1(dir, cmd, keyval, true) |
473 | + return err |
474 | +} |
475 | + |
476 | +// runVerboseOnly is like run but only generates error output to standard error in verbose mode. |
477 | +func (v *Cmd) runVerboseOnly(dir string, cmd string, keyval ...string) error { |
478 | + _, err := v.run1(dir, cmd, keyval, false) |
479 | + return err |
480 | +} |
481 | + |
482 | +// runOutput is like run but returns the output of the command. |
483 | +func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) { |
484 | + return v.run1(dir, cmd, keyval, true) |
485 | +} |
486 | + |
487 | +// run1 is the generalized implementation of run and runOutput. |
488 | +func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { |
489 | + m := make(map[string]string) |
490 | + for i := 0; i < len(keyval); i += 2 { |
491 | + m[keyval[i]] = keyval[i+1] |
492 | + } |
493 | + args := strings.Fields(cmdline) |
494 | + for i, arg := range args { |
495 | + args[i] = expand(m, arg) |
496 | + } |
497 | + |
498 | + _, err := exec.LookPath(v.Cmd) |
499 | + if err != nil { |
500 | + fmt.Fprintf(os.Stderr, |
501 | + "go: missing %s command. See http://golang.org/s/gogetcmd\n", |
502 | + v.Name) |
503 | + return nil, err |
504 | + } |
505 | + |
506 | + cmd := exec.Command(v.Cmd, args...) |
507 | + cmd.Dir = dir |
508 | + cmd.Env = envForDir(cmd.Dir) |
509 | + if ShowCmd { |
510 | + fmt.Printf("cd %s\n", dir) |
511 | + fmt.Printf("%s %s\n", v.Cmd, strings.Join(args, " ")) |
512 | + } |
513 | + var buf bytes.Buffer |
514 | + cmd.Stdout = &buf |
515 | + cmd.Stderr = &buf |
516 | + err = cmd.Run() |
517 | + out := buf.Bytes() |
518 | + if err != nil { |
519 | + if verbose || Verbose { |
520 | + fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.Cmd, strings.Join(args, " ")) |
521 | + os.Stderr.Write(out) |
522 | + } |
523 | + return nil, err |
524 | + } |
525 | + return out, nil |
526 | +} |
527 | + |
528 | +// Ping pings the repo to determine if scheme used is valid. |
529 | +// This repo must be pingable with this scheme and VCS. |
530 | +func (v *Cmd) Ping(scheme, repo string) error { |
531 | + return v.runVerboseOnly(".", v.PingCmd, "scheme", scheme, "repo", repo) |
532 | +} |
533 | + |
534 | +// Create creates a new copy of repo in dir. |
535 | +// The parent of dir must exist; dir must not. |
536 | +func (v *Cmd) Create(dir, repo string) error { |
537 | + return v.run(".", v.CreateCmd, "dir", dir, "repo", repo) |
538 | +} |
539 | + |
540 | +// CreateAtRev creates a new copy of repo in dir at revision rev. |
541 | +// The parent of dir must exist; dir must not. |
542 | +// rev must be a valid revision in repo. |
543 | +func (v *Cmd) CreateAtRev(dir, repo, rev string) error { |
544 | + if err := v.Create(dir, repo); err != nil { |
545 | + return err |
546 | + } |
547 | + return v.run(dir, v.TagSyncCmd, "tag", rev) |
548 | +} |
549 | + |
550 | +// Download downloads any new changes for the repo in dir. |
551 | +// dir must be a valid VCS repo compatible with v. |
552 | +func (v *Cmd) Download(dir string) error { |
553 | + return v.run(dir, v.DownloadCmd) |
554 | +} |
555 | + |
556 | +// Tags returns the list of available tags for the repo in dir. |
557 | +// dir must be a valid VCS repo compatible with v. |
558 | +func (v *Cmd) Tags(dir string) ([]string, error) { |
559 | + var tags []string |
560 | + for _, tc := range v.TagCmd { |
561 | + out, err := v.runOutput(dir, tc.Cmd) |
562 | + if err != nil { |
563 | + return nil, err |
564 | + } |
565 | + re := regexp.MustCompile(`(?m-s)` + tc.Pattern) |
566 | + for _, m := range re.FindAllStringSubmatch(string(out), -1) { |
567 | + tags = append(tags, m[1]) |
568 | + } |
569 | + } |
570 | + return tags, nil |
571 | +} |
572 | + |
573 | +// TagSync syncs the repo in dir to the named tag, which is either a |
574 | +// tag returned by Tags or the empty string (the default tag). |
575 | +// dir must be a valid VCS repo compatible with v and the tag must exist. |
576 | +func (v *Cmd) TagSync(dir, tag string) error { |
577 | + if v.TagSyncCmd == "" { |
578 | + return nil |
579 | + } |
580 | + if tag != "" { |
581 | + for _, tc := range v.TagLookupCmd { |
582 | + out, err := v.runOutput(dir, tc.Cmd, "tag", tag) |
583 | + if err != nil { |
584 | + return err |
585 | + } |
586 | + re := regexp.MustCompile(`(?m-s)` + tc.Pattern) |
587 | + m := re.FindStringSubmatch(string(out)) |
588 | + if len(m) > 1 { |
589 | + tag = m[1] |
590 | + break |
591 | + } |
592 | + } |
593 | + } |
594 | + if tag == "" && v.TagSyncDefault != "" { |
595 | + return v.run(dir, v.TagSyncDefault) |
596 | + } |
597 | + return v.run(dir, v.TagSyncCmd, "tag", tag) |
598 | +} |
599 | + |
600 | +// Log logs the changes for the repo in dir. |
601 | +// dir must be a valid VCS repo compatible with v. |
602 | +func (v *Cmd) Log(dir, logTemplate string) ([]byte, error) { |
603 | + if err := v.Download(dir); err != nil { |
604 | + return []byte{}, err |
605 | + } |
606 | + |
607 | + const N = 50 // how many revisions to grab |
608 | + return v.runOutput(dir, v.LogCmd, "limit", strconv.Itoa(N), "template", logTemplate) |
609 | +} |
610 | + |
611 | +// LogAtRev logs the change for repo in dir at the rev revision. |
612 | +// dir must be a valid VCS repo compatible with v. |
613 | +// rev must be a valid revision for the repo in dir. |
614 | +func (v *Cmd) LogAtRev(dir, rev, logTemplate string) ([]byte, error) { |
615 | + if err := v.Download(dir); err != nil { |
616 | + return []byte{}, err |
617 | + } |
618 | + |
619 | + // Append revision flag to LogCmd. |
620 | + logAtRevCmd := v.LogCmd + " --rev=" + rev |
621 | + return v.runOutput(dir, logAtRevCmd, "limit", strconv.Itoa(1), "template", logTemplate) |
622 | +} |
623 | + |
624 | +// A vcsPath describes how to convert an import path into a |
625 | +// version control system and repository name. |
626 | +type vcsPath struct { |
627 | + prefix string // prefix this description applies to |
628 | + re string // pattern for import path |
629 | + repo string // repository to use (expand with match of re) |
630 | + vcs string // version control system to use (expand with match of re) |
631 | + check func(match map[string]string) error // additional checks |
632 | + ping bool // ping for scheme to use to download repo |
633 | + |
634 | + regexp *regexp.Regexp // cached compiled form of re |
635 | +} |
636 | + |
637 | +// FromDir inspects dir and its parents to determine the |
638 | +// version control system and code repository to use. |
639 | +// On return, root is the import path |
640 | +// corresponding to the root of the repository. |
641 | +func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) { |
642 | + // Clean and double-check that dir is in (a subdirectory of) srcRoot. |
643 | + dir = filepath.Clean(dir) |
644 | + srcRoot = filepath.Clean(srcRoot) |
645 | + if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { |
646 | + return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) |
647 | + } |
648 | + |
649 | + var vcsRet *Cmd |
650 | + var rootRet string |
651 | + |
652 | + origDir := dir |
653 | + for len(dir) > len(srcRoot) { |
654 | + for _, vcs := range vcsList { |
655 | + if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil { |
656 | + root := filepath.ToSlash(dir[len(srcRoot)+1:]) |
657 | + // Record first VCS we find, but keep looking, |
658 | + // to detect mistakes like one kind of VCS inside another. |
659 | + if vcsRet == nil { |
660 | + vcsRet = vcs |
661 | + rootRet = root |
662 | + continue |
663 | + } |
664 | + // Allow .git inside .git, which can arise due to submodules. |
665 | + if vcsRet == vcs && vcs.Cmd == "git" { |
666 | + continue |
667 | + } |
668 | + // Otherwise, we have one VCS inside a different VCS. |
669 | + return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s", |
670 | + filepath.Join(srcRoot, rootRet), vcsRet.Cmd, filepath.Join(srcRoot, root), vcs.Cmd) |
671 | + } |
672 | + } |
673 | + |
674 | + // Move to parent. |
675 | + ndir := filepath.Dir(dir) |
676 | + if len(ndir) >= len(dir) { |
677 | + // Shouldn't happen, but just in case, stop. |
678 | + break |
679 | + } |
680 | + dir = ndir |
681 | + } |
682 | + |
683 | + if vcsRet != nil { |
684 | + return vcsRet, rootRet, nil |
685 | + } |
686 | + |
687 | + return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) |
688 | +} |
689 | + |
690 | +// RepoRoot represents a version control system, a repo, and a root of |
691 | +// where to put it on disk. |
692 | +type RepoRoot struct { |
693 | + VCS *Cmd |
694 | + |
695 | + // Repo is the repository URL, including scheme. |
696 | + Repo string |
697 | + |
698 | + // Root is the import path corresponding to the root of the |
699 | + // repository. |
700 | + Root string |
701 | +} |
702 | + |
703 | +// RepoRootForImportPath analyzes importPath to determine the |
704 | +// version control system, and code repository to use. |
705 | +func RepoRootForImportPath(importPath string, verbose bool) (*RepoRoot, error) { |
706 | + rr, err := RepoRootForImportPathStatic(importPath, "") |
707 | + if err == errUnknownSite { |
708 | + rr, err = RepoRootForImportDynamic(importPath, verbose) |
709 | + |
710 | + // RepoRootForImportDynamic returns error detail |
711 | + // that is irrelevant if the user didn't intend to use a |
712 | + // dynamic import in the first place. |
713 | + // Squelch it. |
714 | + if err != nil { |
715 | + if Verbose { |
716 | + log.Printf("import %q: %v", importPath, err) |
717 | + } |
718 | + err = fmt.Errorf("unrecognized import path %q", importPath) |
719 | + } |
720 | + } |
721 | + |
722 | + if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") { |
723 | + // Do not allow wildcards in the repo root. |
724 | + rr = nil |
725 | + err = fmt.Errorf("cannot expand ... in %q", importPath) |
726 | + } |
727 | + return rr, err |
728 | +} |
729 | + |
730 | +var errUnknownSite = errors.New("dynamic lookup required to find mapping") |
731 | + |
732 | +// RepoRootForImportPathStatic attempts to map importPath to a |
733 | +// RepoRoot using the commonly-used VCS hosting sites in vcsPaths |
734 | +// (github.com/user/dir), or from a fully-qualified importPath already |
735 | +// containing its VCS type (foo.com/repo.git/dir) |
736 | +// |
737 | +// If scheme is non-empty, that scheme is forced. |
738 | +func RepoRootForImportPathStatic(importPath, scheme string) (*RepoRoot, error) { |
739 | + if strings.Contains(importPath, "://") { |
740 | + return nil, fmt.Errorf("invalid import path %q", importPath) |
741 | + } |
742 | + for _, srv := range vcsPaths { |
743 | + if !strings.HasPrefix(importPath, srv.prefix) { |
744 | + continue |
745 | + } |
746 | + m := srv.regexp.FindStringSubmatch(importPath) |
747 | + if m == nil { |
748 | + if srv.prefix != "" { |
749 | + return nil, fmt.Errorf("invalid %s import path %q", srv.prefix, importPath) |
750 | + } |
751 | + continue |
752 | + } |
753 | + |
754 | + // Build map of named subexpression matches for expand. |
755 | + match := map[string]string{ |
756 | + "prefix": srv.prefix, |
757 | + "import": importPath, |
758 | + } |
759 | + for i, name := range srv.regexp.SubexpNames() { |
760 | + if name != "" && match[name] == "" { |
761 | + match[name] = m[i] |
762 | + } |
763 | + } |
764 | + if srv.vcs != "" { |
765 | + match["vcs"] = expand(match, srv.vcs) |
766 | + } |
767 | + if srv.repo != "" { |
768 | + match["repo"] = expand(match, srv.repo) |
769 | + } |
770 | + if srv.check != nil { |
771 | + if err := srv.check(match); err != nil { |
772 | + return nil, err |
773 | + } |
774 | + } |
775 | + vcs := ByCmd(match["vcs"]) |
776 | + if vcs == nil { |
777 | + return nil, fmt.Errorf("unknown version control system %q", match["vcs"]) |
778 | + } |
779 | + if srv.ping { |
780 | + if scheme != "" { |
781 | + match["repo"] = scheme + "://" + match["repo"] |
782 | + } else { |
783 | + for _, scheme := range vcs.Scheme { |
784 | + if vcs.Ping(scheme, match["repo"]) == nil { |
785 | + match["repo"] = scheme + "://" + match["repo"] |
786 | + break |
787 | + } |
788 | + } |
789 | + } |
790 | + } |
791 | + rr := &RepoRoot{ |
792 | + VCS: vcs, |
793 | + Repo: match["repo"], |
794 | + Root: match["root"], |
795 | + } |
796 | + return rr, nil |
797 | + } |
798 | + return nil, errUnknownSite |
799 | +} |
800 | + |
801 | +// RepoRootForImportDynamic finds a *RepoRoot for a custom domain that's not |
802 | +// statically known by RepoRootForImportPathStatic. |
803 | +// |
804 | +// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld". |
805 | +func RepoRootForImportDynamic(importPath string, verbose bool) (*RepoRoot, error) { |
806 | + slash := strings.Index(importPath, "/") |
807 | + if slash < 0 { |
808 | + slash = len(importPath) |
809 | + } |
810 | + host := importPath[:slash] |
811 | + if !strings.Contains(host, ".") { |
812 | + return nil, errors.New("import path doesn't contain a hostname") |
813 | + } |
814 | + urlStr, body, err := httpsOrHTTP(importPath) |
815 | + if err != nil { |
816 | + return nil, fmt.Errorf("http/https fetch: %v", err) |
817 | + } |
818 | + defer body.Close() |
819 | + imports, err := parseMetaGoImports(body) |
820 | + if err != nil { |
821 | + return nil, fmt.Errorf("parsing %s: %v", importPath, err) |
822 | + } |
823 | + metaImport, err := matchGoImport(imports, importPath) |
824 | + if err != nil { |
825 | + if err != errNoMatch { |
826 | + return nil, fmt.Errorf("parse %s: %v", urlStr, err) |
827 | + } |
828 | + return nil, fmt.Errorf("parse %s: no go-import meta tags", urlStr) |
829 | + } |
830 | + if verbose { |
831 | + log.Printf("get %q: found meta tag %#v at %s", importPath, metaImport, urlStr) |
832 | + } |
833 | + // If the import was "uni.edu/bob/project", which said the |
834 | + // prefix was "uni.edu" and the RepoRoot was "evilroot.com", |
835 | + // make sure we don't trust Bob and check out evilroot.com to |
836 | + // "uni.edu" yet (possibly overwriting/preempting another |
837 | + // non-evil student). Instead, first verify the root and see |
838 | + // if it matches Bob's claim. |
839 | + if metaImport.Prefix != importPath { |
840 | + if verbose { |
841 | + log.Printf("get %q: verifying non-authoritative meta tag", importPath) |
842 | + } |
843 | + urlStr0 := urlStr |
844 | + urlStr, body, err = httpsOrHTTP(metaImport.Prefix) |
845 | + if err != nil { |
846 | + return nil, fmt.Errorf("fetch %s: %v", urlStr, err) |
847 | + } |
848 | + imports, err := parseMetaGoImports(body) |
849 | + if err != nil { |
850 | + return nil, fmt.Errorf("parsing %s: %v", importPath, err) |
851 | + } |
852 | + if len(imports) == 0 { |
853 | + return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr) |
854 | + } |
855 | + metaImport2, err := matchGoImport(imports, importPath) |
856 | + if err != nil || metaImport != metaImport2 { |
857 | + return nil, fmt.Errorf("%s and %s disagree about go-import for %s", urlStr0, urlStr, metaImport.Prefix) |
858 | + } |
859 | + } |
860 | + |
861 | + if err := validateRepoRoot(metaImport.RepoRoot); err != nil { |
862 | + return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, metaImport.RepoRoot, err) |
863 | + } |
864 | + rr := &RepoRoot{ |
865 | + VCS: ByCmd(metaImport.VCS), |
866 | + Repo: metaImport.RepoRoot, |
867 | + Root: metaImport.Prefix, |
868 | + } |
869 | + if rr.VCS == nil { |
870 | + return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, metaImport.VCS) |
871 | + } |
872 | + return rr, nil |
873 | +} |
874 | + |
875 | +// validateRepoRoot returns an error if repoRoot does not seem to be |
876 | +// a valid URL with scheme. |
877 | +func validateRepoRoot(repoRoot string) error { |
878 | + url, err := url.Parse(repoRoot) |
879 | + if err != nil { |
880 | + return err |
881 | + } |
882 | + if url.Scheme == "" { |
883 | + return errors.New("no scheme") |
884 | + } |
885 | + return nil |
886 | +} |
887 | + |
888 | +// metaImport represents the parsed <meta name="go-import" |
889 | +// content="prefix vcs reporoot" /> tags from HTML files. |
890 | +type metaImport struct { |
891 | + Prefix, VCS, RepoRoot string |
892 | +} |
893 | + |
894 | +// errNoMatch is returned from matchGoImport when there's no applicable match. |
895 | +var errNoMatch = errors.New("no import match") |
896 | + |
897 | +// pathPrefix reports whether sub is a prefix of s, |
898 | +// only considering entire path components. |
899 | +func pathPrefix(s, sub string) bool { |
900 | + // strings.HasPrefix is necessary but not sufficient. |
901 | + if !strings.HasPrefix(s, sub) { |
902 | + return false |
903 | + } |
904 | + // The remainder after the prefix must either be empty or start with a slash. |
905 | + rem := s[len(sub):] |
906 | + return rem == "" || rem[0] == '/' |
907 | +} |
908 | + |
909 | +// matchGoImport returns the metaImport from imports matching importPath. |
910 | +// An error is returned if there are multiple matches. |
911 | +// errNoMatch is returned if none match. |
912 | +func matchGoImport(imports []metaImport, importPath string) (_ metaImport, err error) { |
913 | + match := -1 |
914 | + for i, im := range imports { |
915 | + if !pathPrefix(importPath, im.Prefix) { |
916 | + continue |
917 | + } |
918 | + |
919 | + if match != -1 { |
920 | + err = fmt.Errorf("multiple meta tags match import path %q", importPath) |
921 | + return |
922 | + } |
923 | + match = i |
924 | + } |
925 | + if match == -1 { |
926 | + err = errNoMatch |
927 | + return |
928 | + } |
929 | + return imports[match], nil |
930 | +} |
931 | + |
932 | +// expand rewrites s to replace {k} with match[k] for each key k in match. |
933 | +func expand(match map[string]string, s string) string { |
934 | + for k, v := range match { |
935 | + s = strings.Replace(s, "{"+k+"}", v, -1) |
936 | + } |
937 | + return s |
938 | +} |
939 | + |
940 | +// vcsPaths lists the known vcs paths. |
941 | +var vcsPaths = []*vcsPath{ |
942 | + // Github |
943 | + { |
944 | + prefix: "github.com/", |
945 | + re: `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[\p{L}0-9_.\-]+)*$`, |
946 | + vcs: "git", |
947 | + repo: "https://{root}", |
948 | + check: noVCSSuffix, |
949 | + }, |
950 | + |
951 | + // Bitbucket |
952 | + { |
953 | + prefix: "bitbucket.org/", |
954 | + re: `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, |
955 | + repo: "https://{root}", |
956 | + check: bitbucketVCS, |
957 | + }, |
958 | + |
959 | + // Launchpad |
960 | + { |
961 | + prefix: "launchpad.net/", |
962 | + re: `^(?P<root>launchpad\.net/((?P<project>[A-Za-z0-9_.\-]+)(?P<series>/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, |
963 | + vcs: "bzr", |
964 | + repo: "https://{root}", |
965 | + check: launchpadVCS, |
966 | + }, |
967 | + |
968 | + // Git at OpenStack |
969 | + { |
970 | + prefix: "git.openstack.org", |
971 | + re: `^(?P<root>git\.openstack\.org/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(\.git)?(/[A-Za-z0-9_.\-]+)*$`, |
972 | + vcs: "git", |
973 | + repo: "https://{root}", |
974 | + check: noVCSSuffix, |
975 | + }, |
976 | + |
977 | + // General syntax for any server. |
978 | + { |
979 | + re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`, |
980 | + ping: true, |
981 | + }, |
982 | +} |
983 | + |
984 | +func init() { |
985 | + // fill in cached regexps. |
986 | + // Doing this eagerly discovers invalid regexp syntax |
987 | + // without having to run a command that needs that regexp. |
988 | + for _, srv := range vcsPaths { |
989 | + srv.regexp = regexp.MustCompile(srv.re) |
990 | + } |
991 | +} |
992 | + |
993 | +// noVCSSuffix checks that the repository name does not |
994 | +// end in .foo for any version control system foo. |
995 | +// The usual culprit is ".git". |
996 | +func noVCSSuffix(match map[string]string) error { |
997 | + repo := match["repo"] |
998 | + for _, vcs := range vcsList { |
999 | + if strings.HasSuffix(repo, "."+vcs.Cmd) { |
1000 | + return fmt.Errorf("invalid version control suffix in %s path", match["prefix"]) |
1001 | + } |
1002 | + } |
1003 | + return nil |
1004 | +} |
1005 | + |
1006 | +// bitbucketVCS determines the version control system for a |
1007 | +// Bitbucket repository, by using the Bitbucket API. |
1008 | +func bitbucketVCS(match map[string]string) error { |
1009 | + if err := noVCSSuffix(match); err != nil { |
1010 | + return err |
1011 | + } |
1012 | + |
1013 | + var resp struct { |
1014 | + SCM string `json:"scm"` |
1015 | + } |
1016 | + url := expand(match, "https://api.bitbucket.org/2.0/repositories/{bitname}?fields=scm") |
1017 | + data, err := httpGET(url) |
1018 | + if err != nil { |
1019 | + return err |
1020 | + } |
1021 | + if err := json.Unmarshal(data, &resp); err != nil { |
1022 | + return fmt.Errorf("decoding %s: %v", url, err) |
1023 | + } |
1024 | + |
1025 | + if ByCmd(resp.SCM) != nil { |
1026 | + match["vcs"] = resp.SCM |
1027 | + if resp.SCM == "git" { |
1028 | + match["repo"] += ".git" |
1029 | + } |
1030 | + return nil |
1031 | + } |
1032 | + |
1033 | + return fmt.Errorf("unable to detect version control system for bitbucket.org/ path") |
1034 | +} |
1035 | + |
1036 | +// launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case, |
1037 | +// "foo" could be a series name registered in Launchpad with its own branch, |
1038 | +// and it could also be the name of a directory within the main project |
1039 | +// branch one level up. |
1040 | +func launchpadVCS(match map[string]string) error { |
1041 | + if match["project"] == "" || match["series"] == "" { |
1042 | + return nil |
1043 | + } |
1044 | + _, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format")) |
1045 | + if err != nil { |
1046 | + match["root"] = expand(match, "launchpad.net/{project}") |
1047 | + match["repo"] = expand(match, "https://{root}") |
1048 | + } |
1049 | + return nil |
1050 | +} |
1051 | diff --git a/debian/go/src/golang.org/x/tools/go/vcs/vcs_test.go b/debian/go/src/golang.org/x/tools/go/vcs/vcs_test.go |
1052 | new file mode 100644 |
1053 | index 0000000..a17b50d |
1054 | --- /dev/null |
1055 | +++ b/debian/go/src/golang.org/x/tools/go/vcs/vcs_test.go |
1056 | @@ -0,0 +1,309 @@ |
1057 | +// Copyright 2013 The Go Authors. All rights reserved. |
1058 | +// Use of this source code is governed by a BSD-style |
1059 | +// license that can be found in the LICENSE file. |
1060 | + |
1061 | +package vcs |
1062 | + |
1063 | +import ( |
1064 | + "errors" |
1065 | + "io/ioutil" |
1066 | + "os" |
1067 | + "path" |
1068 | + "path/filepath" |
1069 | + "reflect" |
1070 | + "runtime" |
1071 | + "strings" |
1072 | + "testing" |
1073 | +) |
1074 | + |
1075 | +// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath. |
1076 | +// TODO(cmang): Add tests for SVN and BZR. |
1077 | +func TestRepoRootForImportPath(t *testing.T) { |
1078 | + if runtime.GOOS == "android" { |
1079 | + t.Skipf("incomplete source tree on %s", runtime.GOOS) |
1080 | + } |
1081 | + |
1082 | + tests := []struct { |
1083 | + path string |
1084 | + want *RepoRoot |
1085 | + }{ |
1086 | + { |
1087 | + "github.com/golang/groupcache", |
1088 | + &RepoRoot{ |
1089 | + VCS: vcsGit, |
1090 | + Repo: "https://github.com/golang/groupcache", |
1091 | + }, |
1092 | + }, |
1093 | + // Unicode letters in directories (issue 18660). |
1094 | + { |
1095 | + "github.com/user/unicode/испытание", |
1096 | + &RepoRoot{ |
1097 | + VCS: vcsGit, |
1098 | + Repo: "https://github.com/user/unicode", |
1099 | + }, |
1100 | + }, |
1101 | + } |
1102 | + |
1103 | + for _, test := range tests { |
1104 | + got, err := RepoRootForImportPath(test.path, false) |
1105 | + if err != nil { |
1106 | + t.Errorf("RepoRootForImportPath(%q): %v", test.path, err) |
1107 | + continue |
1108 | + } |
1109 | + want := test.want |
1110 | + if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo { |
1111 | + t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo) |
1112 | + } |
1113 | + } |
1114 | +} |
1115 | + |
1116 | +// Test that FromDir correctly inspects a given directory and returns the right VCS and root. |
1117 | +func TestFromDir(t *testing.T) { |
1118 | + tempDir, err := ioutil.TempDir("", "vcstest") |
1119 | + if err != nil { |
1120 | + t.Fatal(err) |
1121 | + } |
1122 | + defer os.RemoveAll(tempDir) |
1123 | + |
1124 | + for j, vcs := range vcsList { |
1125 | + dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd) |
1126 | + if j&1 == 0 { |
1127 | + err := os.MkdirAll(dir, 0755) |
1128 | + if err != nil { |
1129 | + t.Fatal(err) |
1130 | + } |
1131 | + } else { |
1132 | + err := os.MkdirAll(filepath.Dir(dir), 0755) |
1133 | + if err != nil { |
1134 | + t.Fatal(err) |
1135 | + } |
1136 | + f, err := os.Create(dir) |
1137 | + if err != nil { |
1138 | + t.Fatal(err) |
1139 | + } |
1140 | + f.Close() |
1141 | + } |
1142 | + |
1143 | + want := RepoRoot{ |
1144 | + VCS: vcs, |
1145 | + Root: path.Join("example.com", vcs.Name), |
1146 | + } |
1147 | + var got RepoRoot |
1148 | + got.VCS, got.Root, err = FromDir(dir, tempDir) |
1149 | + if err != nil { |
1150 | + t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) |
1151 | + continue |
1152 | + } |
1153 | + if got.VCS.Name != want.VCS.Name || got.Root != want.Root { |
1154 | + t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root) |
1155 | + } |
1156 | + } |
1157 | +} |
1158 | + |
1159 | +var parseMetaGoImportsTests = []struct { |
1160 | + in string |
1161 | + out []metaImport |
1162 | +}{ |
1163 | + { |
1164 | + `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`, |
1165 | + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, |
1166 | + }, |
1167 | + { |
1168 | + `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> |
1169 | + <meta name="go-import" content="baz/quux git http://github.com/rsc/baz/quux">`, |
1170 | + []metaImport{ |
1171 | + {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, |
1172 | + {"baz/quux", "git", "http://github.com/rsc/baz/quux"}, |
1173 | + }, |
1174 | + }, |
1175 | + { |
1176 | + `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> |
1177 | + <meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`, |
1178 | + []metaImport{ |
1179 | + {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, |
1180 | + }, |
1181 | + }, |
1182 | + { |
1183 | + `<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux"> |
1184 | + <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`, |
1185 | + []metaImport{ |
1186 | + {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, |
1187 | + }, |
1188 | + }, |
1189 | + { |
1190 | + `<head> |
1191 | + <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> |
1192 | + </head>`, |
1193 | + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, |
1194 | + }, |
1195 | + { |
1196 | + `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> |
1197 | + <body>`, |
1198 | + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, |
1199 | + }, |
1200 | + { |
1201 | + `<!doctype html><meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`, |
1202 | + []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, |
1203 | + }, |
1204 | + { |
1205 | + // XML doesn't like <div style=position:relative>. |
1206 | + `<!doctype html><title>Page Not Found</title><meta name=go-import content="chitin.io/chitin git https://github.com/chitin-io/chitin"><div style=position:relative>DRAFT</div>`, |
1207 | + []metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}}, |
1208 | + }, |
1209 | + { |
1210 | + `<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x"> |
1211 | + <meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master"> |
1212 | + `, |
1213 | + []metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}}, |
1214 | + }, |
1215 | +} |
1216 | + |
1217 | +func TestParseMetaGoImports(t *testing.T) { |
1218 | + for i, tt := range parseMetaGoImportsTests { |
1219 | + out, err := parseMetaGoImports(strings.NewReader(tt.in)) |
1220 | + if err != nil { |
1221 | + t.Errorf("test#%d: %v", i, err) |
1222 | + continue |
1223 | + } |
1224 | + if !reflect.DeepEqual(out, tt.out) { |
1225 | + t.Errorf("test#%d:\n\thave %q\n\twant %q", i, out, tt.out) |
1226 | + } |
1227 | + } |
1228 | +} |
1229 | + |
1230 | +func TestValidateRepoRoot(t *testing.T) { |
1231 | + tests := []struct { |
1232 | + root string |
1233 | + ok bool |
1234 | + }{ |
1235 | + { |
1236 | + root: "", |
1237 | + ok: false, |
1238 | + }, |
1239 | + { |
1240 | + root: "http://", |
1241 | + ok: true, |
1242 | + }, |
1243 | + { |
1244 | + root: "git+ssh://", |
1245 | + ok: true, |
1246 | + }, |
1247 | + { |
1248 | + root: "http#://", |
1249 | + ok: false, |
1250 | + }, |
1251 | + { |
1252 | + root: "-config", |
1253 | + ok: false, |
1254 | + }, |
1255 | + { |
1256 | + root: "-config://", |
1257 | + ok: false, |
1258 | + }, |
1259 | + } |
1260 | + |
1261 | + for _, test := range tests { |
1262 | + err := validateRepoRoot(test.root) |
1263 | + ok := err == nil |
1264 | + if ok != test.ok { |
1265 | + want := "error" |
1266 | + if test.ok { |
1267 | + want = "nil" |
1268 | + } |
1269 | + t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want) |
1270 | + } |
1271 | + } |
1272 | +} |
1273 | + |
1274 | +func TestMatchGoImport(t *testing.T) { |
1275 | + tests := []struct { |
1276 | + imports []metaImport |
1277 | + path string |
1278 | + mi metaImport |
1279 | + err error |
1280 | + }{ |
1281 | + { |
1282 | + imports: []metaImport{ |
1283 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1284 | + }, |
1285 | + path: "example.com/user/foo", |
1286 | + mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1287 | + }, |
1288 | + { |
1289 | + imports: []metaImport{ |
1290 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1291 | + }, |
1292 | + path: "example.com/user/foo/", |
1293 | + mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1294 | + }, |
1295 | + { |
1296 | + imports: []metaImport{ |
1297 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1298 | + {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1299 | + }, |
1300 | + path: "example.com/user/foo", |
1301 | + mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1302 | + }, |
1303 | + { |
1304 | + imports: []metaImport{ |
1305 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1306 | + {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1307 | + }, |
1308 | + path: "example.com/user/fooa", |
1309 | + mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1310 | + }, |
1311 | + { |
1312 | + imports: []metaImport{ |
1313 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1314 | + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1315 | + }, |
1316 | + path: "example.com/user/foo/bar", |
1317 | + err: errors.New("should not be allowed to create nested repo"), |
1318 | + }, |
1319 | + { |
1320 | + imports: []metaImport{ |
1321 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1322 | + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1323 | + }, |
1324 | + path: "example.com/user/foo/bar/baz", |
1325 | + err: errors.New("should not be allowed to create nested repo"), |
1326 | + }, |
1327 | + { |
1328 | + imports: []metaImport{ |
1329 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1330 | + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1331 | + }, |
1332 | + path: "example.com/user/foo/bar/baz/qux", |
1333 | + err: errors.New("should not be allowed to create nested repo"), |
1334 | + }, |
1335 | + { |
1336 | + imports: []metaImport{ |
1337 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1338 | + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1339 | + }, |
1340 | + path: "example.com/user/foo/bar/baz/", |
1341 | + err: errors.New("should not be allowed to create nested repo"), |
1342 | + }, |
1343 | + { |
1344 | + imports: []metaImport{ |
1345 | + {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1346 | + {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, |
1347 | + }, |
1348 | + path: "example.com", |
1349 | + err: errors.New("pathologically short path"), |
1350 | + }, |
1351 | + } |
1352 | + |
1353 | + for _, test := range tests { |
1354 | + mi, err := matchGoImport(test.imports, test.path) |
1355 | + if mi != test.mi { |
1356 | + t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi) |
1357 | + } |
1358 | + |
1359 | + got := err |
1360 | + want := test.err |
1361 | + if (got == nil) != (want == nil) { |
1362 | + t.Errorf("unexpected error; got %v, want %v", got, want) |
1363 | + } |
1364 | + } |
1365 | +} |
1366 | diff --git a/debian/patches/01-Update-the-import-path-of-golang-github-google-go-github.patch b/debian/patches/01-Update-the-import-path-of-golang-github-google-go-github.patch |
1367 | deleted file mode 100644 |
1368 | index 8d3467b..0000000 |
1369 | --- a/debian/patches/01-Update-the-import-path-of-golang-github-google-go-github.patch |
1370 | +++ /dev/null |
1371 | @@ -1,34 +0,0 @@ |
1372 | -From: Roger Shimizu <rosh@debian.org> |
1373 | -Date: Fri, 17 Jul 2020 22:55:35 +0900 |
1374 | -Subject: Update the import path of golang-github-google-go-github |
1375 | - |
1376 | ---- |
1377 | - main.go | 2 +- |
1378 | - 1 file changed, 1 insertion(+), 1 deletion(-) |
1379 | - |
1380 | -Index: dh-make-golang/main.go |
1381 | -=================================================================== |
1382 | ---- dh-make-golang.orig/main.go 2022-11-18 12:45:52.035957432 +0100 |
1383 | -+++ dh-make-golang/main.go 2022-11-18 12:45:52.035957432 +0100 |
1384 | -@@ -4,7 +4,7 @@ |
1385 | - "fmt" |
1386 | - "os" |
1387 | - |
1388 | -- "github.com/google/go-github/v38/github" |
1389 | -+ "github.com/google/go-github/github" |
1390 | - "github.com/gregjones/httpcache" |
1391 | - ) |
1392 | - |
1393 | -Index: dh-make-golang/go.mod |
1394 | -=================================================================== |
1395 | ---- dh-make-golang.orig/go.mod 2022-11-18 12:43:54.600226791 +0100 |
1396 | -+++ dh-make-golang/go.mod 2022-11-18 12:46:09.780533980 +0100 |
1397 | -@@ -4,7 +4,7 @@ |
1398 | - |
1399 | - require ( |
1400 | - github.com/charmbracelet/glamour v0.3.0 |
1401 | -- github.com/google/go-github/v38 v38.1.0 |
1402 | -+ github.com/google/go-github v38.1.0 |
1403 | - github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 |
1404 | - github.com/mattn/go-isatty v0.0.12 |
1405 | - golang.org/x/mod v0.5.1 // indirect |
1406 | diff --git a/debian/patches/series b/debian/patches/series |
1407 | deleted file mode 100644 |
1408 | index c2ce9b0..0000000 |
1409 | --- a/debian/patches/series |
1410 | +++ /dev/null |
1411 | @@ -1 +0,0 @@ |
1412 | -01-Update-the-import-path-of-golang-github-google-go-github.patch |
1413 | diff --git a/debian/rules b/debian/rules |
1414 | index 71be22f..c93b44f 100755 |
1415 | --- a/debian/rules |
1416 | +++ b/debian/rules |
1417 | @@ -1,10 +1,19 @@ |
1418 | #!/usr/bin/make -f |
1419 | |
1420 | +export DEB_BUILD_MAINT_OPTIONS = optimize=-lto |
1421 | export DH_GOLANG_INSTALL_EXTRA := description.json |
1422 | |
1423 | %: |
1424 | dh $@ --builddirectory=_build --buildsystem=golang --with=golang |
1425 | |
1426 | +execute_after_dh_auto_configure: |
1427 | + # golang.org/x/tools/go/vcs v0.1.0-deprecated |
1428 | + ln -s $(CURDIR)/debian/go/src \ |
1429 | + _build/src/github.com/Debian/dh-make-golang/vendor |
1430 | + # github.com/google/go-github/github (with Go modules disabled) |
1431 | + sed -i -e 's#go-github/v[0-9]\+/github#go-github/github#' \ |
1432 | + _build/src/github.com/Debian/dh-make-golang/main.go |
1433 | + |
1434 | override_dh_auto_install: |
1435 | dh_auto_install -- --no-source |
1436 |
* Builds in a PPA with proposed enabled: https:/ /launchpad. net/~dviererbe/ +archive/ ubuntu/ lp2046369- ppa2/+packages
* The package has no autopkgtests.