Source file src/pkg/cmd/go/internal/modfetch/codehost/codehost.go
1
2
3
4
5
6
7 package codehost
8
9 import (
10 "bytes"
11 "crypto/sha256"
12 "fmt"
13 "io"
14 "io/ioutil"
15 "os"
16 "os/exec"
17 "path/filepath"
18 "strings"
19 "sync"
20 "time"
21
22 "cmd/go/internal/cfg"
23 "cmd/go/internal/lockedfile"
24 "cmd/go/internal/str"
25 )
26
27
28 const (
29 MaxGoMod = 16 << 20
30 MaxLICENSE = 16 << 20
31 MaxZipFile = 500 << 20
32 )
33
34
35
36
37
38 type Repo interface {
39
40 Tags(prefix string) (tags []string, err error)
41
42
43
44
45 Stat(rev string) (*RevInfo, error)
46
47
48
49 Latest() (*RevInfo, error)
50
51
52
53
54
55
56 ReadFile(rev, file string, maxSize int64) (data []byte, err error)
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 ReadFileRevs(revs []string, file string, maxSize int64) (files map[string]*FileRev, err error)
72
73
74
75
76
77
78
79
80 ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error)
81
82
83
84
85 RecentTag(rev, prefix, major string) (tag string, err error)
86
87
88
89
90
91 DescendsFrom(rev, tag string) (bool, error)
92 }
93
94
95 type RevInfo struct {
96 Name string
97 Short string
98 Version string
99 Time time.Time
100 Tags []string
101 }
102
103
104 type FileRev struct {
105 Rev string
106 Data []byte
107 Err error
108 }
109
110
111
112 type UnknownRevisionError struct {
113 Rev string
114 }
115
116 func (e *UnknownRevisionError) Error() string {
117 return "unknown revision " + e.Rev
118 }
119 func (UnknownRevisionError) Is(err error) bool {
120 return err == os.ErrNotExist
121 }
122
123
124
125 var ErrNoCommits error = noCommitsError{}
126
127 type noCommitsError struct{}
128
129 func (noCommitsError) Error() string {
130 return "no commits"
131 }
132 func (noCommitsError) Is(err error) bool {
133 return err == os.ErrNotExist
134 }
135
136
137 func AllHex(rev string) bool {
138 for i := 0; i < len(rev); i++ {
139 c := rev[i]
140 if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' {
141 continue
142 }
143 return false
144 }
145 return true
146 }
147
148
149
150 func ShortenSHA1(rev string) string {
151 if AllHex(rev) && len(rev) == 40 {
152 return rev[:12]
153 }
154 return rev
155 }
156
157
158
159 var WorkRoot string
160
161
162
163 func WorkDir(typ, name string) (dir, lockfile string, err error) {
164 if WorkRoot == "" {
165 return "", "", fmt.Errorf("codehost.WorkRoot not set")
166 }
167
168
169
170
171
172
173 if strings.Contains(typ, ":") {
174 return "", "", fmt.Errorf("codehost.WorkDir: type cannot contain colon")
175 }
176 key := typ + ":" + name
177 dir = filepath.Join(WorkRoot, fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
178
179 if cfg.BuildX {
180 fmt.Fprintf(os.Stderr, "mkdir -p %s # %s %s\n", filepath.Dir(dir), typ, name)
181 }
182 if err := os.MkdirAll(filepath.Dir(dir), 0777); err != nil {
183 return "", "", err
184 }
185
186 lockfile = dir + ".lock"
187 if cfg.BuildX {
188 fmt.Fprintf(os.Stderr, "# lock %s", lockfile)
189 }
190
191 unlock, err := lockedfile.MutexAt(lockfile).Lock()
192 if err != nil {
193 return "", "", fmt.Errorf("codehost.WorkDir: can't find or create lock file: %v", err)
194 }
195 defer unlock()
196
197 data, err := ioutil.ReadFile(dir + ".info")
198 info, err2 := os.Stat(dir)
199 if err == nil && err2 == nil && info.IsDir() {
200
201 have := strings.TrimSuffix(string(data), "\n")
202 if have != key {
203 return "", "", fmt.Errorf("%s exists with wrong content (have %q want %q)", dir+".info", have, key)
204 }
205 if cfg.BuildX {
206 fmt.Fprintf(os.Stderr, "# %s for %s %s\n", dir, typ, name)
207 }
208 return dir, lockfile, nil
209 }
210
211
212 if cfg.BuildX {
213 fmt.Fprintf(os.Stderr, "mkdir -p %s # %s %s\n", dir, typ, name)
214 }
215 os.RemoveAll(dir)
216 if err := os.MkdirAll(dir, 0777); err != nil {
217 return "", "", err
218 }
219 if err := ioutil.WriteFile(dir+".info", []byte(key), 0666); err != nil {
220 os.RemoveAll(dir)
221 return "", "", err
222 }
223 return dir, lockfile, nil
224 }
225
226 type RunError struct {
227 Cmd string
228 Err error
229 Stderr []byte
230 HelpText string
231 }
232
233 func (e *RunError) Error() string {
234 text := e.Cmd + ": " + e.Err.Error()
235 stderr := bytes.TrimRight(e.Stderr, "\n")
236 if len(stderr) > 0 {
237 text += ":\n\t" + strings.ReplaceAll(string(stderr), "\n", "\n\t")
238 }
239 if len(e.HelpText) > 0 {
240 text += "\n" + e.HelpText
241 }
242 return text
243 }
244
245 var dirLock sync.Map
246
247
248
249
250
251
252 func Run(dir string, cmdline ...interface{}) ([]byte, error) {
253 return RunWithStdin(dir, nil, cmdline...)
254 }
255
256
257
258 var bashQuoter = strings.NewReplacer(`"`, `\"`, `$`, `\$`, "`", "\\`", `\`, `\\`)
259
260 func RunWithStdin(dir string, stdin io.Reader, cmdline ...interface{}) ([]byte, error) {
261 if dir != "" {
262 muIface, ok := dirLock.Load(dir)
263 if !ok {
264 muIface, _ = dirLock.LoadOrStore(dir, new(sync.Mutex))
265 }
266 mu := muIface.(*sync.Mutex)
267 mu.Lock()
268 defer mu.Unlock()
269 }
270
271 cmd := str.StringList(cmdline...)
272 if cfg.BuildX {
273 text := new(strings.Builder)
274 if dir != "" {
275 text.WriteString("cd ")
276 text.WriteString(dir)
277 text.WriteString("; ")
278 }
279 for i, arg := range cmd {
280 if i > 0 {
281 text.WriteByte(' ')
282 }
283 switch {
284 case strings.ContainsAny(arg, "'"):
285
286 text.WriteByte('"')
287 text.WriteString(bashQuoter.Replace(arg))
288 text.WriteByte('"')
289 case strings.ContainsAny(arg, "$`\\*?[\"\t\n\v\f\r \u0085\u00a0"):
290
291 text.WriteByte('\'')
292 text.WriteString(arg)
293 text.WriteByte('\'')
294 default:
295 text.WriteString(arg)
296 }
297 }
298 fmt.Fprintf(os.Stderr, "%s\n", text)
299 start := time.Now()
300 defer func() {
301 fmt.Fprintf(os.Stderr, "%.3fs # %s\n", time.Since(start).Seconds(), text)
302 }()
303 }
304
305
306 var stderr bytes.Buffer
307 var stdout bytes.Buffer
308 c := exec.Command(cmd[0], cmd[1:]...)
309 c.Dir = dir
310 c.Stdin = stdin
311 c.Stderr = &stderr
312 c.Stdout = &stdout
313 err := c.Run()
314 if err != nil {
315 err = &RunError{Cmd: strings.Join(cmd, " ") + " in " + dir, Stderr: stderr.Bytes(), Err: err}
316 }
317 return stdout.Bytes(), err
318 }
319
View as plain text