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.

207 lines
5.5 KiB

  1. // Copyright 2017 The Gitea 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 cmd
  5. import (
  6. "bufio"
  7. "bytes"
  8. "fmt"
  9. "net/http"
  10. "os"
  11. "strconv"
  12. "strings"
  13. "code.gitea.io/gitea/models"
  14. "code.gitea.io/gitea/modules/git"
  15. "code.gitea.io/gitea/modules/private"
  16. "code.gitea.io/gitea/modules/setting"
  17. "github.com/urfave/cli"
  18. )
  19. var (
  20. // CmdHook represents the available hooks sub-command.
  21. CmdHook = cli.Command{
  22. Name: "hook",
  23. Usage: "Delegate commands to corresponding Git hooks",
  24. Description: "This should only be called by Git",
  25. Subcommands: []cli.Command{
  26. subcmdHookPreReceive,
  27. subcmdHookUpdate,
  28. subcmdHookPostReceive,
  29. },
  30. }
  31. subcmdHookPreReceive = cli.Command{
  32. Name: "pre-receive",
  33. Usage: "Delegate pre-receive Git hook",
  34. Description: "This command should only be called by Git",
  35. Action: runHookPreReceive,
  36. }
  37. subcmdHookUpdate = cli.Command{
  38. Name: "update",
  39. Usage: "Delegate update Git hook",
  40. Description: "This command should only be called by Git",
  41. Action: runHookUpdate,
  42. }
  43. subcmdHookPostReceive = cli.Command{
  44. Name: "post-receive",
  45. Usage: "Delegate post-receive Git hook",
  46. Description: "This command should only be called by Git",
  47. Action: runHookPostReceive,
  48. }
  49. )
  50. func runHookPreReceive(c *cli.Context) error {
  51. if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
  52. if setting.OnlyAllowPushIfGiteaEnvironmentSet {
  53. fail(`Rejecting changes as Gitea environment not set.
  54. If you are pushing over SSH you must push with a key managed by
  55. Gitea or set your environment appropriately.`, "")
  56. } else {
  57. return nil
  58. }
  59. }
  60. setup("hooks/pre-receive.log")
  61. // the environment setted on serv command
  62. isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
  63. username := os.Getenv(models.EnvRepoUsername)
  64. reponame := os.Getenv(models.EnvRepoName)
  65. userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
  66. prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
  67. isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
  68. buf := bytes.NewBuffer(nil)
  69. scanner := bufio.NewScanner(os.Stdin)
  70. for scanner.Scan() {
  71. buf.Write(scanner.Bytes())
  72. buf.WriteByte('\n')
  73. // TODO: support news feeds for wiki
  74. if isWiki {
  75. continue
  76. }
  77. fields := bytes.Fields(scanner.Bytes())
  78. if len(fields) != 3 {
  79. continue
  80. }
  81. oldCommitID := string(fields[0])
  82. newCommitID := string(fields[1])
  83. refFullName := string(fields[2])
  84. // If the ref is a branch, check if it's protected
  85. if strings.HasPrefix(refFullName, git.BranchPrefix) {
  86. statusCode, msg := private.HookPreReceive(username, reponame, private.HookOptions{
  87. OldCommitID: oldCommitID,
  88. NewCommitID: newCommitID,
  89. RefFullName: refFullName,
  90. UserID: userID,
  91. GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
  92. GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
  93. GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
  94. ProtectedBranchID: prID,
  95. IsDeployKey: isDeployKey,
  96. })
  97. switch statusCode {
  98. case http.StatusInternalServerError:
  99. fail("Internal Server Error", msg)
  100. case http.StatusForbidden:
  101. fail(msg, "")
  102. }
  103. }
  104. }
  105. return nil
  106. }
  107. func runHookUpdate(c *cli.Context) error {
  108. if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
  109. if setting.OnlyAllowPushIfGiteaEnvironmentSet {
  110. fail(`Rejecting changes as Gitea environment not set.
  111. If you are pushing over SSH you must push with a key managed by
  112. Gitea or set your environment appropriately.`, "")
  113. } else {
  114. return nil
  115. }
  116. }
  117. setup("hooks/update.log")
  118. return nil
  119. }
  120. func runHookPostReceive(c *cli.Context) error {
  121. if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
  122. if setting.OnlyAllowPushIfGiteaEnvironmentSet {
  123. fail(`Rejecting changes as Gitea environment not set.
  124. If you are pushing over SSH you must push with a key managed by
  125. Gitea or set your environment appropriately.`, "")
  126. } else {
  127. return nil
  128. }
  129. }
  130. setup("hooks/post-receive.log")
  131. // the environment setted on serv command
  132. repoUser := os.Getenv(models.EnvRepoUsername)
  133. isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
  134. repoName := os.Getenv(models.EnvRepoName)
  135. pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
  136. pusherName := os.Getenv(models.EnvPusherName)
  137. buf := bytes.NewBuffer(nil)
  138. scanner := bufio.NewScanner(os.Stdin)
  139. for scanner.Scan() {
  140. buf.Write(scanner.Bytes())
  141. buf.WriteByte('\n')
  142. // TODO: support news feeds for wiki
  143. if isWiki {
  144. continue
  145. }
  146. fields := bytes.Fields(scanner.Bytes())
  147. if len(fields) != 3 {
  148. continue
  149. }
  150. oldCommitID := string(fields[0])
  151. newCommitID := string(fields[1])
  152. refFullName := string(fields[2])
  153. res, err := private.HookPostReceive(repoUser, repoName, private.HookOptions{
  154. OldCommitID: oldCommitID,
  155. NewCommitID: newCommitID,
  156. RefFullName: refFullName,
  157. UserID: pusherID,
  158. UserName: pusherName,
  159. })
  160. if res == nil {
  161. fail("Internal Server Error", err)
  162. }
  163. if res["message"] == false {
  164. continue
  165. }
  166. fmt.Fprintln(os.Stderr, "")
  167. if res["create"] == true {
  168. fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res["branch"])
  169. fmt.Fprintf(os.Stderr, " %s\n", res["url"])
  170. } else {
  171. fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
  172. fmt.Fprintf(os.Stderr, " %s\n", res["url"])
  173. }
  174. fmt.Fprintln(os.Stderr, "")
  175. }
  176. return nil
  177. }