Skip to content

Commit c4a087e

Browse files
committed
Fix issue checking out branches/tags on private repos
Use authentication for remote repository operations that use go-git to ensure we can read remote references in private repositories. Project clone now always checks for a credentials file mounted to the DWO hard-coded credentials path (/.git-credentials/credentials) and attempts to use credentials from there if they match on hostname. For information purposes, project clone now logs whether or not a personal-access-token was found. Signed-off-by: Angel Misevski <[email protected]>
1 parent e93e2a0 commit c4a087e

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

project-clone/internal/git/operations.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
gitConfig "github.com/go-git/go-git/v5/config"
2525
"github.com/go-git/go-git/v5/plumbing"
2626

27+
"github.com/devfile/devworkspace-operator/project-clone/internal"
2728
"github.com/devfile/devworkspace-operator/project-clone/internal/shell"
2829
)
2930

@@ -113,7 +114,13 @@ func CheckoutReference(repo *git.Repository, project *dw.Project, projectPath st
113114
return fmt.Errorf("could not find remote %s: %s", defaultRemoteName, err)
114115
}
115116

116-
refs, err := remote.List(&git.ListOptions{})
117+
auth, err := internal.GetAuthForHost(remote.Config().URLs[0])
118+
if err != nil {
119+
log.Printf("Error reading credentials file: %s", err)
120+
}
121+
refs, err := remote.List(&git.ListOptions{
122+
Auth: auth,
123+
})
117124
if err != nil {
118125
return fmt.Errorf("failed to read remote %s: %s", defaultRemoteName, err)
119126
}

project-clone/internal/global.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,28 @@
1616
package internal
1717

1818
import (
19+
"errors"
20+
"fmt"
1921
"log"
2022
"os"
23+
"regexp"
2124

2225
"github.com/devfile/devworkspace-operator/pkg/library/constants"
26+
gittransport "github.com/go-git/go-git/v5/plumbing/transport"
27+
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
28+
gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
29+
)
30+
31+
const (
32+
credentialsMountPath = "/.git-credentials/credentials"
2333
)
2434

2535
var (
26-
ProjectsRoot string
27-
CloneTmpDir string
36+
ProjectsRoot string
37+
CloneTmpDir string
38+
tokenAuthMethod map[string]*githttp.BasicAuth
39+
sshAuthMethod *gitssh.PublicKeys
40+
credentialsRegex = regexp.MustCompile(`https://(.+):(.+)@(.+)`)
2841
)
2942

3043
// Read and store ProjectsRoot env var for reuse throughout project-clone.
@@ -43,4 +56,60 @@ func init() {
4356
}
4457
log.Printf("Using temporary directory %s", tmpDir)
4558
CloneTmpDir = tmpDir
59+
60+
setupAuth()
61+
}
62+
63+
func GetAuthForHost(repoURLStr string) (gittransport.AuthMethod, error) {
64+
endpoint, err := gittransport.NewEndpoint(repoURLStr)
65+
if err != nil {
66+
return nil, fmt.Errorf("failed to parse url: %w", err)
67+
}
68+
switch endpoint.Protocol {
69+
case "ssh":
70+
// TODO
71+
return nil, fmt.Errorf("SSH support not yet implemented")
72+
case "http", "https":
73+
authMethod, ok := tokenAuthMethod[endpoint.Host]
74+
if !ok {
75+
log.Printf("No personal access token found for URL %s", repoURLStr)
76+
return nil, nil
77+
}
78+
log.Printf("Found personal access token for URL %s", repoURLStr)
79+
return authMethod, nil
80+
default:
81+
log.Printf("No personal access token for URL %s; unsupported protocol: %s", repoURLStr, endpoint.Protocol)
82+
}
83+
return nil, nil
84+
}
85+
86+
func setupAuth() {
87+
gitCredentials, err := os.ReadFile(credentialsMountPath)
88+
if err != nil {
89+
// If file does not exist, no credentials to mount
90+
if !errors.Is(err, os.ErrNotExist) {
91+
log.Printf("Unexpected error reading git credentials file: %s", err)
92+
os.Exit(1)
93+
}
94+
} else {
95+
tokenAuthMethod = parseCredentialsFile(string(gitCredentials))
96+
}
97+
}
98+
99+
func parseCredentialsFile(gitCredentials string) map[string]*githttp.BasicAuth {
100+
result := map[string]*githttp.BasicAuth{}
101+
matches := credentialsRegex.FindAllStringSubmatch(gitCredentials, -1)
102+
for idx, credential := range matches {
103+
if len(credential) != 4 {
104+
log.Printf("Malformed credential found in credentials file on line %d. Skipping", idx+1)
105+
}
106+
username := credential[1]
107+
pat := credential[2]
108+
url := credential[3]
109+
result[url] = &githttp.BasicAuth{
110+
Username: username,
111+
Password: pat,
112+
}
113+
}
114+
return result
46115
}

project-clone/main.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const (
3434
)
3535

3636
// TODO: Handle sparse checkout
37-
// TODO: Add support for auth
3837
func main() {
3938
f, err := os.Create(tmpLogFilePath)
4039
if err != nil {

0 commit comments

Comments
 (0)