Skip to content

Commit

Permalink
use git shell (#1791)
Browse files Browse the repository at this point in the history
  • Loading branch information
motatoes authored Nov 1, 2024
1 parent 61e9413 commit d35a7c0
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 39 deletions.
22 changes: 3 additions & 19 deletions backend/utils/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ import (
"github.com/diggerhq/digger/libs/ci"
github2 "github.com/diggerhq/digger/libs/ci/github"
"github.com/diggerhq/digger/libs/scheduler"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/google/go-github/v61/github"
)

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

func CloneGitRepoAndDoAction(repoUrl string, branch string, token string, action action) error {
dir := createTempDir()
cloneOptions := git.CloneOptions{
URL: repoUrl,
ReferenceName: plumbing.NewBranchReferenceName(branch),
Depth: 1,
SingleBranch: true,
}

if token != "" {
cloneOptions.Auth = &http.BasicAuth{
Username: "x-access-token", // anything except an empty string
Password: token,
}
}

_, err := git.PlainClone(dir, false, &cloneOptions)
git := NewGitShellWithTokenAuth(dir, token)
err := git.Clone(repoUrl, branch)
if err != nil {
log.Printf("PlainClone error: %v\n", err)
return err
}

defer func() {
log.Printf("removing cloned directory %v", dir)
ferr := os.RemoveAll(dir)
Expand Down
11 changes: 11 additions & 0 deletions backend/utils/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ func TestGithubCloneWithPublicRepoThrowsNoError(t *testing.T) {
assert.Nil(t, err)
}

func TestGithubCloneWithPrivateRepoAndValidTokenThrowsNoError(t *testing.T) {
token := os.Getenv("GITHUB_PAT_TOKEN")
if token == "" {
t.Skip()
return
}
f := func(d string) error { return nil }
err := CloneGitRepoAndDoAction("https://github.com/diggerhq/infra-gcp", "main", token, f)
assert.Nil(t, err)
}

func TestGithubCloneWithInvalidBranchThrowsError(t *testing.T) {
token := os.Getenv("GITHUB_PAT_TOKEN")
f := func(d string) error { return nil }
Expand Down
122 changes: 122 additions & 0 deletions backend/utils/gitshell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package utils

import (
"bytes"
"context"
"fmt"
"net/url"
"os"
"os/exec"
"strings"
"time"
)

type GitAuth struct {
Username string
Password string // Can be either password or access token
Token string // x-access-token
}

type GitShell struct {
workDir string
timeout time.Duration
environment []string
auth *GitAuth
}

func NewGitShell(workDir string, auth *GitAuth) *GitShell {
env := os.Environ()

// If authentication is provided, set up credential helper
if auth != nil {
// Add credential helper to avoid interactive password prompts
env = append(env, "GIT_TERMINAL_PROMPT=0")
}

return &GitShell{
workDir: workDir,
timeout: 30 * time.Second,
environment: env,
auth: auth,
}
}

func NewGitShellWithTokenAuth(workDir string, token string) *GitShell {
auth := GitAuth{
Username: "x-access-token",
Password: "",
Token: token,
}
return NewGitShell(workDir, &auth)
}

// formatAuthURL injects credentials into the Git URL
func (g *GitShell) formatAuthURL(repoURL string) (string, error) {
if g.auth == nil {
return repoURL, nil
}

parsedURL, err := url.Parse(repoURL)
if err != nil {
return "", fmt.Errorf("invalid URL: %v", err)
}

// Handle different auth types
if g.auth.Token != "" {
// X-Access-Token authentication
parsedURL.User = url.UserPassword("x-access-token", g.auth.Token)
} else if g.auth.Username != "" {
// Username/password or personal access token
parsedURL.User = url.UserPassword(g.auth.Username, g.auth.Password)
}

return parsedURL.String(), nil
}

func (g *GitShell) runCommand(args ...string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), g.timeout)
defer cancel()

cmd := exec.CommandContext(ctx, "git", args...)
cmd.Dir = g.workDir
cmd.Env = g.environment

// Set up credential helper for HTTPS
if g.auth != nil {
cmd.Env = append(cmd.Env, "GIT_ASKPASS=echo")
if g.auth.Token != "" {
cmd.Env = append(cmd.Env, fmt.Sprintf("GIT_ACCESS_TOKEN=%s", g.auth.Token))
}
}

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

err := cmd.Run()
if err != nil {
if stderr.Len() > 0 {
return "", fmt.Errorf("git command failed: %v: %s", err, stderr.String())
}
return "", err
}
return strings.TrimSpace(stdout.String()), nil
}

// Clone with authentication
func (g *GitShell) Clone(repoURL, branch string) error {
authURL, err := g.formatAuthURL(repoURL)
if err != nil {
return err
}

args := []string{"clone"}
if branch != "" {
args = append(args, "-b", branch)
}
args = append(args, "--depth", "1")
args = append(args, "--single-branch", authURL, g.workDir)

_, err = g.runCommand(args...)
return err
}
23 changes: 3 additions & 20 deletions ee/cli/pkg/utils/github.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package utils

import (
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/diggerhq/digger/backend/utils"
"log"
"os"
)
Expand All @@ -20,27 +18,12 @@ type action func(string) error

func CloneGitRepoAndDoAction(repoUrl string, branch string, token string, action action) error {
dir := createTempDir()
cloneOptions := git.CloneOptions{
URL: repoUrl,
ReferenceName: plumbing.NewBranchReferenceName(branch),
Depth: 1,
SingleBranch: true,
}

if token != "" {
cloneOptions.Auth = &http.BasicAuth{
Username: "x-access-token", // anything except an empty string
Password: token,
}
}

_, err := git.PlainClone(dir, false, &cloneOptions)
git := utils.NewGitShellWithTokenAuth(dir, token)
err := git.Clone(repoUrl, branch)
if err != nil {
log.Printf("PlainClone error: %v\n", err)
return err
}
defer os.RemoveAll(dir)

err = action(dir)
if err != nil {
log.Printf("error performing action: %v", err)
Expand Down

0 comments on commit d35a7c0

Please sign in to comment.