Merge pull request #1770 from git-lfs/tq/extract-endpoint
Tq/extract endpoint
This commit is contained in:
commit
3e715eeaba
@ -8,14 +8,16 @@ import (
|
||||
|
||||
"github.com/git-lfs/git-lfs/api"
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/lfsapi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func NewTestConfig() *config.Configuration {
|
||||
c := config.NewFrom(config.Values{})
|
||||
c.SetManualEndpoint(config.Endpoint{Url: "https://example.com"})
|
||||
c.SetManualEndpoint(lfsapi.Endpoint{Url: "https://example.com"})
|
||||
return c
|
||||
}
|
||||
|
||||
func TestHttpLifecycleMakesRequestsAgainstAbsolutePath(t *testing.T) {
|
||||
SetupTestCredentialsFunc()
|
||||
defer RestoreCredentialsFunc()
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"github.com/git-lfs/git-lfs/httputil"
|
||||
"github.com/git-lfs/git-lfs/lfsapi"
|
||||
|
||||
"github.com/rubyist/tracerx"
|
||||
)
|
||||
@ -129,8 +130,8 @@ func NewBatchRequest(cfg *config.Configuration, operation string) (*http.Request
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func ObjectUrl(endpoint config.Endpoint, oid string) (*url.URL, error) {
|
||||
u, err := url.Parse(endpoint.Url)
|
||||
func ObjectUrl(e lfsapi.Endpoint, oid string) (*url.URL, error) {
|
||||
u, err := url.Parse(e.Url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
13
auth/ssh.go
13
auth/ssh.go
@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/lfsapi"
|
||||
"github.com/rubyist/tracerx"
|
||||
)
|
||||
|
||||
@ -19,7 +20,7 @@ type SshAuthResponse struct {
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
}
|
||||
|
||||
func SshAuthenticate(cfg *config.Configuration, operation, oid string) (SshAuthResponse, config.Endpoint, error) {
|
||||
func SshAuthenticate(cfg *config.Configuration, operation, oid string) (SshAuthResponse, lfsapi.Endpoint, error) {
|
||||
// This is only used as a fallback where the Git URL is SSH but server doesn't support a full SSH binary protocol
|
||||
// and therefore we derive a HTTPS endpoint for binaries instead; but check authentication here via SSH
|
||||
|
||||
@ -61,8 +62,8 @@ func SshAuthenticate(cfg *config.Configuration, operation, oid string) (SshAuthR
|
||||
|
||||
// Return the executable name for ssh on this machine and the base args
|
||||
// Base args includes port settings, user/host, everything pre the command to execute
|
||||
func sshGetExeAndArgs(cfg *config.Configuration, endpoint config.Endpoint) (exe string, baseargs []string) {
|
||||
if len(endpoint.SshUserAndHost) == 0 {
|
||||
func sshGetExeAndArgs(cfg *config.Configuration, e lfsapi.Endpoint) (exe string, baseargs []string) {
|
||||
if len(e.SshUserAndHost) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@ -99,15 +100,15 @@ func sshGetExeAndArgs(cfg *config.Configuration, endpoint config.Endpoint) (exe
|
||||
args = append(args, "-batch")
|
||||
}
|
||||
|
||||
if len(endpoint.SshPort) > 0 {
|
||||
if len(e.SshPort) > 0 {
|
||||
if isPlink || isTortoise {
|
||||
args = append(args, "-P")
|
||||
} else {
|
||||
args = append(args, "-p")
|
||||
}
|
||||
args = append(args, endpoint.SshPort)
|
||||
args = append(args, e.SshPort)
|
||||
}
|
||||
args = append(args, endpoint.SshUserAndHost)
|
||||
args = append(args, e.SshUserAndHost)
|
||||
|
||||
return ssh, args
|
||||
}
|
||||
|
198
config/config.go
198
config/config.go
@ -5,17 +5,14 @@ package config
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ThomsonReutersEikon/go-ntlm/ntlm"
|
||||
"github.com/bgentry/go-netrc/netrc"
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/lfsapi"
|
||||
"github.com/git-lfs/git-lfs/tools"
|
||||
"github.com/rubyist/tracerx"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -68,23 +65,16 @@ type Configuration struct {
|
||||
loading sync.Mutex // guards initialization of gitConfig and remotes
|
||||
remotes []string
|
||||
extensions map[string]Extension
|
||||
manualEndpoint *Endpoint
|
||||
manualEndpoint *lfsapi.Endpoint
|
||||
parsedNetrc netrcfinder
|
||||
urlAliasesMap map[string]string
|
||||
urlAliasMu sync.Mutex
|
||||
endpointFinder lfsapi.EndpointFinder
|
||||
endpointMu sync.Mutex
|
||||
}
|
||||
|
||||
func New() *Configuration {
|
||||
c := &Configuration{
|
||||
Os: EnvironmentOf(NewOsFetcher()),
|
||||
CurrentRemote: defaultRemote,
|
||||
envVars: make(map[string]string),
|
||||
}
|
||||
|
||||
c := &Configuration{Os: EnvironmentOf(NewOsFetcher())}
|
||||
c.Git = &gitEnvironment{config: c}
|
||||
c.IsTracingHttp = c.Os.Bool("GIT_CURL_VERBOSE", false)
|
||||
c.IsDebuggingHttp = c.Os.Bool("LFS_DEBUG_HTTP", false)
|
||||
c.IsLoggingStats = c.Os.Bool("GIT_LOG_STATS", false)
|
||||
initConfig(c)
|
||||
return c
|
||||
}
|
||||
|
||||
@ -103,12 +93,20 @@ type Values struct {
|
||||
//
|
||||
// This method should only be used during testing.
|
||||
func NewFrom(v Values) *Configuration {
|
||||
return &Configuration{
|
||||
c := &Configuration{
|
||||
Os: EnvironmentOf(mapFetcher(v.Os)),
|
||||
Git: EnvironmentOf(mapFetcher(v.Git)),
|
||||
|
||||
envVars: make(map[string]string, 0),
|
||||
}
|
||||
initConfig(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func initConfig(c *Configuration) {
|
||||
c.CurrentRemote = defaultRemote
|
||||
c.envVars = make(map[string]string)
|
||||
c.IsTracingHttp = c.Os.Bool("GIT_CURL_VERBOSE", false)
|
||||
c.IsDebuggingHttp = c.Os.Bool("LFS_DEBUG_HTTP", false)
|
||||
c.IsLoggingStats = c.Os.Bool("GIT_LOG_STATS", false)
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals the *Configuration in context into all of `v`'s fields,
|
||||
@ -202,51 +200,19 @@ func (c *Configuration) parseTag(tag reflect.StructTag) (key string, env Environ
|
||||
// GitRemoteUrl returns the git clone/push url for a given remote (blank if not found)
|
||||
// the forpush argument is to cater for separate remote.name.pushurl settings
|
||||
func (c *Configuration) GitRemoteUrl(remote string, forpush bool) string {
|
||||
if forpush {
|
||||
if u, ok := c.Git.Get("remote." + remote + ".pushurl"); ok {
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
if u, ok := c.Git.Get("remote." + remote + ".url"); ok {
|
||||
return u
|
||||
}
|
||||
|
||||
if err := git.ValidateRemote(remote); err == nil {
|
||||
return remote
|
||||
}
|
||||
|
||||
return ""
|
||||
|
||||
return c.endpointConfig().GitRemoteURL(remote, forpush)
|
||||
}
|
||||
|
||||
// Manually set an Endpoint to use instead of deriving from Git config
|
||||
func (c *Configuration) SetManualEndpoint(e Endpoint) {
|
||||
func (c *Configuration) SetManualEndpoint(e lfsapi.Endpoint) {
|
||||
c.manualEndpoint = &e
|
||||
}
|
||||
|
||||
func (c *Configuration) Endpoint(operation string) Endpoint {
|
||||
func (c *Configuration) Endpoint(operation string) lfsapi.Endpoint {
|
||||
if c.manualEndpoint != nil {
|
||||
return *c.manualEndpoint
|
||||
}
|
||||
|
||||
if operation == "upload" {
|
||||
if url, ok := c.Git.Get("lfs.pushurl"); ok {
|
||||
return NewEndpointWithConfig(url, c)
|
||||
}
|
||||
}
|
||||
|
||||
if url, ok := c.Git.Get("lfs.url"); ok {
|
||||
return NewEndpointWithConfig(url, c)
|
||||
}
|
||||
|
||||
if len(c.CurrentRemote) > 0 && c.CurrentRemote != defaultRemote {
|
||||
if endpoint := c.RemoteEndpoint(c.CurrentRemote, operation); len(endpoint.Url) > 0 {
|
||||
return endpoint
|
||||
}
|
||||
}
|
||||
|
||||
return c.RemoteEndpoint(defaultRemote, operation)
|
||||
return c.endpointConfig().Endpoint(operation, c.CurrentRemote)
|
||||
}
|
||||
|
||||
func (c *Configuration) ConcurrentTransfers() int {
|
||||
@ -283,7 +249,7 @@ func (c *Configuration) BatchTransfer() bool {
|
||||
}
|
||||
|
||||
func (c *Configuration) NtlmAccess(operation string) bool {
|
||||
return c.Access(operation) == "ntlm"
|
||||
return c.Access(operation) == lfsapi.NTLMAccess
|
||||
}
|
||||
|
||||
// PrivateAccess will retrieve the access value and return true if
|
||||
@ -291,17 +257,17 @@ func (c *Configuration) NtlmAccess(operation string) bool {
|
||||
// access, the http requests for the batch api will fetch the credentials
|
||||
// before running, otherwise the request will run without credentials.
|
||||
func (c *Configuration) PrivateAccess(operation string) bool {
|
||||
return c.Access(operation) != "none"
|
||||
return c.Access(operation) != lfsapi.NoneAccess
|
||||
}
|
||||
|
||||
// Access returns the access auth type.
|
||||
func (c *Configuration) Access(operation string) string {
|
||||
func (c *Configuration) Access(operation string) lfsapi.Access {
|
||||
return c.EndpointAccess(c.Endpoint(operation))
|
||||
}
|
||||
|
||||
// SetAccess will set the private access flag in .git/config.
|
||||
func (c *Configuration) SetAccess(operation string, authType string) {
|
||||
c.SetEndpointAccess(c.Endpoint(operation), authType)
|
||||
c.endpointConfig().SetAccess(c.Endpoint(operation).Url, lfsapi.Access(authType))
|
||||
}
|
||||
|
||||
func (c *Configuration) FindNetrcHost(host string) (*netrc.Machine, error) {
|
||||
@ -323,34 +289,8 @@ func (c *Configuration) SetNetrc(n netrcfinder) {
|
||||
c.parsedNetrc = n
|
||||
}
|
||||
|
||||
func (c *Configuration) EndpointAccess(e Endpoint) string {
|
||||
key := fmt.Sprintf("lfs.%s.access", e.Url)
|
||||
if v, ok := c.Git.Get(key); ok && len(v) > 0 {
|
||||
lower := strings.ToLower(v)
|
||||
if lower == "private" {
|
||||
return "basic"
|
||||
}
|
||||
return lower
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
func (c *Configuration) SetEndpointAccess(e Endpoint, authType string) {
|
||||
c.loadGitConfig()
|
||||
|
||||
tracerx.Printf("setting repository access to %s", authType)
|
||||
key := fmt.Sprintf("lfs.%s.access", e.Url)
|
||||
|
||||
// Modify the config cache because it's checked again in this process
|
||||
// without being reloaded.
|
||||
switch authType {
|
||||
case "", "none":
|
||||
git.Config.UnsetLocalKey("", key)
|
||||
c.Git.del(key)
|
||||
default:
|
||||
git.Config.SetLocal("", key, authType)
|
||||
c.Git.set(key, authType)
|
||||
}
|
||||
func (c *Configuration) EndpointAccess(e lfsapi.Endpoint) lfsapi.Access {
|
||||
return c.endpointConfig().AccessFor(e.Url)
|
||||
}
|
||||
|
||||
func (c *Configuration) FetchIncludePaths() []string {
|
||||
@ -363,27 +303,8 @@ func (c *Configuration) FetchExcludePaths() []string {
|
||||
return tools.CleanPaths(patterns, ",")
|
||||
}
|
||||
|
||||
func (c *Configuration) RemoteEndpoint(remote, operation string) Endpoint {
|
||||
if len(remote) == 0 {
|
||||
remote = defaultRemote
|
||||
}
|
||||
|
||||
// Support separate push URL if specified and pushing
|
||||
if operation == "upload" {
|
||||
if url, ok := c.Git.Get("remote." + remote + ".lfspushurl"); ok {
|
||||
return NewEndpointWithConfig(url, c)
|
||||
}
|
||||
}
|
||||
if url, ok := c.Git.Get("remote." + remote + ".lfsurl"); ok {
|
||||
return NewEndpointWithConfig(url, c)
|
||||
}
|
||||
|
||||
// finally fall back on git remote url (also supports pushurl)
|
||||
if url := c.GitRemoteUrl(remote, operation == "upload"); url != "" {
|
||||
return NewEndpointFromCloneURLWithConfig(url, c)
|
||||
}
|
||||
|
||||
return Endpoint{}
|
||||
func (c *Configuration) RemoteEndpoint(remote, operation string) lfsapi.Endpoint {
|
||||
return c.endpointConfig().RemoteEndpoint(operation, remote)
|
||||
}
|
||||
|
||||
func (c *Configuration) Remotes() []string {
|
||||
@ -392,18 +313,23 @@ func (c *Configuration) Remotes() []string {
|
||||
return c.remotes
|
||||
}
|
||||
|
||||
// GitProtocol returns the protocol for the LFS API when converting from a
|
||||
// git:// remote url.
|
||||
func (c *Configuration) GitProtocol() string {
|
||||
if value, ok := c.Git.Get("lfs.gitprotocol"); ok {
|
||||
return value
|
||||
return c.endpointConfig().GitProtocol()
|
||||
}
|
||||
|
||||
func (c *Configuration) endpointConfig() lfsapi.EndpointFinder {
|
||||
c.endpointMu.Lock()
|
||||
defer c.endpointMu.Unlock()
|
||||
|
||||
if c.endpointFinder == nil {
|
||||
c.endpointFinder = lfsapi.NewEndpointFinder(c.Git)
|
||||
}
|
||||
return "https"
|
||||
|
||||
return c.endpointFinder
|
||||
}
|
||||
|
||||
func (c *Configuration) Extensions() map[string]Extension {
|
||||
c.loadGitConfig()
|
||||
|
||||
return c.extensions
|
||||
}
|
||||
|
||||
@ -412,50 +338,6 @@ func (c *Configuration) SortedExtensions() ([]Extension, error) {
|
||||
return SortExtensions(c.Extensions())
|
||||
}
|
||||
|
||||
func (c *Configuration) urlAliases() map[string]string {
|
||||
c.urlAliasMu.Lock()
|
||||
defer c.urlAliasMu.Unlock()
|
||||
|
||||
if c.urlAliasesMap == nil {
|
||||
c.urlAliasesMap = make(map[string]string)
|
||||
prefix := "url."
|
||||
suffix := ".insteadof"
|
||||
for gitkey, gitval := range c.Git.All() {
|
||||
if strings.HasPrefix(gitkey, prefix) && strings.HasSuffix(gitkey, suffix) {
|
||||
if _, ok := c.urlAliasesMap[gitval]; ok {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Multiple 'url.*.insteadof' keys with the same alias: %q\n", gitval)
|
||||
}
|
||||
c.urlAliasesMap[gitval] = gitkey[len(prefix) : len(gitkey)-len(suffix)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.urlAliasesMap
|
||||
}
|
||||
|
||||
// ReplaceUrlAlias returns a url with a prefix from a `url.*.insteadof` git
|
||||
// config setting. If multiple aliases match, use the longest one.
|
||||
// See https://git-scm.com/docs/git-config for Git's docs.
|
||||
func (c *Configuration) ReplaceUrlAlias(rawurl string) string {
|
||||
var longestalias string
|
||||
aliases := c.urlAliases()
|
||||
for alias, _ := range aliases {
|
||||
if !strings.HasPrefix(rawurl, alias) {
|
||||
continue
|
||||
}
|
||||
|
||||
if longestalias < alias {
|
||||
longestalias = alias
|
||||
}
|
||||
}
|
||||
|
||||
if len(longestalias) > 0 {
|
||||
return aliases[longestalias] + rawurl[len(longestalias):]
|
||||
}
|
||||
|
||||
return rawurl
|
||||
}
|
||||
|
||||
func (c *Configuration) FetchPruneConfig() FetchPruneConfig {
|
||||
f := &FetchPruneConfig{
|
||||
FetchRecentRefsDays: 7,
|
||||
|
@ -7,266 +7,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEndpointDefaultsToOrigin(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.lfsurl": "abc"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "abc", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointOverridesOrigin(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"lfs.url": "abc",
|
||||
"remote.origin.lfsurl": "def",
|
||||
},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "abc", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointNoOverrideDefaultRemote(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"remote.origin.lfsurl": "abc",
|
||||
"remote.other.lfsurl": "def",
|
||||
},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "abc", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointUseAlternateRemote(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"remote.origin.lfsurl": "abc",
|
||||
"remote.other.lfsurl": "def",
|
||||
},
|
||||
})
|
||||
|
||||
cfg.CurrentRemote = "other"
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "def", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "https://example.com/foo/bar"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestBareEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "https://example.com/foo/bar.git"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointSeparateClonePushUrl(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"remote.origin.url": "https://example.com/foo/bar.git",
|
||||
"remote.origin.pushurl": "https://readwrite.com/foo/bar.git"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
|
||||
endpoint = cfg.Endpoint("upload")
|
||||
assert.Equal(t, "https://readwrite.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointOverriddenSeparateClonePushLfsUrl(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"remote.origin.url": "https://example.com/foo/bar.git",
|
||||
"remote.origin.pushurl": "https://readwrite.com/foo/bar.git",
|
||||
"remote.origin.lfsurl": "https://examplelfs.com/foo/bar",
|
||||
"remote.origin.lfspushurl": "https://readwritelfs.com/foo/bar"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://examplelfs.com/foo/bar", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
|
||||
endpoint = cfg.Endpoint("upload")
|
||||
assert.Equal(t, "https://readwritelfs.com/foo/bar", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointGlobalSeparateLfsPush(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"lfs.url": "https://readonly.com/foo/bar",
|
||||
"lfs.pushurl": "https://write.com/foo/bar",
|
||||
},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://readonly.com/foo/bar", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
|
||||
endpoint = cfg.Endpoint("upload")
|
||||
assert.Equal(t, "https://write.com/foo/bar", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
}
|
||||
|
||||
func TestSSHEndpointOverridden(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"remote.origin.url": "git@example.com:foo/bar",
|
||||
"remote.origin.lfsurl": "lfs",
|
||||
},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestSSHEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "ssh://git@example.com/foo/bar"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "git@example.com", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestSSHCustomPortEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "ssh://git@example.com:9000/foo/bar"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "git@example.com", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar", endpoint.SshPath)
|
||||
assert.Equal(t, "9000", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestBareSSHEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "git@example.com:foo/bar.git"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "git@example.com", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar.git", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestSSHEndpointFromGlobalLfsUrl(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"lfs.url": "git@example.com:foo/bar.git"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git", endpoint.Url)
|
||||
assert.Equal(t, "git@example.com", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar.git", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestHTTPEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "http://example.com/foo/bar"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "http://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestBareHTTPEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "http://example.com/foo/bar.git"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "http://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestGitEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "git://example.com/foo/bar"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestGitEndpointAddsLfsSuffixWithCustomProtocol(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"remote.origin.url": "git://example.com/foo/bar",
|
||||
"lfs.gitprotocol": "http",
|
||||
},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "http://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestBareGitEndpointAddsLfsSuffix(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{"remote.origin.url": "git://example.com/foo/bar.git"},
|
||||
})
|
||||
|
||||
endpoint := cfg.Endpoint("download")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", endpoint.Url)
|
||||
assert.Equal(t, "", endpoint.SshUserAndHost)
|
||||
assert.Equal(t, "", endpoint.SshPath)
|
||||
assert.Equal(t, "", endpoint.SshPort)
|
||||
}
|
||||
|
||||
func TestConcurrentTransfersSetValue(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
@ -405,82 +145,6 @@ func TestBatchAbsentIsTrue(t *testing.T) {
|
||||
assert.True(t, v)
|
||||
}
|
||||
|
||||
func TestAccessConfig(t *testing.T) {
|
||||
type accessTest struct {
|
||||
Access string
|
||||
PrivateAccess bool
|
||||
}
|
||||
|
||||
tests := map[string]accessTest{
|
||||
"": {"none", false},
|
||||
"basic": {"basic", true},
|
||||
"BASIC": {"basic", true},
|
||||
"private": {"basic", true},
|
||||
"PRIVATE": {"basic", true},
|
||||
"invalidauth": {"invalidauth", true},
|
||||
}
|
||||
|
||||
for value, expected := range tests {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"lfs.url": "http://example.com",
|
||||
"lfs.http://example.com.access": value,
|
||||
"lfs.https://example.com.access": "bad",
|
||||
},
|
||||
})
|
||||
|
||||
if access := cfg.Access("download"); access != expected.Access {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
if access := cfg.Access("upload"); access != expected.Access {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
|
||||
if priv := cfg.PrivateAccess("download"); priv != expected.PrivateAccess {
|
||||
t.Errorf("Expected PrivateAccess() with value %q to be %v, got %v", value, expected.PrivateAccess, priv)
|
||||
}
|
||||
if priv := cfg.PrivateAccess("upload"); priv != expected.PrivateAccess {
|
||||
t.Errorf("Expected PrivateAccess() with value %q to be %v, got %v", value, expected.PrivateAccess, priv)
|
||||
}
|
||||
}
|
||||
|
||||
// Test again but with separate push url
|
||||
for value, expected := range tests {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{
|
||||
"lfs.url": "http://example.com",
|
||||
"lfs.pushurl": "http://examplepush.com",
|
||||
"lfs.http://example.com.access": value,
|
||||
"lfs.http://examplepush.com.access": value,
|
||||
"lfs.https://example.com.access": "bad",
|
||||
},
|
||||
})
|
||||
|
||||
if access := cfg.Access("download"); access != expected.Access {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
if access := cfg.Access("upload"); access != expected.Access {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
|
||||
if priv := cfg.PrivateAccess("download"); priv != expected.PrivateAccess {
|
||||
t.Errorf("Expected PrivateAccess() with value %q to be %v, got %v", value, expected.PrivateAccess, priv)
|
||||
}
|
||||
if priv := cfg.PrivateAccess("upload"); priv != expected.PrivateAccess {
|
||||
t.Errorf("Expected PrivateAccess() with value %q to be %v, got %v", value, expected.PrivateAccess, priv)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAccessAbsentConfig(t *testing.T) {
|
||||
cfg := NewFrom(Values{})
|
||||
assert.Equal(t, "none", cfg.Access("download"))
|
||||
assert.Equal(t, "none", cfg.Access("upload"))
|
||||
assert.False(t, cfg.PrivateAccess("download"))
|
||||
assert.False(t, cfg.PrivateAccess("upload"))
|
||||
}
|
||||
|
||||
func TestLoadValidExtension(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string]string{},
|
||||
|
@ -120,7 +120,7 @@ func Environ(cfg *config.Configuration, manifest *tq.Manifest) []string {
|
||||
|
||||
// TransferManifest builds a tq.Manifest using the given cfg.
|
||||
func TransferManifest(cfg *config.Configuration) *tq.Manifest {
|
||||
return tq.NewManifestWithGitEnv(cfg.Access("download"), cfg.Git)
|
||||
return tq.NewManifestWithGitEnv(string(cfg.Access("download")), cfg.Git)
|
||||
}
|
||||
|
||||
func InRepo() bool {
|
||||
|
@ -1,14 +1,13 @@
|
||||
package config
|
||||
package lfsapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const EndpointUrlUnknown = "<unknown>"
|
||||
const UrlUnknown = "<unknown>"
|
||||
|
||||
// An Endpoint describes how to access a Git LFS server.
|
||||
type Endpoint struct {
|
||||
@ -18,62 +17,6 @@ type Endpoint struct {
|
||||
SshPort string
|
||||
}
|
||||
|
||||
// NewEndpointFromCloneURL creates an Endpoint from a git clone URL by appending
|
||||
// "[.git]/info/lfs".
|
||||
func NewEndpointFromCloneURL(url string) Endpoint {
|
||||
return NewEndpointFromCloneURLWithConfig(url, New())
|
||||
}
|
||||
|
||||
// NewEndpoint initializes a new Endpoint for a given URL.
|
||||
func NewEndpoint(rawurl string) Endpoint {
|
||||
return NewEndpointWithConfig(rawurl, New())
|
||||
}
|
||||
|
||||
// NewEndpointFromCloneURLWithConfig creates an Endpoint from a git clone URL by appending
|
||||
// "[.git]/info/lfs".
|
||||
func NewEndpointFromCloneURLWithConfig(url string, c *Configuration) Endpoint {
|
||||
e := NewEndpointWithConfig(url, c)
|
||||
if e.Url == EndpointUrlUnknown {
|
||||
return e
|
||||
}
|
||||
|
||||
if strings.HasSuffix(url, "/") {
|
||||
e.Url = url[0 : len(url)-1]
|
||||
}
|
||||
|
||||
// When using main remote URL for HTTP, append info/lfs
|
||||
if path.Ext(e.Url) == ".git" {
|
||||
e.Url += "/info/lfs"
|
||||
} else {
|
||||
e.Url += ".git/info/lfs"
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NewEndpointWithConfig initializes a new Endpoint for a given URL.
|
||||
func NewEndpointWithConfig(rawurl string, c *Configuration) Endpoint {
|
||||
rawurl = c.ReplaceUrlAlias(rawurl)
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return endpointFromBareSshUrl(rawurl)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ssh":
|
||||
return endpointFromSshUrl(u)
|
||||
case "http", "https":
|
||||
return endpointFromHttpUrl(u)
|
||||
case "git":
|
||||
return endpointFromGitUrl(u, c)
|
||||
case "":
|
||||
return endpointFromBareSshUrl(u.String())
|
||||
default:
|
||||
// Just passthrough to preserve
|
||||
return Endpoint{Url: rawurl}
|
||||
}
|
||||
}
|
||||
|
||||
// endpointFromBareSshUrl constructs a new endpoint from a bare SSH URL:
|
||||
//
|
||||
// user@host.com:path/to/repo.git
|
||||
@ -95,7 +38,7 @@ func endpointFromBareSshUrl(rawurl string) Endpoint {
|
||||
newrawurl := fmt.Sprintf("ssh://%v", newPath)
|
||||
newu, err := url.Parse(newrawurl)
|
||||
if err != nil {
|
||||
return Endpoint{Url: EndpointUrlUnknown}
|
||||
return Endpoint{Url: UrlUnknown}
|
||||
}
|
||||
|
||||
return endpointFromSshUrl(newu)
|
||||
@ -108,7 +51,7 @@ func endpointFromSshUrl(u *url.URL) Endpoint {
|
||||
regex := regexp.MustCompile(`^([^\:]+)(?:\:(\d+))?$`)
|
||||
match := regex.FindStringSubmatch(u.Host)
|
||||
if match == nil || len(match) < 2 {
|
||||
endpoint.Url = EndpointUrlUnknown
|
||||
endpoint.Url = UrlUnknown
|
||||
return endpoint
|
||||
}
|
||||
|
||||
@ -145,7 +88,7 @@ func endpointFromHttpUrl(u *url.URL) Endpoint {
|
||||
return Endpoint{Url: u.String()}
|
||||
}
|
||||
|
||||
func endpointFromGitUrl(u *url.URL, c *Configuration) Endpoint {
|
||||
u.Scheme = c.GitProtocol()
|
||||
func endpointFromGitUrl(u *url.URL, e *endpointGitFinder) Endpoint {
|
||||
u.Scheme = e.gitProtocol
|
||||
return Endpoint{Url: u.String()}
|
||||
}
|
270
lfsapi/endpoint_finder.go
Normal file
270
lfsapi/endpoint_finder.go
Normal file
@ -0,0 +1,270 @@
|
||||
package lfsapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/rubyist/tracerx"
|
||||
)
|
||||
|
||||
type Access string
|
||||
|
||||
const (
|
||||
NoneAccess Access = "none"
|
||||
BasicAccess Access = "basic"
|
||||
PrivateAccess Access = "private"
|
||||
NTLMAccess Access = "ntlm"
|
||||
emptyAccess Access = ""
|
||||
defaultRemote = "origin"
|
||||
)
|
||||
|
||||
type EndpointFinder interface {
|
||||
NewEndpointFromCloneURL(rawurl string) Endpoint
|
||||
NewEndpoint(rawurl string) Endpoint
|
||||
Endpoint(operation, remote string) Endpoint
|
||||
RemoteEndpoint(operation, remote string) Endpoint
|
||||
GitRemoteURL(remote string, forpush bool) string
|
||||
AccessFor(rawurl string) Access
|
||||
SetAccess(rawurl string, access Access)
|
||||
GitProtocol() string
|
||||
}
|
||||
|
||||
type endpointGitFinder struct {
|
||||
git env
|
||||
gitProtocol string
|
||||
|
||||
aliasMu sync.Mutex
|
||||
aliases map[string]string
|
||||
|
||||
accessMu sync.Mutex
|
||||
urlAccess map[string]Access
|
||||
}
|
||||
|
||||
func NewEndpointFinder(git env) EndpointFinder {
|
||||
e := &endpointGitFinder{
|
||||
gitProtocol: "https",
|
||||
aliases: make(map[string]string),
|
||||
urlAccess: make(map[string]Access),
|
||||
}
|
||||
|
||||
if git != nil {
|
||||
e.git = git
|
||||
if v, ok := git.Get("lfs.gitprotocol"); ok {
|
||||
e.gitProtocol = v
|
||||
}
|
||||
initAliases(e, git)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) Endpoint(operation, remote string) Endpoint {
|
||||
if e.git == nil {
|
||||
return Endpoint{}
|
||||
}
|
||||
|
||||
if operation == "upload" {
|
||||
if url, ok := e.git.Get("lfs.pushurl"); ok {
|
||||
return e.NewEndpoint(url)
|
||||
}
|
||||
}
|
||||
|
||||
if url, ok := e.git.Get("lfs.url"); ok {
|
||||
return e.NewEndpoint(url)
|
||||
}
|
||||
|
||||
if len(remote) > 0 && remote != defaultRemote {
|
||||
if e := e.RemoteEndpoint(operation, remote); len(e.Url) > 0 {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
return e.RemoteEndpoint(operation, defaultRemote)
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) RemoteEndpoint(operation, remote string) Endpoint {
|
||||
if e.git == nil {
|
||||
return Endpoint{}
|
||||
}
|
||||
|
||||
if len(remote) == 0 {
|
||||
remote = defaultRemote
|
||||
}
|
||||
|
||||
// Support separate push URL if specified and pushing
|
||||
if operation == "upload" {
|
||||
if url, ok := e.git.Get("remote." + remote + ".lfspushurl"); ok {
|
||||
return e.NewEndpoint(url)
|
||||
}
|
||||
}
|
||||
if url, ok := e.git.Get("remote." + remote + ".lfsurl"); ok {
|
||||
return e.NewEndpoint(url)
|
||||
}
|
||||
|
||||
// finally fall back on git remote url (also supports pushurl)
|
||||
if url := e.GitRemoteURL(remote, operation == "upload"); url != "" {
|
||||
return e.NewEndpointFromCloneURL(url)
|
||||
}
|
||||
|
||||
return Endpoint{}
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) GitRemoteURL(remote string, forpush bool) string {
|
||||
if e.git != nil {
|
||||
if forpush {
|
||||
if u, ok := e.git.Get("remote." + remote + ".pushurl"); ok {
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
if u, ok := e.git.Get("remote." + remote + ".url"); ok {
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
if err := git.ValidateRemote(remote); err == nil {
|
||||
return remote
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) NewEndpointFromCloneURL(rawurl string) Endpoint {
|
||||
ep := e.NewEndpoint(rawurl)
|
||||
if ep.Url == UrlUnknown {
|
||||
return ep
|
||||
}
|
||||
|
||||
if strings.HasSuffix(rawurl, "/") {
|
||||
ep.Url = rawurl[0 : len(rawurl)-1]
|
||||
}
|
||||
|
||||
// When using main remote URL for HTTP, append info/lfs
|
||||
if path.Ext(ep.Url) == ".git" {
|
||||
ep.Url += "/info/lfs"
|
||||
} else {
|
||||
ep.Url += ".git/info/lfs"
|
||||
}
|
||||
|
||||
return ep
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) NewEndpoint(rawurl string) Endpoint {
|
||||
rawurl = e.ReplaceUrlAlias(rawurl)
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return endpointFromBareSshUrl(rawurl)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ssh":
|
||||
return endpointFromSshUrl(u)
|
||||
case "http", "https":
|
||||
return endpointFromHttpUrl(u)
|
||||
case "git":
|
||||
return endpointFromGitUrl(u, e)
|
||||
case "":
|
||||
return endpointFromBareSshUrl(u.String())
|
||||
default:
|
||||
// Just passthrough to preserve
|
||||
return Endpoint{Url: rawurl}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) AccessFor(rawurl string) Access {
|
||||
if e.git == nil {
|
||||
return NoneAccess
|
||||
}
|
||||
|
||||
e.accessMu.Lock()
|
||||
defer e.accessMu.Unlock()
|
||||
|
||||
if cached, ok := e.urlAccess[rawurl]; ok {
|
||||
return cached
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("lfs.%s.access", rawurl)
|
||||
e.urlAccess[rawurl] = fetchGitAccess(e.git, key)
|
||||
return e.urlAccess[rawurl]
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) SetAccess(rawurl string, access Access) {
|
||||
key := fmt.Sprintf("lfs.%s.access", rawurl)
|
||||
tracerx.Printf("setting repository access to %s", access)
|
||||
|
||||
e.accessMu.Lock()
|
||||
defer e.accessMu.Unlock()
|
||||
|
||||
switch access {
|
||||
case emptyAccess, NoneAccess:
|
||||
git.Config.UnsetLocalKey("", key)
|
||||
e.urlAccess[rawurl] = NoneAccess
|
||||
default:
|
||||
git.Config.SetLocal("", key, string(access))
|
||||
e.urlAccess[rawurl] = access
|
||||
}
|
||||
}
|
||||
|
||||
func fetchGitAccess(git env, key string) Access {
|
||||
if v, _ := git.Get(key); len(v) > 0 {
|
||||
access := Access(strings.ToLower(v))
|
||||
if access == PrivateAccess {
|
||||
return BasicAccess
|
||||
}
|
||||
return access
|
||||
}
|
||||
return NoneAccess
|
||||
}
|
||||
|
||||
func (e *endpointGitFinder) GitProtocol() string {
|
||||
return e.gitProtocol
|
||||
}
|
||||
|
||||
// ReplaceUrlAlias returns a url with a prefix from a `url.*.insteadof` git
|
||||
// config setting. If multiple aliases match, use the longest one.
|
||||
// See https://git-scm.com/docs/git-config for Git's docs.
|
||||
func (e *endpointGitFinder) ReplaceUrlAlias(rawurl string) string {
|
||||
e.aliasMu.Lock()
|
||||
defer e.aliasMu.Unlock()
|
||||
|
||||
var longestalias string
|
||||
for alias, _ := range e.aliases {
|
||||
if !strings.HasPrefix(rawurl, alias) {
|
||||
continue
|
||||
}
|
||||
|
||||
if longestalias < alias {
|
||||
longestalias = alias
|
||||
}
|
||||
}
|
||||
|
||||
if len(longestalias) > 0 {
|
||||
return e.aliases[longestalias] + rawurl[len(longestalias):]
|
||||
}
|
||||
|
||||
return rawurl
|
||||
}
|
||||
|
||||
func initAliases(e *endpointGitFinder, git env) {
|
||||
prefix := "url."
|
||||
suffix := ".insteadof"
|
||||
for gitkey, gitval := range git.All() {
|
||||
if !(strings.HasPrefix(gitkey, prefix) && strings.HasSuffix(gitkey, suffix)) {
|
||||
continue
|
||||
}
|
||||
if _, ok := e.aliases[gitval]; ok {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Multiple 'url.*.insteadof' keys with the same alias: %q\n", gitval)
|
||||
}
|
||||
e.aliases[gitval] = gitkey[len(prefix) : len(gitkey)-len(suffix)]
|
||||
}
|
||||
}
|
||||
|
||||
type env interface {
|
||||
Get(string) (string, bool)
|
||||
All() map[string]string
|
||||
}
|
361
lfsapi/endpoint_finder_test.go
Normal file
361
lfsapi/endpoint_finder_test.go
Normal file
@ -0,0 +1,361 @@
|
||||
package lfsapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEndpointDefaultsToOrigin(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.lfsurl": "abc",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "abc", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointOverridesOrigin(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.url": "abc",
|
||||
"remote.origin.lfsurl": "def",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "abc", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointNoOverrideDefaultRemote(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.lfsurl": "abc",
|
||||
"remote.other.lfsurl": "def",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "abc", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointUseAlternateRemote(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.lfsurl": "abc",
|
||||
"remote.other.lfsurl": "def",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "other")
|
||||
assert.Equal(t, "def", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "https://example.com/foo/bar",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestBareEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "https://example.com/foo/bar.git",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointSeparateClonePushUrl(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "https://example.com/foo/bar.git",
|
||||
"remote.origin.pushurl": "https://readwrite.com/foo/bar.git",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
|
||||
e = finder.Endpoint("upload", "")
|
||||
assert.Equal(t, "https://readwrite.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointOverriddenSeparateClonePushLfsUrl(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "https://example.com/foo/bar.git",
|
||||
"remote.origin.pushurl": "https://readwrite.com/foo/bar.git",
|
||||
"remote.origin.lfsurl": "https://examplelfs.com/foo/bar",
|
||||
"remote.origin.lfspushurl": "https://readwritelfs.com/foo/bar",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://examplelfs.com/foo/bar", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
|
||||
e = finder.Endpoint("upload", "")
|
||||
assert.Equal(t, "https://readwritelfs.com/foo/bar", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestEndpointGlobalSeparateLfsPush(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.url": "https://readonly.com/foo/bar",
|
||||
"lfs.pushurl": "https://write.com/foo/bar",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://readonly.com/foo/bar", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
|
||||
e = finder.Endpoint("upload", "")
|
||||
assert.Equal(t, "https://write.com/foo/bar", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
}
|
||||
|
||||
func TestSSHEndpointOverridden(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "git@example.com:foo/bar",
|
||||
"remote.origin.lfsurl": "lfs",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestSSHEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "ssh://git@example.com/foo/bar",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "git@example.com", e.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestSSHCustomPortEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "ssh://git@example.com:9000/foo/bar",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "git@example.com", e.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar", e.SshPath)
|
||||
assert.Equal(t, "9000", e.SshPort)
|
||||
}
|
||||
|
||||
func TestBareSSHEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "git@example.com:foo/bar.git",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "git@example.com", e.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar.git", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestSSHEndpointFromGlobalLfsUrl(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.url": "git@example.com:foo/bar.git",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git", e.Url)
|
||||
assert.Equal(t, "git@example.com", e.SshUserAndHost)
|
||||
assert.Equal(t, "foo/bar.git", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestHTTPEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "http://example.com/foo/bar",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "http://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestBareHTTPEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "http://example.com/foo/bar.git",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "http://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestGitEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "git://example.com/foo/bar",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestGitEndpointAddsLfsSuffixWithCustomProtocol(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "git://example.com/foo/bar",
|
||||
"lfs.gitprotocol": "http",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "http://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestBareGitEndpointAddsLfsSuffix(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"remote.origin.url": "git://example.com/foo/bar.git",
|
||||
}))
|
||||
|
||||
e := finder.Endpoint("download", "")
|
||||
assert.Equal(t, "https://example.com/foo/bar.git/info/lfs", e.Url)
|
||||
assert.Equal(t, "", e.SshUserAndHost)
|
||||
assert.Equal(t, "", e.SshPath)
|
||||
assert.Equal(t, "", e.SshPort)
|
||||
}
|
||||
|
||||
func TestAccessConfig(t *testing.T) {
|
||||
type accessTest struct {
|
||||
Access string
|
||||
PrivateAccess bool
|
||||
}
|
||||
|
||||
tests := map[string]accessTest{
|
||||
"": {"none", false},
|
||||
"basic": {"basic", true},
|
||||
"BASIC": {"basic", true},
|
||||
"private": {"basic", true},
|
||||
"PRIVATE": {"basic", true},
|
||||
"invalidauth": {"invalidauth", true},
|
||||
}
|
||||
|
||||
for value, expected := range tests {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.url": "http://example.com",
|
||||
"lfs.http://example.com.access": value,
|
||||
"lfs.https://example.com.access": "bad",
|
||||
}))
|
||||
|
||||
dl := finder.Endpoint("upload", "")
|
||||
ul := finder.Endpoint("download", "")
|
||||
|
||||
if access := finder.AccessFor(dl.Url); access != Access(expected.Access) {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
if access := finder.AccessFor(ul.Url); access != Access(expected.Access) {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
}
|
||||
|
||||
// Test again but with separate push url
|
||||
for value, expected := range tests {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.url": "http://example.com",
|
||||
"lfs.pushurl": "http://examplepush.com",
|
||||
"lfs.http://example.com.access": value,
|
||||
"lfs.http://examplepush.com.access": value,
|
||||
"lfs.https://example.com.access": "bad",
|
||||
}))
|
||||
|
||||
dl := finder.Endpoint("upload", "")
|
||||
ul := finder.Endpoint("download", "")
|
||||
|
||||
if access := finder.AccessFor(dl.Url); access != Access(expected.Access) {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
if access := finder.AccessFor(ul.Url); access != Access(expected.Access) {
|
||||
t.Errorf("Expected Access() with value %q to be %v, got %v", value, expected.Access, access)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessAbsentConfig(t *testing.T) {
|
||||
finder := NewEndpointFinder(nil)
|
||||
assert.Equal(t, NoneAccess, finder.AccessFor(finder.Endpoint("download", "").Url))
|
||||
assert.Equal(t, NoneAccess, finder.AccessFor(finder.Endpoint("upload", "").Url))
|
||||
}
|
||||
|
||||
func TestSetAccess(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{}))
|
||||
|
||||
assert.Equal(t, NoneAccess, finder.AccessFor("http://example.com"))
|
||||
finder.SetAccess("http://example.com", NTLMAccess)
|
||||
assert.Equal(t, NTLMAccess, finder.AccessFor("http://example.com"))
|
||||
}
|
||||
|
||||
func TestChangeAccess(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.http://example.com.access": "basic",
|
||||
}))
|
||||
|
||||
assert.Equal(t, BasicAccess, finder.AccessFor("http://example.com"))
|
||||
finder.SetAccess("http://example.com", NTLMAccess)
|
||||
assert.Equal(t, NTLMAccess, finder.AccessFor("http://example.com"))
|
||||
}
|
||||
|
||||
func TestDeleteAccessWithNone(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.http://example.com.access": "basic",
|
||||
}))
|
||||
|
||||
assert.Equal(t, BasicAccess, finder.AccessFor("http://example.com"))
|
||||
finder.SetAccess("http://example.com", NoneAccess)
|
||||
assert.Equal(t, NoneAccess, finder.AccessFor("http://example.com"))
|
||||
}
|
||||
|
||||
func TestDeleteAccessWithEmptyString(t *testing.T) {
|
||||
finder := NewEndpointFinder(gitEnv(map[string]string{
|
||||
"lfs.http://example.com.access": "basic",
|
||||
}))
|
||||
|
||||
assert.Equal(t, BasicAccess, finder.AccessFor("http://example.com"))
|
||||
finder.SetAccess("http://example.com", Access(""))
|
||||
assert.Equal(t, NoneAccess, finder.AccessFor("http://example.com"))
|
||||
}
|
||||
|
||||
type gitEnv map[string]string
|
||||
|
||||
func (e gitEnv) Get(key string) (string, bool) {
|
||||
v, ok := e[key]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (e gitEnv) All() map[string]string {
|
||||
return e
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package config
|
||||
package lfsapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@ -13,9 +13,9 @@ func TestNewEndpointFromCloneURLWithConfig(t *testing.T) {
|
||||
"https://foo/bar.git/",
|
||||
}
|
||||
|
||||
cfg := New()
|
||||
finder := NewEndpointFinder(nil)
|
||||
for _, actual := range tests {
|
||||
e := NewEndpointFromCloneURLWithConfig(actual, cfg)
|
||||
e := finder.NewEndpointFromCloneURL(actual)
|
||||
if e.Url != expected {
|
||||
t.Errorf("%s returned bad endpoint url %s", actual, e.Url)
|
||||
}
|
@ -14,6 +14,7 @@ var (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Endpoints EndpointFinder
|
||||
}
|
||||
|
||||
func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
"github.com/git-lfs/git-lfs/lfsapi"
|
||||
"github.com/git-lfs/git-lfs/progress"
|
||||
"github.com/git-lfs/git-lfs/test"
|
||||
"github.com/git-lfs/git-lfs/tq"
|
||||
@ -66,11 +67,12 @@ func testServerApi(cmd *cobra.Command, args []string) {
|
||||
config.Config.Git.All()
|
||||
|
||||
// Configure the endpoint manually
|
||||
var endp config.Endpoint
|
||||
var endp lfsapi.Endpoint
|
||||
finder := lfsapi.NewEndpointFinder(config.Config.Git)
|
||||
if len(cloneUrl) > 0 {
|
||||
endp = config.NewEndpointFromCloneURL(cloneUrl)
|
||||
endp = finder.NewEndpointFromCloneURL(cloneUrl)
|
||||
} else {
|
||||
endp = config.NewEndpoint(apiUrl)
|
||||
endp = finder.NewEndpoint(apiUrl)
|
||||
}
|
||||
config.Config.SetManualEndpoint(endp)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user