1package lfs
2
3import (
4 "fmt"
5 "net/url"
6 "strings"
7)
8
9// Endpoint is a Git LFS endpoint.
10type Endpoint = *url.URL
11
12// NewEndpoint returns a new Git LFS endpoint.
13func NewEndpoint(rawurl string) (Endpoint, error) {
14 u, err := url.Parse(rawurl)
15 if err != nil {
16 e, err := endpointFromBareSSH(rawurl)
17 if err != nil {
18 return nil, err
19 }
20 u = e
21 }
22
23 u.Path = strings.TrimSuffix(u.Path, "/")
24
25 switch u.Scheme {
26 case "git":
27 // Use https for git:// URLs and strip the port if it exists.
28 u.Scheme = httpsScheme
29 if u.Port() != "" {
30 u.Host = u.Hostname()
31 }
32 fallthrough
33 case httpScheme, httpsScheme:
34 if strings.HasSuffix(u.Path, ".git") {
35 u.Path += "/info/lfs"
36 } else {
37 u.Path += ".git/info/lfs"
38 }
39 case "ssh", "git+ssh", "ssh+git":
40 default:
41 return nil, fmt.Errorf("unknown url: %s", rawurl)
42 }
43
44 return u, nil
45}
46
47// endpointFromBareSSH creates a new endpoint from a bare ssh repo.
48//
49// user@host.com:path/to/repo.git or
50// [user@host.com:port]:path/to/repo.git
51func endpointFromBareSSH(rawurl string) (*url.URL, error) {
52 parts := strings.Split(rawurl, ":")
53 partsLen := len(parts)
54 if partsLen < 2 {
55 return url.Parse(rawurl) //nolint:wrapcheck
56 }
57
58 // Treat presence of ':' as a bare URL
59 var newPath string
60 if len(parts) > 2 { // port included; really should only ever be 3 parts
61 // Correctly handle [host:port]:path URLs
62 parts[0] = strings.TrimPrefix(parts[0], "[")
63 parts[1] = strings.TrimSuffix(parts[1], "]")
64 newPath = fmt.Sprintf("%v:%v", parts[0], strings.Join(parts[1:], "/"))
65 } else {
66 newPath = strings.Join(parts, "/")
67 }
68 newrawurl := fmt.Sprintf("ssh://%v", newPath)
69 return url.Parse(newrawurl) //nolint:wrapcheck
70}