Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

command.go 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package git
  5. import (
  6. "bytes"
  7. "context"
  8. "fmt"
  9. "io"
  10. "os/exec"
  11. "strings"
  12. "time"
  13. )
  14. var (
  15. // GlobalCommandArgs global command args for external package setting
  16. GlobalCommandArgs []string
  17. // DefaultCommandExecutionTimeout default command execution timeout duration
  18. DefaultCommandExecutionTimeout = 60 * time.Second
  19. )
  20. // Command represents a command with its subcommands or arguments.
  21. type Command struct {
  22. name string
  23. args []string
  24. }
  25. func (c *Command) String() string {
  26. if len(c.args) == 0 {
  27. return c.name
  28. }
  29. return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " "))
  30. }
  31. // NewCommand creates and returns a new Git Command based on given command and arguments.
  32. func NewCommand(args ...string) *Command {
  33. // Make an explicit copy of GlobalCommandArgs, otherwise append might overwrite it
  34. cargs := make([]string, len(GlobalCommandArgs))
  35. copy(cargs, GlobalCommandArgs)
  36. return &Command{
  37. name: GitExecutable,
  38. args: append(cargs, args...),
  39. }
  40. }
  41. // AddArguments adds new argument(s) to the command.
  42. func (c *Command) AddArguments(args ...string) *Command {
  43. c.args = append(c.args, args...)
  44. return c
  45. }
  46. // RunInDirTimeoutEnvPipeline executes the command in given directory with given timeout,
  47. // it pipes stdout and stderr to given io.Writer.
  48. func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer) error {
  49. return c.RunInDirTimeoutEnvFullPipeline(env, timeout, dir, stdout, stderr, nil)
  50. }
  51. // RunInDirTimeoutEnvFullPipeline executes the command in given directory with given timeout,
  52. // it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin.
  53. func (c *Command) RunInDirTimeoutEnvFullPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error {
  54. if timeout == -1 {
  55. timeout = DefaultCommandExecutionTimeout
  56. }
  57. if len(dir) == 0 {
  58. log(c.String())
  59. } else {
  60. log("%s: %v", dir, c)
  61. }
  62. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  63. defer cancel()
  64. cmd := exec.CommandContext(ctx, c.name, c.args...)
  65. cmd.Env = env
  66. cmd.Dir = dir
  67. cmd.Stdout = stdout
  68. cmd.Stderr = stderr
  69. cmd.Stdin = stdin
  70. if err := cmd.Start(); err != nil {
  71. return err
  72. }
  73. if err := cmd.Wait(); err != nil {
  74. return err
  75. }
  76. return ctx.Err()
  77. }
  78. // RunInDirTimeoutPipeline executes the command in given directory with given timeout,
  79. // it pipes stdout and stderr to given io.Writer.
  80. func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
  81. return c.RunInDirTimeoutEnvPipeline(nil, timeout, dir, stdout, stderr)
  82. }
  83. // RunInDirTimeoutFullPipeline executes the command in given directory with given timeout,
  84. // it pipes stdout and stderr to given io.Writer, and stdin from the given io.Reader
  85. func (c *Command) RunInDirTimeoutFullPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error {
  86. return c.RunInDirTimeoutEnvFullPipeline(nil, timeout, dir, stdout, stderr, stdin)
  87. }
  88. // RunInDirTimeout executes the command in given directory with given timeout,
  89. // and returns stdout in []byte and error (combined with stderr).
  90. func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
  91. return c.RunInDirTimeoutEnv(nil, timeout, dir)
  92. }
  93. // RunInDirTimeoutEnv executes the command in given directory with given timeout,
  94. // and returns stdout in []byte and error (combined with stderr).
  95. func (c *Command) RunInDirTimeoutEnv(env []string, timeout time.Duration, dir string) ([]byte, error) {
  96. stdout := new(bytes.Buffer)
  97. stderr := new(bytes.Buffer)
  98. if err := c.RunInDirTimeoutEnvPipeline(env, timeout, dir, stdout, stderr); err != nil {
  99. return nil, concatenateError(err, stderr.String())
  100. }
  101. if stdout.Len() > 0 {
  102. log("stdout:\n%s", stdout.Bytes()[:1024])
  103. }
  104. return stdout.Bytes(), nil
  105. }
  106. // RunInDirPipeline executes the command in given directory,
  107. // it pipes stdout and stderr to given io.Writer.
  108. func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
  109. return c.RunInDirFullPipeline(dir, stdout, stderr, nil)
  110. }
  111. // RunInDirFullPipeline executes the command in given directory,
  112. // it pipes stdout and stderr to given io.Writer.
  113. func (c *Command) RunInDirFullPipeline(dir string, stdout, stderr io.Writer, stdin io.Reader) error {
  114. return c.RunInDirTimeoutFullPipeline(-1, dir, stdout, stderr, stdin)
  115. }
  116. // RunInDirBytes executes the command in given directory
  117. // and returns stdout in []byte and error (combined with stderr).
  118. func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
  119. return c.RunInDirTimeout(-1, dir)
  120. }
  121. // RunInDir executes the command in given directory
  122. // and returns stdout in string and error (combined with stderr).
  123. func (c *Command) RunInDir(dir string) (string, error) {
  124. return c.RunInDirWithEnv(dir, nil)
  125. }
  126. // RunInDirWithEnv executes the command in given directory
  127. // and returns stdout in string and error (combined with stderr).
  128. func (c *Command) RunInDirWithEnv(dir string, env []string) (string, error) {
  129. stdout, err := c.RunInDirTimeoutEnv(env, -1, dir)
  130. if err != nil {
  131. return "", err
  132. }
  133. return string(stdout), nil
  134. }
  135. // RunTimeout executes the command in default working directory with given timeout,
  136. // and returns stdout in string and error (combined with stderr).
  137. func (c *Command) RunTimeout(timeout time.Duration) (string, error) {
  138. stdout, err := c.RunInDirTimeout(timeout, "")
  139. if err != nil {
  140. return "", err
  141. }
  142. return string(stdout), nil
  143. }
  144. // Run executes the command in default working directory
  145. // and returns stdout in string and error (combined with stderr).
  146. func (c *Command) Run() (string, error) {
  147. return c.RunTimeout(-1)
  148. }