зеркало из https://github.com/golang/dep.git
dep: add prune options to manifests
Signed-off-by: Ibrahim AshShohail <ibra.sho@gmail.com>
This commit is contained in:
Родитель
4724b1f005
Коммит
13aa6fcff1
|
@ -30,3 +30,8 @@
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
non-go = true
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
|
|
|
@ -38,7 +38,7 @@ func TestAnalyzerDeriveManifestAndLock(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.Fatalf("expected %s\n got %s", want, string(got))
|
t.Fatalf("(WNT):\n%s\n(GOT):\n%s", want, string(got))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,12 @@ import (
|
||||||
// PruneOptions represents the pruning options used to write the dependecy tree.
|
// PruneOptions represents the pruning options used to write the dependecy tree.
|
||||||
type PruneOptions uint8
|
type PruneOptions uint8
|
||||||
|
|
||||||
|
// PruneProjectOptions is map of prune options per project name.
|
||||||
|
type PruneProjectOptions map[ProjectRoot]PruneOptions
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// PruneNestedVendorDirs indicates if nested vendor directories should be pruned.
|
// PruneNestedVendorDirs indicates if nested vendor directories should be pruned.
|
||||||
PruneNestedVendorDirs = 1 << iota
|
PruneNestedVendorDirs PruneOptions = 1 << iota
|
||||||
// PruneUnusedPackages indicates if unused Go packages should be pruned.
|
// PruneUnusedPackages indicates if unused Go packages should be pruned.
|
||||||
PruneUnusedPackages
|
PruneUnusedPackages
|
||||||
// PruneNonGoFiles indicates if non-Go files should be pruned.
|
// PruneNonGoFiles indicates if non-Go files should be pruned.
|
||||||
|
|
183
manifest.go
183
manifest.go
|
@ -24,19 +24,31 @@ const ManifestName = "Gopkg.toml"
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
var (
|
var (
|
||||||
errInvalidConstraint = errors.New("\"constraint\" must be a TOML array of tables")
|
errInvalidConstraint = errors.Errorf("%q must be a TOML array of tables", "constraint")
|
||||||
errInvalidOverride = errors.New("\"override\" must be a TOML array of tables")
|
errInvalidOverride = errors.Errorf("%q must be a TOML array of tables", "override")
|
||||||
errInvalidRequired = errors.New("\"required\" must be a TOML list of strings")
|
errInvalidRequired = errors.Errorf("%q must be a TOML list of strings", "required")
|
||||||
errInvalidIgnored = errors.New("\"ignored\" must be a TOML list of strings")
|
errInvalidIgnored = errors.Errorf("%q must be a TOML list of strings", "ignored")
|
||||||
|
errInvalidPrune = errors.Errorf("%q must be a TOML table of booleans", "prune")
|
||||||
|
|
||||||
errInvalidProjectRoot = errors.New("ProjectRoot name validation failed")
|
errInvalidProjectRoot = errors.New("ProjectRoot name validation failed")
|
||||||
|
errInvalidPruneValue = errors.New("prune options values must be booleans")
|
||||||
|
errInvalidPruneProject = errors.Errorf("%q must be a TOML array of tables", "prune.project")
|
||||||
|
errPruneSubProject = errors.New("prune projects should not contain sub projects")
|
||||||
|
|
||||||
|
errInvalidRootPruneValue = errors.New("root prune options must be omitted instead of being set to false")
|
||||||
|
errInvalidPruneProjectName = errors.Errorf("%q in %q must be a string", "name", "prune.project")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manifest holds manifest file data and implements gps.RootManifest.
|
// Manifest holds manifest file data and implements gps.RootManifest.
|
||||||
type Manifest struct {
|
type Manifest struct {
|
||||||
Constraints gps.ProjectConstraints
|
Constraints gps.ProjectConstraints
|
||||||
Ovr gps.ProjectConstraints
|
Ovr gps.ProjectConstraints
|
||||||
|
|
||||||
Ignored []string
|
Ignored []string
|
||||||
Required []string
|
Required []string
|
||||||
|
|
||||||
|
PruneOptions gps.PruneOptions
|
||||||
|
PruneProjectOptions gps.PruneProjectOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawManifest struct {
|
type rawManifest struct {
|
||||||
|
@ -44,6 +56,7 @@ type rawManifest struct {
|
||||||
Overrides []rawProject `toml:"override,omitempty"`
|
Overrides []rawProject `toml:"override,omitempty"`
|
||||||
Ignored []string `toml:"ignored,omitempty"`
|
Ignored []string `toml:"ignored,omitempty"`
|
||||||
Required []string `toml:"required,omitempty"`
|
Required []string `toml:"required,omitempty"`
|
||||||
|
PruneOptions rawPruneOptions `toml:"prune,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawProject struct {
|
type rawProject struct {
|
||||||
|
@ -54,11 +67,33 @@ type rawProject struct {
|
||||||
Source string `toml:"source,omitempty"`
|
Source string `toml:"source,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManifest instantiates a new manifest.
|
type rawPruneOptions struct {
|
||||||
|
UnusedPackages bool `toml:"unused-packages,omitempty"`
|
||||||
|
NonGoFiles bool `toml:"non-go,omitempty"`
|
||||||
|
GoTests bool `toml:"go-tests,omitempty"`
|
||||||
|
|
||||||
|
Projects []rawPruneProjectOptions `toml:"project,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rawPruneProjectOptions struct {
|
||||||
|
Name string `toml:"name"`
|
||||||
|
UnusedPackages bool `toml:"unused-packages,omitempty"`
|
||||||
|
NonGoFiles bool `toml:"non-go,omitempty"`
|
||||||
|
GoTests bool `toml:"go-tests,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
pruneOptionUnusedPackages = "unused-packages"
|
||||||
|
pruneOptionGoTests = "go-tests"
|
||||||
|
pruneOptionNonGo = "non-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewManifest instantites a new manifest.
|
||||||
func NewManifest() *Manifest {
|
func NewManifest() *Manifest {
|
||||||
return &Manifest{
|
return &Manifest{
|
||||||
Constraints: make(gps.ProjectConstraints),
|
Constraints: make(gps.ProjectConstraints),
|
||||||
Ovr: make(gps.ProjectConstraints),
|
Ovr: make(gps.ProjectConstraints),
|
||||||
|
PruneOptions: gps.PruneNestedVendorDirs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +186,12 @@ func validateManifest(s string) ([]error, error) {
|
||||||
return warns, errInvalidRequired
|
return warns, errInvalidRequired
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "prune":
|
||||||
|
pruneWarns, err := validatePruneOptions(val, true)
|
||||||
|
warns = append(warns, pruneWarns...)
|
||||||
|
if err != nil {
|
||||||
|
return warns, err
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
warns = append(warns, fmt.Errorf("unknown field in manifest: %v", prop))
|
warns = append(warns, fmt.Errorf("unknown field in manifest: %v", prop))
|
||||||
}
|
}
|
||||||
|
@ -159,6 +200,70 @@ func validateManifest(s string) ([]error, error) {
|
||||||
return warns, nil
|
return warns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validatePruneOptions(val interface{}, root bool) (warns []error, err error) {
|
||||||
|
if reflect.TypeOf(val).Kind() != reflect.Map {
|
||||||
|
return warns, errInvalidPrune
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range val.(map[string]interface{}) {
|
||||||
|
switch key {
|
||||||
|
case pruneOptionNonGo, pruneOptionGoTests, pruneOptionUnusedPackages:
|
||||||
|
if option, ok := value.(bool); !ok {
|
||||||
|
return warns, errInvalidPruneValue
|
||||||
|
} else if root && !option {
|
||||||
|
return warns, errInvalidRootPruneValue
|
||||||
|
}
|
||||||
|
case "name":
|
||||||
|
if root {
|
||||||
|
warns = append(warns, errors.Errorf("%q should not include a name", "prune"))
|
||||||
|
} else if _, ok := value.(string); !ok {
|
||||||
|
return warns, errInvalidPruneProjectName
|
||||||
|
}
|
||||||
|
case "project":
|
||||||
|
if !root {
|
||||||
|
return warns, errPruneSubProject
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(value).Kind() != reflect.Slice {
|
||||||
|
return warns, errInvalidPruneProject
|
||||||
|
}
|
||||||
|
for _, project := range value.([]interface{}) {
|
||||||
|
projectWarns, err := validatePruneOptions(project, false)
|
||||||
|
warns = append(warns, projectWarns...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if root {
|
||||||
|
warns = append(warns, errors.Errorf("unknown field %q in %q", key, "prune"))
|
||||||
|
} else {
|
||||||
|
warns = append(warns, errors.Errorf("unknown field %q in %q", key, "prune.project"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return warns, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRedundantPruneOptions(raw rawManifest) (warns []error) {
|
||||||
|
rootOptions := raw.PruneOptions
|
||||||
|
|
||||||
|
for _, project := range raw.PruneOptions.Projects {
|
||||||
|
if rootOptions.GoTests && project.GoTests {
|
||||||
|
warns = append(warns, errors.Errorf("redundant prune option %q set for %q", pruneOptionGoTests, project.Name))
|
||||||
|
}
|
||||||
|
if rootOptions.NonGoFiles && project.NonGoFiles {
|
||||||
|
warns = append(warns, errors.Errorf("redundant prune option %q set for %q", pruneOptionNonGo, project.Name))
|
||||||
|
}
|
||||||
|
if rootOptions.UnusedPackages && project.UnusedPackages {
|
||||||
|
warns = append(warns, errors.Errorf("redundant prune option %q set for %q", pruneOptionUnusedPackages, project.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return warns
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateProjectRoots validates the project roots present in manifest.
|
// ValidateProjectRoots validates the project roots present in manifest.
|
||||||
func ValidateProjectRoots(c *Ctx, m *Manifest, sm gps.SourceManager) error {
|
func ValidateProjectRoots(c *Ctx, m *Manifest, sm gps.SourceManager) error {
|
||||||
// Channel to receive all the errors
|
// Channel to receive all the errors
|
||||||
|
@ -184,6 +289,10 @@ func ValidateProjectRoots(c *Ctx, m *Manifest, sm gps.SourceManager) error {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go validate(pr)
|
go validate(pr)
|
||||||
}
|
}
|
||||||
|
for pr := range m.PruneProjectOptions {
|
||||||
|
wg.Add(1)
|
||||||
|
go validate(pr)
|
||||||
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(errorCh)
|
close(errorCh)
|
||||||
|
@ -220,6 +329,8 @@ func readManifest(r io.Reader) (*Manifest, []error, error) {
|
||||||
return nil, warns, errors.Wrap(err, "unable to parse the manifest as TOML")
|
return nil, warns, errors.Wrap(err, "unable to parse the manifest as TOML")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
warns = append(warns, checkRedundantPruneOptions(raw)...)
|
||||||
|
|
||||||
m, err := fromRawManifest(raw)
|
m, err := fromRawManifest(raw)
|
||||||
return m, warns, err
|
return m, warns, err
|
||||||
}
|
}
|
||||||
|
@ -254,9 +365,43 @@ func fromRawManifest(raw rawManifest) (*Manifest, error) {
|
||||||
m.Ovr[name] = prj
|
m.Ovr[name] = prj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.PruneOptions, m.PruneProjectOptions = fromRawPruneOptions(raw.PruneOptions)
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fromRawPruneOptions(raw rawPruneOptions) (gps.PruneOptions, gps.PruneProjectOptions) {
|
||||||
|
rootOptions := gps.PruneNestedVendorDirs
|
||||||
|
pruneProjects := make(gps.PruneProjectOptions)
|
||||||
|
|
||||||
|
if raw.UnusedPackages {
|
||||||
|
rootOptions |= gps.PruneUnusedPackages
|
||||||
|
}
|
||||||
|
if raw.GoTests {
|
||||||
|
rootOptions |= gps.PruneGoTestFiles
|
||||||
|
}
|
||||||
|
if raw.NonGoFiles {
|
||||||
|
rootOptions |= gps.PruneNonGoFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range raw.Projects {
|
||||||
|
pr := gps.ProjectRoot(p.Name)
|
||||||
|
pruneProjects[pr] = gps.PruneNestedVendorDirs
|
||||||
|
|
||||||
|
if raw.UnusedPackages {
|
||||||
|
pruneProjects[pr] |= gps.PruneUnusedPackages
|
||||||
|
}
|
||||||
|
if raw.GoTests {
|
||||||
|
pruneProjects[pr] |= gps.PruneGoTestFiles
|
||||||
|
}
|
||||||
|
if raw.NonGoFiles {
|
||||||
|
pruneProjects[pr] |= gps.PruneNonGoFiles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootOptions, pruneProjects
|
||||||
|
}
|
||||||
|
|
||||||
// toProject interprets the string representations of project information held in
|
// toProject interprets the string representations of project information held in
|
||||||
// a rawProject, converting them into a proper gps.ProjectProperties. An
|
// a rawProject, converting them into a proper gps.ProjectProperties. An
|
||||||
// error is returned if the rawProject contains some invalid combination -
|
// error is returned if the rawProject contains some invalid combination -
|
||||||
|
@ -288,9 +433,17 @@ func toProject(raw rawProject) (n gps.ProjectRoot, pp gps.ProjectProperties, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pp.Source = raw.Source
|
pp.Source = raw.Source
|
||||||
|
|
||||||
return n, pp, nil
|
return n, pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalTOML serializes this manifest into TOML via an intermediate raw form.
|
||||||
|
func (m *Manifest) MarshalTOML() ([]byte, error) {
|
||||||
|
raw := m.toRaw()
|
||||||
|
result, err := toml.Marshal(raw)
|
||||||
|
return result, errors.Wrap(err, "unable to marshal the lock to a TOML string")
|
||||||
|
}
|
||||||
|
|
||||||
// toRaw converts the manifest into a representation suitable to write to the manifest file
|
// toRaw converts the manifest into a representation suitable to write to the manifest file
|
||||||
func (m *Manifest) toRaw() rawManifest {
|
func (m *Manifest) toRaw() rawManifest {
|
||||||
raw := rawManifest{
|
raw := rawManifest{
|
||||||
|
@ -298,7 +451,9 @@ func (m *Manifest) toRaw() rawManifest {
|
||||||
Overrides: make([]rawProject, 0, len(m.Ovr)),
|
Overrides: make([]rawProject, 0, len(m.Ovr)),
|
||||||
Ignored: m.Ignored,
|
Ignored: m.Ignored,
|
||||||
Required: m.Required,
|
Required: m.Required,
|
||||||
|
PruneOptions: rawPruneOptions{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for n, prj := range m.Constraints {
|
for n, prj := range m.Constraints {
|
||||||
raw.Constraints = append(raw.Constraints, toRawProject(n, prj))
|
raw.Constraints = append(raw.Constraints, toRawProject(n, prj))
|
||||||
}
|
}
|
||||||
|
@ -309,6 +464,8 @@ func (m *Manifest) toRaw() rawManifest {
|
||||||
}
|
}
|
||||||
sort.Sort(sortedRawProjects(raw.Overrides))
|
sort.Sort(sortedRawProjects(raw.Overrides))
|
||||||
|
|
||||||
|
// TODO(ibrasho): write out prune options.
|
||||||
|
|
||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,13 +486,6 @@ func (s sortedRawProjects) Less(i, j int) bool {
|
||||||
return l.Source < r.Source
|
return l.Source < r.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalTOML serializes this manifest into TOML via an intermediate raw form.
|
|
||||||
func (m *Manifest) MarshalTOML() ([]byte, error) {
|
|
||||||
raw := m.toRaw()
|
|
||||||
result, err := toml.Marshal(raw)
|
|
||||||
return result, errors.Wrap(err, "Unable to marshal the lock to a TOML string")
|
|
||||||
}
|
|
||||||
|
|
||||||
func toRawProject(name gps.ProjectRoot, project gps.ProjectProperties) rawProject {
|
func toRawProject(name gps.ProjectRoot, project gps.ProjectProperties) rawProject {
|
||||||
raw := rawProject{
|
raw := rawProject{
|
||||||
Name: string(name),
|
Name: string(name),
|
||||||
|
@ -363,6 +513,7 @@ func toRawProject(name gps.ProjectRoot, project gps.ProjectProperties) rawProjec
|
||||||
// Has to be a semver range.
|
// Has to be a semver range.
|
||||||
raw.Version = project.Constraint.ImpliedCaretString()
|
raw.Version = project.Constraint.ImpliedCaretString()
|
||||||
}
|
}
|
||||||
|
|
||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,3 +558,11 @@ func (m *Manifest) RequiredPackages() map[string]bool {
|
||||||
|
|
||||||
return mp
|
return mp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manifest) PruneOptionsFor(pr gps.ProjectRoot) gps.PruneOptions {
|
||||||
|
if po, ok := m.PruneProjectOptions[pr]; ok {
|
||||||
|
return po
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.PruneOptions
|
||||||
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ func TestReadManifest(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ignored: []string{"github.com/foo/bar"},
|
Ignored: []string{"github.com/foo/bar"},
|
||||||
|
PruneOptions: gps.PruneNestedVendorDirs | gps.PruneNonGoFiles,
|
||||||
|
PruneProjectOptions: gps.PruneProjectOptions{
|
||||||
|
gps.ProjectRoot("github.com/golang/dep"): gps.PruneNestedVendorDirs,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(got.Constraints, want.Constraints) {
|
if !reflect.DeepEqual(got.Constraints, want.Constraints) {
|
||||||
|
@ -78,6 +82,11 @@ func TestWriteManifest(t *testing.T) {
|
||||||
}
|
}
|
||||||
m.Ignored = []string{"github.com/foo/bar"}
|
m.Ignored = []string{"github.com/foo/bar"}
|
||||||
|
|
||||||
|
m.PruneOptions = gps.PruneNestedVendorDirs | gps.PruneNonGoFiles
|
||||||
|
m.PruneProjectOptions = gps.PruneProjectOptions{
|
||||||
|
gps.ProjectRoot("github.com/golang/dep"): gps.PruneNestedVendorDirs,
|
||||||
|
}
|
||||||
|
|
||||||
got, err := m.MarshalTOML()
|
got, err := m.MarshalTOML()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error while marshaling valid manifest to TOML: %q", err)
|
t.Fatalf("error while marshaling valid manifest to TOML: %q", err)
|
||||||
|
@ -89,7 +98,7 @@ func TestWriteManifest(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("valid manifest did not marshal to TOML as expected:\n\t(GOT): %s\n\t(WNT): %s", string(got), want)
|
t.Errorf("valid manifest did not marshal to TOML as expected:\n(GOT):\n%s\n(WNT):\n%s", string(got), want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,6 +377,32 @@ func TestValidateManifest(t *testing.T) {
|
||||||
wantWarn: []error{errors.New("revision \"8d43f8c0b836\" should not be in abbreviated form")},
|
wantWarn: []error{errors.New("revision \"8d43f8c0b836\" should not be in abbreviated form")},
|
||||||
wantError: nil,
|
wantError: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "valid prune options",
|
||||||
|
tomlString: `
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/foo/bar"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
non-go = true
|
||||||
|
`,
|
||||||
|
wantWarn: []error{},
|
||||||
|
wantError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid root prune options",
|
||||||
|
tomlString: `
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/foo/bar"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
non-go = false
|
||||||
|
`,
|
||||||
|
wantWarn: []error{},
|
||||||
|
wantError: errInvalidRootPruneValue,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// contains for error
|
// contains for error
|
||||||
|
|
Загрузка…
Ссылка в новой задаче