1
1
//
2
- // Copyright 2022 Red Hat, Inc.
2
+ // Copyright 2023 Red Hat, Inc.
3
3
//
4
4
// Licensed under the Apache License, Version 2.0 (the "License");
5
5
// you may not use this file except in compliance with the License.
@@ -17,12 +17,9 @@ package util
17
17
18
18
import (
19
19
"fmt"
20
- gitpkg "github.com/go-git/go-git/v5"
21
- "github.com/go-git/go-git/v5/plumbing"
22
- githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
23
- "net/http"
24
20
"net/url"
25
21
"os"
22
+ "os/exec"
26
23
"path/filepath"
27
24
"strings"
28
25
)
@@ -39,27 +36,18 @@ const (
39
36
)
40
37
41
38
type GitUrl struct {
42
- Protocol string
43
- Host string
44
- Owner string
45
- Repo string
46
- Branch string
47
- Path string
48
- token string
49
- IsFile bool
39
+ Protocol string // URL scheme
40
+ Host string // URL domain name
41
+ Owner string // name of the repo owner
42
+ Repo string // name of the repo
43
+ Branch string // branch name
44
+ Path string // path to a directory or file in the repo
45
+ token string // used for authenticating a private repo
46
+ IsFile bool // defines if the URL points to a file in the repo
50
47
}
51
48
52
49
// ParseGitUrl extracts information from a GitHub, GitLab, or Bitbucket url
53
- // A client is used to check whether the url is private or public, and sets
54
- // the providers personal access token from the environment variable
55
50
func ParseGitUrl (fullUrl string ) (GitUrl , error ) {
56
- var c = http.Client {
57
- Timeout : HTTPRequestResponseTimeout ,
58
- }
59
- return parseGitUrlWithClient (fullUrl , c )
60
- }
61
-
62
- func parseGitUrlWithClient (fullUrl string , c http.Client ) (GitUrl , error ) {
63
51
var g GitUrl
64
52
65
53
err := ValidateURL (fullUrl )
@@ -77,24 +65,25 @@ func parseGitUrlWithClient(fullUrl string, c http.Client) (GitUrl, error) {
77
65
}
78
66
79
67
if parsedUrl .Host == RawGitHubHost || parsedUrl .Host == GitHubHost {
80
- g , err = parseGitHubUrl (g , parsedUrl , c )
68
+ err = g . parseGitHubUrl (parsedUrl )
81
69
} else if parsedUrl .Host == GitLabHost {
82
- g , err = parseGitLabUrl (g , parsedUrl , c )
70
+ err = g . parseGitLabUrl (parsedUrl )
83
71
} else if parsedUrl .Host == BitbucketHost {
84
- g , err = parseBitbucketUrl (g , parsedUrl , c )
72
+ err = g . parseBitbucketUrl (parsedUrl )
85
73
} else {
86
74
err = fmt .Errorf ("url host should be a valid GitHub, GitLab, or Bitbucket host; received: %s" , parsedUrl .Host )
87
75
}
88
76
89
77
return g , err
90
78
}
91
79
92
- func parseGitHubUrl (g GitUrl , url * url.URL , c http. Client ) ( GitUrl , error ) {
80
+ func (g * GitUrl ) parseGitHubUrl ( url * url.URL ) error {
93
81
var splitUrl []string
94
82
var err error
95
83
96
84
g .Protocol = url .Scheme
97
85
g .Host = url .Host
86
+ g .token = os .Getenv (GitHubToken )
98
87
99
88
if g .Host == RawGitHubHost {
100
89
g .IsFile = true
@@ -131,20 +120,17 @@ func parseGitHubUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
131
120
}
132
121
}
133
122
134
- if ! isGitUrlPublic (g , c ) {
135
- g .token = os .Getenv (GitHubToken )
136
- }
137
-
138
- return g , err
123
+ return err
139
124
}
140
125
141
- func parseGitLabUrl (g GitUrl , url * url.URL , c http. Client ) ( GitUrl , error ) {
126
+ func (g * GitUrl ) parseGitLabUrl ( url * url.URL ) error {
142
127
var splitFile , splitOrg []string
143
128
var err error
144
129
145
130
g .Protocol = url .Scheme
146
131
g .Host = url .Host
147
132
g .IsFile = false
133
+ g .token = os .Getenv (GitLabToken )
148
134
149
135
// GitLab urls contain a '-' separating the root of the repo
150
136
// and the path to a file or directory
@@ -175,20 +161,17 @@ func parseGitLabUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
175
161
}
176
162
}
177
163
178
- if ! isGitUrlPublic (g , c ) {
179
- g .token = os .Getenv (GitLabToken )
180
- }
181
-
182
- return g , err
164
+ return err
183
165
}
184
166
185
- func parseBitbucketUrl (g GitUrl , url * url.URL , c http. Client ) ( GitUrl , error ) {
167
+ func (g * GitUrl ) parseBitbucketUrl ( url * url.URL ) error {
186
168
var splitUrl []string
187
169
var err error
188
170
189
171
g .Protocol = url .Scheme
190
172
g .Host = url .Host
191
173
g .IsFile = false
174
+ g .token = os .Getenv (BitbucketToken )
192
175
193
176
splitUrl = strings .SplitN (url .Path [1 :], "/" , 5 )
194
177
if len (splitUrl ) < 2 {
@@ -215,61 +198,51 @@ func parseBitbucketUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
215
198
}
216
199
}
217
200
218
- if ! isGitUrlPublic (g , c ) {
219
- g .token = os .Getenv (BitbucketToken )
220
- }
221
-
222
- return g , err
201
+ return err
223
202
}
224
203
225
- func isGitUrlPublic (g GitUrl , c http.Client ) bool {
226
- host := g .Host
227
- if host == RawGitHubHost {
228
- host = GitHubHost
229
- }
230
-
231
- repo := fmt .Sprintf ("%s://%s/%s/%s" , g .Protocol , host , g .Owner , g .Repo )
232
-
233
- if res , err := c .Get (repo ); err != nil {
234
- return false
235
- } else if res .StatusCode == http .StatusOK {
236
- return true
237
- }
238
- return false
239
- }
240
-
241
- // CloneGitRepo clones a GitHub Repo to a destination directory
204
+ // CloneGitRepo clones a git repo to a destination directory
242
205
func CloneGitRepo (g GitUrl , destDir string ) error {
243
- var cloneOptions * gitpkg.CloneOptions
244
-
245
206
host := g .Host
246
207
if host == RawGitHubHost {
247
208
host = GitHubHost
248
209
}
249
210
211
+ isPublic := true
250
212
repoUrl := fmt .Sprintf ("%s://%s/%s/%s.git" , g .Protocol , host , g .Owner , g .Repo )
251
- branch := fmt .Sprintf ("refs/heads/%s" , g .Branch )
252
213
253
- cloneOptions = & gitpkg.CloneOptions {
254
- URL : repoUrl ,
255
- ReferenceName : plumbing .ReferenceName (branch ),
256
- SingleBranch : true ,
257
- Depth : 1 ,
214
+ params := HTTPRequestParams {
215
+ URL : repoUrl ,
258
216
}
259
217
260
- if g .token != "" {
261
- cloneOptions .Auth = & githttp.BasicAuth {
262
- // go-git auth allows username to be anything except
263
- // an empty string for GitHub and GitLab, however requires
264
- // for Bitbucket to be "x-token-auth"
265
- Username : "x-token-auth" ,
266
- Password : g .token ,
218
+ // check if the git repo is public
219
+ _ , err := HTTPGetRequest (params , 0 )
220
+ if err != nil {
221
+ // private git repo requires authentication
222
+ isPublic = false
223
+ repoUrl = fmt .Sprintf ("%s://token:%s@%s/%s/%s.git" , g .Protocol , g .token , host , g .Owner , g .Repo )
224
+ if g .Host == BitbucketHost {
225
+ repoUrl = fmt .Sprintf ("%s://x-token-auth:%s@%s/%s/%s.git" , g .Protocol , g .token , host , g .Owner , g .Repo )
267
226
}
268
227
}
269
228
270
- _ , err := gitpkg .PlainClone (destDir , false , cloneOptions )
229
+ /* #nosec G204 -- user input is processed into an expected format for the git clone command */
230
+ c := exec .Command ("git" , "clone" , repoUrl , destDir )
231
+ c .Dir = destDir
232
+
233
+ // set env to skip authentication prompt and directly error out
234
+ c .Env = os .Environ ()
235
+ c .Env = append (c .Env , "GIT_TERMINAL_PROMPT=0" , "GIT_ASKPASS=/bin/echo" )
236
+
237
+ _ , err = c .CombinedOutput ()
271
238
if err != nil {
272
- return err
239
+ if ! isPublic {
240
+ if g .token == "" {
241
+ return fmt .Errorf ("failed to clone repo without a token, ensure that a token is set if the repo is private. error: %v" , err )
242
+ }
243
+ return fmt .Errorf ("failed to clone repo with token, ensure that the url and token is correct. error: %v" , err )
244
+ }
245
+ return fmt .Errorf ("failed to clone repo, ensure that the url is correct. error: %v" , err )
273
246
}
274
247
275
248
return nil
0 commit comments