@@ -24,26 +24,39 @@ const ManifestName = "Gopkg.toml"
24
24
25
25
// Errors
26
26
var (
27
- errInvalidConstraint = errors .New ("\" constraint\" must be a TOML array of tables" )
28
- errInvalidOverride = errors .New ("\" override\" must be a TOML array of tables" )
29
- errInvalidRequired = errors .New ("\" required\" must be a TOML list of strings" )
30
- errInvalidIgnored = errors .New ("\" ignored\" must be a TOML list of strings" )
31
- errInvalidProjectRoot = errors .New ("ProjectRoot name validation failed" )
27
+ errInvalidConstraint = errors .Errorf ("%q must be a TOML array of tables" , "constraint" )
28
+ errInvalidOverride = errors .Errorf ("%q must be a TOML array of tables" , "override" )
29
+ errInvalidRequired = errors .Errorf ("%q must be a TOML list of strings" , "required" )
30
+ errInvalidIgnored = errors .Errorf ("%q must be a TOML list of strings" , "ignored" )
31
+ errInvalidPrune = errors .Errorf ("%q must be a TOML table of booleans" , "prune" )
32
+
33
+ errInvalidProjectRoot = errors .New ("ProjectRoot name validation failed" )
34
+ errInvalidPruneValue = errors .New ("prune options values must be booleans" )
35
+ errInvalidPruneProject = errors .Errorf ("%q must be a TOML array of tables" , "prune.project" )
36
+ errPruneSubProject = errors .New ("prune projects should not contain sub projects" )
37
+
38
+ errInvalidRootPruneValue = errors .New ("root prune options must be omitted instead of being set to false" )
39
+ errInvalidPruneProjectName = errors .Errorf ("%q in %q must be a string" , "name" , "prune.project" )
32
40
)
33
41
34
42
// Manifest holds manifest file data and implements gps.RootManifest.
35
43
type Manifest struct {
36
44
Constraints gps.ProjectConstraints
37
45
Ovr gps.ProjectConstraints
38
- Ignored []string
39
- Required []string
46
+
47
+ Ignored []string
48
+ Required []string
49
+
50
+ PruneOptions gps.PruneOptions
51
+ PruneProjectOptions gps.PruneProjectOptions
40
52
}
41
53
42
54
type rawManifest struct {
43
- Constraints []rawProject `toml:"constraint,omitempty"`
44
- Overrides []rawProject `toml:"override,omitempty"`
45
- Ignored []string `toml:"ignored,omitempty"`
46
- Required []string `toml:"required,omitempty"`
55
+ Constraints []rawProject `toml:"constraint,omitempty"`
56
+ Overrides []rawProject `toml:"override,omitempty"`
57
+ Ignored []string `toml:"ignored,omitempty"`
58
+ Required []string `toml:"required,omitempty"`
59
+ PruneOptions rawPruneOptions `toml:"prune,omitempty"`
47
60
}
48
61
49
62
type rawProject struct {
@@ -54,11 +67,33 @@ type rawProject struct {
54
67
Source string `toml:"source,omitempty"`
55
68
}
56
69
57
- // NewManifest instantiates a new manifest.
70
+ type rawPruneOptions struct {
71
+ UnusedPackages bool `toml:"unused-packages,omitempty"`
72
+ NonGoFiles bool `toml:"non-go,omitempty"`
73
+ GoTests bool `toml:"go-tests,omitempty"`
74
+
75
+ Projects []rawPruneProjectOptions `toml:"project,omitempty"`
76
+ }
77
+
78
+ type rawPruneProjectOptions struct {
79
+ Name string `toml:"name"`
80
+ UnusedPackages bool `toml:"unused-packages,omitempty"`
81
+ NonGoFiles bool `toml:"non-go,omitempty"`
82
+ GoTests bool `toml:"go-tests,omitempty"`
83
+ }
84
+
85
+ const (
86
+ pruneOptionUnusedPackages = "unused-packages"
87
+ pruneOptionGoTests = "go-tests"
88
+ pruneOptionNonGo = "non-go"
89
+ )
90
+
91
+ // NewManifest instantites a new manifest.
58
92
func NewManifest () * Manifest {
59
93
return & Manifest {
60
- Constraints : make (gps.ProjectConstraints ),
61
- Ovr : make (gps.ProjectConstraints ),
94
+ Constraints : make (gps.ProjectConstraints ),
95
+ Ovr : make (gps.ProjectConstraints ),
96
+ PruneOptions : gps .PruneNestedVendorDirs ,
62
97
}
63
98
}
64
99
@@ -151,6 +186,12 @@ func validateManifest(s string) ([]error, error) {
151
186
return warns , errInvalidRequired
152
187
}
153
188
}
189
+ case "prune" :
190
+ pruneWarns , err := validatePruneOptions (val , true )
191
+ warns = append (warns , pruneWarns ... )
192
+ if err != nil {
193
+ return warns , err
194
+ }
154
195
default :
155
196
warns = append (warns , fmt .Errorf ("unknown field in manifest: %v" , prop ))
156
197
}
@@ -159,6 +200,70 @@ func validateManifest(s string) ([]error, error) {
159
200
return warns , nil
160
201
}
161
202
203
+ func validatePruneOptions (val interface {}, root bool ) (warns []error , err error ) {
204
+ if reflect .TypeOf (val ).Kind () != reflect .Map {
205
+ return warns , errInvalidPrune
206
+ }
207
+
208
+ for key , value := range val .(map [string ]interface {}) {
209
+ switch key {
210
+ case pruneOptionNonGo , pruneOptionGoTests , pruneOptionUnusedPackages :
211
+ if option , ok := value .(bool ); ! ok {
212
+ return warns , errInvalidPruneValue
213
+ } else if root && ! option {
214
+ return warns , errInvalidRootPruneValue
215
+ }
216
+ case "name" :
217
+ if root {
218
+ warns = append (warns , errors .Errorf ("%q should not include a name" , "prune" ))
219
+ } else if _ , ok := value .(string ); ! ok {
220
+ return warns , errInvalidPruneProjectName
221
+ }
222
+ case "project" :
223
+ if ! root {
224
+ return warns , errPruneSubProject
225
+ }
226
+ if reflect .TypeOf (value ).Kind () != reflect .Slice {
227
+ return warns , errInvalidPruneProject
228
+ }
229
+ for _ , project := range value .([]interface {}) {
230
+ projectWarns , err := validatePruneOptions (project , false )
231
+ warns = append (warns , projectWarns ... )
232
+ if err != nil {
233
+ return nil , err
234
+ }
235
+ }
236
+
237
+ default :
238
+ if root {
239
+ warns = append (warns , errors .Errorf ("unknown field %q in %q" , key , "prune" ))
240
+ } else {
241
+ warns = append (warns , errors .Errorf ("unknown field %q in %q" , key , "prune.project" ))
242
+ }
243
+ }
244
+ }
245
+
246
+ return warns , err
247
+ }
248
+
249
+ func checkRedundantPruneOptions (raw rawManifest ) (warns []error ) {
250
+ rootOptions := raw .PruneOptions
251
+
252
+ for _ , project := range raw .PruneOptions .Projects {
253
+ if rootOptions .GoTests && project .GoTests {
254
+ warns = append (warns , errors .Errorf ("redundant prune option %q set for %q" , pruneOptionGoTests , project .Name ))
255
+ }
256
+ if rootOptions .NonGoFiles && project .NonGoFiles {
257
+ warns = append (warns , errors .Errorf ("redundant prune option %q set for %q" , pruneOptionNonGo , project .Name ))
258
+ }
259
+ if rootOptions .UnusedPackages && project .UnusedPackages {
260
+ warns = append (warns , errors .Errorf ("redundant prune option %q set for %q" , pruneOptionUnusedPackages , project .Name ))
261
+ }
262
+ }
263
+
264
+ return warns
265
+ }
266
+
162
267
// ValidateProjectRoots validates the project roots present in manifest.
163
268
func ValidateProjectRoots (c * Ctx , m * Manifest , sm gps.SourceManager ) error {
164
269
// Channel to receive all the errors
@@ -184,6 +289,10 @@ func ValidateProjectRoots(c *Ctx, m *Manifest, sm gps.SourceManager) error {
184
289
wg .Add (1 )
185
290
go validate (pr )
186
291
}
292
+ for pr := range m .PruneProjectOptions {
293
+ wg .Add (1 )
294
+ go validate (pr )
295
+ }
187
296
188
297
wg .Wait ()
189
298
close (errorCh )
@@ -220,6 +329,8 @@ func readManifest(r io.Reader) (*Manifest, []error, error) {
220
329
return nil , warns , errors .Wrap (err , "unable to parse the manifest as TOML" )
221
330
}
222
331
332
+ warns = append (warns , checkRedundantPruneOptions (raw )... )
333
+
223
334
m , err := fromRawManifest (raw )
224
335
return m , warns , err
225
336
}
@@ -254,9 +365,43 @@ func fromRawManifest(raw rawManifest) (*Manifest, error) {
254
365
m .Ovr [name ] = prj
255
366
}
256
367
368
+ m .PruneOptions , m .PruneProjectOptions = fromRawPruneOptions (raw .PruneOptions )
369
+
257
370
return m , nil
258
371
}
259
372
373
+ func fromRawPruneOptions (raw rawPruneOptions ) (gps.PruneOptions , gps.PruneProjectOptions ) {
374
+ rootOptions := gps .PruneNestedVendorDirs
375
+ pruneProjects := make (gps.PruneProjectOptions )
376
+
377
+ if raw .UnusedPackages {
378
+ rootOptions |= gps .PruneUnusedPackages
379
+ }
380
+ if raw .GoTests {
381
+ rootOptions |= gps .PruneGoTestFiles
382
+ }
383
+ if raw .NonGoFiles {
384
+ rootOptions |= gps .PruneNonGoFiles
385
+ }
386
+
387
+ for _ , p := range raw .Projects {
388
+ pr := gps .ProjectRoot (p .Name )
389
+ pruneProjects [pr ] = gps .PruneNestedVendorDirs
390
+
391
+ if raw .UnusedPackages {
392
+ pruneProjects [pr ] |= gps .PruneUnusedPackages
393
+ }
394
+ if raw .GoTests {
395
+ pruneProjects [pr ] |= gps .PruneGoTestFiles
396
+ }
397
+ if raw .NonGoFiles {
398
+ pruneProjects [pr ] |= gps .PruneNonGoFiles
399
+ }
400
+ }
401
+
402
+ return rootOptions , pruneProjects
403
+ }
404
+
260
405
// toProject interprets the string representations of project information held in
261
406
// a rawProject, converting them into a proper gps.ProjectProperties. An
262
407
// error is returned if the rawProject contains some invalid combination -
@@ -288,17 +433,27 @@ func toProject(raw rawProject) (n gps.ProjectRoot, pp gps.ProjectProperties, err
288
433
}
289
434
290
435
pp .Source = raw .Source
436
+
291
437
return n , pp , nil
292
438
}
293
439
440
+ // MarshalTOML serializes this manifest into TOML via an intermediate raw form.
441
+ func (m * Manifest ) MarshalTOML () ([]byte , error ) {
442
+ raw := m .toRaw ()
443
+ result , err := toml .Marshal (raw )
444
+ return result , errors .Wrap (err , "unable to marshal the lock to a TOML string" )
445
+ }
446
+
294
447
// toRaw converts the manifest into a representation suitable to write to the manifest file
295
448
func (m * Manifest ) toRaw () rawManifest {
296
449
raw := rawManifest {
297
- Constraints : make ([]rawProject , 0 , len (m .Constraints )),
298
- Overrides : make ([]rawProject , 0 , len (m .Ovr )),
299
- Ignored : m .Ignored ,
300
- Required : m .Required ,
450
+ Constraints : make ([]rawProject , 0 , len (m .Constraints )),
451
+ Overrides : make ([]rawProject , 0 , len (m .Ovr )),
452
+ Ignored : m .Ignored ,
453
+ Required : m .Required ,
454
+ PruneOptions : rawPruneOptions {},
301
455
}
456
+
302
457
for n , prj := range m .Constraints {
303
458
raw .Constraints = append (raw .Constraints , toRawProject (n , prj ))
304
459
}
@@ -309,6 +464,8 @@ func (m *Manifest) toRaw() rawManifest {
309
464
}
310
465
sort .Sort (sortedRawProjects (raw .Overrides ))
311
466
467
+ // TODO(ibrasho): write out prune options.
468
+
312
469
return raw
313
470
}
314
471
@@ -329,13 +486,6 @@ func (s sortedRawProjects) Less(i, j int) bool {
329
486
return l .Source < r .Source
330
487
}
331
488
332
- // MarshalTOML serializes this manifest into TOML via an intermediate raw form.
333
- func (m * Manifest ) MarshalTOML () ([]byte , error ) {
334
- raw := m .toRaw ()
335
- result , err := toml .Marshal (raw )
336
- return result , errors .Wrap (err , "Unable to marshal the lock to a TOML string" )
337
- }
338
-
339
489
func toRawProject (name gps.ProjectRoot , project gps.ProjectProperties ) rawProject {
340
490
raw := rawProject {
341
491
Name : string (name ),
@@ -363,6 +513,7 @@ func toRawProject(name gps.ProjectRoot, project gps.ProjectProperties) rawProjec
363
513
// Has to be a semver range.
364
514
raw .Version = project .Constraint .ImpliedCaretString ()
365
515
}
516
+
366
517
return raw
367
518
}
368
519
@@ -407,3 +558,11 @@ func (m *Manifest) RequiredPackages() map[string]bool {
407
558
408
559
return mp
409
560
}
561
+
562
+ func (m * Manifest ) PruneOptionsFor (pr gps.ProjectRoot ) gps.PruneOptions {
563
+ if po , ok := m .PruneProjectOptions [pr ]; ok {
564
+ return po
565
+ }
566
+
567
+ return m .PruneOptions
568
+ }
0 commit comments