Implements #39: Method for overriding aliases
Aliases are stored in an overrides.yaml file in the GLIDE_HOME
diff --git a/Makefile b/Makefile
index 340fd63..917c951 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@
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
+ ${GLIDE_GO_EXECUTABLE} test . ./gb ./path ./action ./tree ./util ./godep ./godep/strip ./gpm ./cfg ./dependency ./importer ./msg ./repo ./overrides
clean:
rm -f ./glide.test
diff --git a/action/ensure.go b/action/ensure.go
index 2f5f5af..a2254a6 100644
--- a/action/ensure.go
+++ b/action/ensure.go
@@ -10,6 +10,7 @@
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/msg"
+ "github.com/Masterminds/glide/overrides"
gpath "github.com/Masterminds/glide/path"
"github.com/Masterminds/glide/util"
)
@@ -56,6 +57,11 @@
}
}
+ err = overrides.Load()
+ if err != nil {
+ msg.Err("Unable to load overrides: %s", err)
+ }
+
return conf
}
diff --git a/action/install.go b/action/install.go
index 2247e1f..c8e7253 100644
--- a/action/install.go
+++ b/action/install.go
@@ -53,7 +53,7 @@
// Set reference
if err := repo.SetReference(newConf, installer.ResolveTest); err != nil {
- msg.Err("Failed to set references: %s (Skip to cleanup)", err)
+ msg.Die("Failed to set references: %s (Skip to cleanup)", err)
}
err = installer.Export(newConf)
diff --git a/action/overrides.go b/action/overrides.go
new file mode 100644
index 0000000..de87f61
--- /dev/null
+++ b/action/overrides.go
@@ -0,0 +1,146 @@
+package action
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/Masterminds/glide/msg"
+ "github.com/Masterminds/glide/overrides"
+ gpath "github.com/Masterminds/glide/path"
+)
+
+// OverridesList displays a list of currently setup overrides.
+func OverridesList() error {
+ home := gpath.Home()
+
+ op := filepath.Join(home, "overrides.yaml")
+
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Info("No overrides exist. No overrides.yaml file not found")
+ return nil
+ }
+
+ ov, err := overrides.ReadOverridesFile(op)
+ if err != nil {
+ msg.Die("Unable to read overrides.yaml file: %s", err)
+ }
+
+ if len(ov.Repos) == 0 {
+ msg.Info("No overrides found")
+ return nil
+ }
+
+ msg.Info("Overrides...")
+ for _, r := range ov.Repos {
+ if r.Vcs == "" {
+ msg.Info("--> %s replaced by %s", r.Original, r.Repo)
+ } else {
+ msg.Info("--> %s replaced by %s (%s)", r.Original, r.Repo, r.Vcs)
+ }
+ }
+
+ return nil
+}
+
+// OverridesSet sets an override to use
+func OverridesSet(o, r, v string) error {
+ if o == "" || r == "" {
+ msg.Err("Both the original and overriding values are required")
+ return nil
+ }
+
+ home := gpath.Home()
+
+ op := filepath.Join(home, "overrides.yaml")
+
+ var ov *overrides.Overrides
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Info("No overrides.yaml file exists. Creating new one")
+ ov = &overrides.Overrides{
+ Repos: make(overrides.OverrideRepos, 0),
+ }
+ } else {
+ ov, err = overrides.ReadOverridesFile(op)
+ if err != nil {
+ msg.Die("Error reading existing overrides.yaml file: %s", err)
+ }
+ }
+
+ found := false
+ for i, re := range ov.Repos {
+ if re.Original == o {
+ found = true
+ msg.Info("%s found in overrides. Replacing with new settings", o)
+ ov.Repos[i].Repo = r
+ ov.Repos[i].Vcs = v
+ }
+ }
+
+ if !found {
+ nr := &overrides.OverrideRepo{
+ Original: o,
+ Repo: r,
+ Vcs: v,
+ }
+ ov.Repos = append(ov.Repos, nr)
+ }
+
+ msg.Info("%s being set to %s", o, r)
+
+ err := ov.WriteFile(op)
+ if err != nil {
+ msg.Err("Error writing overrides.yaml file: %s", err)
+ } else {
+ msg.Info("overrides.yaml written with changes")
+ }
+
+ return nil
+}
+
+// OverridesRemove removes an override setting
+func OverridesRemove(k string) error {
+ if k == "" {
+ msg.Err("The override to remove is required")
+ return nil
+ }
+
+ home := gpath.Home()
+
+ op := filepath.Join(home, "overrides.yaml")
+
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Err("overrides.yaml file not found")
+ return nil
+ }
+
+ ov, err := overrides.ReadOverridesFile(op)
+ if err != nil {
+ msg.Die("Unable to read overrides.yaml file: %s", err)
+ }
+
+ var nre overrides.OverrideRepos
+ var found bool
+ for _, re := range ov.Repos {
+ if re.Original != k {
+ nre = append(nre, re)
+ } else {
+ found = true
+ }
+ }
+
+ if !found {
+ msg.Warn("%s was not found in overrides", k)
+ } else {
+ msg.Info("%s was removed from overrides", k)
+ ov.Repos = nre
+
+ err = ov.WriteFile(op)
+ if err != nil {
+ msg.Err("Error writing overrides.yaml file: %s", err)
+ } else {
+ msg.Info("overrides.yaml written with changes")
+ }
+ }
+
+ return nil
+}
diff --git a/cfg/config.go b/cfg/config.go
index 06ba9b9..49b3c65 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -8,6 +8,7 @@
"sort"
"strings"
+ "github.com/Masterminds/glide/overrides"
"github.com/Masterminds/glide/util"
"github.com/Masterminds/vcs"
"gopkg.in/yaml.v2"
@@ -462,11 +463,38 @@
// Remote returns the remote location to fetch source from. This location is
// the central place where overrides happen from.
func (d *Dependency) Remote() string {
+ var r string
+
if d.Repository != "" {
- return d.Repository
+ r = d.Repository
+ } else {
+ r = "https://" + d.Name
}
- return "https://" + d.Name
+ f, nr, _ := overrides.Get(r)
+ if f {
+ return nr
+ }
+
+ return r
+}
+
+// Vcs returns the VCS type to fetch source from.
+func (d *Dependency) Vcs() string {
+ var r string
+
+ if d.Repository != "" {
+ r = d.Repository
+ } else {
+ r = "https://" + d.Name
+ }
+
+ f, _, nv := overrides.Get(r)
+ if f {
+ return nv
+ }
+
+ return d.VcsType
}
// GetRepo retrieves a Masterminds/vcs repo object configured for the root
@@ -475,16 +503,13 @@
// The remote location is either the configured repo or the package
// name as an https url.
- var remote string
- if len(d.Repository) > 0 {
- remote = d.Repository
- } else {
- remote = "https://" + d.Name
- }
+ remote := d.Remote()
+
+ VcsType := d.Vcs()
// If the VCS type has a value we try that first.
- if len(d.VcsType) > 0 && d.VcsType != "None" {
- switch vcs.Type(d.VcsType) {
+ if len(VcsType) > 0 && VcsType != "None" {
+ switch vcs.Type(VcsType) {
case vcs.Git:
return vcs.NewGitRepo(remote, dest)
case vcs.Svn:
@@ -494,7 +519,7 @@
case vcs.Bzr:
return vcs.NewBzrRepo(remote, dest)
default:
- return nil, fmt.Errorf("Unknown VCS type %s set for %s", d.VcsType, d.Name)
+ return nil, fmt.Errorf("Unknown VCS type %s set for %s", VcsType, d.Name)
}
}
diff --git a/glide.go b/glide.go
index e138505..2609a24 100644
--- a/glide.go
+++ b/glide.go
@@ -760,6 +760,90 @@
return nil
},
},
+ {
+ Name: "override",
+ Usage: "Manage overrides",
+ Description: `Overrides provide the ability to replace a repo location with
+ another location. This is useful when you want to have a cache for your
+ continious integration (CI) system or if you want to work on a dependency
+ in a local location, such as the GOPATH.
+
+ The overrides are stored in an overrides.yaml file in your GLIDE_HOME.
+
+ The three commands to manager overrides are 'list', 'set', and 'remove'.
+
+ Use 'set' in the form:
+
+ glide override set [original] [replacement]
+
+ or
+
+ glide override set [original] [replacement] --vcs [type]
+
+ for example,
+
+ glide override set https://github.com/example/foo https://git.example.com/example/foo.git
+
+ glide override set https://github.com/example/foo file:///path/to/local/repo --vcs git
+
+ Use 'remove' in the form:
+
+ glide override remove [original]
+
+ for example,
+
+ glide override remove https://github.com/example/foo`,
+ Subcommands: []cli.Command{
+ {
+ Name: "list",
+ Usage: "List the current overrides",
+ Action: func(c *cli.Context) error {
+ return action.OverridesList()
+ },
+ },
+ {
+ Name: "set",
+ Usage: "Set an override. This overwrites an existing entry if one exists",
+ Description: `Use 'set' in the form:
+
+ glide override set [original] [replacement]
+
+ or
+
+ glide override set [original] [replacement] --vcs [type]
+
+ for example,
+
+ glide override set https://github.com/example/foo https://git.example.com/example/foo.git
+
+ glide override set https://github.com/example/foo file:///path/to/local/repo --vcs git`,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "vcs",
+ Usage: "The VCS type to use. Autodiscovery is attempted when not supplied. Can be one of git, svn, bzr, or hg",
+ },
+ },
+ Action: func(c *cli.Context) error {
+ return action.OverridesSet(c.Args().Get(0), c.Args().Get(1), c.String("vcs"))
+ },
+ },
+ {
+ Name: "remove",
+ ShortName: "rm",
+ Usage: "Remove an override",
+ Description: `Use 'remove' in the form:
+
+ glide override remove [original]
+
+ for example,
+
+ glide override remove https://github.com/example/foo`,
+ Action: func(c *cli.Context) error {
+ return action.OverridesRemove(c.Args().Get(0))
+ },
+ },
+ },
+ },
}
}
diff --git a/overrides/cfg.go b/overrides/cfg.go
new file mode 100644
index 0000000..d79284e
--- /dev/null
+++ b/overrides/cfg.go
@@ -0,0 +1,98 @@
+package overrides
+
+import (
+ "io/ioutil"
+ "sort"
+ "strings"
+
+ "gopkg.in/yaml.v2"
+)
+
+// Overrides contains global overrides to local configuration
+type Overrides struct {
+
+ // Repos contains repo override configuration
+ Repos OverrideRepos `yaml:"repos"`
+}
+
+// Marshal converts an Overrides instance to YAML
+func (ov *Overrides) Marshal() ([]byte, error) {
+ yml, err := yaml.Marshal(&ov)
+ if err != nil {
+ return []byte{}, err
+ }
+ return yml, nil
+}
+
+// WriteFile writes an overrides.yaml file
+//
+// This is a convenience function that marshals the YAML and then writes it to
+// the given file. If the file exists, it will be clobbered.
+func (ov *Overrides) WriteFile(opath string) error {
+ o, err := ov.Marshal()
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(opath, o, 0666)
+}
+
+// ReadOverridesFile loads the contents of an overrides.yaml file.
+func ReadOverridesFile(opath string) (*Overrides, error) {
+ yml, err := ioutil.ReadFile(opath)
+ if err != nil {
+ return nil, err
+ }
+ ov, err := FromYaml(yml)
+ if err != nil {
+ return nil, err
+ }
+ return ov, nil
+}
+
+// FromYaml returns an instance of Overrides from YAML
+func FromYaml(yml []byte) (*Overrides, error) {
+ ov := &Overrides{}
+ err := yaml.Unmarshal([]byte(yml), &ov)
+ return ov, err
+}
+
+// MarshalYAML is a hook for gopkg.in/yaml.v2.
+// It sorts override repos lexicographically for reproducibility.
+func (ov *Overrides) MarshalYAML() (interface{}, error) {
+
+ sort.Sort(ov.Repos)
+
+ return ov, nil
+}
+
+// OverrideRepos is a slice of Override pointers
+type OverrideRepos []*OverrideRepo
+
+// Len returns the length of the OverrideRepos. This is needed for sorting with
+// the sort package.
+func (o OverrideRepos) Len() int {
+ return len(o)
+}
+
+// Less is needed for the sort interface. It compares two OverrideRepos based on
+// their original value.
+func (o OverrideRepos) Less(i, j int) bool {
+
+ // Names are normalized to lowercase because case affects sorting order. For
+ // example, Masterminds comes before kylelemons. Making them lowercase
+ // causes kylelemons to come first which is what is expected.
+ return strings.ToLower(o[i].Original) < strings.ToLower(o[j].Original)
+}
+
+// Swap is needed for the sort interface. It swaps the position of two
+// OverrideRepos.
+func (o OverrideRepos) Swap(i, j int) {
+ o[i], o[j] = o[j], o[i]
+}
+
+// OverrideRepo represents a single repo override
+type OverrideRepo struct {
+ Original string `yaml:"original"`
+ Repo string `yaml:"repo"`
+ Vcs string `yaml:"vcs,omitempty"`
+}
diff --git a/overrides/overrides.go b/overrides/overrides.go
new file mode 100644
index 0000000..8299ede
--- /dev/null
+++ b/overrides/overrides.go
@@ -0,0 +1,66 @@
+// Package overrides handles managing overrides in the running application
+package overrides
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/Masterminds/glide/msg"
+ gpath "github.com/Masterminds/glide/path"
+)
+
+var overrides map[string]*override
+
+func init() {
+ overrides = make(map[string]*override)
+}
+
+type override struct {
+ Repo, Vcs string
+}
+
+// Get retrieves informtion about an override. It returns.
+// - bool if found
+// - new repo location
+// - vcs type
+func Get(k string) (bool, string, string) {
+ o, f := overrides[k]
+ if !f {
+ return false, "", ""
+ }
+
+ return true, o.Repo, o.Vcs
+}
+
+// Load pulls the overrides into memory
+func Load() error {
+ home := gpath.Home()
+
+ op := filepath.Join(home, "overrides.yaml")
+
+ var ov *Overrides
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Debug("No overrides.yaml file exists")
+ ov = &Overrides{
+ Repos: make(OverrideRepos, 0),
+ }
+ } else {
+ ov, err = ReadOverridesFile(op)
+ if err != nil {
+ return fmt.Errorf("Error reading existing overrides.yaml file: %s", err)
+ }
+ }
+
+ msg.Info("Loading overrides from overrides.yaml file")
+ for _, o := range ov.Repos {
+ msg.Debug("Found override: %s to %s (%s)", o.Original, o.Repo, o.Vcs)
+ no := &override{
+ Repo: o.Repo,
+ Vcs: o.Vcs,
+ }
+ overrides[o.Original] = no
+ }
+
+ return nil
+}
diff --git a/overrides/overrides_test.go b/overrides/overrides_test.go
new file mode 100644
index 0000000..dddc500
--- /dev/null
+++ b/overrides/overrides_test.go
@@ -0,0 +1,36 @@
+package overrides
+
+import "testing"
+
+var oyml = `
+repos:
+- original: github.com/Masterminds/semver
+ repo: file:///path/to/local/repo
+ vcs: git
+- original: github.com/Masterminds/atest
+ repo: github.com/example/atest
+`
+
+var ooutyml = `repos:
+- original: github.com/Masterminds/atest
+ repo: github.com/example/atest
+- original: github.com/Masterminds/semver
+ repo: file:///path/to/local/repo
+ vcs: git
+`
+
+func TestSortOverrides(t *testing.T) {
+ ov, err := FromYaml([]byte(oyml))
+ if err != nil {
+ t.Error("Unable to read overrides yaml")
+ }
+
+ out, err := ov.Marshal()
+ if err != nil {
+ t.Error("Unable to marshal overrides yaml")
+ }
+
+ if string(out) != ooutyml {
+ t.Error("Output overrides sorting failed")
+ }
+}
diff --git a/repo/installer.go b/repo/installer.go
index 9d7d722..433dca2 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -92,9 +92,13 @@
msg.Info("Downloading dependencies. Please wait...")
- LazyConcurrentUpdate(newConf.Imports, i, newConf)
- LazyConcurrentUpdate(newConf.DevImports, i, newConf)
- return newConf, nil
+ err := LazyConcurrentUpdate(newConf.Imports, i, newConf)
+ if err != nil {
+ return newConf, err
+ }
+ err = LazyConcurrentUpdate(newConf.DevImports, i, newConf)
+
+ return newConf, err
}
// Checkout reads the config file and checks out all dependencies mentioned there.