Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit 16dc0cd

Browse files
committed
dep: add prune options to manifests
Signed-off-by: Ibrahim AshShohail <[email protected]>
1 parent b260d7c commit 16dc0cd

File tree

5 files changed

+229
-29
lines changed

5 files changed

+229
-29
lines changed

Gopkg.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@
3030
[[constraint]]
3131
name = "github.com/golang/protobuf"
3232
branch = "master"
33+
34+
[prune]
35+
non-go = true
36+
go-tests = true
37+
unused-packages = true

analyzer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestAnalyzerDeriveManifestAndLock(t *testing.T) {
3838
t.Fatal(err)
3939
}
4040
} else {
41-
t.Fatalf("expected %s\n got %s", want, string(got))
41+
t.Fatalf("(WNT):\n%s\n(GOT):\n%s", want, string(got))
4242
}
4343
}
4444

internal/gps/prune.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ import (
1616
// PruneOptions represents the pruning options used to write the dependecy tree.
1717
type PruneOptions uint8
1818

19+
// PruneProjectOptions is map of prune options per project name.
20+
type PruneProjectOptions map[ProjectRoot]PruneOptions
21+
1922
const (
2023
// PruneNestedVendorDirs indicates if nested vendor directories should be pruned.
21-
PruneNestedVendorDirs = 1 << iota
24+
PruneNestedVendorDirs PruneOptions = 1 << iota
2225
// PruneUnusedPackages indicates if unused Go packages should be pruned.
2326
PruneUnusedPackages
2427
// PruneNonGoFiles indicates if non-Go files should be pruned.

manifest.go

Lines changed: 182 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,39 @@ const ManifestName = "Gopkg.toml"
2424

2525
// Errors
2626
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")
3240
)
3341

3442
// Manifest holds manifest file data and implements gps.RootManifest.
3543
type Manifest struct {
3644
Constraints gps.ProjectConstraints
3745
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
4052
}
4153

4254
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 rawRootPruneOptions `toml:"prune,omitempty"`
4760
}
4861

4962
type rawProject struct {
@@ -54,11 +67,33 @@ type rawProject struct {
5467
Source string `toml:"source,omitempty"`
5568
}
5669

57-
// NewManifest instantiates a new manifest.
70+
type rawRootPruneOptions 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.
5892
func NewManifest() *Manifest {
5993
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,
6297
}
6398
}
6499

@@ -151,6 +186,12 @@ func validateManifest(s string) ([]error, error) {
151186
return warns, errInvalidRequired
152187
}
153188
}
189+
case "prune":
190+
pruneWarns, err := validatePruneOptions(val, true)
191+
warns = append(warns, pruneWarns...)
192+
if err != nil {
193+
return warns, err
194+
}
154195
default:
155196
warns = append(warns, fmt.Errorf("unknown field in manifest: %v", prop))
156197
}
@@ -159,6 +200,70 @@ func validateManifest(s string) ([]error, error) {
159200
return warns, nil
160201
}
161202

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+
162267
// ValidateProjectRoots validates the project roots present in manifest.
163268
func ValidateProjectRoots(c *Ctx, m *Manifest, sm gps.SourceManager) error {
164269
// Channel to receive all the errors
@@ -184,6 +289,10 @@ func ValidateProjectRoots(c *Ctx, m *Manifest, sm gps.SourceManager) error {
184289
wg.Add(1)
185290
go validate(pr)
186291
}
292+
for pr := range m.PruneProjectOptions {
293+
wg.Add(1)
294+
go validate(pr)
295+
}
187296

188297
wg.Wait()
189298
close(errorCh)
@@ -254,9 +363,43 @@ func fromRawManifest(raw rawManifest) (*Manifest, error) {
254363
m.Ovr[name] = prj
255364
}
256365

366+
m.PruneOptions, m.PruneProjectOptions = fromRawRootPruneOptions(raw.PruneOptions)
367+
257368
return m, nil
258369
}
259370

