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