feat(git): add interactive push mode for SSH passphrase support
Introduces gitInteractive() that connects stdin/stdout to the terminal, allowing SSH to prompt for passphrases. Also adds GitError type to improve error messages by including stderr output. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2252d6bda1
commit
dcb0871b61
1 changed files with 50 additions and 2 deletions
|
|
@ -4,6 +4,8 @@ package git
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -139,9 +141,32 @@ func getAheadBehind(ctx context.Context, path string) (ahead, behind int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push pushes commits for a single repository.
|
// Push pushes commits for a single repository.
|
||||||
|
// Uses interactive mode to support SSH passphrase prompts.
|
||||||
func Push(ctx context.Context, path string) error {
|
func Push(ctx context.Context, path string) error {
|
||||||
_, err := gitCommand(ctx, path, "push")
|
return gitInteractive(ctx, path, "push")
|
||||||
return err
|
}
|
||||||
|
|
||||||
|
// gitInteractive runs a git command with terminal attached for user interaction.
|
||||||
|
func gitInteractive(ctx context.Context, dir string, args ...string) error {
|
||||||
|
cmd := exec.CommandContext(ctx, "git", args...)
|
||||||
|
cmd.Dir = dir
|
||||||
|
|
||||||
|
// Connect to terminal for SSH passphrase prompts
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
|
||||||
|
// Capture stderr for error reporting while also showing it
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stderr = io.MultiWriter(os.Stderr, &stderr)
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
if stderr.Len() > 0 {
|
||||||
|
return &GitError{Err: err, Stderr: stderr.String()}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushResult represents the result of a push operation.
|
// PushResult represents the result of a push operation.
|
||||||
|
|
@ -191,8 +216,31 @@ func gitCommand(ctx context.Context, dir string, args ...string) (string, error)
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
|
// Include stderr in error message for better diagnostics
|
||||||
|
if stderr.Len() > 0 {
|
||||||
|
return "", &GitError{Err: err, Stderr: stderr.String()}
|
||||||
|
}
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return stdout.String(), nil
|
return stdout.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GitError wraps a git command error with stderr output.
|
||||||
|
type GitError struct {
|
||||||
|
Err error
|
||||||
|
Stderr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GitError) Error() string {
|
||||||
|
// Return just the stderr message, trimmed
|
||||||
|
msg := strings.TrimSpace(e.Stderr)
|
||||||
|
if msg != "" {
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GitError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue