-
Notifications
You must be signed in to change notification settings - Fork 151
/
repo_url.go
182 lines (146 loc) · 4.67 KB
/
repo_url.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package gitproviders
import (
"fmt"
"net/url"
"strings"
"github.com/fluxcd/go-git-providers/github"
"github.com/fluxcd/go-git-providers/gitlab"
"github.com/spf13/viper"
"github.com/weaveworks/weave-gitops/pkg/utils"
)
type RepositoryURLProtocol string
const RepositoryURLProtocolHTTPS RepositoryURLProtocol = "https"
const RepositoryURLProtocolSSH RepositoryURLProtocol = "ssh"
type RepoURL struct {
repoName string
owner string
url *url.URL
normalized string
provider GitProviderName
protocol RepositoryURLProtocol
}
func NewRepoURL(uri string) (RepoURL, error) {
providerName, err := detectGitProviderFromUrl(uri, ViperGetStringMapString("git-host-types"))
if err != nil {
return RepoURL{}, fmt.Errorf("could not get provider name from URL %s: %w", uri, err)
}
normalized, err := normalizeRepoURLString(uri)
if err != nil {
return RepoURL{}, fmt.Errorf("could not normalize repo URL %s: %w", uri, err)
}
u, err := url.Parse(normalized)
if err != nil {
return RepoURL{}, fmt.Errorf("could not create normalized repo URL %s: %w", uri, err)
}
owner, err := getOwnerFromUrl(*u, providerName)
if err != nil {
return RepoURL{}, fmt.Errorf("could not get owner name from URL %s: %w", uri, err)
}
protocol := RepositoryURLProtocolSSH
if u.Scheme == "https" {
protocol = RepositoryURLProtocolHTTPS
}
return RepoURL{
repoName: utils.UrlToRepoName(uri),
owner: owner,
url: u,
normalized: normalized,
provider: providerName,
protocol: protocol,
}, nil
}
func (n RepoURL) String() string {
return n.normalized
}
func (n RepoURL) URL() *url.URL {
return n.url
}
func (n RepoURL) Owner() string {
return n.owner
}
func (n RepoURL) RepositoryName() string {
return n.repoName
}
func (n RepoURL) Provider() GitProviderName {
return n.provider
}
func (n RepoURL) Protocol() RepositoryURLProtocol {
return n.protocol
}
func getOwnerFromUrl(url url.URL, providerName GitProviderName) (string, error) {
url.Path = strings.TrimPrefix(url.Path, "/")
parts := strings.Split(url.Path, "/")
if len(parts) < 2 {
return "", fmt.Errorf("could not get owner from url %v", url.String())
}
if providerName == GitProviderGitLab {
if len(parts) > 3 {
return "", fmt.Errorf("a subgroup in a subgroup is not currently supported")
}
if len(parts) > 2 {
return parts[0] + "/" + parts[1], nil
}
}
return parts[0], nil
}
// detectGitProviderFromUrl accepts a url related to a git repo and
// returns the name of the provider associated.
func detectGitProviderFromUrl(raw string, gitHostTypes map[string]string) (GitProviderName, error) {
u, err := parseGitURL(raw)
if err != nil {
return "", fmt.Errorf("could not parse git repo url %q: %w", raw, err)
}
// defaults for github and gitlab
gitHostTypes[github.DefaultDomain] = string(GitProviderGitHub)
gitHostTypes[gitlab.DefaultDomain] = string(GitProviderGitLab)
provider := gitHostTypes[u.Host]
if provider == "" {
return "", fmt.Errorf("no git providers found for %q", raw)
}
return GitProviderName(provider), nil
}
// Hacks around "scp" formatted urls ($user@$host:$path)
// the `:` delimiter between host and path throws off the std. url parser
func parseGitURL(raw string) (*url.URL, error) {
if strings.HasPrefix(raw, "git@") {
// The first occurance of `:` should be the host:path delimiter.
raw = strings.Replace(raw, ":", "/", 1)
raw = "ssh://" + raw
}
return url.Parse(raw)
}
// normalizeRepoURLString accepts a url like git@github.com:someuser/podinfo.git and converts it into
// a string like ssh://git@github.com/someuser/podinfo.git. This helps standardize the different
// user inputs that might be provided.
func normalizeRepoURLString(url string) (string, error) {
// https://github.com/weaveworks/weave-gitops/issues/878
// A trailing slash causes problems when naming secrets.
url = strings.TrimSuffix(url, "/")
if !strings.HasSuffix(url, ".git") {
url = url + ".git"
}
u, err := parseGitURL(url)
if err != nil {
return "", fmt.Errorf("could not parse git repo url while normalizing %q: %w", url, err)
}
return fmt.Sprintf("ssh://git@%s%s", u.Host, u.Path), nil
}
// ViperGetStringMapString looks up a command line flag or env var in the format "foo=1,bar=2"
// GetStringMapString tries to JSON decode the env var
// If that fails (silently), try and decode the classic "foo=1,bar=2" form.
// https://github.com/spf13/viper/issues/911
func ViperGetStringMapString(key string) map[string]string {
sms := viper.GetStringMapString(key)
if len(sms) > 0 {
return sms
}
ss := viper.GetStringSlice(key)
out := map[string]string{}
for _, pair := range ss {
kv := strings.SplitN(pair, "=", 2)
if len(kv) == 2 {
out[kv[0]] = kv[1]
}
}
return out
}