Merge branch 'Release-0.9'
diff --git a/README.md b/README.md index 6ab8cde..6b1a900 100644 --- a/README.md +++ b/README.md
@@ -408,13 +408,13 @@ 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, or gb? +#### Q: How do I import settings from GPM, Godep, gom or gb? There are two parts to importing. -1. If a package you import has configuration for GPM, Godep, or gb Glide will +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, or gb to Glide see +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.
diff --git a/action/import_gom.go b/action/import_gom.go new file mode 100644 index 0000000..87ab616 --- /dev/null +++ b/action/import_gom.go
@@ -0,0 +1,21 @@ +package action + +import ( + "github.com/Masterminds/glide/gom" + "github.com/Masterminds/glide/msg" +) + +// ImportGom imports a Gomfile. +func ImportGom(dest string) { + base := "." + config := EnsureConfig() + if !gom.Has(base) { + msg.Die("No gom data found.") + } + deps, err := gom.Parse(base) + if err != nil { + msg.Die("Failed to extract Gomfile: %s", err) + } + appendImports(deps, config) + writeConfigToFileOrStdout(config, dest) +}
diff --git a/glide.go b/glide.go index 26dc1b5..6d4bed5 100644 --- a/glide.go +++ b/glide.go
@@ -307,6 +307,19 @@ action.ImportGB(c.String("file")) }, }, + { + Name: "gom", + Usage: "Import Gomfile 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) { + action.ImportGom(c.String("file")) + }, + }, }, }, {
diff --git a/gom/gom.go b/gom/gom.go new file mode 100644 index 0000000..fae091a --- /dev/null +++ b/gom/gom.go
@@ -0,0 +1,110 @@ +package gom + +import ( + "errors" + "os" + "path/filepath" + + "github.com/Masterminds/glide/cfg" + "github.com/Masterminds/glide/msg" + "github.com/Masterminds/glide/util" +) + +// Has returns true if this dir has a Gomfile. +func Has(dir string) bool { + path := filepath.Join(dir, "Gomfile") + fi, err := os.Stat(path) + return err == nil && !fi.IsDir() +} + +// Parse parses a Gomfile. +func Parse(dir string) ([]*cfg.Dependency, error) { + path := filepath.Join(dir, "Gomfile") + if fi, err := os.Stat(path); err != nil || fi.IsDir() { + return []*cfg.Dependency{}, nil + } + + msg.Info("Found Gomfile.\n") + buf := []*cfg.Dependency{} + + goms, err := parseGomfile(path) + if err != nil { + return []*cfg.Dependency{}, err + } + + for _, gom := range goms { + // Do we need to skip this dependency? + if val, ok := gom.options["skipdep"]; ok && val.(string) == "true" { + continue + } + + // Check for custom cloning command + if _, ok := gom.options["command"]; ok { + return []*cfg.Dependency{}, errors.New("Glide does not support custom Gomfile commands") + } + + // Check for groups/environments + if val, ok := gom.options["group"]; ok { + groups := toStringSlice(val) + if !stringsContain(groups, "development") && !stringsContain(groups, "production") { + // right now we only support development and production + msg.Info("Skipping dependency '%s' because it isn't in the development or production group", gom.name) + continue + } + } + + pkg, sub := util.NormalizeName(gom.name) + + dep := &cfg.Dependency{ + Name: pkg, + } + + if len(sub) > 0 { + dep.Subpackages = []string{sub} + } + + // Check for a specific revision + if val, ok := gom.options["commit"]; ok { + dep.Reference = val.(string) + } + if val, ok := gom.options["tag"]; ok { + dep.Reference = val.(string) + } + if val, ok := gom.options["branch"]; ok { + dep.Reference = val.(string) + } + + // Parse goos and goarch + if val, ok := gom.options["goos"]; ok { + dep.Os = toStringSlice(val) + } + if val, ok := gom.options["goarch"]; ok { + dep.Arch = toStringSlice(val) + } + + buf = append(buf, dep) + } + + return buf, nil +} + +func stringsContain(v []string, key string) bool { + for _, s := range v { + if s == key { + return true + } + } + return false +} + +func toStringSlice(v interface{}) []string { + if v, ok := v.(string); ok { + return []string{v} + } + + if v, ok := v.([]string); ok { + return v + } + + return []string{} +} \ No newline at end of file
diff --git a/gom/parser.go b/gom/parser.go new file mode 100644 index 0000000..bf54f61 --- /dev/null +++ b/gom/parser.go
@@ -0,0 +1,130 @@ +package gom + +// This is copied + slightly adapted from gom's `gomfile.go` file. +// +// gom's license is MIT-style. + +import ( + "bufio" + "fmt" + "io" + "os" + "regexp" + "strings" +) + +var qx = `'[^']*'|"[^"]*"` +var kx = `:[a-z][a-z0-9_]*` +var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)` +var re_group = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`) +var re_end = regexp.MustCompile(`\s*end\s*$`) +var re_gom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`) +var re_options = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`) + +func unquote(name string) string { + name = strings.TrimSpace(name) + if len(name) > 2 { + if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') { + return name[1 : len(name)-1] + } + } + return name +} + +func parseOptions(line string, options map[string]interface{}) { + ss := re_options.FindAllStringSubmatch(line, -1) + re_a := regexp.MustCompile(ax) + for _, s := range ss { + kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2) + kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1]) + if kvs[1][0] == '[' { + as := re_a.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1) + a := []string{} + for i := range as { + it := strings.TrimSpace(as[i][0]) + if strings.HasPrefix(it, ",") { + it = strings.TrimSpace(it[1:]) + } + if strings.HasPrefix(it, ":") { + it = strings.TrimSpace(it[1:]) + } + a = append(a, it) + } + options[kvs[0][1:]] = a + } else { + options[kvs[0][1:]] = unquote(kvs[1]) + } + } +} + +type Gom struct { + name string + options map[string]interface{} +} + +func parseGomfile(filename string) ([]Gom, error) { + f, err := os.Open(filename + ".lock") + if err != nil { + f, err = os.Open(filename) + if err != nil { + return nil, err + } + } + br := bufio.NewReader(f) + + goms := make([]Gom, 0) + + n := 0 + skip := 0 + valid := true + var envs []string + for { + n++ + lb, _, err := br.ReadLine() + if err != nil { + if err == io.EOF { + return goms, nil + } + return nil, err + } + line := strings.TrimSpace(string(lb)) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + name := "" + options := make(map[string]interface{}) + var items []string + if re_group.MatchString(line) { + envs = strings.Split(re_group.FindStringSubmatch(line)[1], ",") + for i := range envs { + envs[i] = strings.TrimSpace(envs[i])[1:] + } + valid = true + continue + } else if re_end.MatchString(line) { + if !valid { + skip-- + if skip < 0 { + return nil, fmt.Errorf("Syntax Error at line %d", n) + } + } + valid = false + envs = nil + continue + } else if skip > 0 { + continue + } else if re_gom.MatchString(line) { + items = re_gom.FindStringSubmatch(line)[1:] + name = unquote(items[0]) + parseOptions(items[1], options) + } else { + return nil, fmt.Errorf("Syntax Error at line %d", n) + } + if envs != nil { + options["group"] = envs + } + goms = append(goms, Gom{name, options}) + } + return goms, nil +} \ No newline at end of file
diff --git a/importer/importer.go b/importer/importer.go index 51a9ba5..9d5d2ac 100644 --- a/importer/importer.go +++ b/importer/importer.go
@@ -1,4 +1,4 @@ -// Package importer imports dependency configuration from Glide, Godep, GPM, and GB +// Package importer imports dependency configuration from Glide, Godep, GPM, GB and gom package importer import ( @@ -9,12 +9,13 @@ "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/gb" "github.com/Masterminds/glide/godep" + "github.com/Masterminds/glide/gom" "github.com/Masterminds/glide/gpm" ) var i = &DefaultImporter{} -// Import uses the DefaultImporter to import from Glide, Godep, GPM, and GB. +// Import uses the DefaultImporter to import from Glide, Godep, GPM, GB and gom. func Import(path string) (bool, []*cfg.Dependency, error) { return i.Import(path) } @@ -29,10 +30,10 @@ Import(path string) (bool, []*cfg.Dependency, error) } -// DefaultImporter imports from Glide, Godep, GPM, and GB. +// DefaultImporter imports from Glide, Godep, GPM, GB and gom. type DefaultImporter struct{} -// Import tries to import configuration from Glide, Godep, GPM, and GB. +// Import tries to import configuration from Glide, Godep, GPM, GB and gom. func (d *DefaultImporter) Import(path string) (bool, []*cfg.Dependency, error) { // Try importing from Glide first. @@ -77,6 +78,15 @@ return true, deps, nil } + // Try importing from gom + if gom.Has(path) { + deps, err := gom.Parse(path) + if err != nil { + return false, []*cfg.Dependency{}, err + } + return true, deps, nil + } + // When none are found. return false, []*cfg.Dependency{}, nil }