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.

hooks.go 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // Copyright 2020 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 repository
  5. import (
  6. "context"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "xorm.io/builder"
  15. )
  16. // CreateDelegateHooks creates all the hooks scripts for the repo
  17. func CreateDelegateHooks(repoPath string) error {
  18. return createDelegateHooks(repoPath)
  19. }
  20. // createDelegateHooks creates all the hooks scripts for the repo
  21. func createDelegateHooks(repoPath string) (err error) {
  22. var (
  23. hookNames = []string{"pre-receive", "update", "post-receive"}
  24. hookTpls = []string{
  25. fmt.Sprintf("#!/usr/bin/env %s\ndata=$(cat)\nexitcodes=\"\"\nhookname=$(basename $0)\nGIT_DIR=${GIT_DIR:-$(dirname $0)}\n\nfor hook in ${GIT_DIR}/hooks/${hookname}.d/*; do\ntest -x \"${hook}\" || continue\necho \"${data}\" | \"${hook}\"\nexitcodes=\"${exitcodes} $?\"\ndone\n\nfor i in ${exitcodes}; do\n[ ${i} -eq 0 ] || exit ${i}\ndone\n", setting.ScriptType),
  26. fmt.Sprintf("#!/usr/bin/env %s\nexitcodes=\"\"\nhookname=$(basename $0)\nGIT_DIR=${GIT_DIR:-$(dirname $0)}\n\nfor hook in ${GIT_DIR}/hooks/${hookname}.d/*; do\ntest -x \"${hook}\" || continue\n\"${hook}\" $1 $2 $3\nexitcodes=\"${exitcodes} $?\"\ndone\n\nfor i in ${exitcodes}; do\n[ ${i} -eq 0 ] || exit ${i}\ndone\n", setting.ScriptType),
  27. fmt.Sprintf("#!/usr/bin/env %s\ndata=$(cat)\nexitcodes=\"\"\nhookname=$(basename $0)\nGIT_DIR=${GIT_DIR:-$(dirname $0)}\n\nfor hook in ${GIT_DIR}/hooks/${hookname}.d/*; do\ntest -x \"${hook}\" || continue\necho \"${data}\" | \"${hook}\"\nexitcodes=\"${exitcodes} $?\"\ndone\n\nfor i in ${exitcodes}; do\n[ ${i} -eq 0 ] || exit ${i}\ndone\n", setting.ScriptType),
  28. }
  29. giteaHookTpls = []string{
  30. fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
  31. fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
  32. fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
  33. }
  34. )
  35. hookDir := filepath.Join(repoPath, "hooks")
  36. for i, hookName := range hookNames {
  37. oldHookPath := filepath.Join(hookDir, hookName)
  38. newHookPath := filepath.Join(hookDir, hookName+".d", "gitea")
  39. if err := os.MkdirAll(filepath.Join(hookDir, hookName+".d"), os.ModePerm); err != nil {
  40. return fmt.Errorf("create hooks dir '%s': %v", filepath.Join(hookDir, hookName+".d"), err)
  41. }
  42. // WARNING: This will override all old server-side hooks
  43. if err = os.Remove(oldHookPath); err != nil && !os.IsNotExist(err) {
  44. return fmt.Errorf("unable to pre-remove old hook file '%s' prior to rewriting: %v ", oldHookPath, err)
  45. }
  46. if err = ioutil.WriteFile(oldHookPath, []byte(hookTpls[i]), 0777); err != nil {
  47. return fmt.Errorf("write old hook file '%s': %v", oldHookPath, err)
  48. }
  49. if err = os.Remove(newHookPath); err != nil && !os.IsNotExist(err) {
  50. return fmt.Errorf("unable to pre-remove new hook file '%s' prior to rewriting: %v", newHookPath, err)
  51. }
  52. if err = ioutil.WriteFile(newHookPath, []byte(giteaHookTpls[i]), 0777); err != nil {
  53. return fmt.Errorf("write new hook file '%s': %v", newHookPath, err)
  54. }
  55. }
  56. return nil
  57. }
  58. // SyncRepositoryHooks rewrites all repositories' pre-receive, update and post-receive hooks
  59. // to make sure the binary and custom conf path are up-to-date.
  60. func SyncRepositoryHooks(ctx context.Context) error {
  61. log.Trace("Doing: SyncRepositoryHooks")
  62. if err := models.Iterate(
  63. models.DefaultDBContext(),
  64. new(models.Repository),
  65. builder.Gt{"id": 0},
  66. func(idx int, bean interface{}) error {
  67. select {
  68. case <-ctx.Done():
  69. return fmt.Errorf("Aborted due to shutdown")
  70. default:
  71. }
  72. if err := createDelegateHooks(bean.(*models.Repository).RepoPath()); err != nil {
  73. return fmt.Errorf("SyncRepositoryHook: %v", err)
  74. }
  75. if bean.(*models.Repository).HasWiki() {
  76. if err := createDelegateHooks(bean.(*models.Repository).WikiPath()); err != nil {
  77. return fmt.Errorf("SyncRepositoryHook: %v", err)
  78. }
  79. }
  80. return nil
  81. },
  82. ); err != nil {
  83. return err
  84. }
  85. log.Trace("Finished: SyncRepositoryHooks")
  86. return nil
  87. }