Add glide.yaml autoconversion logic and tests
diff --git a/cfg/config.go b/cfg/config.go
index 3b46764..f0e1248 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -7,6 +7,7 @@
"io/ioutil"
"reflect"
"sort"
+ "strconv"
"strings"
"github.com/Masterminds/vcs"
@@ -45,8 +46,15 @@
// Imports contains a list of all dependency constraints for a project. For
// more detail on how these are captured see the Dependency type.
+ // TODO rename
+ // TODO mapify
Imports Dependencies `yaml:"dependencies"`
+ // DevImports contains the test or other development dependency constraints
+ // for a project. See the Dependency type for more details on how this is
+ // recorded.
+ // TODO rename
+ // TODO mapify
DevImports Dependencies `yaml:"testDependencies"`
}
@@ -58,22 +66,27 @@
License string `yaml:"license,omitempty"`
Owners Owners `yaml:"owners,omitempty"`
Ignore []string `yaml:"ignore,omitempty"`
- Imports Dependencies `yaml:"dependencies"`
+ Imports Dependencies `yaml:"dependencies,omitempty"`
DevImports Dependencies `yaml:"testDependencies,omitempty"`
+ // used to guarantee that this wil fail on unmarshaling legacy yamls
+ Compat int `yaml:"import,omitempty"`
+ Compat2 int `yaml:"testImport,omitempty"`
}
// ConfigFromYaml returns an instance of Config from YAML
-func ConfigFromYaml(yml []byte) (*Config, bool, error) {
- cfg := &Config{}
- err := yaml.Unmarshal([]byte(yml), &cfg)
+func ConfigFromYaml(yml []byte) (cfg *Config, legacy bool, err error) {
+ cfg = &Config{}
+ err = yaml.Unmarshal(yml, cfg)
if err != nil {
lcfg := &lConfig1{}
- err = yaml.Unmarshal([]byte(yml), &lcfg)
- if err != nil {
- // TODO(sdboyer) convert to new form, then return
+ err = yaml.Unmarshal(yml, &lcfg)
+ if err == nil {
+ legacy = true
+ cfg, err = lcfg.Convert()
}
}
- return cfg, false, err
+
+ return
}
// Marshal converts a Config instance to YAML
@@ -404,7 +417,7 @@
// A transitive representation of a dependency for yaml import/export.
type dep struct {
Name string `yaml:"package"`
- Reference string `yaml:"version,omitempty"`
+ Reference string `yaml:"version,omitempty"` // TODO rename
Branch string `yaml:"branch,omitempty"`
Repository string `yaml:"repo,omitempty"`
Arch []string `yaml:"arch,omitempty"`
@@ -473,6 +486,45 @@
return nil
}
+// deduceConstraint tries to puzzle out what kind of version is given in a string -
+// semver, a revision, or as a fallback, a plain tag
+func deduceConstraint(s string) gps.Constraint {
+ // always semver if we can
+ c, err := gps.NewSemverConstraint(s)
+ if err == nil {
+ return c
+ }
+
+ if len(s) == 40 {
+ if _, err = hex.DecodeString(s); err == nil {
+ // Whether or not it's intended to be a SHA1 digest, this is a
+ // valid byte sequence for that, so go with Revision. This
+ // covers git and hg
+ return gps.Revision(s)
+ }
+ }
+ // Next, try for bzr, which has a three-component GUID separated by
+ // dashes. There should be two, but the email part could contain
+ // internal dashes
+ if strings.Count(s, "-") >= 2 {
+ // Work from the back to avoid potential confusion from the email
+ i3 := strings.LastIndex(s, "-")
+ if _, err = hex.DecodeString(s[i3:]); err == nil {
+ i2 := strings.LastIndex(s[:i3], "-")
+ if _, err = strconv.ParseUint(s[i2:i3], 10, 64); err != nil {
+ // Getting this far means it'd pretty much be nuts if it's not a
+ // bzr rev, so don't bother parsing the email.
+ return gps.Revision(s)
+ }
+ }
+ }
+
+ // If not a plain SHA1 or bzr custom GUID, assume a plain version.
+ //
+ // svn, you ask? lol, madame. lol.
+ return gps.NewVersion(s)
+}
+
// MarshalYAML is a hook for gopkg.in/yaml.v2 in the marshaling process
func (d *Dependency) MarshalYAML() (interface{}, error) {
newDep := &dep{
diff --git a/cfg/config_test.go b/cfg/config_test.go
index 58e0904..db563dd 100644
--- a/cfg/config_test.go
+++ b/cfg/config_test.go
@@ -37,6 +37,7 @@
- i386
- arm
- package: github.com/Masterminds/structable
+ version: v1.0.0
- package: github.com/Masterminds/cookoo/color
- package: github.com/Masterminds/cookoo/convert
@@ -238,6 +239,58 @@
}
}
+func TestLegacyConfigAutoconvert(t *testing.T) {
+ c, leg, err := ConfigFromYaml([]byte(lyml))
+ if err != nil {
+ t.Errorf("ConfigFromYaml failed to detect and autoconvert legacy yaml file with err %s", err)
+ }
+
+ if !leg {
+ t.Errorf("ConfigFromYaml failed to report autoconversion of legacy yaml file")
+ }
+
+ if c.Name != "fake/testing" {
+ t.Error("ConfigFromYaml failed to properly autoconvert legacy yaml file")
+ }
+
+ // Two should survive the conversion
+ if len(c.Imports) != 2 {
+ t.Error("Expected two dep clauses to survive conversion, but got ", len(c.Imports))
+ }
+
+ found := false
+ found2 := false
+ for _, i := range c.Imports {
+ if i.Name == "github.com/Masterminds/convert" {
+ found = true
+ ref := gps.Revision("a9949121a2e2192ca92fa6dddfeaaa4a4412d955")
+ if i.Constraint != ref {
+ t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Constraint)
+ }
+
+ repo := "git@github.com:Masterminds/convert.git"
+ if i.Repository != repo {
+ t.Errorf("(%s) Expected %q for repository, got %q", i.Name, repo, i.Repository)
+ }
+ }
+
+ if i.Name == "github.com/Masterminds/structable" {
+ found2 = true
+ ref := gps.NewVersion("v1.0.0")
+ if i.Constraint != ref {
+ t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Constraint)
+ }
+ }
+ }
+ if !found {
+ t.Error("Unable to find github.com/Masterminds/convert")
+ }
+ if !found2 {
+ t.Error("Unable to find github.com/Masterminds/structable")
+ }
+
+}
+
func TestConfigFromYaml(t *testing.T) {
c, _, err := ConfigFromYaml([]byte(yml))
if err != nil {
@@ -255,7 +308,7 @@
t.Error("ConfigFromYaml failed to parse yaml for HasDependency")
}
- if c.HasDependency("github.com/Masterminds/convert") != true {
+ if !c.HasDependency("github.com/Masterminds/convert") {
t.Error("HasDependency failing to pickup depenency")
}
diff --git a/cfg/legacy.go b/cfg/legacy.go
index 4cf294d..3c78703 100644
--- a/cfg/legacy.go
+++ b/cfg/legacy.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "path"
"reflect"
"strings"
@@ -42,9 +43,54 @@
return err
}
+func (c *lConfig1) Convert() (*Config, error) {
+ // This is probably already done, but do it just in case
+ err := c.DeDupe()
+ if err != nil {
+ return nil, err
+ }
+
+ // Pull over the easy values
+ c2 := &Config{
+ Name: c.Name,
+ Description: c.Description,
+ Home: c.Home,
+ License: c.License,
+ Owners: c.Owners,
+ Ignore: c.Ignore,
+ }
+
+ // _if_ the name is set, path-prepend it to exclude, and add that to ignore.
+ // Otherwise, we just skip excludes. Not that big a deal, since they're a
+ // root-only property anyway; the user can reasonably recreate.
+
+ if c.Name != "" {
+ for _, excl := range c.Exclude {
+ c.Ignore = append(c.Ignore, path.Join(c.Name, excl))
+ // The trailing * is interpreted by gps as an ignore on that and all
+ // child paths (or, soon - https://github.com/sdboyer/gps/issues/88)
+ c.Ignore = append(c.Ignore, path.Join(c.Name, excl, "*"))
+ }
+ }
+
+ // Quitting early on this might seem risky, but a) all possible errs
+ // _should_ have already been surfaced in the earlier DeDupe(), and b) there
+ // are no new errs introduced by the conversion itself, so this doesn't
+ // actually increase the surface area for failures vis-a-vis pre-gps glide.
+ c2.Imports, err = c.Imports.Convert()
+ if err != nil {
+ return nil, err
+ }
+ c2.DevImports, err = c.DevImports.Convert()
+ if err != nil {
+ return nil, err
+ }
+
+ return c2, nil
+}
+
// DeDupe consolidates duplicate dependencies on a Config instance
func (c *lConfig1) DeDupe() error {
-
// Remove duplicates in the imports
var err error
c.Imports, err = c.Imports.DeDupe()
@@ -118,6 +164,35 @@
type lDependencies1 []*lDependency1
+func (ds lDependencies1) Convert() (Dependencies, error) {
+ dds, err := ds.DeDupe()
+ if err != nil {
+ return nil, err
+ }
+
+ var ds2 Dependencies
+ for _, d := range dds {
+ // If we have neither a repo nor a reference, then it's pointless to
+ // include this dep in the list (it will add no information to gps)
+ if d.Repository == "" && d.Reference == "" {
+ continue
+ }
+
+ d2 := &Dependency{
+ Name: d.Name,
+ Repository: d.Repository,
+ }
+
+ d2.Constraint = deduceConstraint(d.Reference)
+ // TODO(sdboyer) if the above result is a plain version, the old
+ // semantics are ambiguous wrt if the user wants a branch or tag. Check
+ // the version list (via source manager) to convert most sanely?
+ ds2 = append(ds2, d2)
+ }
+
+ return ds2, nil
+}
+
type lDependency1 struct {
Name string `yaml:"package"`
Reference string `yaml:"version,omitempty"`
@@ -129,7 +204,7 @@
Os []string `yaml:"os,omitempty"`
}
-// Legacy unmarshaler
+// Legacy unmarshaler for dependency component of yaml files
func (d *lDependency1) UnmarshalYAML(unmarshal func(interface{}) error) error {
newDep := &ldep1{}
err := unmarshal(&newDep)