diff options
Diffstat (limited to 'modules/git/command.go')
-rw-r--r-- | modules/git/command.go | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/modules/git/command.go b/modules/git/command.go new file mode 100644 index 0000000000..d354635119 --- /dev/null +++ b/modules/git/command.go @@ -0,0 +1,137 @@ +// Copyright 2015 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package git + +import ( + "bytes" + "context" + "fmt" + "io" + "os/exec" + "strings" + "time" +) + +var ( + // GlobalCommandArgs global command args for external package setting + GlobalCommandArgs []string + + // DefaultCommandExecutionTimeout default command execution timeout duration + DefaultCommandExecutionTimeout = 60 * time.Second +) + +// Command represents a command with its subcommands or arguments. +type Command struct { + name string + args []string +} + +func (c *Command) String() string { + if len(c.args) == 0 { + return c.name + } + return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " ")) +} + +// NewCommand creates and returns a new Git Command based on given command and arguments. +func NewCommand(args ...string) *Command { + // Make an explicit copy of GlobalCommandArgs, otherwise append might overwrite it + cargs := make([]string, len(GlobalCommandArgs)) + copy(cargs, GlobalCommandArgs) + return &Command{ + name: "git", + args: append(cargs, args...), + } +} + +// AddArguments adds new argument(s) to the command. +func (c *Command) AddArguments(args ...string) *Command { + c.args = append(c.args, args...) + return c +} + +// RunInDirTimeoutPipeline executes the command in given directory with given timeout, +// it pipes stdout and stderr to given io.Writer. +func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error { + if timeout == -1 { + timeout = DefaultCommandExecutionTimeout + } + + if len(dir) == 0 { + log(c.String()) + } else { + log("%s: %v", dir, c) + } + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + cmd := exec.CommandContext(ctx, c.name, c.args...) + cmd.Dir = dir + cmd.Stdout = stdout + cmd.Stderr = stderr + if err := cmd.Start(); err != nil { + return err + } + + if err := cmd.Wait(); err != nil { + return err + } + + return ctx.Err() +} + +// RunInDirTimeout executes the command in given directory with given timeout, +// and returns stdout in []byte and error (combined with stderr). +func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) { + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil { + return nil, concatenateError(err, stderr.String()) + } + + if stdout.Len() > 0 { + log("stdout:\n%s", stdout.Bytes()[:1024]) + } + return stdout.Bytes(), nil +} + +// RunInDirPipeline executes the command in given directory, +// it pipes stdout and stderr to given io.Writer. +func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error { + return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr) +} + +// 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) { + return c.RunInDirTimeout(-1, dir) +} + +// 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) { + stdout, err := c.RunInDirTimeout(-1, dir) + if err != nil { + return "", err + } + return string(stdout), nil +} + +// RunTimeout executes the command in default working directory with given timeout, +// and returns stdout in string and error (combined with stderr). +func (c *Command) RunTimeout(timeout time.Duration) (string, error) { + stdout, err := c.RunInDirTimeout(timeout, "") + if err != nil { + return "", err + } + return string(stdout), nil +} + +// Run executes the command in default working directory +// and returns stdout in string and error (combined with stderr). +func (c *Command) Run() (string, error) { + return c.RunTimeout(-1) +} |