371+
func fromRawRootPruneOptions(raw rawRootPruneOptions) (gps.PruneOptions, gps.PruneProjectOptions) {
372+
rootOptions := gps.PruneNestedVendorDirs
373+
pruneProjects := make(gps.PruneProjectOptions)
374+
375+
if raw.UnusedPackages {
376+
rootOptions |= gps.PruneUnusedPackages
377+
}
378+
if raw.GoTests {
379+
rootOptions |= gps.PruneGoTestFiles
380+
}
381+
if raw.NonGoFiles {
382+
rootOptions |= gps.PruneNonGoFiles
383+
}
384+
385+
for _, p := range raw.Projects {
386+
pr := gps.ProjectRoot(p.Name)
387+
pruneProjects[pr] = gps.PruneNestedVendorDirs
388+
389+
if raw.UnusedPackages {
390+
pruneProjects[pr] |= gps.PruneUnusedPackages
391+
}
392+
if raw.GoTests {
393+
pruneProjects[pr] |= gps.PruneGoTestFiles
394+
}
395+
if raw.NonGoFiles {
396+
pruneProjects[pr] |= gps.PruneNonGoFiles
397+
}
398+
}
399+
400+
return rootOptions, pruneProjects
401+
}
402+
260403
// toProject interprets the string representations of project information held in
261404
// a rawProject, converting them into a proper gps.ProjectProperties. An
262405
// error is returned if the rawProject contains some invalid combination -
@@ -288,17 +431,27 @@ func toProject(raw rawProject) (n gps.ProjectRoot, pp gps.ProjectProperties, err
288431
}
289432

290433
pp.Source = raw.Source
434+
291435
return n, pp, nil
292436
}
293437

438+
// MarshalTOML serializes this manifest into TOML via an intermediate raw form.
439+
func (m *Manifest) MarshalTOML() ([]byte, error) {
440+
raw := m.toRaw()
441+
result, err := toml.Marshal(raw)
442+
return result, errors.Wrap(err, "unable to marshal the lock to a TOML string")
443+
}
444+
294445
// toRaw converts the manifest into a representation suitable to write to the manifest file
295446
func (m *Manifest) toRaw() rawManifest {
296447
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,
448+
Constraints: make([]rawProject, 0, len(m.Constraints)),
449+
Overrides: make([]rawProject, 0, len(m.Ovr)),
450+
Ignored: m.Ignored,
451+
Required: m.Required,
452+
PruneOptions: rawRootPruneOptions{},
301453
}
454+
302455
for n, prj := range m.Constraints {
303456
raw.Constraints = append(raw.Constraints, toRawProject(n, prj))
304457
}
@@ -309,6 +462,8 @@ func (m *Manifest) toRaw() rawManifest {
309462
}
310463
sort.Sort(sortedRawProjects(raw.Overrides))
311464

465+
// TODO(ibrasho): write out prune options.
466+
312467
return raw
313468
}
314469

@@ -329,13 +484,6 @@ func (s sortedRawProjects) Less(i, j int) bool {
329484
return l.Source < r.Source
330485
}
331486

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-
339487
func toRawProject(name gps.ProjectRoot, project gps.ProjectProperties) rawProject {
340488
raw := rawProject{
341489
Name: string(name),
@@ -363,6 +511,7 @@ func toRawProject(name gps.ProjectRoot, project gps.ProjectProperties) rawProjec
363511
// Has to be a semver range.
364512
raw.Version = project.Constraint.ImpliedCaretString()
365513
}
514+
366515
return raw
367516
}
368517

@@ -407,3 +556,11 @@ func (m *Manifest) RequiredPackages() map[string]bool {
407556

408557
return mp
409558
}
559+
560+
func (m *Manifest) PruneOptionsFor(pr gps.ProjectRoot) gps.PruneOptions {
561+
if po, ok := m.PruneProjectOptions[pr]; ok {
562+
return po
563+
}
564+
565+
return m.PruneOptions
566+
}

0 commit comments

Comments
 (0)