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.4 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. "crypto/tls"
  9. "fmt"
  10. "os"
  11. "strconv"
  12. "strings"
  13. "code.gitea.io/git"
  14. "code.gitea.io/gitea/models"
  15. "code.gitea.io/gitea/modules/base"
  16. "code.gitea.io/gitea/modules/httplib"
  17. "code.gitea.io/gitea/modules/log"
  18. "code.gitea.io/gitea/modules/setting"
  19. "github.com/Unknwon/com"
  20. "github.com/urfave/cli"
  21. )
  22. var (
  23. // CmdHook represents the available hooks sub-command.
  24. CmdHook = cli.Command{
  25. Name: "hook",
  26. Usage: "Delegate commands to corresponding Git hooks",
  27. Description: "This should only be called by Git",
  28. Flags: []cli.Flag{
  29. cli.StringFlag{
  30. Name: "config, c",
  31. Value: "custom/conf/app.ini",
  32. Usage: "Custom configuration file path",
  33. },
  34. },
  35. Subcommands: []cli.Command{
  36. subcmdHookPreReceive,
  37. subcmdHookUpadte,
  38. subcmdHookPostReceive,
  39. },
  40. }
  41. subcmdHookPreReceive = cli.Command{
  42. Name: "pre-receive",
  43. Usage: "Delegate pre-receive Git hook",
  44. Description: "This command should only be called by Git",
  45. Action: runHookPreReceive,
  46. }
  47. subcmdHookUpadte = cli.Command{
  48. Name: "update",
  49. Usage: "Delegate update Git hook",
  50. Description: "This command should only be called by Git",
  51. Action: runHookUpdate,
  52. }
  53. subcmdHookPostReceive = cli.Command{
  54. Name: "post-receive",
  55. Usage: "Delegate post-receive Git hook",
  56. Description: "This command should only be called by Git",
  57. Action: runHookPostReceive,
  58. }
  59. )
  60. func runHookPreReceive(c *cli.Context) error {
  61. if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
  62. return nil
  63. }
  64. if err := setup("hooks/pre-receive.log"); err != nil {
  65. fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err))
  66. }
  67. // the environment setted on serv command
  68. repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
  69. isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
  70. buf := bytes.NewBuffer(nil)
  71. scanner := bufio.NewScanner(os.Stdin)
  72. for scanner.Scan() {
  73. buf.Write(scanner.Bytes())
  74. buf.WriteByte('\n')
  75. // TODO: support news feeds for wiki
  76. if isWiki {
  77. continue
  78. }
  79. fields := bytes.Fields(scanner.Bytes())
  80. if len(fields) != 3 {
  81. continue
  82. }
  83. oldCommitID := string(fields[0])
  84. newCommitID := string(fields[1])
  85. refFullName := string(fields[2])
  86. branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
  87. protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
  88. if err != nil {
  89. log.GitLogger.Fatal(2, "retrieve protected branches information failed")
  90. }
  91. if protectBranch != nil {
  92. fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
  93. }
  94. // check and deletion
  95. if newCommitID == git.EmptySHA {
  96. fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "")
  97. }
  98. // Check force push
  99. output, err := git.NewCommand("rev-list", oldCommitID, "^"+newCommitID).Run()
  100. if err != nil {
  101. fail("Internal error", "Fail to detect force push: %v", err)
  102. } else if len(output) > 0 {
  103. fail(fmt.Sprintf("Branch '%s' is protected from force push", branchName), "")
  104. }
  105. }
  106. return nil
  107. }
  108. func runHookUpdate(c *cli.Context) error {
  109. if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
  110. return nil
  111. }
  112. if err := setup("hooks/update.log"); err != nil {
  113. fail("Hook update init failed", fmt.Sprintf("setup: %v", err))
  114. }
  115. return nil
  116. }
  117. func runHookPostReceive(c *cli.Context) error {
  118. if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
  119. return nil
  120. }
  121. if err := setup("hooks/post-receive.log"); err != nil {
  122. fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
  123. }
  124. // the environment setted on serv command
  125. repoUser := os.Getenv(models.EnvRepoUsername)
  126. repoUserSalt := os.Getenv(models.EnvRepoUserSalt)
  127. isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
  128. repoName := os.Getenv(models.EnvRepoName)
  129. pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
  130. pusherName := os.Getenv(models.EnvPusherName)
  131. buf := bytes.NewBuffer(nil)
  132. scanner := bufio.NewScanner(os.Stdin)
  133. for scanner.Scan() {
  134. buf.Write(scanner.Bytes())
  135. buf.WriteByte('\n')
  136. // TODO: support news feeds for wiki
  137. if isWiki {
  138. continue
  139. }
  140. fields := bytes.Fields(scanner.Bytes())
  141. if len(fields) != 3 {
  142. continue
  143. }
  144. oldCommitID := string(fields[0])
  145. newCommitID := string(fields[1])
  146. refFullName := string(fields[2])
  147. if err := models.PushUpdate(models.PushUpdateOptions{
  148. RefFullName: refFullName,
  149. OldCommitID: oldCommitID,
  150. NewCommitID: newCommitID,
  151. PusherID: pusherID,
  152. PusherName: pusherName,
  153. RepoUserName: repoUser,
  154. RepoName: repoName,
  155. }); err != nil {
  156. log.GitLogger.Error(2, "Update: %v", err)
  157. }
  158. // Ask for running deliver hook and test pull request tasks.
  159. reqURL := setting.LocalURL + repoUser + "/" + repoName + "/tasks/trigger?branch=" +
  160. strings.TrimPrefix(refFullName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUserSalt) + "&pusher=" + com.ToStr(pusherID)
  161. log.GitLogger.Trace("Trigger task: %s", reqURL)
  162. resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
  163. InsecureSkipVerify: true,
  164. }).Response()
  165. if err == nil {
  166. resp.Body.Close()
  167. if resp.StatusCode/100 != 2 {
  168. log.GitLogger.Error(2, "Failed to trigger task: not 2xx response code")
  169. }
  170. } else {
  171. log.GitLogger.Error(2, "Failed to trigger task: %v", err)
  172. }
  173. }
  174. return nil
  175. }