Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions git/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package git

import (
"os"
"os/exec"
"strings"
)

const gitTerminalPromptKey = "GIT_TERMINAL_PROMPT="

// nonInteractiveGitEnv returns a copy of env with GIT_TERMINAL_PROMPT=0 so git
// never prompts for credentials on the controlling TTY during batch operations.
func nonInteractiveGitEnv(env []string) []string {
if env == nil {
env = os.Environ()
}
out := make([]string, 0, len(env)+1)
for _, e := range env {
if strings.HasPrefix(e, gitTerminalPromptKey) {
continue
}
out = append(out, e)
}
return append(out, "GIT_TERMINAL_PROMPT=0")
}

// PrepareNetworkGit configures cmd for fetch/push operations that may contact
// a remote. Callers must still set cmd.Dir (and stdout/stderr as needed).
func PrepareNetworkGit(cmd *exec.Cmd) {
cmd.Env = nonInteractiveGitEnv(cmd.Env)
}
61 changes: 61 additions & 0 deletions git/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package git

import (
"os/exec"
"strings"
"testing"

testutil "github.com/git-fire/git-testkit"
)

func TestNonInteractiveGitEnv_OverridesExistingPrompt(t *testing.T) {
env := nonInteractiveGitEnv([]string{
"HOME=/tmp",
"GIT_TERMINAL_PROMPT=1",
"PATH=/bin",
})
if !containsEnv(env, "GIT_TERMINAL_PROMPT=0") {
t.Fatalf("expected GIT_TERMINAL_PROMPT=0 in env, got %#v", env)
}
for _, e := range env {
if e == "GIT_TERMINAL_PROMPT=1" {
t.Fatalf("did not override existing GIT_TERMINAL_PROMPT: %#v", env)
}
}
}

func TestPrepareNetworkGit_SetsEnvOnCommand(t *testing.T) {
cmd := exec.Command("git", "version")
PrepareNetworkGit(cmd)
if !containsEnv(cmd.Env, "GIT_TERMINAL_PROMPT=0") {
t.Fatalf("prepareNetworkGit did not set GIT_TERMINAL_PROMPT=0: %#v", cmd.Env)
}
}

func TestFetchRemote_UnauthenticatedHTTPSFailsWithoutPrompt(t *testing.T) {
repo := testutil.CreateTestRepo(t, testutil.RepoOptions{
Name: "https-fetch-repo",
Remotes: map[string]string{
"origin": "https://github.com/git-fire/nonexistent-repo-auth-test.git",
},
})

err := FetchRemote(repo, "origin")
if err == nil {
t.Fatal("expected fetch to fail without credentials")
}
msg := strings.ToLower(err.Error())
if !strings.Contains(msg, "terminal prompts disabled") &&
!strings.Contains(msg, "could not read username") {
t.Fatalf("expected non-interactive auth failure, got: %v", err)
}
}

func containsEnv(env []string, want string) bool {
for _, e := range env {
if e == want {
return true
}
}
return false
}
4 changes: 4 additions & 0 deletions git/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func IsDirty(repoPath string) (bool, error) {
func DetectConflict(repoPath, branch, remote string) (bool, string, string, error) {
cmd := exec.Command("git", "fetch", remote)
cmd.Dir = repoPath
PrepareNetworkGit(cmd)
if output, err := cmd.CombinedOutput(); err != nil {
return false, "", "", commandError("git fetch", err, output)
}
Expand Down Expand Up @@ -162,6 +163,7 @@ func RefIsAncestor(repoPath, ancestorRef, descendantRef string) (bool, error) {
func FetchRemote(repoPath, remote string) error {
cmd := exec.Command("git", "fetch", remote)
cmd.Dir = repoPath
PrepareNetworkGit(cmd)
if output, err := cmd.CombinedOutput(); err != nil {
return commandError("git fetch", err, output)
}
Expand Down Expand Up @@ -191,6 +193,7 @@ func CreateFireBranch(repoPath, originalBranch, localSHA string) (string, error)
func PushBranch(repoPath, remote, branch string) error {
cmd := exec.Command("git", "push", remote, branch)
cmd.Dir = repoPath
PrepareNetworkGit(cmd)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
Expand All @@ -203,6 +206,7 @@ func PushBranch(repoPath, remote, branch string) error {
func PushAllBranches(repoPath, remote string) error {
cmd := exec.Command("git", "push", remote, "--all")
cmd.Dir = repoPath
PrepareNetworkGit(cmd)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
Expand Down
Loading