Merge pull request #438 from klnusbaum/check_basedir_symlink
diff --git a/.gitignore b/.gitignore
index 3b111ff..2bd0e51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@
*.sublime-workspace
dist/
.DS_Store
+.idea
diff --git a/README.md b/README.md
index ec6f5af..566bd30 100644
--- a/README.md
+++ b/README.md
@@ -95,8 +95,9 @@
1. Clone this repository into `$GOPATH/src/github.com/Masterminds/glide` and
change directory into it
-2. Ensure that the environment variable GO15VENDOREXPERIMENT is set, for
- example by running `export GO15VENDOREXPERIMENT=1`
+2. If you are using Go 1.5 ensure the environment variable GO15VENDOREXPERIMENT is set, for
+ example by running `export GO15VENDOREXPERIMENT=1`. In Go 1.6 it is enabled by default and
+ in Go 1.7 it is always enabled without the ability to turn it off.
3. Run `make build`
This will leave you with `./glide`, which you can put in your `$PATH` if
diff --git a/action/cache.go b/action/cache.go
new file mode 100644
index 0000000..9896f21
--- /dev/null
+++ b/action/cache.go
@@ -0,0 +1,29 @@
+package action
+
+import (
+ "os"
+
+ "github.com/Masterminds/glide/cache"
+ "github.com/Masterminds/glide/msg"
+)
+
+// CacheClear clears the Glide cache
+func CacheClear() {
+ l, err := cache.Location()
+ if err != nil {
+ msg.Die("Unable to clear the cache: %s", err)
+ }
+
+ err = os.RemoveAll(l)
+ if err != nil {
+ msg.Die("Unable to clear the cache: %s", err)
+ }
+
+ cache.SetupReset()
+ err = cache.Setup()
+ if err != nil {
+ msg.Die("Unable to clear the cache: %s", err)
+ }
+
+ msg.Info("Glide cache has been cleared.")
+}
diff --git a/action/create.go b/action/create.go
index b55a116..b66257c 100644
--- a/action/create.go
+++ b/action/create.go
@@ -73,29 +73,7 @@
// Attempt to import from other package managers.
if !skipImport {
- msg.Info("Attempting to import from other package managers (use --skip-import to skip)")
- deps := []*cfg.Dependency{}
- absBase, err := filepath.Abs(base)
- if err != nil {
- msg.Die("Failed to resolve location of %s: %s", base, err)
- }
-
- if d, ok := guessImportGodep(absBase); ok {
- msg.Info("Importing Godep configuration")
- msg.Warn("Godep uses commit id versions. Consider using Semantic Versions with Glide")
- deps = d
- } else if d, ok := guessImportGPM(absBase); ok {
- msg.Info("Importing GPM configuration")
- deps = d
- } else if d, ok := guessImportGB(absBase); ok {
- msg.Info("Importing GB configuration")
- deps = d
- }
-
- for _, i := range deps {
- msg.Info("Found imported reference to %s\n", i.Name)
- config.Imports = append(config.Imports, i)
- }
+ guessImportDeps(base, config)
}
// Resolve dependencies by looking at the tree.
@@ -147,6 +125,32 @@
return config
}
+func guessImportDeps(base string, config *cfg.Config) {
+ msg.Info("Attempting to import from other package managers (use --skip-import to skip)")
+ deps := []*cfg.Dependency{}
+ absBase, err := filepath.Abs(base)
+ if err != nil {
+ msg.Die("Failed to resolve location of %s: %s", base, err)
+ }
+
+ if d, ok := guessImportGodep(absBase); ok {
+ msg.Info("Importing Godep configuration")
+ msg.Warn("Godep uses commit id versions. Consider using Semantic Versions with Glide")
+ deps = d
+ } else if d, ok := guessImportGPM(absBase); ok {
+ msg.Info("Importing GPM configuration")
+ deps = d
+ } else if d, ok := guessImportGB(absBase); ok {
+ msg.Info("Importing GB configuration")
+ deps = d
+ }
+
+ for _, i := range deps {
+ msg.Info("Found imported reference to %s\n", i.Name)
+ config.Imports = append(config.Imports, i)
+ }
+}
+
func guessImportGodep(dir string) ([]*cfg.Dependency, bool) {
d, err := godep.Parse(dir)
if err != nil || len(d) == 0 {
diff --git a/action/ensure.go b/action/ensure.go
index 041cfce..2f5f5af 100644
--- a/action/ensure.go
+++ b/action/ensure.go
@@ -59,11 +59,6 @@
return conf
}
-// EnsureCacheDir ensures the existence of the cache directory
-func EnsureCacheDir() {
- msg.Warn("ensure.go: ensureCacheDir is not implemented.")
-}
-
// EnsureGoVendor ensures that the Go version is correct.
func EnsureGoVendor() {
// 6l was removed in 1.5, when vendoring was introduced.
@@ -148,7 +143,6 @@
return ""
}
-
// goExecutable checks for a set environment variable of GLIDE_GO_EXECUTABLE
// for the go executable name. The Google App Engine SDK ships with a python
// wrapper called goapp
diff --git a/action/get.go b/action/get.go
index 5665ef6..58384de 100644
--- a/action/get.go
+++ b/action/get.go
@@ -5,6 +5,7 @@
"path/filepath"
"strings"
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/godep"
"github.com/Masterminds/glide/msg"
@@ -17,6 +18,10 @@
//
// This includes resolving dependency resolution and re-generating the lock file.
func Get(names []string, installer *repo.Installer, insecure, skipRecursive, strip, stripVendor bool) {
+ if installer.UseCache {
+ cache.SystemLock()
+ }
+
base := gpath.Basepath()
EnsureGopath()
EnsureVendorDir()
diff --git a/action/init.go b/action/init.go
index b0c29da..2094902 100644
--- a/action/init.go
+++ b/action/init.go
@@ -7,5 +7,5 @@
// Init initializes the action subsystem for handling one or more subesequent actions.
func Init(yaml, home string) {
gpath.GlideFile = yaml
- gpath.HomeDir = home
+ gpath.SetHome(home)
}
diff --git a/action/install.go b/action/install.go
index 3ae39c0..25a5f08 100644
--- a/action/install.go
+++ b/action/install.go
@@ -4,6 +4,7 @@
"io/ioutil"
"path/filepath"
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/dependency"
"github.com/Masterminds/glide/msg"
@@ -13,6 +14,10 @@
// Install installs a vendor directory based on an existing Glide configuration.
func Install(installer *repo.Installer, strip, stripVendor bool) {
+ if installer.UseCache {
+ cache.SystemLock()
+ }
+
base := "."
// Ensure GOPATH
EnsureGopath()
diff --git a/action/update.go b/action/update.go
index defaf07..23d3f4f 100644
--- a/action/update.go
+++ b/action/update.go
@@ -4,6 +4,7 @@
"io/ioutil"
"path/filepath"
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/dependency"
"github.com/Masterminds/glide/godep"
@@ -14,6 +15,10 @@
// Update updates repos and the lock file from the main glide yaml.
func Update(installer *repo.Installer, skipRecursive, strip, stripVendor bool) {
+ if installer.UseCache {
+ cache.SystemLock()
+ }
+
base := "."
EnsureGopath()
EnsureVendorDir()
diff --git a/cache/cache.go b/cache/cache.go
new file mode 100644
index 0000000..5a8538d
--- /dev/null
+++ b/cache/cache.go
@@ -0,0 +1,231 @@
+// Package cache provides an interface for interfacing with the Glide local cache
+//
+// Glide has a local cache of metadata and repositories similar to the GOPATH.
+// To store the cache Glide creates a .glide directory with a cache subdirectory.
+// This is usually in the users home directory unless there is no accessible
+// home directory in which case the .glide directory is in the root of the
+// repository.
+//
+// To get the cache location use the `cache.Location()` function. This will
+// return the proper base location in your environment.
+//
+// Within the cache directory there are two subdirectories. They are the src
+// and info directories. The src directory contains version control checkouts
+// of the packages. The info direcory contains metadata. The metadata maps to
+// the RepoInfo struct. Both stores are happed to keys.
+//
+// Using the `cache.Key()` function you can get a key for a repo. Pass in a
+// location such as `https://github.com/foo/bar` or `git@example.com:foo.git`
+// and a key will be returned that can be used for caching operations.
+//
+// Note, the caching is based on repo rather than package. This is important
+// for a couple reasons.
+//
+// 1. Forks or package replacements are supported in Glide. Where a different
+// repo maps to a package.
+// 2. Permissions enable different access. For example `https://example.com/foo.git`
+// and `git@example.com:foo.git` may have access to different branches or tags.
+package cache
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/Masterminds/glide/msg"
+ gpath "github.com/Masterminds/glide/path"
+)
+
+// Enabled sets if the cache is globally enabled. Defaults to true.
+var Enabled = true
+
+// ErrCacheDisabled is returned with the cache is disabled.
+var ErrCacheDisabled = errors.New("Cache disabled")
+
+var isSetup bool
+
+var setupMutex sync.Mutex
+
+// Setup creates the cache location.
+func Setup() error {
+ setupMutex.Lock()
+ defer setupMutex.Unlock()
+
+ if isSetup {
+ return nil
+ }
+ msg.Debug("Setting up the cache directory")
+ pths := []string{
+ "cache",
+ filepath.Join("cache", "src"),
+ filepath.Join("cache", "info"),
+ }
+
+ for _, l := range pths {
+ err := os.MkdirAll(filepath.Join(gpath.Home(), l), 0755)
+ if err != nil {
+ return err
+ }
+ }
+
+ isSetup = true
+ return nil
+}
+
+// SetupReset resets if setup has been completed. The next time setup is run
+// it will attempt a full setup.
+func SetupReset() {
+ isSetup = false
+}
+
+// Location returns the location of the cache.
+func Location() (string, error) {
+ p := filepath.Join(gpath.Home(), "cache")
+ err := Setup()
+
+ return p, err
+}
+
+// scpSyntaxRe matches the SCP-like addresses used to access repos over SSH.
+var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`)
+
+// Key generates a cache key based on a url or scp string. The key is file
+// system safe.
+func Key(repo string) (string, error) {
+
+ var u *url.URL
+ var err error
+ var strip bool
+ if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil {
+ // Match SCP-like syntax and convert it to a URL.
+ // Eg, "git@github.com:user/repo" becomes
+ // "ssh://git@github.com/user/repo".
+ u = &url.URL{
+ Scheme: "ssh",
+ User: url.User(m[1]),
+ Host: m[2],
+ Path: "/" + m[3],
+ }
+ strip = true
+ } else {
+ u, err = url.Parse(repo)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ if strip {
+ u.Scheme = ""
+ }
+
+ var key string
+ if u.Scheme != "" {
+ key = u.Scheme + "-"
+ }
+ if u.User != nil && u.User.Username() != "" {
+ key = key + u.User.Username() + "-"
+ }
+ key = key + u.Host
+ if u.Path != "" {
+ key = key + strings.Replace(u.Path, "/", "-", -1)
+ }
+
+ key = strings.Replace(key, ":", "-", -1)
+
+ return key, nil
+}
+
+// RepoInfo holds information about a repo.
+type RepoInfo struct {
+ DefaultBranch string `json:"default-branch"`
+ LastUpdate string `json:"last-update"`
+}
+
+// SaveRepoData stores data about a repo in the Glide cache
+func SaveRepoData(key string, data RepoInfo) error {
+ if !Enabled {
+ return ErrCacheDisabled
+ }
+ location, err := Location()
+ if err != nil {
+ return err
+ }
+ data.LastUpdate = time.Now().String()
+ d, err := json.Marshal(data)
+ if err != nil {
+ return err
+ }
+
+ pp := filepath.Join(location, "info")
+ err = os.MkdirAll(pp, 0755)
+ if err != nil {
+ return err
+ }
+
+ p := filepath.Join(pp, key+".json")
+ f, err := os.Create(p)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ _, err = f.Write(d)
+ return err
+}
+
+// RepoData retrieves cached information about a repo.
+func RepoData(key string) (*RepoInfo, error) {
+ if !Enabled {
+ return &RepoInfo{}, ErrCacheDisabled
+ }
+ location, err := Location()
+ if err != nil {
+ return &RepoInfo{}, err
+ }
+ c := &RepoInfo{}
+ p := filepath.Join(location, "info", key+".json")
+ f, err := ioutil.ReadFile(p)
+ if err != nil {
+ return &RepoInfo{}, err
+ }
+ err = json.Unmarshal(f, c)
+ if err != nil {
+ return &RepoInfo{}, err
+ }
+ return c, nil
+}
+
+var lockSync sync.Mutex
+
+var lockData = make(map[string]*sync.Mutex)
+
+// Lock locks a particular key name
+func Lock(name string) {
+ lockSync.Lock()
+ m, ok := lockData[name]
+ if !ok {
+ m = &sync.Mutex{}
+ lockData[name] = m
+ }
+ lockSync.Unlock()
+ msg.Debug("Locking %s", name)
+ m.Lock()
+}
+
+// Unlock unlocks a particular key name
+func Unlock(name string) {
+ msg.Debug("Unlocking %s", name)
+ lockSync.Lock()
+ if m, ok := lockData[name]; ok {
+ m.Unlock()
+ }
+
+ lockSync.Unlock()
+}
diff --git a/cache/cache_test.go b/cache/cache_test.go
new file mode 100644
index 0000000..ce6f5f5
--- /dev/null
+++ b/cache/cache_test.go
@@ -0,0 +1,22 @@
+package cache
+
+import "testing"
+
+func TestKey(t *testing.T) {
+ tests := map[string]string{
+ "https://github.com/foo/bar": "https-github.com-foo-bar",
+ "git@github.com:foo/bar": "git-github.com-foo-bar",
+ "https://github.com:123/foo/bar": "https-github.com-123-foo-bar",
+ }
+
+ for k, v := range tests {
+ key, err := Key(k)
+ if err != nil {
+ t.Errorf("Cache key generation err: %s", err)
+ continue
+ }
+ if key != v {
+ t.Errorf("Expected cache key %s for %s but got %s", v, k, key)
+ }
+ }
+}
diff --git a/cache/global_lock.go b/cache/global_lock.go
new file mode 100644
index 0000000..52cf841
--- /dev/null
+++ b/cache/global_lock.go
@@ -0,0 +1,111 @@
+package cache
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/Masterminds/glide/msg"
+ gpath "github.com/Masterminds/glide/path"
+)
+
+var isStarted bool
+
+// SystemLock starts a system rather than application lock. This way multiple
+// app instances don't cause race conditions when working in the cache.
+func SystemLock() error {
+ if isStarted {
+ return nil
+ }
+ err := waitOnLock()
+ if err != nil {
+ return err
+ }
+ err = startLock()
+ isStarted = true
+ return err
+}
+
+// SystemUnlock removes the system wide Glide cache lock.
+func SystemUnlock() {
+ lockdone <- struct{}{}
+ os.Remove(lockFileName)
+}
+
+var lockdone = make(chan struct{}, 1)
+
+type lockdata struct {
+ Comment string `json:"comment"`
+ Pid int `json:"pid"`
+ Time string `json:"time"`
+}
+
+var lockFileName = filepath.Join(gpath.Home(), "lock.json")
+
+// Write a lock for now.
+func writeLock() error {
+ ld := &lockdata{
+ Comment: "File managed by Glide (https://glide.sh)",
+ Pid: os.Getpid(),
+ Time: time.Now().Format(time.RFC3339Nano),
+ }
+
+ out, err := json.Marshal(ld)
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(lockFileName, out, 0755)
+ return err
+}
+
+func startLock() error {
+ err := writeLock()
+ if err != nil {
+ return err
+ }
+
+ go func() {
+ for {
+ select {
+ case <-lockdone:
+ return
+ default:
+ time.Sleep(10 * time.Second)
+ err := writeLock()
+ if err != nil {
+ msg.Die("Error using Glide lock: %s", err)
+ }
+ }
+ }
+ }()
+
+ return nil
+}
+
+func waitOnLock() error {
+ var announced bool
+ for {
+ fi, err := os.Stat(lockFileName)
+ if err != nil && os.IsNotExist(err) {
+ return nil
+ } else if err != nil {
+ return err
+ }
+
+ diff := time.Now().Sub(fi.ModTime())
+ if diff.Seconds() > 15 {
+ return nil
+ }
+
+ if !announced {
+ announced = true
+ msg.Info("Waiting on Glide global cache access")
+ }
+
+ // Check on the lock file every 15 seconds.
+ // TODO(mattfarina): should this be a different length?
+ time.Sleep(time.Second)
+ }
+}
diff --git a/dependency/resolver.go b/dependency/resolver.go
index a9fa5ae..300ca4d 100644
--- a/dependency/resolver.go
+++ b/dependency/resolver.go
@@ -901,10 +901,11 @@
// https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath
info.Loc = LocAppengine
r.findCache[name] = info
- } else if name == "context" {
- // context is a package being added to the Go 1.7 standard library. Some
- // packages, such as golang.org/x/net are importing it with build flags
- // in files for go1.7. Need to detect this and handle it.
+ } else if name == "context" || name == "net/http/httptrace" {
+ // context and net/http/httptrace are packages being added to
+ // the Go 1.7 standard library. Some packages, such as golang.org/x/net
+ // are importing it with build flags in files for go1.7. Need to detect
+ // this and handle it.
info.Loc = LocGoroot
r.findCache[name] = info
}
diff --git a/docs/example-glide.yaml b/docs/example-glide.yaml
index 556df8b..241787f 100644
--- a/docs/example-glide.yaml
+++ b/docs/example-glide.yaml
@@ -38,21 +38,6 @@
vcs: git
repo: git@github.com:technosophos/goamz.git
- # This will fetch the entire technosophos/luge package, but only
- # 'go build' the technosophos/luge/foo and technosophos/luge/bar packages.
- - package: github.com/technosophos/luge
- vcs: git
- subpackages:
- - foo
- - bar
- # Shell globs are also supported:
- - pa?e
- - [wc]*
-
- # Use '...' to tell glide to build ALL of the subpackages.
- - package: github.com/Masterminds/blarg
- subpackages: ...
-
- package: bzr.example.com/foo/bar/trunk
vcs: bzr
repo: bzr://bzr.example.com/foo/bar/trunk
diff --git a/docs/glide.yaml.md b/docs/glide.yaml.md
index 2216bee..2943853 100644
--- a/docs/glide.yaml.md
+++ b/docs/glide.yaml.md
@@ -41,6 +41,7 @@
- `version`: A semantic version, semantic version range, branch, tag, or commit id to use. For more information see the [versioning documentation](versions.md).
- `repo`: If the package name isn't the repo location or this is a private repository it can go here. The package will be checked out from the repo and put where the package name specifies. This allows using forks.
- `vcs`: A VCS to use such as git, hg, bzr, or svn. This is only needed when the type cannot be detected from the name. For example, a repo ending in .git or on GitHub can be detected to be Git. For a repo on Bitbucket we can contact the API to discover the type.
+ - `subpackages`: A record of packages being used within a repository. This does not include all packages within a repository but rather those being used.
- `os`: A list of operating systems used for filtering. If set it will compare the current runtime OS to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOOS` environment variable.
- `arch`: A list of architectures used for filtering. If set it will compare the current runtime architecture to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOARCH` environment variable.
- `devImport`: A list of development packages. Each package has the same details as those listed under import.
diff --git a/glide.go b/glide.go
index e205796..0e1e355 100644
--- a/glide.go
+++ b/glide.go
@@ -40,6 +40,7 @@
"path/filepath"
"github.com/Masterminds/glide/action"
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
"github.com/Masterminds/glide/repo"
@@ -49,7 +50,6 @@
"fmt"
"os"
- "os/user"
)
var version = "0.11.0-dev"
@@ -97,7 +97,7 @@
},
cli.StringFlag{
Name: "home",
- Value: defaultGlideDir(),
+ Value: gpath.Home(),
Usage: "The location of Glide files",
EnvVar: "GLIDE_HOME",
},
@@ -111,6 +111,7 @@
action.Plugin(command, os.Args)
}
app.Before = startup
+ app.After = shutdown
app.Commands = commands()
// Detect errors from the Before and After calls and exit on them.
@@ -220,7 +221,7 @@
},
cli.BoolFlag{
Name: "strip-vcs, s",
- Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.",
+ Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
},
cli.BoolFlag{
Name: "strip-vendor, v",
@@ -426,7 +427,7 @@
},
cli.BoolFlag{
Name: "strip-vcs, s",
- Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.",
+ Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
},
cli.BoolFlag{
Name: "strip-vendor, v",
@@ -444,8 +445,8 @@
installer.UseGopath = c.Bool("use-gopath")
installer.UseCacheGopath = c.Bool("cache-gopath")
installer.UpdateVendored = c.Bool("update-vendored")
- installer.Home = gpath.Home()
- installer.DeleteUnused = c.Bool("deleteOptIn")
+ installer.Home = c.GlobalString("home")
+ installer.DeleteUnused = c.Bool("delete")
action.Install(installer, c.Bool("strip-vcs"), c.Bool("strip-vendor"))
},
@@ -533,7 +534,7 @@
},
cli.BoolFlag{
Name: "strip-vcs, s",
- Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.",
+ Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
},
cli.BoolFlag{
Name: "strip-vendor, v",
@@ -557,8 +558,8 @@
installer.UseCacheGopath = c.Bool("cache-gopath")
installer.UpdateVendored = c.Bool("update-vendored")
installer.ResolveAllFiles = c.Bool("all-dependencies")
- installer.Home = gpath.Home()
- installer.DeleteUnused = c.Bool("deleteOptIn")
+ installer.Home = c.GlobalString("home")
+ installer.DeleteUnused = c.Bool("delete")
action.Update(installer, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor"))
},
@@ -640,6 +641,14 @@
},
},
{
+ Name: "cache-clear",
+ ShortName: "cc",
+ Usage: "Clears the Glide cache.",
+ Action: func(c *cli.Context) {
+ action.CacheClear()
+ },
+ },
+ {
Name: "about",
Usage: "Learn about Glide",
Action: func(c *cli.Context) {
@@ -649,14 +658,6 @@
}
}
-func defaultGlideDir() string {
- c, err := user.Current()
- if err != nil {
- return ""
- }
- return filepath.Join(c.HomeDir, ".glide")
-}
-
// startup sets up the base environment.
//
// It does not assume the presence of a Glide.yaml file or vendor/ directory,
@@ -670,6 +671,11 @@
return nil
}
+func shutdown(c *cli.Context) error {
+ cache.SystemUnlock()
+ return nil
+}
+
// Get the path to the glide.yaml file.
//
// This returns the name of the path, even if the file does not exist. The value
diff --git a/msg/msg.go b/msg/msg.go
index b2a7bfe..af49ecf 100644
--- a/msg/msg.go
+++ b/msg/msg.go
@@ -61,7 +61,7 @@
if m.Quiet {
return
}
- prefix := m.Color(Green, "[INFO] ")
+ prefix := m.Color(Green, "[INFO]\t")
m.Msg(prefix+msg, args...)
}
@@ -75,7 +75,7 @@
if m.Quiet || !m.IsDebugging {
return
}
- prefix := "[DEBUG] "
+ prefix := "[DEBUG]\t"
Msg(prefix+msg, args...)
}
@@ -86,7 +86,7 @@
// Warn logs a warning
func (m *Messenger) Warn(msg string, args ...interface{}) {
- prefix := m.Color(Yellow, "[WARN] ")
+ prefix := m.Color(Yellow, "[WARN]\t")
m.Msg(prefix+msg, args...)
}
@@ -97,7 +97,7 @@
// Err logs an error.
func (m *Messenger) Err(msg string, args ...interface{}) {
- prefix := m.Color(Red, "[ERROR] ")
+ prefix := m.Color(Red, "[ERROR]\t")
m.Msg(prefix+msg, args...)
m.hasErrored = true
}
diff --git a/path/path.go b/path/path.go
index 9e7162f..0b19442 100644
--- a/path/path.go
+++ b/path/path.go
@@ -8,6 +8,7 @@
"fmt"
"io"
"os"
+ "os/user"
"path/filepath"
"strings"
)
@@ -20,10 +21,8 @@
// As of Go 1.5, this is always vendor.
var VendorDir = "vendor"
-// HomeDir is the home directory for Glide.
-//
-// HomeDir is where cache files and other configuration data are stored.
-var HomeDir = "$HOME/.glide"
+// Cache the location of the homedirectory.
+var homeDir = ""
// GlideFile is the name of the Glide file.
//
@@ -38,12 +37,29 @@
//
// This normalizes to an absolute path, and passes through os.ExpandEnv.
func Home() string {
- h := os.ExpandEnv(HomeDir)
- var err error
- if h, err = filepath.Abs(HomeDir); err != nil {
- return HomeDir
+ if homeDir != "" {
+ return homeDir
}
- return h
+
+ // Initialize the default user.
+ u, err := user.Current()
+ if err == nil && u.HomeDir != "" {
+ homeDir = filepath.Join(u.HomeDir, ".glide")
+ } else {
+ cwd, err := os.Getwd()
+ if err == nil {
+ homeDir = filepath.Join(cwd, ".glide")
+ } else {
+ homeDir = ".glide"
+ }
+ }
+
+ return homeDir
+}
+
+// SetHome sets the home directory for Glide.
+func SetHome(h string) {
+ homeDir = h
}
// Vendor calculates the path to the vendor directory.
diff --git a/repo/cache.go b/repo/cache.go
deleted file mode 100644
index babd1c2..0000000
--- a/repo/cache.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package repo
-
-import (
- "encoding/json"
- "errors"
- "io/ioutil"
- "net/url"
- "os"
- "path/filepath"
- "regexp"
- "strings"
- "time"
- //"github.com/Masterminds/glide/msg"
-)
-
-var cacheEnabled = true
-
-var errCacheDisabled = errors.New("Cache disabled")
-
-// EnsureCacheDir Creates the $HOME/.glide/cache directory (unless home is
-// specified to be different) if it does not exist.
-/*
-func EnsureCacheDir(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
- home := p.Get("home", "").(string)
- if home == "" {
- cacheEnabled = false
- msg.Warn("Unable to locate home directory")
- return false, nil
- }
- err := os.MkdirAll(filepath.Join(home, "cache", "info"), os.ModeDir|os.ModePerm)
- if err != nil {
- cacheEnabled = false
- Warn("Error creating Glide directory %s", home)
- }
- return false, nil
-}
-*/
-
-// scpSyntaxRe matches the SCP-like addresses used to access repos over SSH.
-var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`)
-
-// Pass in a repo location and get a cache key from it.
-func cacheCreateKey(repo string) (string, error) {
-
- var u *url.URL
- var err error
- var strip bool
- if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil {
- // Match SCP-like syntax and convert it to a URL.
- // Eg, "git@github.com:user/repo" becomes
- // "ssh://git@github.com/user/repo".
- u = &url.URL{
- Scheme: "ssh",
- User: url.User(m[1]),
- Host: m[2],
- Path: "/" + m[3],
- }
- strip = true
- } else {
- u, err = url.Parse(repo)
- if err != nil {
- return "", err
- }
- }
-
- if strip {
- u.Scheme = ""
- }
-
- var key string
- if u.Scheme != "" {
- key = u.Scheme + "-"
- }
- if u.User != nil && u.User.Username() != "" {
- key = key + u.User.Username() + "-"
- }
- key = key + u.Host
- if u.Path != "" {
- key = key + strings.Replace(u.Path, "/", "-", -1)
- }
-
- key = strings.Replace(key, ":", "-", -1)
-
- return key, nil
-}
-
-type cacheRepoInfo struct {
- DefaultBranch string `json:"default-branch"`
- LastUpdate string `json:"last-update"`
-}
-
-func saveCacheRepoData(key string, data cacheRepoInfo, location string) error {
- if !cacheEnabled {
- return errCacheDisabled
- }
- data.LastUpdate = time.Now().String()
- d, err := json.Marshal(data)
- if err != nil {
- return err
- }
-
- p := filepath.Join(location, "cache", "info", key+".json")
- f, err := os.Create(p)
- if err != nil {
- return err
- }
- defer f.Close()
-
- _, err = f.Write(d)
- return err
-}
-
-func cacheRepoData(key, location string) (*cacheRepoInfo, error) {
- if !cacheEnabled {
- return &cacheRepoInfo{}, errCacheDisabled
- }
- c := &cacheRepoInfo{}
- p := filepath.Join(location, "cache", "info", key+".json")
- f, err := ioutil.ReadFile(p)
- if err != nil {
- return &cacheRepoInfo{}, err
- }
- err = json.Unmarshal(f, c)
- if err != nil {
- return &cacheRepoInfo{}, err
- }
- return c, nil
-}
diff --git a/repo/cache_test.go b/repo/cache_test.go
deleted file mode 100644
index d5e1c1d..0000000
--- a/repo/cache_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package repo
-
-import "testing"
-
-func TestCacheCreateKey(t *testing.T) {
- tests := map[string]string{
- "https://github.com/foo/bar": "https-github.com-foo-bar",
- "git@github.com:foo/bar": "git-github.com-foo-bar",
- }
-
- for k, v := range tests {
- key, err := cacheCreateKey(k)
- if err != nil {
- t.Errorf("Cache key generation err: %s", err)
- continue
- }
- if key != v {
- t.Errorf("Expected cache key %s for %s but got %s", v, k, key)
- }
- }
-}
diff --git a/repo/installer.go b/repo/installer.go
index e8e4bf6..1ae1901 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -8,6 +8,7 @@
"sync"
"time"
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/dependency"
"github.com/Masterminds/glide/importer"
@@ -255,6 +256,17 @@
for {
select {
case dep := <-ch:
+ var loc string
+ if dep.Repository != "" {
+ loc = dep.Repository
+ } else {
+ loc = "https://" + dep.Name
+ }
+ key, err := cache.Key(loc)
+ if err != nil {
+ msg.Die(err.Error())
+ }
+ cache.Lock(key)
dest := filepath.Join(i.VendorPath(), dep.Name)
if err := VcsUpdate(dep, dest, i.Home, i.UseCache, i.UseCacheGopath, i.UseGopath, i.Force, i.UpdateVendored, i.Updated); err != nil {
msg.Err("Update failed for %s: %s\n", dep.Name, err)
@@ -268,6 +280,7 @@
}
lock.Unlock()
}
+ cache.Unlock(key)
wg.Done()
case <-done:
return
diff --git a/repo/vcs.go b/repo/vcs.go
index 16c4382..b2d471f 100644
--- a/repo/vcs.go
+++ b/repo/vcs.go
@@ -7,12 +7,12 @@
"net/http"
"net/url"
"os"
- "os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
+ cp "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
@@ -345,12 +345,12 @@
} else {
loc = "https://" + dep.Name
}
- key, err := cacheCreateKey(loc)
+ key, err := cp.Key(loc)
if err == nil {
msg.Debug("Saving default branch for %s", repo.Remote())
- c := cacheRepoInfo{DefaultBranch: branch}
- err = saveCacheRepoData(key, c, home)
- if msg.Default.IsDebugging && err == errCacheDisabled {
+ c := cp.RepoInfo{DefaultBranch: branch}
+ err = cp.SaveRepoData(key, c)
+ if msg.Default.IsDebugging && err == cp.ErrCacheDisabled {
msg.Debug("Unable to cache default branch because caching is disabled")
}
}
@@ -376,9 +376,13 @@
} else {
loc = "https://" + dep.Name
}
- key, err := cacheCreateKey(loc)
+ key, err := cp.Key(loc)
if err == nil {
- d := filepath.Join(home, "cache", "src", key)
+ location, err := cp.Location()
+ if err != nil {
+ return err
+ }
+ d := filepath.Join(location, "src", key)
repo, err := dep.GetRepo(d)
if err != nil {
@@ -400,12 +404,12 @@
} else {
loc = "https://" + dep.Name
}
- key, err := cacheCreateKey(loc)
+ key, err := cp.Key(loc)
if err == nil {
msg.Debug("Saving default branch for %s", repo.Remote())
- c := cacheRepoInfo{DefaultBranch: branch}
- err = saveCacheRepoData(key, c, home)
- if err == errCacheDisabled {
+ c := cp.RepoInfo{DefaultBranch: branch}
+ err = cp.SaveRepoData(key, c)
+ if err == cp.ErrCacheDisabled {
msg.Debug("Unable to cache default branch because caching is disabled")
} else if err != nil {
msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
@@ -451,12 +455,12 @@
} else {
loc = "https://" + dep.Name
}
- key, err := cacheCreateKey(loc)
+ key, err := cp.Key(loc)
if err == nil {
msg.Debug("Saving default branch for %s", repo.Remote())
- c := cacheRepoInfo{DefaultBranch: branch}
- err = saveCacheRepoData(key, c, home)
- if err == errCacheDisabled {
+ c := cp.RepoInfo{DefaultBranch: branch}
+ err = cp.SaveRepoData(key, c)
+ if err == cp.ErrCacheDisabled {
msg.Debug("Unable to cache default branch because caching is disabled")
} else if err != nil {
msg.Debug("Error saving %s to cache - Error: %s", repo.Remote(), err)
@@ -528,10 +532,10 @@
}
// Check the cache for a value.
- key, kerr := cacheCreateKey(repo.Remote())
- var d cacheRepoInfo
+ key, kerr := cp.Key(repo.Remote())
+ var d cp.RepoInfo
if kerr == nil {
- d, err := cacheRepoData(key, home)
+ d, err := cp.RepoData(key)
if err == nil {
if d.DefaultBranch != "" {
return d.DefaultBranch
@@ -579,8 +583,8 @@
db := gh["default_branch"].(string)
if kerr == nil {
d.DefaultBranch = db
- err := saveCacheRepoData(key, d, home)
- if err == errCacheDisabled {
+ err := cp.SaveRepoData(key, d)
+ if err == cp.ErrCacheDisabled {
msg.Debug("Unable to cache default branch because caching is disabled")
} else if err != nil {
msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
@@ -613,8 +617,8 @@
db := bb["name"].(string)
if kerr == nil {
d.DefaultBranch = db
- err := saveCacheRepoData(key, d, home)
- if err == errCacheDisabled {
+ err := cp.SaveRepoData(key, d)
+ if err == cp.ErrCacheDisabled {
msg.Debug("Unable to cache default branch because caching is disabled")
} else if err != nil {
msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
@@ -627,6 +631,8 @@
}
// From a local repo find out the current branch name if there is one.
+// Note, this should only be used right after a fresh clone to get accurate
+// information.
func findCurrentBranch(repo v.Repo) string {
msg.Debug("Attempting to find current branch for %s", repo.Remote())
// Svn and Bzr don't have default branches.
@@ -634,28 +640,13 @@
return ""
}
- if repo.Vcs() == v.Git {
- c := exec.Command("git", "symbolic-ref", "--short", "HEAD")
- c.Dir = repo.LocalPath()
- c.Env = envForDir(c.Dir)
- out, err := c.CombinedOutput()
+ if repo.Vcs() == v.Git || repo.Vcs() == v.Hg {
+ ver, err := repo.Current()
if err != nil {
msg.Debug("Unable to find current branch for %s, error: %s", repo.Remote(), err)
return ""
}
- return strings.TrimSpace(string(out))
- }
-
- if repo.Vcs() == v.Hg {
- c := exec.Command("hg", "branch")
- c.Dir = repo.LocalPath()
- c.Env = envForDir(c.Dir)
- out, err := c.CombinedOutput()
- if err != nil {
- msg.Debug("Unable to find current branch for %s, error: %s", repo.Remote(), err)
- return ""
- }
- return strings.TrimSpace(string(out))
+ return ver
}
return ""
diff --git a/tree/tree.go b/tree/tree.go
index 1d9def2..5b19336 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -172,10 +172,11 @@
// where Google products are playing with each other.
// https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath
info.Loc = dependency.LocAppengine
- } else if name == "context" {
- // context is a package being added to the Go 1.7 standard library. Some
- // packages, such as golang.org/x/net are importing it with build flags
- // in files for go1.7. Need to detect this and handle it.
+ } else if name == "context" || name == "net/http/httptrace" {
+ // context and net/http/httptrace are packages being added to
+ // the Go 1.7 standard library. Some packages, such as golang.org/x/net
+ // are importing it with build flags in files for go1.7. Need to detect
+ // this and handle it.
info.Loc = dependency.LocGoroot
}