]>
Commit | Line | Data |
---|---|---|
dd931d9b ILT |
1 | // Copyright 2018 The Go Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE file. | |
4 | ||
5 | package modconv | |
6 | ||
7 | import ( | |
8 | "fmt" | |
aa8901e9 ILT |
9 | "internal/lazyregexp" |
10 | "net/url" | |
11 | "path" | |
dd931d9b ILT |
12 | "strconv" |
13 | "strings" | |
14 | ||
5a8ea165 ILT |
15 | "golang.org/x/mod/modfile" |
16 | "golang.org/x/mod/module" | |
17 | "golang.org/x/mod/semver" | |
dd931d9b ILT |
18 | ) |
19 | ||
20 | func ParseGopkgLock(file string, data []byte) (*modfile.File, error) { | |
aa8901e9 ILT |
21 | type pkg struct { |
22 | Path string | |
23 | Version string | |
24 | Source string | |
25 | } | |
dd931d9b | 26 | mf := new(modfile.File) |
aa8901e9 ILT |
27 | var list []pkg |
28 | var r *pkg | |
dd931d9b ILT |
29 | for lineno, line := range strings.Split(string(data), "\n") { |
30 | lineno++ | |
31 | if i := strings.Index(line, "#"); i >= 0 { | |
32 | line = line[:i] | |
33 | } | |
34 | line = strings.TrimSpace(line) | |
35 | if line == "[[projects]]" { | |
aa8901e9 | 36 | list = append(list, pkg{}) |
dd931d9b ILT |
37 | r = &list[len(list)-1] |
38 | continue | |
39 | } | |
40 | if strings.HasPrefix(line, "[") { | |
41 | r = nil | |
42 | continue | |
43 | } | |
44 | if r == nil { | |
45 | continue | |
46 | } | |
47 | i := strings.Index(line, "=") | |
48 | if i < 0 { | |
49 | continue | |
50 | } | |
51 | key := strings.TrimSpace(line[:i]) | |
52 | val := strings.TrimSpace(line[i+1:]) | |
53 | if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' { | |
54 | q, err := strconv.Unquote(val) // Go unquoting, but close enough for now | |
55 | if err != nil { | |
56 | return nil, fmt.Errorf("%s:%d: invalid quoted string: %v", file, lineno, err) | |
57 | } | |
58 | val = q | |
59 | } | |
60 | switch key { | |
61 | case "name": | |
62 | r.Path = val | |
aa8901e9 ILT |
63 | case "source": |
64 | r.Source = val | |
dd931d9b ILT |
65 | case "revision", "version": |
66 | // Note: key "version" should take priority over "revision", | |
67 | // and it does, because dep writes toml keys in alphabetical order, | |
68 | // so we see version (if present) second. | |
69 | if key == "version" { | |
70 | if !semver.IsValid(val) || semver.Canonical(val) != val { | |
71 | break | |
72 | } | |
73 | } | |
74 | r.Version = val | |
75 | } | |
76 | } | |
77 | for _, r := range list { | |
78 | if r.Path == "" || r.Version == "" { | |
79 | return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path) | |
80 | } | |
aa8901e9 ILT |
81 | mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: r.Path, Version: r.Version}}) |
82 | ||
83 | if r.Source != "" { | |
84 | // Convert "source" to import path, such as | |
85 | // git@test.com:x/y.git and https://test.com/x/y.git. | |
86 | // We get "test.com/x/y" at last. | |
87 | source, err := decodeSource(r.Source) | |
88 | if err != nil { | |
89 | return nil, err | |
90 | } | |
91 | old := module.Version{Path: r.Path, Version: r.Version} | |
92 | new := module.Version{Path: source, Version: r.Version} | |
93 | mf.Replace = append(mf.Replace, &modfile.Replace{Old: old, New: new}) | |
94 | } | |
dd931d9b ILT |
95 | } |
96 | return mf, nil | |
97 | } | |
aa8901e9 ILT |
98 | |
99 | var scpSyntaxReg = lazyregexp.New(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) | |
100 | ||
101 | func decodeSource(source string) (string, error) { | |
102 | var u *url.URL | |
103 | var p string | |
104 | if m := scpSyntaxReg.FindStringSubmatch(source); m != nil { | |
105 | // Match SCP-like syntax and convert it to a URL. | |
106 | // Eg, "git@github.com:user/repo" becomes | |
107 | // "ssh://git@github.com/user/repo". | |
108 | u = &url.URL{ | |
109 | Scheme: "ssh", | |
110 | User: url.User(m[1]), | |
111 | Host: m[2], | |
112 | Path: "/" + m[3], | |
113 | } | |
114 | } else { | |
115 | var err error | |
116 | u, err = url.Parse(source) | |
117 | if err != nil { | |
118 | return "", fmt.Errorf("%q is not a valid URI", source) | |
119 | } | |
120 | } | |
121 | ||
122 | // If no scheme was passed, then the entire path will have been put into | |
123 | // u.Path. Either way, construct the normalized path correctly. | |
124 | if u.Host == "" { | |
125 | p = source | |
126 | } else { | |
127 | p = path.Join(u.Host, u.Path) | |
128 | } | |
129 | p = strings.TrimSuffix(p, ".git") | |
130 | p = strings.TrimSuffix(p, ".hg") | |
131 | return p, nil | |
132 | } |