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 }