Skip to content

Commit 28571a2

Browse files
committed
checking for private repo in clone function
Signed-off-by: Michael Hoang <mhoang@redhat.com>
1 parent 4bdc1e7 commit 28571a2

File tree

5 files changed

+163
-178
lines changed

5 files changed

+163
-178
lines changed

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,21 @@ The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/g
167167
resources, err := ParseKubernetesYaml(values)
168168
```
169169

170+
8. To get resources from Git repositories
171+
```go
172+
// Parse the repo url
173+
gitUrl, err := util.ParseGitUrl(url)
174+
175+
// Clone the repo to a destination dir
176+
err = util.CloneGitRepo(gitUrl, destDir)
177+
```
178+
If repository is private, set the correct environment variables
179+
```shell
180+
# credentials for private repositories
181+
export GITHUB_TOKEN=<account_token>
182+
export GITLAB_TOKEN=<account_token>
183+
export BITBUCKET_TOKEN=<account_token>
184+
```
170185

171186
## Projects using devfile/library
172187

go.mod

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ require (
77
github.com/devfile/registry-support/registry-library v0.0.0-20221018213054-47b3ffaeadba
88
github.com/fatih/color v1.7.0
99
github.com/fsnotify/fsnotify v1.4.9
10-
github.com/go-git/go-git/v5 v5.4.2
1110
github.com/gobwas/glob v0.2.3
1211
github.com/golang/mock v1.6.0
1312
github.com/google/go-cmp v0.5.6

pkg/devfile/parser/parse.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2022 Red Hat, Inc.
2+
// Copyright 2022-2023 Red Hat, Inc.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.

pkg/util/git.go

+50-77
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2022 Red Hat, Inc.
2+
// Copyright 2023 Red Hat, Inc.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.
@@ -17,12 +17,9 @@ package util
1717

1818
import (
1919
"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"
2420
"net/url"
2521
"os"
22+
"os/exec"
2623
"path/filepath"
2724
"strings"
2825
)
@@ -39,27 +36,18 @@ const (
3936
)
4037

4138
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
5047
}
5148

5249
// 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
5550
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) {
6351
var g GitUrl
6452

6553
err := ValidateURL(fullUrl)
@@ -77,24 +65,25 @@ func parseGitUrlWithClient(fullUrl string, c http.Client) (GitUrl, error) {
7765
}
7866

7967
if parsedUrl.Host == RawGitHubHost || parsedUrl.Host == GitHubHost {
80-
g, err = parseGitHubUrl(g, parsedUrl, c)
68+
err = g.parseGitHubUrl(parsedUrl)
8169
} else if parsedUrl.Host == GitLabHost {
82-
g, err = parseGitLabUrl(g, parsedUrl, c)
70+
err = g.parseGitLabUrl(parsedUrl)
8371
} else if parsedUrl.Host == BitbucketHost {
84-
g, err = parseBitbucketUrl(g, parsedUrl, c)
72+
err = g.parseBitbucketUrl(parsedUrl)
8573
} else {
8674
err = fmt.Errorf("url host should be a valid GitHub, GitLab, or Bitbucket host; received: %s", parsedUrl.Host)
8775
}
8876

8977
return g, err
9078
}
9179

92-
func parseGitHubUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
80+
func (g *GitUrl) parseGitHubUrl(url *url.URL) error {
9381
var splitUrl []string
9482
var err error
9583

9684
g.Protocol = url.Scheme
9785
g.Host = url.Host
86+
g.token = os.Getenv(GitHubToken)
9887

9988
if g.Host == RawGitHubHost {
10089
g.IsFile = true
@@ -131,20 +120,17 @@ func parseGitHubUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
131120
}
132121
}
133122

134-
if !isGitUrlPublic(g, c) {
135-
g.token = os.Getenv(GitHubToken)
136-
}
137-
138-
return g, err
123+
return err
139124
}
140125

141-
func parseGitLabUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
126+
func (g *GitUrl) parseGitLabUrl(url *url.URL) error {
142127
var splitFile, splitOrg []string
143128
var err error
144129

145130
g.Protocol = url.Scheme
146131
g.Host = url.Host
147132
g.IsFile = false
133+
g.token = os.Getenv(GitLabToken)
148134

149135
// GitLab urls contain a '-' separating the root of the repo
150136
// and the path to a file or directory
@@ -175,20 +161,17 @@ func parseGitLabUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
175161
}
176162
}
177163

178-
if !isGitUrlPublic(g, c) {
179-
g.token = os.Getenv(GitLabToken)
180-
}
181-
182-
return g, err
164+
return err
183165
}
184166

185-
func parseBitbucketUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
167+
func (g *GitUrl) parseBitbucketUrl(url *url.URL) error {
186168
var splitUrl []string
187169
var err error
188170

189171
g.Protocol = url.Scheme
190172
g.Host = url.Host
191173
g.IsFile = false
174+
g.token = os.Getenv(BitbucketToken)
192175

193176
splitUrl = strings.SplitN(url.Path[1:], "/", 5)
194177
if len(splitUrl) < 2 {
@@ -215,61 +198,51 @@ func parseBitbucketUrl(g GitUrl, url *url.URL, c http.Client) (GitUrl, error) {
215198
}
216199
}
217200

218-
if !isGitUrlPublic(g, c) {
219-
g.token = os.Getenv(BitbucketToken)
220-
}
221-
222-
return g, err
201+
return err
223202
}
224203

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
242205
func CloneGitRepo(g GitUrl, destDir string) error {
243-
var cloneOptions *gitpkg.CloneOptions
244-
245206
host := g.Host
246207
if host == RawGitHubHost {
247208
host = GitHubHost
248209
}
249210

211+
isPublic := true
250212
repoUrl := fmt.Sprintf("%s://%s/%s/%s.git", g.Protocol, host, g.Owner, g.Repo)
251-
branch := fmt.Sprintf("refs/heads/%s", g.Branch)
252213

253-
cloneOptions = &gitpkg.CloneOptions{
254-
URL: repoUrl,
255-
ReferenceName: plumbing.ReferenceName(branch),
256-
SingleBranch: true,
257-
Depth: 1,
214+
params := HTTPRequestParams{
215+
URL: repoUrl,
258216
}
259217

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)
267226
}
268227
}
269228

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()
271238
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)
273246
}
274247

275248
return nil

0 commit comments

Comments
 (0)