diff --git a/Makefile b/Makefile index 946edfea..eb22816c 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ install: build install -m 755 ./glide ${DESTDIR}/usr/local/bin/glide test: - ${GLIDE_GO_EXECUTABLE} test . ./gb ./path ./action ./tree ./util ./godep ./godep/strip ./gpm ./cfg ./dependency ./importer ./msg ./repo ./mirrors + ${GLIDE_GO_EXECUTABLE} test . ./gb ./path ./action ./tree ./util ./godep ./godep/strip ./gpm ./trash ./cfg ./dependency ./importer ./msg ./repo ./mirrors integration-test: ${GLIDE_GO_EXECUTABLE} build diff --git a/README.md b/README.md index e6da8a84..bb98b88d 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ When a dependent package is encountered its imports are scanned to determine dependencies of dependencies (transitive dependencies). If the dependent project contains a `glide.yaml` file that information is used to help determine the dependency rules when fetching from a location or version to use. Configuration -from Godep, GB, GOM, and GPM is also imported. +from Godep, GB, GOM, GPM and Trash is also imported. The dependencies are exported to the `vendor/` directory where the `go` tools can find and use them. A `glide.lock` file is generated containing all the @@ -186,7 +186,7 @@ $ glide get github.com/Masterminds/cookoo ``` When `glide get` is used it will introspect the listed package to resolve its -dependencies including using Godep, GPM, Gom, and GB config files. +dependencies including using Godep, GPM, Gom, GB and Trash config files. ### glide update (aliased to up) @@ -199,7 +199,7 @@ $ glide up ``` This will recurse over the packages looking for other projects managed by Glide, -Godep, gb, gom, and GPM. When one is found those packages will be installed as needed. +Godep, gb, gom, GPM and Trash. When one is found those packages will be installed as needed. A `glide.lock` file will be created or updated with the dependencies pinned to specific versions. For example, if in the `glide.yaml` file a version was @@ -403,16 +403,16 @@ That's up to you. It's not necessary, but it may also cause you extra work and lots of extra space in your VCS. There may also be unforeseen errors ([see an example](https://github.com/mattfarina/golang-broken-vendor)). -#### Q: How do I import settings from GPM, Godep, gom or gb? +#### Q: How do I import settings from GPM, Godep, gom, gb or Trash? There are two parts to importing. -1. If a package you import has configuration for GPM, Godep, gom or gb Glide will - recursively install the dependencies automatically. -2. If you would like to import configuration from GPM, Godep, gom or gb to Glide see - the `glide import` command. For example, you can run `glide import godep` for - Glide to detect the projects Godep configuration and generate a `glide.yaml` - file for you. +1. If a package you import has configuration for GPM, Godep, gom, gb or Trash + Glide will recursively install the dependencies automatically. +2. If you would like to import configuration from GPM, Godep, gom, gb or Trash + to Glide see the `glide import` command. For example, you can run + `glide import godep` for Glide to detect the projects Godep configuration + and generate a `glide.yaml` file for you. Each of these will merge your existing `glide.yaml` file with the dependencies it finds for those managers, and then emit the file as diff --git a/action/create.go b/action/create.go index 5d421995..20a2da4a 100644 --- a/action/create.go +++ b/action/create.go @@ -13,6 +13,7 @@ import ( "github.com/Masterminds/glide/gpm" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" + "github.com/Masterminds/glide/trash" "github.com/Masterminds/glide/util" ) @@ -204,6 +205,9 @@ func guessImportDeps(base string, config *cfg.Config) { } else if d, ok := guessImportGB(absBase); ok { msg.Info("Importing GB configuration") deps = d + } else if d, ok := guessImportTrash(absBase); ok { + msg.Info("Importing Trash configuration") + deps = d } for _, i := range deps { @@ -243,3 +247,12 @@ func guessImportGB(dir string) ([]*cfg.Dependency, bool) { return d, true } + +func guessImportTrash(dir string) ([]*cfg.Dependency, bool) { + d, err := trash.Parse(dir) + if err != nil || len(d) == 0 { + return []*cfg.Dependency{}, false + } + + return d, true +} diff --git a/action/import_trash.go b/action/import_trash.go new file mode 100644 index 00000000..879e7d06 --- /dev/null +++ b/action/import_trash.go @@ -0,0 +1,21 @@ +package action + +import ( + "github.com/Masterminds/glide/msg" + "github.com/Masterminds/glide/trash" +) + +// ImporTrash imports a Trash vendor file. +func ImporTrash(dest string) { + base := "." + config := EnsureConfig() + if !trash.Has(base) { + msg.Die("No Trash vendor file found.") + } + deps, err := trash.Parse(base) + if err != nil { + msg.Die("Failed to extract Trash vendor file: %s", err) + } + appendImports(deps, config) + writeConfigToFileOrStdout(config, dest) +} diff --git a/glide.go b/glide.go index 5f9c14f4..31b758e9 100644 --- a/glide.go +++ b/glide.go @@ -166,7 +166,7 @@ func commands() []cli.Command { the subpackage 'web'. If a fetched dependency has a glide.yaml file, configuration from Godep, - GPM, GOM, or GB Glide that configuration will be used to find the dependencies + GPM, GOM, GB or Trash Glide that configuration will be used to find the dependencies and versions to fetch. If those are not available the dependent packages will be fetched as either a version specified elsewhere or the latest version. @@ -374,6 +374,20 @@ func commands() []cli.Command { return nil }, }, + { + Name: "trash", + Usage: "Import Trash vendor file and display the would-be yaml file", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "file, f", + Usage: "Save all of the discovered dependencies to a Glide YAML file.", + }, + }, + Action: func(c *cli.Context) error { + action.ImporTrash(c.String("file")) + return nil + }, + }, }, }, { @@ -527,9 +541,9 @@ Example: '--no-recursive'. When this behavior is skipped a glide.lock file is not generated because the full dependency tree cannot be known. - Glide will also import Godep, GB, GOM, and GPM files as it finds them in dependencies. - It will create a glide.yaml file from the Godeps data, and then update. This - has no effect if '--no-recursive' is set. + Glide will also import Godep, GB, GOM, GPM and Trash files as it finds them + in dependencies. It will create a glide.yaml file from the Godeps data, and + then update. This has no effect if '--no-recursive' is set. The '--strip-vendor' flag will remove any nested 'vendor' folders and 'Godeps/_workspace' folders after an update (along with undoing any Godep diff --git a/importer/importer.go b/importer/importer.go index 9d5d2ac7..0d66d4ef 100644 --- a/importer/importer.go +++ b/importer/importer.go @@ -1,4 +1,5 @@ -// Package importer imports dependency configuration from Glide, Godep, GPM, GB and gom +// Package importer imports dependency configuration from Glide, Godep, GPM, GB, gom +// and Trash package importer import ( @@ -11,11 +12,13 @@ import ( "github.com/Masterminds/glide/godep" "github.com/Masterminds/glide/gom" "github.com/Masterminds/glide/gpm" + "github.com/Masterminds/glide/trash" ) var i = &DefaultImporter{} -// Import uses the DefaultImporter to import from Glide, Godep, GPM, GB and gom. +// Import uses the DefaultImporter to import from Glide, Godep, GPM, GB, gom +// and Trash. func Import(path string) (bool, []*cfg.Dependency, error) { return i.Import(path) } @@ -30,10 +33,10 @@ type Importer interface { Import(path string) (bool, []*cfg.Dependency, error) } -// DefaultImporter imports from Glide, Godep, GPM, GB and gom. +// DefaultImporter imports from Glide, Godep, GPM, GB, gom and Trash. type DefaultImporter struct{} -// Import tries to import configuration from Glide, Godep, GPM, GB and gom. +// Import tries to import configuration from Glide, Godep, GPM, GB, gom and Trash. func (d *DefaultImporter) Import(path string) (bool, []*cfg.Dependency, error) { // Try importing from Glide first. @@ -87,6 +90,15 @@ func (d *DefaultImporter) Import(path string) (bool, []*cfg.Dependency, error) { return true, deps, nil } + // Try importing from Trash + if trash.Has(path) { + deps, err := trash.Parse(path) + if err != nil { + return false, []*cfg.Dependency{}, err + } + return true, deps, nil + } + // When none are found. return false, []*cfg.Dependency{}, nil } diff --git a/trash/conf/conf.go b/trash/conf/conf.go new file mode 100644 index 00000000..1ceb3530 --- /dev/null +++ b/trash/conf/conf.go @@ -0,0 +1,126 @@ +// Package govendor provides compatibility with govendor vendorfiles. + +// This is a copy of Trash's `conf/conf.go` file, and has been ported +// to use gopkg.in/yaml.v2 +// Trash is governed by a MIT license that can be found in the +// LICENSE file of Trash project + +package conf + +import ( + "bufio" + "os" + "sort" + "strings" + + "io/ioutil" + + "gopkg.in/yaml.v2" +) + +// Conf of a Trash vendor file. +type Conf struct { + Package string `yaml:"package,omitempty"` + Imports []Import `yaml:"import,omitempty"` + Excludes []string `yaml:"exclude,omitempty"` + importMap map[string]Import + confFile string + yamlType bool +} + +// Import as defined in a Trash vendor file. +type Import struct { + Package string `yaml:"package,omitempty"` + Version string `yaml:"version,omitempty"` + Repo string `yaml:"repo,omitempty"` +} + +// Parse a Trash vendor conf file. +func Parse(path string) (*Conf, error) { + ymlfile, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + trashConf := &Conf{confFile: path} + err = yaml.Unmarshal(ymlfile, &trashConf) + if err == nil { + trashConf.yamlType = true + trashConf.Dedupe() + return trashConf, nil + } + + trashConf = &Conf{confFile: path} + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + scanner := bufio.NewScanner(bufio.NewReader(file)) + for scanner.Scan() { + line := scanner.Text() + if commentStart := strings.Index(line, "#"); commentStart >= 0 { + line = line[0:commentStart] + } + if line = strings.TrimSpace(line); line == "" { + continue + } + fields := strings.Fields(line) + + if len(fields) == 1 && trashConf.Package == "" { + trashConf.Package = fields[0] // use the first 1-field line as the root package + continue + } + // If we have a `-` suffix, it's an exclude pattern + if fields[0][0] == '-' { + trashConf.Excludes = append(trashConf.Excludes, strings.TrimSpace(fields[0][1:])) + continue + } + // Otherwise it's an import pattern + packageImport := Import{} + packageImport.Package = fields[0] // at least 1 field at this point: trimmed the line and skipped empty + if len(fields) > 2 { + packageImport.Repo = fields[2] + } + if len(fields) > 1 { + packageImport.Version = fields[1] + } + trashConf.Imports = append(trashConf.Imports, packageImport) + } + + trashConf.Dedupe() + return trashConf, nil +} + +// Dedupe deletes duplicates and sorts the imports +func (t *Conf) Dedupe() { + t.importMap = map[string]Import{} + for _, i := range t.Imports { + if _, ok := t.importMap[i.Package]; ok { + continue + } + t.importMap[i.Package] = i + } + ps := make([]string, 0, len(t.importMap)) + for p := range t.importMap { + ps = append(ps, p) + } + sort.Strings(ps) + imports := make([]Import, 0, len(t.importMap)) + for _, p := range ps { + imports = append(imports, t.importMap[p]) + } + t.Imports = imports +} + +// Get the import of a specified package. +func (t *Conf) Get(pkg string) (Import, bool) { + i, ok := t.importMap[pkg] + return i, ok +} + +// ConfFile from which the config has been read. +func (t *Conf) ConfFile() string { + return t.confFile +} diff --git a/trash/trash.go b/trash/trash.go new file mode 100644 index 00000000..046523d2 --- /dev/null +++ b/trash/trash.go @@ -0,0 +1,52 @@ +// Package trash reads Trash's vendor files. +// +// It is not a complete implementaton of Trash. +package trash + +import ( + "os" + "path/filepath" + + "github.com/Masterminds/glide/cfg" + "github.com/Masterminds/glide/msg" + gpath "github.com/Masterminds/glide/path" + "github.com/Masterminds/glide/trash/conf" +) + +// Has indicates whether a Trash file exists. +func Has(dir string) bool { + path := filepath.Join(dir, "vendor.conf") + _, err := os.Stat(path) + return err == nil +} + +// Parse parses a Trash vendor.conf file. +func Parse(dir string) ([]*cfg.Dependency, error) { + path := filepath.Join(dir, "vendor.conf") + if i, err := os.Stat(path); err != nil { + return []*cfg.Dependency{}, nil + } else if i.IsDir() { + msg.Info("vendor.conf is a directory.\n") + return []*cfg.Dependency{}, nil + } + msg.Info("Found vendor.conf file in %s", gpath.StripBasepath(dir)) + msg.Info("--> Parsing Trash metadata...") + + buf := []*cfg.Dependency{} + + trashconf, err := conf.Parse(path) + if err != nil { + return buf, err + } + + for _, trashimport := range trashconf.Imports { + dep := &cfg.Dependency{} + dep.Name = trashimport.Package + dep.Reference = trashimport.Version + dep.Repository = trashimport.Repo + + buf = append(buf, dep) + } + + return buf, nil +}