You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
3.4 KiB

  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. )
  18. // Command represents a command with its subcommands or arguments.
  19. type Command struct {
  20. name string
  21. args []string
  22. }
  23. func (c *Command) String() string {
  24. if len(c.args) == 0 {
  25. return c.name
  26. }
  27. return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " "))
  28. }
  29. // NewCommand creates and returns a new Git Command based on given command and arguments.
  30. func NewCommand(args ...string) *Command {
  31. return &Command{
  32. name: "git",
  33. args: append(GlobalCommandArgs, args...),
  34. }
  35. }
  36. // AddArguments adds new argument(s) to the command.
  37. func (c *Command) AddArguments(args ...string) *Command {
  38. c.args = append(c.args, args...)
  39. return c
  40. }
  41. // RunInDirTimeoutPipeline executes the command in given directory with given timeout,
  42. // it pipes stdout and stderr to given io.Writer.
  43. func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
  44. if timeout == -1 {
  45. timeout = 60 * time.Second
  46. }
  47. if len(dir) == 0 {
  48. log(c.String())
  49. } else {
  50. log("%s: %v", dir, c)
  51. }
  52. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  53. defer cancel()
  54. cmd := exec.CommandContext(ctx, c.name, c.args...)
  55. cmd.Dir = dir
  56. cmd.Stdout = stdout
  57. cmd.Stderr = stderr
  58. if err := cmd.Start(); err != nil {
  59. return err
  60. }
  61. if err := cmd.Wait(); err != nil {
  62. return err
  63. }
  64. return ctx.Err()
  65. }
  66. // RunInDirTimeout executes the command in given directory with given timeout,
  67. // and returns stdout in []byte and error (combined with stderr).
  68. func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
  69. stdout := new(bytes.Buffer)
  70. stderr := new(bytes.Buffer)
  71. if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil {
  72. return nil, concatenateError(err, stderr.String())
  73. }
  74. if stdout.Len() > 0 {
  75. log("stdout:\n%s", stdout.Bytes()[:1024])
  76. }
  77. return stdout.Bytes(), nil
  78. }
  79. // RunInDirPipeline executes the command in given directory,
  80. // it pipes stdout and stderr to given io.Writer.
  81. func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
  82. return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr)
  83. }
  84. // RunInDirBytes executes the command in given directory
  85. // and returns stdout in []byte and error (combined with stderr).
  86. func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
  87. return c.RunInDirTimeout(-1, dir)
  88. }
  89. // RunInDir executes the command in given directory
  90. // and returns stdout in string and error (combined with stderr).
  91. func (c *Command) RunInDir(dir string) (string, error) {
  92. stdout, err := c.RunInDirTimeout(-1, dir)
  93. if err != nil {
  94. return "", err
  95. }
  96. return string(stdout), nil
  97. }
  98. // RunTimeout executes the command in defualt working directory with given timeout,
  99. // and returns stdout in string and error (combined with stderr).
  100. func (c *Command) RunTimeout(timeout time.Duration) (string, error) {
  101. stdout, err := c.RunInDirTimeout(timeout, "")
  102. if err != nil {
  103. return "", err
  104. }
  105. return string(stdout), nil
  106. }
  107. // Run executes the command in defualt working directory
  108. // and returns stdout in string and error (combined with stderr).
  109. func (c *Command) Run() (string, error) {
  110. return c.RunTimeout(-1)
  111. }