code.gitea.io/gitea@v1.19.3/modules/private/hook.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package private 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "net/http" 11 "net/url" 12 "strconv" 13 "time" 14 15 "code.gitea.io/gitea/modules/json" 16 "code.gitea.io/gitea/modules/setting" 17 ) 18 19 // Git environment variables 20 const ( 21 GitAlternativeObjectDirectories = "GIT_ALTERNATE_OBJECT_DIRECTORIES" 22 GitObjectDirectory = "GIT_OBJECT_DIRECTORY" 23 GitQuarantinePath = "GIT_QUARANTINE_PATH" 24 GitPushOptionCount = "GIT_PUSH_OPTION_COUNT" 25 ) 26 27 // GitPushOptions is a wrapper around a map[string]string 28 type GitPushOptions map[string]string 29 30 // GitPushOptions keys 31 const ( 32 GitPushOptionRepoPrivate = "repo.private" 33 GitPushOptionRepoTemplate = "repo.template" 34 ) 35 36 // Bool checks for a key in the map and parses as a boolean 37 func (g GitPushOptions) Bool(key string, def bool) bool { 38 if val, ok := g[key]; ok { 39 if b, err := strconv.ParseBool(val); err == nil { 40 return b 41 } 42 } 43 return def 44 } 45 46 // HookOptions represents the options for the Hook calls 47 type HookOptions struct { 48 OldCommitIDs []string 49 NewCommitIDs []string 50 RefFullNames []string 51 UserID int64 52 UserName string 53 GitObjectDirectory string 54 GitAlternativeObjectDirectories string 55 GitQuarantinePath string 56 GitPushOptions GitPushOptions 57 PullRequestID int64 58 DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user. 59 IsWiki bool 60 ActionPerm int 61 } 62 63 // SSHLogOption ssh log options 64 type SSHLogOption struct { 65 IsError bool 66 Message string 67 } 68 69 // HookPostReceiveResult represents an individual result from PostReceive 70 type HookPostReceiveResult struct { 71 Results []HookPostReceiveBranchResult 72 RepoWasEmpty bool 73 Err string 74 } 75 76 // HookPostReceiveBranchResult represents an individual branch result from PostReceive 77 type HookPostReceiveBranchResult struct { 78 Message bool 79 Create bool 80 Branch string 81 URL string 82 } 83 84 // HookProcReceiveResult represents an individual result from ProcReceive 85 type HookProcReceiveResult struct { 86 Results []HookProcReceiveRefResult 87 Err string 88 } 89 90 // HookProcReceiveRefResult represents an individual result from ProcReceive 91 type HookProcReceiveRefResult struct { 92 OldOID string 93 NewOID string 94 Ref string 95 OriginalRef string 96 IsForcePush bool 97 IsNotMatched bool 98 Err string 99 } 100 101 // HookPreReceive check whether the provided commits are allowed 102 func HookPreReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (int, string) { 103 reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s", 104 url.PathEscape(ownerName), 105 url.PathEscape(repoName), 106 ) 107 req := newInternalRequest(ctx, reqURL, "POST") 108 req = req.Header("Content-Type", "application/json") 109 jsonBytes, _ := json.Marshal(opts) 110 req.Body(jsonBytes) 111 req.SetTimeout(60*time.Second, time.Duration(60+len(opts.OldCommitIDs))*time.Second) 112 resp, err := req.Response() 113 if err != nil { 114 return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error()) 115 } 116 defer resp.Body.Close() 117 118 if resp.StatusCode != http.StatusOK { 119 return resp.StatusCode, decodeJSONError(resp).Err 120 } 121 122 return http.StatusOK, "" 123 } 124 125 // HookPostReceive updates services and users 126 func HookPostReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookPostReceiveResult, string) { 127 reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s", 128 url.PathEscape(ownerName), 129 url.PathEscape(repoName), 130 ) 131 132 req := newInternalRequest(ctx, reqURL, "POST") 133 req = req.Header("Content-Type", "application/json") 134 req.SetTimeout(60*time.Second, time.Duration(60+len(opts.OldCommitIDs))*time.Second) 135 jsonBytes, _ := json.Marshal(opts) 136 req.Body(jsonBytes) 137 resp, err := req.Response() 138 if err != nil { 139 return nil, fmt.Sprintf("Unable to contact gitea: %v", err.Error()) 140 } 141 defer resp.Body.Close() 142 143 if resp.StatusCode != http.StatusOK { 144 return nil, decodeJSONError(resp).Err 145 } 146 res := &HookPostReceiveResult{} 147 _ = json.NewDecoder(resp.Body).Decode(res) 148 149 return res, "" 150 } 151 152 // HookProcReceive proc-receive hook 153 func HookProcReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookProcReceiveResult, error) { 154 reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/proc-receive/%s/%s", 155 url.PathEscape(ownerName), 156 url.PathEscape(repoName), 157 ) 158 159 req := newInternalRequest(ctx, reqURL, "POST") 160 req = req.Header("Content-Type", "application/json") 161 req.SetTimeout(60*time.Second, time.Duration(60+len(opts.OldCommitIDs))*time.Second) 162 jsonBytes, _ := json.Marshal(opts) 163 req.Body(jsonBytes) 164 resp, err := req.Response() 165 if err != nil { 166 return nil, fmt.Errorf("Unable to contact gitea: %w", err) 167 } 168 defer resp.Body.Close() 169 170 if resp.StatusCode != http.StatusOK { 171 return nil, errors.New(decodeJSONError(resp).Err) 172 } 173 res := &HookProcReceiveResult{} 174 _ = json.NewDecoder(resp.Body).Decode(res) 175 176 return res, nil 177 } 178 179 // SetDefaultBranch will set the default branch to the provided branch for the provided repository 180 func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) error { 181 reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/set-default-branch/%s/%s/%s", 182 url.PathEscape(ownerName), 183 url.PathEscape(repoName), 184 url.PathEscape(branch), 185 ) 186 req := newInternalRequest(ctx, reqURL, "POST") 187 req = req.Header("Content-Type", "application/json") 188 189 req.SetTimeout(60*time.Second, 60*time.Second) 190 resp, err := req.Response() 191 if err != nil { 192 return fmt.Errorf("Unable to contact gitea: %w", err) 193 } 194 defer resp.Body.Close() 195 if resp.StatusCode != http.StatusOK { 196 return fmt.Errorf("Error returned from gitea: %v", decodeJSONError(resp).Err) 197 } 198 return nil 199 } 200 201 // SSHLog sends ssh error log response 202 func SSHLog(ctx context.Context, isErr bool, msg string) error { 203 reqURL := setting.LocalURL + "api/internal/ssh/log" 204 req := newInternalRequest(ctx, reqURL, "POST") 205 req = req.Header("Content-Type", "application/json") 206 207 jsonBytes, _ := json.Marshal(&SSHLogOption{ 208 IsError: isErr, 209 Message: msg, 210 }) 211 req.Body(jsonBytes) 212 213 req.SetTimeout(60*time.Second, 60*time.Second) 214 resp, err := req.Response() 215 if err != nil { 216 return fmt.Errorf("unable to contact gitea: %w", err) 217 } 218 219 defer resp.Body.Close() 220 if resp.StatusCode != http.StatusOK { 221 return fmt.Errorf("Error returned from gitea: %v", decodeJSONError(resp).Err) 222 } 223 return nil 224 }