1
|
|
|
package cfg |
2
|
|
|
|
3
|
|
|
import ( |
4
|
|
|
"crypto/sha256" |
5
|
|
|
"fmt" |
6
|
|
|
"io/ioutil" |
7
|
|
|
"reflect" |
8
|
|
|
"sort" |
9
|
|
|
"strings" |
10
|
|
|
|
11
|
|
|
"github.com/Masterminds/glide/mirrors" |
12
|
|
|
"github.com/Masterminds/glide/util" |
13
|
|
|
"github.com/Masterminds/vcs" |
14
|
|
|
"gopkg.in/yaml.v2" |
15
|
|
|
) |
16
|
|
|
|
17
|
|
|
// Config is the top-level configuration object. |
18
|
|
|
type Config struct { |
19
|
|
|
|
20
|
|
|
// Name is the name of the package or application. |
21
|
|
|
Name string `yaml:"package"` |
22
|
|
|
|
23
|
|
|
// Description is a short description for a package, application, or library. |
24
|
|
|
// This description is similar but different to a Go package description as |
25
|
|
|
// it is for marketing and presentation purposes rather than technical ones. |
26
|
|
|
Description string `json:"description,omitempty"` |
27
|
|
|
|
28
|
|
|
// Home is a url to a website for the package. |
29
|
|
|
Home string `yaml:"homepage,omitempty"` |
30
|
|
|
|
31
|
|
|
// License provides either a SPDX license or a path to a file containing |
32
|
|
|
// the license. For more information on SPDX see http://spdx.org/licenses/. |
33
|
|
|
// When more than one license an SPDX expression can be used. |
34
|
|
|
License string `yaml:"license,omitempty"` |
35
|
|
|
|
36
|
|
|
// Owners is an array of owners for a project. See the Owner type for |
37
|
|
|
// more detail. These can be one or more people, companies, or other |
38
|
|
|
// organizations. |
39
|
|
|
Owners Owners `yaml:"owners,omitempty"` |
40
|
|
|
|
41
|
|
|
// Ignore contains a list of packages to ignore fetching. This is useful |
42
|
|
|
// when walking the package tree (including packages of packages) to list |
43
|
|
|
// those to skip. |
44
|
|
|
Ignore []string `yaml:"ignore,omitempty"` |
45
|
|
|
|
46
|
|
|
// Exclude contains a list of directories in the local application to |
47
|
|
|
// exclude from scanning for dependencies. |
48
|
|
|
Exclude []string `yaml:"excludeDirs,omitempty"` |
49
|
|
|
|
50
|
|
|
// Imports contains a list of all non-development imports for a project. For |
51
|
|
|
// more detail on how these are captured see the Dependency type. |
52
|
|
|
Imports Dependencies `yaml:"import"` |
53
|
|
|
|
54
|
|
|
// DevImports contains the test or other development imports for a project. |
55
|
|
|
// See the Dependency type for more details on how this is recorded. |
56
|
|
|
DevImports Dependencies `yaml:"testImport,omitempty"` |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
// A transitive representation of a dependency for importing and exporting to yaml. |
60
|
|
|
type cf struct { |
61
|
|
|
Name string `yaml:"package"` |
62
|
|
|
Description string `yaml:"description,omitempty"` |
63
|
|
|
Home string `yaml:"homepage,omitempty"` |
64
|
|
|
License string `yaml:"license,omitempty"` |
65
|
|
|
Owners Owners `yaml:"owners,omitempty"` |
66
|
|
|
Ignore []string `yaml:"ignore,omitempty"` |
67
|
|
|
Exclude []string `yaml:"excludeDirs,omitempty"` |
68
|
|
|
Imports Dependencies `yaml:"import"` |
69
|
|
|
DevImports Dependencies `yaml:"testImport,omitempty"` |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
// ConfigFromYaml returns an instance of Config from YAML |
73
|
|
|
func ConfigFromYaml(yml []byte) (*Config, error) { |
74
|
|
|
cfg := &Config{} |
75
|
|
|
err := yaml.Unmarshal([]byte(yml), &cfg) |
76
|
|
|
return cfg, err |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
// Marshal converts a Config instance to YAML |
80
|
|
|
func (c *Config) Marshal() ([]byte, error) { |
81
|
|
|
yml, err := yaml.Marshal(&c) |
82
|
|
|
if err != nil { |
83
|
|
|
return []byte{}, err |
84
|
|
|
} |
85
|
|
|
return yml, nil |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
// UnmarshalYAML is a hook for gopkg.in/yaml.v2 in the unmarshalling process |
89
|
|
|
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { |
90
|
|
|
newConfig := &cf{} |
91
|
|
|
if err := unmarshal(&newConfig); err != nil { |
92
|
|
|
return err |
93
|
|
|
} |
94
|
|
|
c.Name = newConfig.Name |
95
|
|
|
c.Description = newConfig.Description |
96
|
|
|
c.Home = newConfig.Home |
97
|
|
|
c.License = newConfig.License |
98
|
|
|
c.Owners = newConfig.Owners |
99
|
|
|
c.Ignore = newConfig.Ignore |
100
|
|
|
c.Exclude = newConfig.Exclude |
101
|
|
|
c.Imports = newConfig.Imports |
102
|
|
|
c.DevImports = newConfig.DevImports |
103
|
|
|
|
104
|
|
|
// Cleanup the Config object now that we have it. |
105
|
|
|
err := c.DeDupe() |
106
|
|
|
|
107
|
|
|
return err |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
// MarshalYAML is a hook for gopkg.in/yaml.v2 in the marshaling process |
111
|
|
|
func (c *Config) MarshalYAML() (interface{}, error) { |
112
|
|
|
newConfig := &cf{ |
113
|
|
|
Name: c.Name, |
114
|
|
|
Description: c.Description, |
115
|
|
|
Home: c.Home, |
116
|
|
|
License: c.License, |
117
|
|
|
Owners: c.Owners, |
118
|
|
|
Ignore: c.Ignore, |
119
|
|
|
Exclude: c.Exclude, |
120
|
|
|
} |
121
|
|
|
i, err := c.Imports.Clone().DeDupe() |
122
|
|
|
if err != nil { |
123
|
|
|
return newConfig, err |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
di, err := c.DevImports.Clone().DeDupe() |
127
|
|
|
if err != nil { |
128
|
|
|
return newConfig, err |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
newConfig.Imports = i |
132
|
|
|
newConfig.DevImports = di |
133
|
|
|
|
134
|
|
|
return newConfig, nil |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
// HasDependency returns true if the given name is listed as an import or dev import. |
138
|
|
|
func (c *Config) HasDependency(name string) bool { |
139
|
|
|
for _, d := range c.Imports { |
140
|
|
|
if d.Name == name { |
141
|
|
|
return true |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
for _, d := range c.DevImports { |
145
|
|
|
if d.Name == name { |
146
|
|
|
return true |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
return false |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
// HasIgnore returns true if the given name is listed on the ignore list. |
153
|
|
|
func (c *Config) HasIgnore(name string) bool { |
154
|
|
|
for _, v := range c.Ignore { |
155
|
|
|
|
156
|
|
|
// Check for both a name and to make sure sub-packages are ignored as |
157
|
|
|
// well. |
158
|
|
|
if v == name || strings.HasPrefix(name, v+"/") { |
159
|
|
|
return true |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
return false |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// HasExclude returns true if the given name is listed on the exclude list. |
167
|
|
|
func (c *Config) HasExclude(ex string) bool { |
168
|
|
|
ep := normalizeSlash(ex) |
169
|
|
|
for _, v := range c.Exclude { |
170
|
|
|
if vp := normalizeSlash(v); vp == ep { |
171
|
|
|
return true |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
return false |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
// Clone performs a deep clone of the Config instance |
179
|
|
|
func (c *Config) Clone() *Config { |
180
|
|
|
n := &Config{} |
181
|
|
|
n.Name = c.Name |
182
|
|
|
n.Description = c.Description |
183
|
|
|
n.Home = c.Home |
184
|
|
|
n.License = c.License |
185
|
|
|
n.Owners = c.Owners.Clone() |
186
|
|
|
n.Ignore = c.Ignore |
187
|
|
|
n.Exclude = c.Exclude |
188
|
|
|
n.Imports = c.Imports.Clone() |
189
|
|
|
n.DevImports = c.DevImports.Clone() |
190
|
|
|
return n |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
// WriteFile writes a Glide YAML file. |
194
|
|
|
// |
195
|
|
|
// This is a convenience function that marshals the YAML and then writes it to |
196
|
|
|
// the given file. If the file exists, it will be clobbered. |
197
|
|
|
func (c *Config) WriteFile(glidepath string) error { |
198
|
|
|
o, err := c.Marshal() |
199
|
|
|
if err != nil { |
200
|
|
|
return err |
201
|
|
|
} |
202
|
|
|
return ioutil.WriteFile(glidepath, o, 0666) |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
// DeDupe consolidates duplicate dependencies on a Config instance |
206
|
|
|
func (c *Config) DeDupe() error { |
207
|
|
|
|
208
|
|
|
// Remove duplicates in the imports |
209
|
|
|
var err error |
210
|
|
|
c.Imports, err = c.Imports.DeDupe() |
211
|
|
|
if err != nil { |
212
|
|
|
return err |
213
|
|
|
} |
214
|
|
|
c.DevImports, err = c.DevImports.DeDupe() |
215
|
|
|
if err != nil { |
216
|
|
|
return err |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
// If the name on the config object is part of the imports remove it. |
220
|
|
|
found := -1 |
221
|
|
|
for i, dep := range c.Imports { |
222
|
|
|
if dep.Name == c.Name { |
223
|
|
|
found = i |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
if found >= 0 { |
227
|
|
|
c.Imports = append(c.Imports[:found], c.Imports[found+1:]...) |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
found = -1 |
231
|
|
|
for i, dep := range c.DevImports { |
232
|
|
|
if dep.Name == c.Name { |
233
|
|
|
found = i |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
if found >= 0 { |
237
|
|
|
c.DevImports = append(c.DevImports[:found], c.DevImports[found+1:]...) |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
// If something is on the ignore list remove it from the imports. |
241
|
|
|
for _, v := range c.Ignore { |
242
|
|
|
found = -1 |
243
|
|
|
for k, d := range c.Imports { |
244
|
|
|
if v == d.Name { |
245
|
|
|
found = k |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
if found >= 0 { |
249
|
|
|
c.Imports = append(c.Imports[:found], c.Imports[found+1:]...) |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
found = -1 |
253
|
|
|
for k, d := range c.DevImports { |
254
|
|
|
if v == d.Name { |
255
|
|
|
found = k |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
if found >= 0 { |
259
|
|
|
c.DevImports = append(c.DevImports[:found], c.DevImports[found+1:]...) |
260
|
|
|
} |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
return nil |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
// AddImport appends dependencies to the import list, deduplicating as we go. |
267
|
|
|
func (c *Config) AddImport(deps ...*Dependency) error { |
268
|
|
|
t := c.Imports |
269
|
|
|
t = append(t, deps...) |
270
|
|
|
t, err := t.DeDupe() |
271
|
|
|
if err != nil { |
272
|
|
|
return err |
273
|
|
|
} |
274
|
|
|
c.Imports = t |
275
|
|
|
return nil |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
// Hash generates a sha256 hash for a given Config |
279
|
|
|
func (c *Config) Hash() (string, error) { |
280
|
|
|
yml, err := c.Marshal() |
281
|
|
|
if err != nil { |
282
|
|
|
return "", err |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
hash := sha256.New() |
286
|
|
|
hash.Write(yml) |
287
|
|
|
return fmt.Sprintf("%x", hash.Sum(nil)), nil |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
// Dependencies is a collection of Dependency |
291
|
|
|
type Dependencies []*Dependency |
292
|
|
|
|
293
|
|
|
// Get a dependency by name |
294
|
|
|
func (d Dependencies) Get(name string) *Dependency { |
295
|
|
|
for _, dep := range d { |
296
|
|
|
if dep.Name == name { |
297
|
|
|
return dep |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
return nil |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
// Has checks if a dependency is on a list of dependencies such as import or testImport |
304
|
|
|
func (d Dependencies) Has(name string) bool { |
305
|
|
|
for _, dep := range d { |
306
|
|
|
if dep.Name == name { |
307
|
|
|
return true |
308
|
|
|
} |
309
|
|
|
} |
310
|
|
|
return false |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
// Remove removes a dependency from a list of dependencies |
314
|
|
|
func (d Dependencies) Remove(name string) Dependencies { |
315
|
|
|
found := -1 |
316
|
|
|
for i, dep := range d { |
317
|
|
|
if dep.Name == name { |
318
|
|
|
found = i |
319
|
|
|
} |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
if found >= 0 { |
323
|
|
|
copy(d[found:], d[found+1:]) |
324
|
|
|
d[len(d)-1] = nil |
325
|
|
|
return d[:len(d)-1] |
326
|
|
|
} |
327
|
|
|
return d |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
// Clone performs a deep clone of Dependencies |
331
|
|
|
func (d Dependencies) Clone() Dependencies { |
332
|
|
|
n := make(Dependencies, 0, len(d)) |
333
|
|
|
for _, v := range d { |
334
|
|
|
n = append(n, v.Clone()) |
335
|
|
|
} |
336
|
|
|
return n |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
// DeDupe cleans up duplicates on a list of dependencies. |
340
|
|
|
func (d Dependencies) DeDupe() (Dependencies, error) { |
341
|
|
|
checked := map[string]int{} |
342
|
|
|
imports := make(Dependencies, 0, 1) |
343
|
|
|
i := 0 |
344
|
|
|
for _, dep := range d { |
345
|
|
|
// The first time we encounter a dependency add it to the list |
346
|
|
|
if val, ok := checked[dep.Name]; !ok { |
347
|
|
|
checked[dep.Name] = i |
348
|
|
|
imports = append(imports, dep) |
349
|
|
|
i++ |
350
|
|
|
} else { |
351
|
|
|
// In here we've encountered a dependency for the second time. |
352
|
|
|
// Make sure the details are the same or return an error. |
353
|
|
|
v := imports[val] |
354
|
|
|
if dep.Reference != v.Reference { |
355
|
|
|
return d, fmt.Errorf("Import %s repeated with different versions '%s' and '%s'", dep.Name, dep.Reference, v.Reference) |
356
|
|
|
} |
357
|
|
|
if dep.Repository != v.Repository || dep.VcsType != v.VcsType { |
358
|
|
|
return d, fmt.Errorf("Import %s repeated with different Repository details", dep.Name) |
359
|
|
|
} |
360
|
|
|
if !reflect.DeepEqual(dep.Os, v.Os) || !reflect.DeepEqual(dep.Arch, v.Arch) { |
361
|
|
|
return d, fmt.Errorf("Import %s repeated with different OS or Architecture filtering", dep.Name) |
362
|
|
|
} |
363
|
|
|
imports[checked[dep.Name]].Subpackages = stringArrayDeDupe(v.Subpackages, dep.Subpackages...) |
364
|
|
|
} |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
return imports, nil |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
// Dependency describes a package that the present package depends upon. |
371
|
|
|
type Dependency struct { |
372
|
|
|
Name string `yaml:"package"` |
373
|
|
|
Reference string `yaml:"version,omitempty"` |
374
|
|
|
Pin string `yaml:"-"` |
375
|
|
|
Repository string `yaml:"repo,omitempty"` |
376
|
|
|
VcsType string `yaml:"vcs,omitempty"` |
377
|
|
|
Subpackages []string `yaml:"subpackages,omitempty"` |
378
|
|
|
Arch []string `yaml:"arch,omitempty"` |
379
|
|
|
Os []string `yaml:"os,omitempty"` |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
// A transitive representation of a dependency for importing and exploting to yaml. |
383
|
|
|
type dep struct { |
384
|
|
|
Name string `yaml:"package"` |
385
|
|
|
Reference string `yaml:"version,omitempty"` |
386
|
|
|
Ref string `yaml:"ref,omitempty"` |
387
|
|
|
Repository string `yaml:"repo,omitempty"` |
388
|
|
|
VcsType string `yaml:"vcs,omitempty"` |
389
|
|
|
Subpackages []string `yaml:"subpackages,omitempty"` |
390
|
|
|
Arch []string `yaml:"arch,omitempty"` |
391
|
|
|
Os []string `yaml:"os,omitempty"` |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
// DependencyFromLock converts a Lock to a Dependency |
395
|
|
|
func DependencyFromLock(lock *Lock) *Dependency { |
396
|
|
|
return &Dependency{ |
397
|
|
|
Name: lock.Name, |
398
|
|
|
Reference: lock.Version, |
399
|
|
|
Repository: lock.Repository, |
400
|
|
|
VcsType: lock.VcsType, |
401
|
|
|
Subpackages: lock.Subpackages, |
402
|
|
|
Arch: lock.Arch, |
403
|
|
|
Os: lock.Os, |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
// UnmarshalYAML is a hook for gopkg.in/yaml.v2 in the unmarshaling process |
408
|
|
|
func (d *Dependency) UnmarshalYAML(unmarshal func(interface{}) error) error { |
409
|
|
|
newDep := &dep{} |
410
|
|
|
err := unmarshal(&newDep) |
411
|
|
|
if err != nil { |
412
|
|
|
return err |
413
|
|
|
} |
414
|
|
|
d.Name = newDep.Name |
415
|
|
|
d.Reference = newDep.Reference |
416
|
|
|
d.Repository = newDep.Repository |
417
|
|
|
d.VcsType = newDep.VcsType |
418
|
|
|
d.Subpackages = newDep.Subpackages |
419
|
|
|
d.Arch = newDep.Arch |
420
|
|
|
d.Os = newDep.Os |
421
|
|
|
|
422
|
|
|
if d.Reference == "" && newDep.Ref != "" { |
423
|
|
|
d.Reference = newDep.Ref |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
// Make sure only legitimate VCS are listed. |
427
|
|
|
d.VcsType = filterVcsType(d.VcsType) |
428
|
|
|
|
429
|
|
|
// Get the root name for the package |
430
|
|
|
tn, subpkg := util.NormalizeName(d.Name) |
431
|
|
|
d.Name = tn |
432
|
|
|
if subpkg != "" { |
433
|
|
|
d.Subpackages = append(d.Subpackages, subpkg) |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
// Older versions of Glide had a / prefix on subpackages in some cases. |
437
|
|
|
// Here that's cleaned up. Someday we should be able to remove this. |
438
|
|
|
for k, v := range d.Subpackages { |
439
|
|
|
d.Subpackages[k] = strings.TrimPrefix(v, "/") |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
return nil |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
// MarshalYAML is a hook for gopkg.in/yaml.v2 in the marshaling process |
446
|
|
|
func (d *Dependency) MarshalYAML() (interface{}, error) { |
447
|
|
|
|
448
|
|
|
// Make sure we only write the correct vcs type to file |
449
|
|
|
t := filterVcsType(d.VcsType) |
450
|
|
|
newDep := &dep{ |
451
|
|
|
Name: d.Name, |
452
|
|
|
Reference: d.Reference, |
453
|
|
|
Repository: d.Repository, |
454
|
|
|
VcsType: t, |
455
|
|
|
Subpackages: d.Subpackages, |
456
|
|
|
Arch: d.Arch, |
457
|
|
|
Os: d.Os, |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
return newDep, nil |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
// Remote returns the remote location to fetch source from. This location is |
464
|
|
|
// the central place where mirrors can alter the location. |
465
|
|
|
func (d *Dependency) Remote() string { |
466
|
|
|
var r string |
467
|
|
|
|
468
|
|
|
if d.Repository != "" { |
469
|
|
|
r = d.Repository |
470
|
|
|
} else { |
471
|
|
|
r = "https://" + d.Name |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
f, nr, _ := mirrors.Get(r) |
475
|
|
|
if f { |
476
|
|
|
return nr |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
return r |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
// Vcs returns the VCS type to fetch source from. |
483
|
|
|
func (d *Dependency) Vcs() string { |
484
|
|
|
var r string |
485
|
|
|
|
486
|
|
|
if d.Repository != "" { |
487
|
|
|
r = d.Repository |
488
|
|
|
} else { |
489
|
|
|
r = "https://" + d.Name |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
f, _, nv := mirrors.Get(r) |
493
|
|
|
if f { |
494
|
|
|
return nv |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
return d.VcsType |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
// GetRepo retrieves a Masterminds/vcs repo object configured for the root |
501
|
|
|
// of the package being retrieved. |
502
|
|
|
func (d *Dependency) GetRepo(dest string) (vcs.Repo, error) { |
503
|
|
|
|
504
|
|
|
// The remote location is either the configured repo or the package |
505
|
|
|
// name as an https url. |
506
|
|
|
remote := d.Remote() |
507
|
|
|
|
508
|
|
|
VcsType := d.Vcs() |
509
|
|
|
|
510
|
|
|
// If the VCS type has a value we try that first. |
511
|
|
|
if len(VcsType) > 0 && VcsType != "None" { |
512
|
|
|
switch vcs.Type(VcsType) { |
513
|
|
|
case vcs.Git: |
514
|
|
|
return vcs.NewGitRepo(remote, dest) |
515
|
|
|
case vcs.Svn: |
516
|
|
|
return vcs.NewSvnRepo(remote, dest) |
517
|
|
|
case vcs.Hg: |
518
|
|
|
return vcs.NewHgRepo(remote, dest) |
519
|
|
|
case vcs.Bzr: |
520
|
|
|
return vcs.NewBzrRepo(remote, dest) |
521
|
|
|
default: |
522
|
|
|
return nil, fmt.Errorf("Unknown VCS type %s set for %s", VcsType, d.Name) |
523
|
|
|
} |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
// When no type set we try to autodetect. |
527
|
|
|
return vcs.NewRepo(remote, dest) |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
// Clone creates a clone of a Dependency |
531
|
|
|
func (d *Dependency) Clone() *Dependency { |
532
|
|
|
return &Dependency{ |
533
|
|
|
Name: d.Name, |
534
|
|
|
Reference: d.Reference, |
535
|
|
|
Pin: d.Pin, |
536
|
|
|
Repository: d.Repository, |
537
|
|
|
VcsType: d.VcsType, |
538
|
|
|
Subpackages: d.Subpackages, |
539
|
|
|
Arch: d.Arch, |
540
|
|
|
Os: d.Os, |
541
|
|
|
} |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
// HasSubpackage returns if the subpackage is present on the dependency |
545
|
|
|
func (d *Dependency) HasSubpackage(sub string) bool { |
546
|
|
|
|
547
|
|
|
for _, v := range d.Subpackages { |
548
|
|
|
if sub == v { |
549
|
|
|
return true |
550
|
|
|
} |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
return false |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
// Owners is a list of owners for a project. |
557
|
|
|
type Owners []*Owner |
558
|
|
|
|
559
|
|
|
// Clone performs a deep clone of Owners |
560
|
|
|
func (o Owners) Clone() Owners { |
561
|
|
|
n := make(Owners, 0, 1) |
562
|
|
|
for _, v := range o { |
563
|
|
|
n = append(n, v.Clone()) |
564
|
|
|
} |
565
|
|
|
return n |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
// Owner describes an owner of a package. This can be a person, company, or |
569
|
|
|
// other organization. This is useful if someone needs to contact the |
570
|
|
|
// owner of a package to address things like a security issue. |
571
|
|
|
type Owner struct { |
572
|
|
|
|
573
|
|
|
// Name describes the name of an organization. |
574
|
|
|
Name string `yaml:"name,omitempty"` |
575
|
|
|
|
576
|
|
|
// Email is an email address to reach the owner at. |
577
|
|
|
Email string `yaml:"email,omitempty"` |
578
|
|
|
|
579
|
|
|
// Home is a url to a website for the owner. |
580
|
|
|
Home string `yaml:"homepage,omitempty"` |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
// Clone creates a clone of a Dependency |
584
|
|
|
func (o *Owner) Clone() *Owner { |
585
|
|
|
return &Owner{ |
586
|
|
|
Name: o.Name, |
587
|
|
|
Email: o.Email, |
588
|
|
|
Home: o.Home, |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
func stringArrayDeDupe(s []string, items ...string) []string { |
593
|
|
|
for _, item := range items { |
594
|
|
|
exists := false |
595
|
|
|
for _, v := range s { |
596
|
|
|
if v == item { |
597
|
|
|
exists = true |
598
|
|
|
} |
599
|
|
|
} |
600
|
|
|
if !exists { |
601
|
|
|
s = append(s, item) |
602
|
|
|
} |
603
|
|
|
} |
604
|
|
|
sort.Strings(s) |
605
|
|
|
return s |
606
|
|
|
} |
607
|
|
|
|
608
|
|
|
func filterVcsType(vcs string) string { |
609
|
|
|
switch vcs { |
610
|
|
|
case "git", "hg", "bzr", "svn": |
611
|
|
|
return vcs |
612
|
|
|
case "mercurial": |
613
|
|
|
return "hg" |
614
|
|
|
case "bazaar": |
615
|
|
|
return "bzr" |
616
|
|
|
case "subversion": |
617
|
|
|
return "svn" |
618
|
|
|
default: |
619
|
|
|
return "" |
620
|
|
|
} |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
func normalizeSlash(k string) string { |
624
|
|
|
return strings.Replace(k, "\\", "/", -1) |
625
|
|
|
} |
626
|
|
|
|