Merge pull request #70 from interlock/flatten
Add flatten open to imports
diff --git a/README.md b/README.md
index de66731..fd52b41 100644
--- a/README.md
+++ b/README.md
@@ -325,6 +325,10 @@
The package will not be fetched for other architectures or OSes.
+**Q: How do I prevent vendored packages from importing the same package**
+
+You can use the `flatten: true` config option on the entire project or just one specific dependency.
+
## LICENSE
This package is made available under an MIT-style license. See
diff --git a/cmd/get_imports.go b/cmd/get_imports.go
index 561d173..9cadddd 100644
--- a/cmd/get_imports.go
+++ b/cmd/get_imports.go
@@ -239,7 +239,6 @@
if empty == false && err == v.ErrCannotDetectVCS {
Warn("%s appears to be a vendored package. Unable to update. Consider the '--update-vendored' flag.\n", dep.Name)
} else {
-
repo, err := dep.GetRepo(dest)
// Tried to checkout a repo to a path that does not work. Either the
diff --git a/cmd/recursive_glide.go b/cmd/recursive_glide.go
index 4916458..2166002 100644
--- a/cmd/recursive_glide.go
+++ b/cmd/recursive_glide.go
@@ -1,12 +1,12 @@
package cmd
import (
+ "github.com/Masterminds/cookoo"
+ "github.com/kylelemons/go-gypsy/yaml"
"io/ioutil"
"os"
"path"
-
- "github.com/Masterminds/cookoo"
- "github.com/kylelemons/go-gypsy/yaml"
+ "strings"
)
// Recurse does glide installs on dependent packages.
@@ -18,7 +18,7 @@
}
force := p.Get("force", true).(bool)
- godeps, gpm := false, false
+ godeps, gpm, deleteFlatten := false, false, false
if g, ok := p.Has("importGodeps"); ok {
godeps = g.(bool)
}
@@ -26,14 +26,21 @@
gpm = g.(bool)
}
+ if g, ok := p.Has("deleteFlatten"); ok {
+ deleteFlatten = g.(bool)
+ }
+
Info("Checking dependencies for updates. Godeps: %v, GPM: %v\n", godeps, gpm)
+ if deleteFlatten == true {
+ Info("Deleting flattened dependencies enabled\n")
+ }
conf := p.Get("conf", &Config{}).(*Config)
vend, _ := VendorPath(c)
- return recDepResolve(conf, vend, godeps, gpm, force)
+ return recDepResolve(conf, vend, godeps, gpm, force, deleteFlatten)
}
-func recDepResolve(conf *Config, vend string, godeps, gpm, force bool) (interface{}, error) {
+func recDepResolve(conf *Config, vend string, godeps, gpm, force, deleteFlatten bool) (interface{}, error) {
Info("Inspecting %s.\n", vend)
@@ -43,9 +50,11 @@
// Look in each package to see whether it has a glide.yaml, and no vendor/
for _, imp := range conf.Imports {
+ if imp.Flattened == true {
+ continue
+ }
base := path.Join(vend, imp.Name)
Info("Looking in %s for a glide.yaml file.\n", base)
-
if !needsGlideUp(base) {
if godeps {
importGodep(base, imp.Name)
@@ -58,7 +67,8 @@
continue
}
}
- if err := dependencyGlideUp(base, godeps, gpm, force); err != nil {
+
+ if err := dependencyGlideUp(conf, base, godeps, gpm, force, deleteFlatten); err != nil {
Warn("Failed to update dependency %s: %s", imp.Name, err)
}
}
@@ -67,7 +77,8 @@
return nil, nil
}
-func dependencyGlideUp(base string, godep, gpm, force bool) error {
+func dependencyGlideUp(parentConf *Config, base string, godep, gpm, force, deleteFlatten bool) error {
+ Info("Doing a glide in %s\n", base)
//conf := new(Config)
fname := path.Join(base, "glide.yaml")
f, err := yaml.ReadFile(fname)
@@ -76,15 +87,48 @@
}
conf, err := FromYaml(f.Root)
+ conf.Parent = parentConf
if err != nil {
return err
}
for _, imp := range conf.Imports {
+ vdir := path.Join(base, "vendor")
+ wd := path.Join(vdir, imp.Name)
+ // if our root glide.yaml says to flatten this, we skip it
+ if dep := conf.GetRoot().Imports.Get(imp.Name); dep != nil {
+ flatten := conf.GetRoot().Flatten
+ if flatten == true && dep.Flatten == false ||
+ flatten == false && dep.Flatten == true {
+ flatten = dep.Flatten
+ }
+ if flatten == true {
+ Info("Skipping importing %s due to flatten being set in root import glide.yaml\n", imp.Name)
+ imp.Flattened = true
+ }
+
+ if flatten == true && imp.Reference != dep.Reference {
+ Warn("Flattened package %s ref (%s) is diferent from sub vendored package ref (%s)\n", imp.Name, imp.Reference, dep.Reference)
+ }
+
+ if imp.Flattened == true && deleteFlatten == true {
+ if exists, _ := fileExist(wd); exists == true || true {
+ remove := wd + string(os.PathSeparator)
+ Warn("Removing flattened sub vendored package: %s\n", strings.TrimPrefix(remove, base))
+ rerr := os.RemoveAll(remove)
+ if rerr != nil {
+ return rerr
+ }
+ }
+ }
+ if imp.Flattened == true {
+ continue
+ }
+ }
+
// We don't use the global var to find vendor dir name because the
// user may mis-use that var to modify the local vendor dir, and
// we don't want that to break the embedded vendor dirs.
- wd := path.Join(base, "vendor", imp.Name)
- vdir := path.Join(base, "vendor")
+
if err := ensureDir(wd); err != nil {
Warn("Skipped getting %s (vendor/ error): %s\n", imp.Name, err)
continue
@@ -113,7 +157,7 @@
//recDepResolve(conf, path.Join(wd, "vendor"))
}
- recDepResolve(conf, path.Join(base, "vendor"), godep, gpm, force)
+ recDepResolve(conf, path.Join(base, "vendor"), godep, gpm, force, deleteFlatten)
return nil
}
diff --git a/cmd/util.go b/cmd/util.go
index 2fc583e..4c83cf6 100644
--- a/cmd/util.go
+++ b/cmd/util.go
@@ -98,7 +98,7 @@
return false, err
}
-// Get GOPATH from environment and return the most relevant path.
+// GoPath Get GOPATH from environment and return the most relevant path.
//
// A GOPATH can contain a colon-separated list of paths. This retrieves the
// GOPATH and returns only the FIRST ("most relevant") path.
@@ -118,3 +118,14 @@
}
return ps
}
+
+func fileExist(name string) (bool, error) {
+ _, err := os.Stat(name)
+ if err == nil {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return true, err
+}
diff --git a/cmd/yaml.go b/cmd/yaml.go
index 930d761..551e0ec 100644
--- a/cmd/yaml.go
+++ b/cmd/yaml.go
@@ -180,6 +180,22 @@
return strings.TrimSpace(val.(yaml.Scalar).String())
}
+// boolOrDefault returns a bool, with the dft returned if there is an error or the value is not true/false
+func boolOrDefault(key string, store map[string]yaml.Node, dft bool) bool {
+ val, ok := store[key]
+ if !ok {
+ return dft
+ }
+ switch val.(yaml.Scalar).String() {
+ case "true":
+ return true
+ case "false":
+ return false
+ default:
+ return dft
+ }
+}
+
// valOrList gets a single value or a list of values.
//
// Supports syntaxes like:
@@ -258,12 +274,14 @@
// Config is the top-level configuration object.
type Config struct {
+ Parent *Config
Name string
- Imports []*Dependency
- DevImports []*Dependency
+ Imports Dependencies
+ DevImports Dependencies
// InCommand is the default shell command run to start a 'glide in'
// session.
InCommand string
+ Flatten bool
}
// HasDependency returns true if the given name is listed as an import or dev import.
@@ -281,6 +299,24 @@
return false
}
+// HasRecursiveDependency returns true if this config or one of it's parents has this dependency
+func (c *Config) HasRecursiveDependency(name string) bool {
+ if c.HasDependency(name) == true {
+ return true
+ } else if c.Parent != nil {
+ return c.Parent.HasRecursiveDependency(name)
+ }
+ return false
+}
+
+// GetRoot follows the Parent down to the top node
+func (c *Config) GetRoot() *Config {
+ if c.Parent != nil {
+ return c.Parent.GetRoot()
+ }
+ return c
+}
+
// FromYaml creates a *Config from a YAML node.
func FromYaml(top yaml.Node) (*Config, error) {
conf := new(Config)
@@ -302,7 +338,10 @@
conf.InCommand = incmd.(yaml.Scalar).String()
}
- conf.Imports = make([]*Dependency, 0, 1)
+ // Package level Flatten
+ conf.Flatten = boolOrDefault("flatten", vals, false)
+
+ conf.Imports = make(Dependencies, 0, 1)
if imp, ok := vals["import"]; ok {
imports, ok := imp.(yaml.List)
@@ -319,7 +358,7 @@
// Same for (experimental) devimport.
// These are currently unused. Not sure what we'll do with it yet.
- conf.DevImports = []*Dependency{}
+ conf.DevImports = make(Dependencies, 0, 0)
if imp, ok := vals["devimport"]; ok {
imports, ok := imp.(yaml.List)
if ok {
@@ -372,6 +411,8 @@
VcsType string
Subpackages, Arch, Os []string
UpdateAsVendored bool
+ Flatten bool
+ Flattened bool
}
// DependencyFromYaml creates a dependency from a yaml.Node.
@@ -388,6 +429,7 @@
Subpackages: valOrList("subpackages", pkg),
Arch: valOrList("arch", pkg),
Os: valOrList("os", pkg),
+ Flatten: boolOrDefault("flatten", pkg, false),
}
return dep, nil
@@ -473,3 +515,16 @@
return yaml.Map(dep)
}
+
+// Dependencies is a collection of Dependency
+type Dependencies []*Dependency
+
+// Get a dependency by name
+func (d Dependencies) Get(name string) *Dependency {
+ for _, dep := range d {
+ if dep.Name == name {
+ return dep
+ }
+ }
+ return nil
+}
diff --git a/cmd/yaml_test.go b/cmd/yaml_test.go
index 0affb07..4bdde1c 100644
--- a/cmd/yaml_test.go
+++ b/cmd/yaml_test.go
@@ -11,6 +11,7 @@
import:
- package: github.com/kylelemons/go-gypsy
subpackages: yaml
+ flatten: true
- package: github.com/technosophos/structable
# Intentionally left spaces at end of next line.
- package: github.com/Masterminds/convert
@@ -24,22 +25,34 @@
arch:
- i386
- arm
+ flatten: true
devimport:
- package: github.com/kylelemons/go-gypsy
`
+var childYamlFile = `
+package: fake/testing/more
+import:
+ - package: github.com/kylelemons/go-gypsy
+ subpackages: yaml
+`
+
func TestFromYaml(t *testing.T) {
reg, router, cxt := cookoo.Cookoo()
reg.Route("t", "Testing").
- Does(ParseYamlString, "cfg").Using("yaml").WithDefault(yamlFile)
+ Does(ParseYamlString, "cfg").Using("yaml").WithDefault(yamlFile).
+ Does(ParseYamlString, "childCfg").Using("yaml").WithDefault(childYamlFile)
if err := router.HandleRequest("t", cxt, false); err != nil {
t.Errorf("Failed to parse YAML: %s", err)
}
cfg := cxt.Get("cfg", nil).(*Config)
+ cfgChild := cxt.Get("childCfg", nil).(*Config)
+ cfgChild.Parent = cfg
+
if cfg.Name != "fake/testing" {
t.Errorf("Expected name to be 'fake/teting', not '%s'", cfg.Name)
}
@@ -48,6 +61,22 @@
t.Errorf("Expected 3 imports, got %d", len(cfg.Imports))
}
+ if cfg.Parent != nil {
+ t.Error("Expected root glide Parent to be nil")
+ }
+
+ if cfg.Imports.Get("github.com/Masterminds/convert") == nil {
+ t.Error("Expected Imports.Get to return Dependency")
+ }
+
+ if cfg.Imports.Get("github.com/doesnot/exist") != nil {
+ t.Error("Execpted Imports.Get to return nil")
+ }
+
+ if cfgChild.HasRecursiveDependency("github.com/Masterminds/convert") == false {
+ t.Errorf("Expected to find a recursive dependency")
+ }
+
imp := cfg.Imports[2]
if imp.Name != "github.com/Masterminds/convert" {
t.Errorf("Expected the convert package, got %s", imp.Name)
@@ -82,6 +111,10 @@
t.Errorf("Got wrong reference.")
}
+ if imp.Flatten != true {
+ t.Errorf("Expected Flatten: true")
+ }
+
if len(cfg.DevImports) != 1 {
t.Errorf("Expected one dev import.")
}
diff --git a/docs/example-glide.yaml b/docs/example-glide.yaml
index 82ab9e3..f972ca2 100644
--- a/docs/example-glide.yaml
+++ b/docs/example-glide.yaml
@@ -5,6 +5,11 @@
# will just spawn a new shell.
incmd: bash -l
+# Any listed import will only be imported once. Effectively preventing vendored
+# packages from importing the same pkg via a glide.yaml file.
+# Defaults to false
+flatten: true
+
# External dependencies.
import:
# Minimal definition
@@ -86,3 +91,8 @@
- darwin
arch:
- amd64
+
+ # If you would like to only include the package once and prevent vendored
+ # packages from including the same package.
+ - package: github.com/awesome/only_once
+ flatten: true
diff --git a/glide.go b/glide.go
index 9ad7669..59c9a38 100644
--- a/glide.go
+++ b/glide.go
@@ -61,11 +61,13 @@
subpackages: **
- package: github.com/kylelemons/go-gypsy
subpackages: yaml
+ flatten: true
NOTE: As of Glide 0.5, the commands 'in', 'into', 'gopath', 'status', and 'env'
no longer exist.
`
+// VendorDir default vendor directory name
var VendorDir = "vendor"
func main() {
@@ -268,10 +270,13 @@
from the Godeps data, and then update. This has no effect if '--no-recursive'
is set.
- if the '--update-vendored' flag (aliased to '-u') is present vendored
+ If the '--update-vendored' flag (aliased to '-u') is present vendored
dependecies, stored in your projects VCS repository, will be updated. This
works by removing the old package, checking out an the repo and setting the
version, and removing the VCS directory.
+
+ If the '--delete-flatten' flag is present, Glide will remove any depenedencies
+ markred flatten within dependencies.
`,
Flags: []cli.Flag{
cli.BoolFlag{
@@ -294,11 +299,16 @@
Name: "update-vendored, u",
Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.",
},
+ cli.BoolFlag{
+ Name: "delete-flatten",
+ Usage: "Delete flattened vendor packages.",
+ },
},
Action: func(c *cli.Context) {
cxt.Put("deleteOptIn", c.Bool("delete"))
cxt.Put("forceUpdate", c.Bool("force"))
cxt.Put("recursiveDependencies", !c.Bool("no-recursive"))
+ cxt.Put("deleteFlatten", c.Bool("delete-flatten"))
if c.Bool("import") {
cxt.Put("importGodeps", true)
cxt.Put("importGPM", true)
@@ -399,6 +409,7 @@
Using("force").From("cxt:forceUpdate").
Does(cmd.SetReference, "version").Using("conf").From("cxt:cfg").
Does(cmd.Recurse, "recurse").Using("conf").From("cxt:cfg").
+ Using("deleteFlatten").From("cxt:deleteFlatten").
Using("importGodeps").From("cxt:importGodeps").
Using("importGPM").From("cxt:importGPM").
Using("enable").From("cxt:recursiveDependencies").
diff --git a/glide.yaml b/glide.yaml
index 4d4de47..cc28dbb 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -3,6 +3,7 @@
- package: github.com/kylelemons/go-gypsy
subpackages:
- yaml
+ flatten: true
- package: github.com/Masterminds/cookoo
ref: master
repo: git@github.com:Masterminds/cookoo.git