github.com/avvmoto/glide@v0.12.3/cache/cache.go (about) 1 // Package cache provides an interface for interfacing with the Glide local cache 2 // 3 // Glide has a local cache of metadata and repositories similar to the GOPATH. 4 // To store the cache Glide creates a .glide directory with a cache subdirectory. 5 // This is usually in the users home directory unless there is no accessible 6 // home directory in which case the .glide directory is in the root of the 7 // repository. 8 // 9 // To get the cache location use the `cache.Location()` function. This will 10 // return the proper base location in your environment. 11 // 12 // Within the cache directory there are two subdirectories. They are the src 13 // and info directories. The src directory contains version control checkouts 14 // of the packages. The info direcory contains metadata. The metadata maps to 15 // the RepoInfo struct. Both stores are happed to keys. 16 // 17 // Using the `cache.Key()` function you can get a key for a repo. Pass in a 18 // location such as `https://github.com/foo/bar` or `git@example.com:foo.git` 19 // and a key will be returned that can be used for caching operations. 20 // 21 // Note, the caching is based on repo rather than package. This is important 22 // for a couple reasons. 23 // 24 // 1. Forks or package replacements are supported in Glide. Where a different 25 // repo maps to a package. 26 // 2. Permissions enable different access. For example `https://example.com/foo.git` 27 // and `git@example.com:foo.git` may have access to different branches or tags. 28 package cache 29 30 import ( 31 "encoding/json" 32 "errors" 33 "io/ioutil" 34 "net/url" 35 "os" 36 "path/filepath" 37 "regexp" 38 "strings" 39 "sync" 40 "time" 41 42 "github.com/Masterminds/glide/msg" 43 gpath "github.com/Masterminds/glide/path" 44 ) 45 46 // Enabled sets if the cache is globally enabled. Defaults to true. 47 var Enabled = true 48 49 // ErrCacheDisabled is returned with the cache is disabled. 50 var ErrCacheDisabled = errors.New("Cache disabled") 51 52 var isSetup bool 53 54 var setupMutex sync.Mutex 55 56 // Setup creates the cache location. 57 func Setup() { 58 setupMutex.Lock() 59 defer setupMutex.Unlock() 60 61 if isSetup { 62 return 63 } 64 msg.Debug("Setting up the cache directory") 65 pths := []string{ 66 "cache", 67 filepath.Join("cache", "src"), 68 filepath.Join("cache", "info"), 69 } 70 71 for _, l := range pths { 72 err := os.MkdirAll(filepath.Join(gpath.Home(), l), 0755) 73 if err != nil { 74 msg.Die("Cache directory unavailable: %s", err) 75 } 76 } 77 78 isSetup = true 79 } 80 81 // SetupReset resets if setup has been completed. The next time setup is run 82 // it will attempt a full setup. 83 func SetupReset() { 84 isSetup = false 85 } 86 87 // Location returns the location of the cache. 88 func Location() string { 89 p := filepath.Join(gpath.Home(), "cache") 90 Setup() 91 92 return p 93 } 94 95 // scpSyntaxRe matches the SCP-like addresses used to access repos over SSH. 96 var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) 97 98 // Key generates a cache key based on a url or scp string. The key is file 99 // system safe. 100 func Key(repo string) (string, error) { 101 102 var u *url.URL 103 var err error 104 var strip bool 105 if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil { 106 // Match SCP-like syntax and convert it to a URL. 107 // Eg, "git@github.com:user/repo" becomes 108 // "ssh://git@github.com/user/repo". 109 u = &url.URL{ 110 Scheme: "ssh", 111 User: url.User(m[1]), 112 Host: m[2], 113 Path: "/" + m[3], 114 } 115 strip = true 116 } else { 117 u, err = url.Parse(repo) 118 if err != nil { 119 return "", err 120 } 121 } 122 123 if strip { 124 u.Scheme = "" 125 } 126 127 var key string 128 if u.Scheme != "" { 129 key = u.Scheme + "-" 130 } 131 if u.User != nil && u.User.Username() != "" { 132 key = key + u.User.Username() + "-" 133 } 134 key = key + u.Host 135 if u.Path != "" { 136 key = key + strings.Replace(u.Path, "/", "-", -1) 137 } 138 139 key = strings.Replace(key, ":", "-", -1) 140 141 return key, nil 142 } 143 144 // RepoInfo holds information about a repo. 145 type RepoInfo struct { 146 DefaultBranch string `json:"default-branch"` 147 LastUpdate string `json:"last-update"` 148 } 149 150 // SaveRepoData stores data about a repo in the Glide cache 151 func SaveRepoData(key string, data RepoInfo) error { 152 if !Enabled { 153 return ErrCacheDisabled 154 } 155 location := Location() 156 data.LastUpdate = time.Now().String() 157 d, err := json.Marshal(data) 158 if err != nil { 159 return err 160 } 161 162 pp := filepath.Join(location, "info") 163 err = os.MkdirAll(pp, 0755) 164 if err != nil { 165 return err 166 } 167 168 p := filepath.Join(pp, key+".json") 169 f, err := os.Create(p) 170 if err != nil { 171 return err 172 } 173 defer f.Close() 174 175 _, err = f.Write(d) 176 return err 177 } 178 179 // RepoData retrieves cached information about a repo. 180 func RepoData(key string) (*RepoInfo, error) { 181 if !Enabled { 182 return &RepoInfo{}, ErrCacheDisabled 183 } 184 location := Location() 185 c := &RepoInfo{} 186 p := filepath.Join(location, "info", key+".json") 187 f, err := ioutil.ReadFile(p) 188 if err != nil { 189 return &RepoInfo{}, err 190 } 191 err = json.Unmarshal(f, c) 192 if err != nil { 193 return &RepoInfo{}, err 194 } 195 return c, nil 196 } 197 198 var lockSync sync.Mutex 199 200 var lockData = make(map[string]*sync.Mutex) 201 202 // Lock locks a particular key name 203 func Lock(name string) { 204 lockSync.Lock() 205 m, ok := lockData[name] 206 if !ok { 207 m = &sync.Mutex{} 208 lockData[name] = m 209 } 210 lockSync.Unlock() 211 msg.Debug("Locking %s", name) 212 m.Lock() 213 } 214 215 // Unlock unlocks a particular key name 216 func Unlock(name string) { 217 msg.Debug("Unlocking %s", name) 218 lockSync.Lock() 219 if m, ok := lockData[name]; ok { 220 m.Unlock() 221 } 222 223 lockSync.Unlock() 224 }