1
0
Fork 0
forked from forgejo/forgejo

Remove git.Command.Run and git.Command.RunInDir* (#19280)

Follows #19266, #8553, Close #18553, now there are only three `Run..(&RunOpts{})` functions.
 * before: `stdout, err := RunInDir(path)`
 * now: `stdout, _, err := RunStdString(&git.RunOpts{Dir:path})`
This commit is contained in:
wxiaoguang 2022-04-01 10:55:30 +08:00 committed by GitHub
parent 3a73645502
commit 124b072f0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 594 additions and 672 deletions

View file

@ -94,8 +94,8 @@ func (c *Command) AddArguments(args ...string) *Command {
return c
}
// RunContext represents parameters to run the command
type RunContext struct {
// RunOpts represents parameters to run the command
type RunOpts struct {
Env []string
Timeout time.Duration
Dir string
@ -104,16 +104,19 @@ type RunContext struct {
PipelineFunc func(context.Context, context.CancelFunc) error
}
// RunWithContext run the command with context
func (c *Command) RunWithContext(rc *RunContext) error {
if rc.Timeout <= 0 {
rc.Timeout = defaultCommandExecutionTimeout
// Run runs the command with the RunOpts
func (c *Command) Run(opts *RunOpts) error {
if opts == nil {
opts = &RunOpts{}
}
if opts.Timeout <= 0 {
opts.Timeout = defaultCommandExecutionTimeout
}
if len(rc.Dir) == 0 {
if len(opts.Dir) == 0 {
log.Debug("%s", c)
} else {
log.Debug("%s: %v", rc.Dir, c)
log.Debug("%s: %v", opts.Dir, c)
}
desc := c.desc
@ -132,17 +135,17 @@ func (c *Command) RunWithContext(rc *RunContext) error {
args[urlArgIndex] = util.SanitizeCredentialURLs(args[urlArgIndex])
}
}
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), rc.Dir)
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), opts.Dir)
}
ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc)
ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, opts.Timeout, desc)
defer finished()
cmd := exec.CommandContext(ctx, c.name, c.args...)
if rc.Env == nil {
if opts.Env == nil {
cmd.Env = os.Environ()
} else {
cmd.Env = rc.Env
cmd.Env = opts.Env
}
cmd.Env = append(
@ -154,16 +157,16 @@ func (c *Command) RunWithContext(rc *RunContext) error {
"GIT_NO_REPLACE_OBJECTS=1",
)
cmd.Dir = rc.Dir
cmd.Stdout = rc.Stdout
cmd.Stderr = rc.Stderr
cmd.Stdin = rc.Stdin
cmd.Dir = opts.Dir
cmd.Stdout = opts.Stdout
cmd.Stderr = opts.Stderr
cmd.Stdin = opts.Stdin
if err := cmd.Start(); err != nil {
return err
}
if rc.PipelineFunc != nil {
err := rc.PipelineFunc(ctx, cancel)
if opts.PipelineFunc != nil {
err := opts.PipelineFunc(ctx, cancel)
if err != nil {
cancel()
_ = cmd.Wait()
@ -178,18 +181,18 @@ func (c *Command) RunWithContext(rc *RunContext) error {
return ctx.Err()
}
type RunError interface {
type RunStdError interface {
error
Stderr() string
}
type runError struct {
type runStdError struct {
err error
stderr string
errMsg string
}
func (r *runError) Error() string {
func (r *runStdError) Error() string {
// the stderr must be in the returned error text, some code only checks `strings.Contains(err.Error(), "git error")`
if r.errMsg == "" {
r.errMsg = ConcatenateError(r.err, r.stderr).Error()
@ -197,11 +200,11 @@ func (r *runError) Error() string {
return r.errMsg
}
func (r *runError) Unwrap() error {
func (r *runStdError) Unwrap() error {
return r.err
}
func (r *runError) Stderr() string {
func (r *runStdError) Stderr() string {
return r.stderr
}
@ -209,64 +212,40 @@ func bytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b)) // that's what Golang's strings.Builder.String() does (go/src/strings/builder.go)
}
// RunWithContextString run the command with context and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
func (c *Command) RunWithContextString(rc *RunContext) (stdout, stderr string, runErr RunError) {
stdoutBytes, stderrBytes, err := c.RunWithContextBytes(rc)
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) {
stdoutBytes, stderrBytes, err := c.RunStdBytes(opts)
stdout = bytesToString(stdoutBytes)
stderr = bytesToString(stderrBytes)
if err != nil {
return stdout, stderr, &runError{err: err, stderr: stderr}
return stdout, stderr, &runStdError{err: err, stderr: stderr}
}
// even if there is no err, there could still be some stderr output, so we just return stdout/stderr as they are
return stdout, stderr, nil
}
// RunWithContextBytes run the command with context and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
func (c *Command) RunWithContextBytes(rc *RunContext) (stdout, stderr []byte, runErr RunError) {
if rc.Stdout != nil || rc.Stderr != nil {
// RunStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
if opts == nil {
opts = &RunOpts{}
}
if opts.Stdout != nil || opts.Stderr != nil {
// we must panic here, otherwise there would be bugs if developers set Stdin/Stderr by mistake, and it would be very difficult to debug
panic("stdout and stderr field must be nil when using RunWithContextBytes")
panic("stdout and stderr field must be nil when using RunStdBytes")
}
stdoutBuf := &bytes.Buffer{}
stderrBuf := &bytes.Buffer{}
rc.Stdout = stdoutBuf
rc.Stderr = stderrBuf
err := c.RunWithContext(rc)
opts.Stdout = stdoutBuf
opts.Stderr = stderrBuf
err := c.Run(opts)
stderr = stderrBuf.Bytes()
if err != nil {
return nil, stderr, &runError{err: err, stderr: string(stderr)}
return nil, stderr, &runStdError{err: err, stderr: bytesToString(stderr)}
}
// even if there is no err, there could still be some stderr output
return stdoutBuf.Bytes(), stderr, nil
}
// RunInDirBytes executes the command in given directory
// and returns stdout in []byte and error (combined with stderr).
func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
stdout, _, err := c.RunWithContextBytes(&RunContext{Dir: dir})
return stdout, err
}
// RunInDir executes the command in given directory
// and returns stdout in string and error (combined with stderr).
func (c *Command) RunInDir(dir string) (string, error) {
return c.RunInDirWithEnv(dir, nil)
}
// RunInDirWithEnv executes the command in given directory
// and returns stdout in string and error (combined with stderr).
func (c *Command) RunInDirWithEnv(dir string, env []string) (string, error) {
stdout, _, err := c.RunWithContextString(&RunContext{Env: env, Dir: dir})
return stdout, err
}
// Run executes the command in default working directory
// and returns stdout in string and error (combined with stderr).
func (c *Command) Run() (string, error) {
stdout, _, err := c.RunWithContextString(&RunContext{})
return stdout, err
}
// AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests
func AllowLFSFiltersArgs() []string {
// Now here we should explicitly allow lfs filters to run