github.com/golang/dep@v0.5.4/gps/maybe_source.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gps 6 7 import ( 8 "context" 9 "fmt" 10 "net/url" 11 "os" 12 "path/filepath" 13 14 "github.com/Masterminds/vcs" 15 ) 16 17 // A maybeSource represents a set of information that, given some 18 // typically-expensive network effort, could be transformed into a proper source. 19 // 20 // Wrapping these up as their own type achieves two goals: 21 // 22 // * Allows control over when deduction logic triggers network activity 23 // * Makes it easy to attempt multiple URLs for a given import path 24 type maybeSource interface { 25 // try tries to set up a source. 26 try(ctx context.Context, cachedir string) (source, error) 27 URL() *url.URL 28 fmt.Stringer 29 } 30 31 type maybeSources []maybeSource 32 33 func (mbs maybeSources) possibleURLs() []*url.URL { 34 urlslice := make([]*url.URL, len(mbs)) 35 for i, mb := range mbs { 36 urlslice[i] = mb.URL() 37 } 38 return urlslice 39 } 40 41 // sourceCachePath returns a url-sanitized source cache dir path. 42 func sourceCachePath(cacheDir, sourceURL string) string { 43 return filepath.Join(cacheDir, "sources", sanitizer.Replace(sourceURL)) 44 } 45 46 type maybeGitSource struct { 47 url *url.URL 48 } 49 50 func (m maybeGitSource) try(ctx context.Context, cachedir string) (source, error) { 51 ustr := m.url.String() 52 path := sourceCachePath(cachedir, ustr) 53 54 r, err := vcs.NewGitRepo(ustr, path) 55 if err != nil { 56 os.RemoveAll(path) 57 r, err = vcs.NewGitRepo(ustr, path) 58 if err != nil { 59 return nil, unwrapVcsErr(err) 60 } 61 } 62 63 return &gitSource{ 64 baseVCSSource: baseVCSSource{ 65 repo: &gitRepo{r}, 66 }, 67 }, nil 68 } 69 70 func (m maybeGitSource) URL() *url.URL { 71 return m.url 72 } 73 74 func (m maybeGitSource) String() string { 75 return fmt.Sprintf("%T: %s", m, ufmt(m.url)) 76 } 77 78 type maybeGopkginSource struct { 79 // the original gopkg.in import path. this is used to create the on-disk 80 // location to avoid duplicate resource management - e.g., if instances of 81 // a gopkg.in project are accessed via different schemes, or if the 82 // underlying github repository is accessed directly. 83 opath string 84 // the actual upstream URL - always github 85 url *url.URL 86 // the major version to apply for filtering 87 major uint64 88 // whether or not the source package is "unstable" 89 unstable bool 90 } 91 92 func (m maybeGopkginSource) try(ctx context.Context, cachedir string) (source, error) { 93 // We don't actually need a fully consistent transform into the on-disk path 94 // - just something that's unique to the particular gopkg.in domain context. 95 // So, it's OK to just dumb-join the scheme with the path. 96 aliasURL := m.url.Scheme + "://" + m.opath 97 path := sourceCachePath(cachedir, aliasURL) 98 ustr := m.url.String() 99 100 r, err := vcs.NewGitRepo(ustr, path) 101 if err != nil { 102 os.RemoveAll(path) 103 r, err = vcs.NewGitRepo(ustr, path) 104 if err != nil { 105 return nil, unwrapVcsErr(err) 106 } 107 } 108 109 return &gopkginSource{ 110 gitSource: gitSource{ 111 baseVCSSource: baseVCSSource{ 112 repo: &gitRepo{r}, 113 }, 114 }, 115 major: m.major, 116 unstable: m.unstable, 117 aliasURL: aliasURL, 118 }, nil 119 } 120 121 func (m maybeGopkginSource) URL() *url.URL { 122 return &url.URL{ 123 Scheme: m.url.Scheme, 124 Path: m.opath, 125 } 126 } 127 128 func (m maybeGopkginSource) String() string { 129 return fmt.Sprintf("%T: %s (v%v) %s ", m, m.opath, m.major, ufmt(m.url)) 130 } 131 132 type maybeBzrSource struct { 133 url *url.URL 134 } 135 136 func (m maybeBzrSource) try(ctx context.Context, cachedir string) (source, error) { 137 ustr := m.url.String() 138 path := sourceCachePath(cachedir, ustr) 139 140 r, err := vcs.NewBzrRepo(ustr, path) 141 if err != nil { 142 os.RemoveAll(path) 143 r, err = vcs.NewBzrRepo(ustr, path) 144 if err != nil { 145 return nil, unwrapVcsErr(err) 146 } 147 } 148 149 return &bzrSource{ 150 baseVCSSource: baseVCSSource{ 151 repo: &bzrRepo{r}, 152 }, 153 }, nil 154 } 155 156 func (m maybeBzrSource) URL() *url.URL { 157 return m.url 158 } 159 160 func (m maybeBzrSource) String() string { 161 return fmt.Sprintf("%T: %s", m, ufmt(m.url)) 162 } 163 164 type maybeHgSource struct { 165 url *url.URL 166 } 167 168 func (m maybeHgSource) try(ctx context.Context, cachedir string) (source, error) { 169 ustr := m.url.String() 170 path := sourceCachePath(cachedir, ustr) 171 172 r, err := vcs.NewHgRepo(ustr, path) 173 if err != nil { 174 os.RemoveAll(path) 175 r, err = vcs.NewHgRepo(ustr, path) 176 if err != nil { 177 return nil, unwrapVcsErr(err) 178 } 179 } 180 181 return &hgSource{ 182 baseVCSSource: baseVCSSource{ 183 repo: &hgRepo{r}, 184 }, 185 }, nil 186 } 187 188 func (m maybeHgSource) URL() *url.URL { 189 return m.url 190 } 191 192 func (m maybeHgSource) String() string { 193 return fmt.Sprintf("%T: %s", m, ufmt(m.url)) 194 } 195 196 // borrow from stdlib 197 // more useful string for debugging than fmt's struct printer 198 func ufmt(u *url.URL) string { 199 var user, pass interface{} 200 if u.User != nil { 201 user = u.User.Username() 202 if p, ok := u.User.Password(); ok { 203 pass = p 204 } 205 } 206 return fmt.Sprintf("host=%q, path=%q, opaque=%q, scheme=%q, user=%#v, pass=%#v, rawpath=%q, rawq=%q, frag=%q", 207 u.Host, u.Path, u.Opaque, u.Scheme, user, pass, u.RawPath, u.RawQuery, u.Fragment) 208 }