github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/git/command.go (about) 1 package git 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "io" 8 "os/exec" 9 10 "github.com/ungtb10d/cli/v2/internal/run" 11 ) 12 13 type commandCtx = func(ctx context.Context, name string, args ...string) *exec.Cmd 14 15 type gitCommand struct { 16 *exec.Cmd 17 } 18 19 func (gc *gitCommand) Run() error { 20 stderr := &bytes.Buffer{} 21 if gc.Cmd.Stderr == nil { 22 gc.Cmd.Stderr = stderr 23 } 24 // This is a hack in order to not break the hundreds of 25 // existing tests that rely on `run.PrepareCmd` to be invoked. 26 err := run.PrepareCmd(gc.Cmd).Run() 27 if err != nil { 28 ge := GitError{err: err, Stderr: stderr.String()} 29 var exitError *exec.ExitError 30 if errors.As(err, &exitError) { 31 ge.ExitCode = exitError.ExitCode() 32 } 33 return &ge 34 } 35 return nil 36 } 37 38 func (gc *gitCommand) Output() ([]byte, error) { 39 gc.Stdout = nil 40 gc.Stderr = nil 41 // This is a hack in order to not break the hundreds of 42 // existing tests that rely on `run.PrepareCmd` to be invoked. 43 out, err := run.PrepareCmd(gc.Cmd).Output() 44 if err != nil { 45 ge := GitError{err: err} 46 var exitError *exec.ExitError 47 if errors.As(err, &exitError) { 48 ge.Stderr = string(exitError.Stderr) 49 ge.ExitCode = exitError.ExitCode() 50 } 51 err = &ge 52 } 53 return out, err 54 } 55 56 func (gc *gitCommand) setRepoDir(repoDir string) { 57 for i, arg := range gc.Args { 58 if arg == "-C" { 59 gc.Args[i+1] = repoDir 60 return 61 } 62 } 63 // Handle "--" invocations for testing purposes. 64 var index int 65 for i, arg := range gc.Args { 66 if arg == "--" { 67 index = i + 1 68 } 69 } 70 gc.Args = append(gc.Args[:index+3], gc.Args[index+1:]...) 71 gc.Args[index+1] = "-C" 72 gc.Args[index+2] = repoDir 73 } 74 75 // Allow individual commands to be modified from the default client options. 76 type CommandModifier func(*gitCommand) 77 78 func WithStderr(stderr io.Writer) CommandModifier { 79 return func(gc *gitCommand) { 80 gc.Stderr = stderr 81 } 82 } 83 84 func WithStdout(stdout io.Writer) CommandModifier { 85 return func(gc *gitCommand) { 86 gc.Stdout = stdout 87 } 88 } 89 90 func WithStdin(stdin io.Reader) CommandModifier { 91 return func(gc *gitCommand) { 92 gc.Stdin = stdin 93 } 94 } 95 96 func WithRepoDir(repoDir string) CommandModifier { 97 return func(gc *gitCommand) { 98 gc.setRepoDir(repoDir) 99 } 100 }