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.

hook.go 5.3KB

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