Skip to content

Commit d35a7c0

Browse files
authored
use git shell (#1791)
1 parent 61e9413 commit d35a7c0

File tree

4 files changed

+139
-39
lines changed

4 files changed

+139
-39
lines changed

backend/utils/github.go

+3-19
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ import (
1515
"github.com/diggerhq/digger/libs/ci"
1616
github2 "github.com/diggerhq/digger/libs/ci/github"
1717
"github.com/diggerhq/digger/libs/scheduler"
18-
"github.com/go-git/go-git/v5"
19-
"github.com/go-git/go-git/v5/plumbing"
20-
"github.com/go-git/go-git/v5/plumbing/transport/http"
2118
"github.com/google/go-github/v61/github"
2219
)
2320

@@ -33,25 +30,12 @@ type action func(string) error
3330

3431
func CloneGitRepoAndDoAction(repoUrl string, branch string, token string, action action) error {
3532
dir := createTempDir()
36-
cloneOptions := git.CloneOptions{
37-
URL: repoUrl,
38-
ReferenceName: plumbing.NewBranchReferenceName(branch),
39-
Depth: 1,
40-
SingleBranch: true,
41-
}
42-
43-
if token != "" {
44-
cloneOptions.Auth = &http.BasicAuth{
45-
Username: "x-access-token", // anything except an empty string
46-
Password: token,
47-
}
48-
}
49-
50-
_, err := git.PlainClone(dir, false, &cloneOptions)
33+
git := NewGitShellWithTokenAuth(dir, token)
34+
err := git.Clone(repoUrl, branch)
5135
if err != nil {
52-
log.Printf("PlainClone error: %v\n", err)
5336
return err
5437
}
38+
5539
defer func() {
5640
log.Printf("removing cloned directory %v", dir)
5741
ferr := os.RemoveAll(dir)

backend/utils/github_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ func TestGithubCloneWithPublicRepoThrowsNoError(t *testing.T) {
2525
assert.Nil(t, err)
2626
}
2727

28+
func TestGithubCloneWithPrivateRepoAndValidTokenThrowsNoError(t *testing.T) {
29+
token := os.Getenv("GITHUB_PAT_TOKEN")
30+
if token == "" {
31+
t.Skip()
32+
return
33+
}
34+
f := func(d string) error { return nil }
35+
err := CloneGitRepoAndDoAction("https://github.com/diggerhq/infra-gcp", "main", token, f)
36+
assert.Nil(t, err)
37+
}
38+
2839
func TestGithubCloneWithInvalidBranchThrowsError(t *testing.T) {
2940
token := os.Getenv("GITHUB_PAT_TOKEN")
3041
f := func(d string) error { return nil }

backend/utils/gitshell.go

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package utils
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"net/url"
8+
"os"
9+
"os/exec"
10+
"strings"
11+
"time"
12+
)
13+
14+
type GitAuth struct {
15+
Username string
16+
Password string // Can be either password or access token
17+
Token string // x-access-token
18+
}
19+
20+
type GitShell struct {
21+
workDir string
22+
timeout time.Duration
23+
environment []string
24+
auth *GitAuth
25+
}
26+
27+
func NewGitShell(workDir string, auth *GitAuth) *GitShell {
28+
env := os.Environ()
29+
30+
// If authentication is provided, set up credential helper
31+
if auth != nil {
32+
// Add credential helper to avoid interactive password prompts
33+
env = append(env, "GIT_TERMINAL_PROMPT=0")
34+
}
35+
36+
return &GitShell{
37+
workDir: workDir,
38+
timeout: 30 * time.Second,
39+
environment: env,
40+
auth: auth,
41+
}
42+
}
43+
44+
func NewGitShellWithTokenAuth(workDir string, token string) *GitShell {
45+
auth := GitAuth{
46+
Username: "x-access-token",
47+
Password: "",
48+
Token: token,
49+
}
50+
return NewGitShell(workDir, &auth)
51+
}
52+
53+
// formatAuthURL injects credentials into the Git URL
54+
func (g *GitShell) formatAuthURL(repoURL string) (string, error) {
55+
if g.auth == nil {
56+
return repoURL, nil
57+
}
58+
59+
parsedURL, err := url.Parse(repoURL)
60+
if err != nil {
61+
return "", fmt.Errorf("invalid URL: %v", err)
62+
}
63+
64+
// Handle different auth types
65+
if g.auth.Token != "" {
66+
// X-Access-Token authentication
67+
parsedURL.User = url.UserPassword("x-access-token", g.auth.Token)
68+
} else if g.auth.Username != "" {
69+
// Username/password or personal access token
70+
parsedURL.User = url.UserPassword(g.auth.Username, g.auth.Password)
71+
}
72+
73+
return parsedURL.String(), nil
74+
}
75+
76+
func (g *GitShell) runCommand(args ...string) (string, error) {
77+
ctx, cancel := context.WithTimeout(context.Background(), g.timeout)
78+
defer cancel()
79+
80+
cmd := exec.CommandContext(ctx, "git", args...)
81+
cmd.Dir = g.workDir
82+
cmd.Env = g.environment
83+
84+
// Set up credential helper for HTTPS
85+
if g.auth != nil {
86+
cmd.Env = append(cmd.Env, "GIT_ASKPASS=echo")
87+
if g.auth.Token != "" {
88+
cmd.Env = append(cmd.Env, fmt.Sprintf("GIT_ACCESS_TOKEN=%s", g.auth.Token))
89+
}
90+
}
91+
92+
var stdout, stderr bytes.Buffer
93+
cmd.Stdout = &stdout
94+
cmd.Stderr = &stderr
95+
96+
err := cmd.Run()
97+
if err != nil {
98+
if stderr.Len() > 0 {
99+
return "", fmt.Errorf("git command failed: %v: %s", err, stderr.String())
100+
}
101+
return "", err
102+
}
103+
return strings.TrimSpace(stdout.String()), nil
104+
}
105+
106+
// Clone with authentication
107+
func (g *GitShell) Clone(repoURL, branch string) error {
108+
authURL, err := g.formatAuthURL(repoURL)
109+
if err != nil {
110+
return err
111+
}
112+
113+
args := []string{"clone"}
114+
if branch != "" {
115+
args = append(args, "-b", branch)
116+
}
117+
args = append(args, "--depth", "1")
118+
args = append(args, "--single-branch", authURL, g.workDir)
119+
120+
_, err = g.runCommand(args...)
121+
return err
122+
}

ee/cli/pkg/utils/github.go

+3-20
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package utils
22

33
import (
4-
"github.com/go-git/go-git/v5"
5-
"github.com/go-git/go-git/v5/plumbing"
6-
"github.com/go-git/go-git/v5/plumbing/transport/http"
4+
"github.com/diggerhq/digger/backend/utils"
75
"log"
86
"os"
97
)
@@ -20,27 +18,12 @@ type action func(string) error
2018

2119
func CloneGitRepoAndDoAction(repoUrl string, branch string, token string, action action) error {
2220
dir := createTempDir()
23-
cloneOptions := git.CloneOptions{
24-
URL: repoUrl,
25-
ReferenceName: plumbing.NewBranchReferenceName(branch),
26-
Depth: 1,
27-
SingleBranch: true,
28-
}
29-
30-
if token != "" {
31-
cloneOptions.Auth = &http.BasicAuth{
32-
Username: "x-access-token", // anything except an empty string
33-
Password: token,
34-
}
35-
}
36-
37-
_, err := git.PlainClone(dir, false, &cloneOptions)
21+
git := utils.NewGitShellWithTokenAuth(dir, token)
22+
err := git.Clone(repoUrl, branch)
3823
if err != nil {
39-
log.Printf("PlainClone error: %v\n", err)
4024
return err
4125
}
4226
defer os.RemoveAll(dir)
43-
4427
err = action(dir)
4528
if err != nil {
4629
log.Printf("error performing action: %v", err)

0 commit comments

Comments
 (0)