| // Package cache provides an interface for interfacing with the Glide local cache |
| 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() |
| } |