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