code.gitea.io/gitea@v1.19.3/modules/git/hook.go (about) 1 // Copyright 2015 The Gogs Authors. All rights reserved. 2 // Copyright 2021 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package git 6 7 import ( 8 "errors" 9 "os" 10 "path" 11 "path/filepath" 12 "strings" 13 14 "code.gitea.io/gitea/modules/log" 15 "code.gitea.io/gitea/modules/util" 16 ) 17 18 // hookNames is a list of Git server hooks' name that are supported. 19 var hookNames = []string{ 20 "pre-receive", 21 "update", 22 "post-receive", 23 } 24 25 // ErrNotValidHook error when a git hook is not valid 26 var ErrNotValidHook = errors.New("not a valid Git hook") 27 28 // IsValidHookName returns true if given name is a valid Git hook. 29 func IsValidHookName(name string) bool { 30 for _, hn := range hookNames { 31 if hn == name { 32 return true 33 } 34 } 35 return false 36 } 37 38 // Hook represents a Git hook. 39 type Hook struct { 40 name string 41 IsActive bool // Indicates whether repository has this hook. 42 Content string // Content of hook if it's active. 43 Sample string // Sample content from Git. 44 path string // Hook file path. 45 } 46 47 // GetHook returns a Git hook by given name and repository. 48 func GetHook(repoPath, name string) (*Hook, error) { 49 if !IsValidHookName(name) { 50 return nil, ErrNotValidHook 51 } 52 h := &Hook{ 53 name: name, 54 path: path.Join(repoPath, "hooks", name+".d", name), 55 } 56 samplePath := filepath.Join(repoPath, "hooks", name+".sample") 57 if isFile(h.path) { 58 data, err := os.ReadFile(h.path) 59 if err != nil { 60 return nil, err 61 } 62 h.IsActive = true 63 h.Content = string(data) 64 } else if isFile(samplePath) { 65 data, err := os.ReadFile(samplePath) 66 if err != nil { 67 return nil, err 68 } 69 h.Sample = string(data) 70 } 71 return h, nil 72 } 73 74 // Name return the name of the hook 75 func (h *Hook) Name() string { 76 return h.name 77 } 78 79 // Update updates hook settings. 80 func (h *Hook) Update() error { 81 if len(strings.TrimSpace(h.Content)) == 0 { 82 if isExist(h.path) { 83 err := util.Remove(h.path) 84 if err != nil { 85 return err 86 } 87 } 88 h.IsActive = false 89 return nil 90 } 91 d := filepath.Dir(h.path) 92 if err := os.MkdirAll(d, os.ModePerm); err != nil { 93 return err 94 } 95 96 err := os.WriteFile(h.path, []byte(strings.ReplaceAll(h.Content, "\r", "")), os.ModePerm) 97 if err != nil { 98 return err 99 } 100 h.IsActive = true 101 return nil 102 } 103 104 // ListHooks returns a list of Git hooks of given repository. 105 func ListHooks(repoPath string) (_ []*Hook, err error) { 106 if !isDir(path.Join(repoPath, "hooks")) { 107 return nil, errors.New("hooks path does not exist") 108 } 109 110 hooks := make([]*Hook, len(hookNames)) 111 for i, name := range hookNames { 112 hooks[i], err = GetHook(repoPath, name) 113 if err != nil { 114 return nil, err 115 } 116 } 117 return hooks, nil 118 } 119 120 const ( 121 // HookPathUpdate hook update path 122 HookPathUpdate = "hooks/update" 123 ) 124 125 // SetUpdateHook writes given content to update hook of the repository. 126 func SetUpdateHook(repoPath, content string) (err error) { 127 log.Debug("Setting update hook: %s", repoPath) 128 hookPath := path.Join(repoPath, HookPathUpdate) 129 isExist, err := util.IsExist(hookPath) 130 if err != nil { 131 log.Debug("Unable to check if %s exists. Error: %v", hookPath, err) 132 return err 133 } 134 if isExist { 135 err = util.Remove(hookPath) 136 } else { 137 err = os.MkdirAll(path.Dir(hookPath), os.ModePerm) 138 } 139 if err != nil { 140 return err 141 } 142 return os.WriteFile(hookPath, []byte(content), 0o777) 143 }