cache/cache.go   A
last analyzed

Size/Duplication

Total Lines 196
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 119
dl 0
loc 196
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
B cache.Key 0 42 8
A cache.SetupReset 0 2 1
A cache.RepoData 0 16 4
B cache.SaveRepoData 0 26 5
A cache.Unlock 0 8 2
A cache.Location 0 5 1
A cache.Setup 0 22 5
A cache.Lock 0 10 2
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 `[email protected]: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 `[email protected]: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, "[email protected]:user/repo" becomes
108
		// "ssh://[email protected]/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
}
225