##// END OF EJS Templates
vcs: Do not trigger external hook in case of actions on shadow respositories.
Martin Bornhold -
r900:3895c836 default
parent child Browse files
Show More
@@ -1,378 +1,389 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2013-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 Set of hooks run by RhodeCode Enterprise
24 24 """
25 25
26 26 import os
27 27 import collections
28 28
29 29 import rhodecode
30 30 from rhodecode import events
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.utils import action_logger
33 33 from rhodecode.lib.utils2 import safe_str
34 34 from rhodecode.lib.exceptions import HTTPLockedRC, UserCreationError
35 35 from rhodecode.model.db import Repository, User
36 36
37 37
38 38 HookResponse = collections.namedtuple('HookResponse', ('status', 'output'))
39 39
40 40
41 def is_shadow_repo(extras):
42 """
43 Returns ``True`` if this is an action executed against a shadow repository.
44 """
45 return extras['is_shadow_repo']
46
47
41 48 def _get_scm_size(alias, root_path):
42 49
43 50 if not alias.startswith('.'):
44 51 alias += '.'
45 52
46 53 size_scm, size_root = 0, 0
47 54 for path, unused_dirs, files in os.walk(safe_str(root_path)):
48 55 if path.find(alias) != -1:
49 56 for f in files:
50 57 try:
51 58 size_scm += os.path.getsize(os.path.join(path, f))
52 59 except OSError:
53 60 pass
54 61 else:
55 62 for f in files:
56 63 try:
57 64 size_root += os.path.getsize(os.path.join(path, f))
58 65 except OSError:
59 66 pass
60 67
61 68 size_scm_f = h.format_byte_size_binary(size_scm)
62 69 size_root_f = h.format_byte_size_binary(size_root)
63 70 size_total_f = h.format_byte_size_binary(size_root + size_scm)
64 71
65 72 return size_scm_f, size_root_f, size_total_f
66 73
67 74
68 75 # actual hooks called by Mercurial internally, and GIT by our Python Hooks
69 76 def repo_size(extras):
70 77 """Present size of repository after push."""
71 78 repo = Repository.get_by_repo_name(extras.repository)
72 79 vcs_part = safe_str(u'.%s' % repo.repo_type)
73 80 size_vcs, size_root, size_total = _get_scm_size(vcs_part,
74 81 repo.repo_full_path)
75 82 msg = ('Repository `%s` size summary %s:%s repo:%s total:%s\n'
76 83 % (repo.repo_name, vcs_part, size_vcs, size_root, size_total))
77 84 return HookResponse(0, msg)
78 85
79 86
80 87 def pre_push(extras):
81 88 """
82 89 Hook executed before pushing code.
83 90
84 91 It bans pushing when the repository is locked.
85 92 """
86 93 usr = User.get_by_username(extras.username)
87 94
88
89 95 output = ''
90 96 if extras.locked_by[0] and usr.user_id != int(extras.locked_by[0]):
91 97 locked_by = User.get(extras.locked_by[0]).username
92 98 reason = extras.locked_by[2]
93 99 # this exception is interpreted in git/hg middlewares and based
94 100 # on that proper return code is server to client
95 101 _http_ret = HTTPLockedRC(
96 102 _locked_by_explanation(extras.repository, locked_by, reason))
97 103 if str(_http_ret.code).startswith('2'):
98 104 # 2xx Codes don't raise exceptions
99 105 output = _http_ret.title
100 106 else:
101 107 raise _http_ret
102 108
103 # Calling hooks after checking the lock, for consistent behavior
109 # Propagate to external components. This is done after checking the
110 # lock, for consistent behavior.
111 if not is_shadow_repo(extras):
104 112 pre_push_extension(repo_store_path=Repository.base_path(), **extras)
105
106 events.trigger(events.RepoPrePushEvent(repo_name=extras.repository,
107 extras=extras))
113 events.trigger(events.RepoPrePushEvent(
114 repo_name=extras.repository, extras=extras))
108 115
109 116 return HookResponse(0, output)
110 117
111 118
112 119 def pre_pull(extras):
113 120 """
114 121 Hook executed before pulling the code.
115 122
116 123 It bans pulling when the repository is locked.
117 124 """
118 125
119 126 output = ''
120 127 if extras.locked_by[0]:
121 128 locked_by = User.get(extras.locked_by[0]).username
122 129 reason = extras.locked_by[2]
123 130 # this exception is interpreted in git/hg middlewares and based
124 131 # on that proper return code is server to client
125 132 _http_ret = HTTPLockedRC(
126 133 _locked_by_explanation(extras.repository, locked_by, reason))
127 134 if str(_http_ret.code).startswith('2'):
128 135 # 2xx Codes don't raise exceptions
129 136 output = _http_ret.title
130 137 else:
131 138 raise _http_ret
132 139
133 # Calling hooks after checking the lock, for consistent behavior
140 # Propagate to external components. This is done after checking the
141 # lock, for consistent behavior.
142 if not is_shadow_repo(extras):
134 143 pre_pull_extension(**extras)
135 events.trigger(events.RepoPrePullEvent(repo_name=extras.repository,
136 extras=extras))
144 events.trigger(events.RepoPrePullEvent(
145 repo_name=extras.repository, extras=extras))
137 146
138 147 return HookResponse(0, output)
139 148
140 149
141 150 def post_pull(extras):
142 151 """Hook executed after client pulls the code."""
143 152 user = User.get_by_username(extras.username)
144 153 action = 'pull'
145 154 action_logger(user, action, extras.repository, extras.ip, commit=True)
146 155
147 events.trigger(events.RepoPullEvent(repo_name=extras.repository,
148 extras=extras))
149 # extension hook call
156 # Propagate to external components.
157 if not is_shadow_repo(extras):
150 158 post_pull_extension(**extras)
159 events.trigger(events.RepoPullEvent(
160 repo_name=extras.repository, extras=extras))
151 161
152 162 output = ''
153 163 # make lock is a tri state False, True, None. We only make lock on True
154 if extras.make_lock is True:
164 if extras.make_lock is True and not is_shadow_repo(extras):
155 165 Repository.lock(Repository.get_by_repo_name(extras.repository),
156 166 user.user_id,
157 167 lock_reason=Repository.LOCK_PULL)
158 168 msg = 'Made lock on repo `%s`' % (extras.repository,)
159 169 output += msg
160 170
161 171 if extras.locked_by[0]:
162 172 locked_by = User.get(extras.locked_by[0]).username
163 173 reason = extras.locked_by[2]
164 174 _http_ret = HTTPLockedRC(
165 175 _locked_by_explanation(extras.repository, locked_by, reason))
166 176 if str(_http_ret.code).startswith('2'):
167 177 # 2xx Codes don't raise exceptions
168 178 output += _http_ret.title
169 179
170 180 return HookResponse(0, output)
171 181
172 182
173 183 def post_push(extras):
174 184 """Hook executed after user pushes to the repository."""
175 185 action_tmpl = extras.action + ':%s'
176 186 commit_ids = extras.commit_ids[:29000]
177 187
178 188 action = action_tmpl % ','.join(commit_ids)
179 189 action_logger(
180 190 extras.username, action, extras.repository, extras.ip, commit=True)
181 191
182 events.trigger(events.RepoPushEvent(repo_name=extras.repository,
183 pushed_commit_ids=commit_ids,
184 extras=extras))
185
186 # extension hook call
192 # Propagate to external components.
193 if not is_shadow_repo(extras):
187 194 post_push_extension(
188 195 repo_store_path=Repository.base_path(),
189 196 pushed_revs=commit_ids,
190 197 **extras)
198 events.trigger(events.RepoPushEvent(
199 repo_name=extras.repository,
200 pushed_commit_ids=commit_ids,
201 extras=extras))
191 202
192 203 output = ''
193 204 # make lock is a tri state False, True, None. We only release lock on False
194 if extras.make_lock is False:
205 if extras.make_lock is False and not is_shadow_repo(extras):
195 206 Repository.unlock(Repository.get_by_repo_name(extras.repository))
196 207 msg = 'Released lock on repo `%s`\n' % extras.repository
197 208 output += msg
198 209
199 210 if extras.locked_by[0]:
200 211 locked_by = User.get(extras.locked_by[0]).username
201 212 reason = extras.locked_by[2]
202 213 _http_ret = HTTPLockedRC(
203 214 _locked_by_explanation(extras.repository, locked_by, reason))
204 215 # TODO: johbo: if not?
205 216 if str(_http_ret.code).startswith('2'):
206 217 # 2xx Codes don't raise exceptions
207 218 output += _http_ret.title
208 219
209 220 output += 'RhodeCode: push completed\n'
210 221
211 222 return HookResponse(0, output)
212 223
213 224
214 225 def _locked_by_explanation(repo_name, user_name, reason):
215 226 message = (
216 227 'Repository `%s` locked by user `%s`. Reason:`%s`'
217 228 % (repo_name, user_name, reason))
218 229 return message
219 230
220 231
221 232 def check_allowed_create_user(user_dict, created_by, **kwargs):
222 233 # pre create hooks
223 234 if pre_create_user.is_active():
224 235 allowed, reason = pre_create_user(created_by=created_by, **user_dict)
225 236 if not allowed:
226 237 raise UserCreationError(reason)
227 238
228 239
229 240 class ExtensionCallback(object):
230 241 """
231 242 Forwards a given call to rcextensions, sanitizes keyword arguments.
232 243
233 244 Does check if there is an extension active for that hook. If it is
234 245 there, it will forward all `kwargs_keys` keyword arguments to the
235 246 extension callback.
236 247 """
237 248
238 249 def __init__(self, hook_name, kwargs_keys):
239 250 self._hook_name = hook_name
240 251 self._kwargs_keys = set(kwargs_keys)
241 252
242 253 def __call__(self, *args, **kwargs):
243 254 kwargs_to_pass = dict((key, kwargs[key]) for key in self._kwargs_keys)
244 255 callback = self._get_callback()
245 256 if callback:
246 257 return callback(**kwargs_to_pass)
247 258
248 259 def is_active(self):
249 260 return hasattr(rhodecode.EXTENSIONS, self._hook_name)
250 261
251 262 def _get_callback(self):
252 263 return getattr(rhodecode.EXTENSIONS, self._hook_name, None)
253 264
254 265
255 266 pre_pull_extension = ExtensionCallback(
256 267 hook_name='PRE_PULL_HOOK',
257 268 kwargs_keys=(
258 269 'server_url', 'config', 'scm', 'username', 'ip', 'action',
259 270 'repository'))
260 271
261 272
262 273 post_pull_extension = ExtensionCallback(
263 274 hook_name='PULL_HOOK',
264 275 kwargs_keys=(
265 276 'server_url', 'config', 'scm', 'username', 'ip', 'action',
266 277 'repository'))
267 278
268 279
269 280 pre_push_extension = ExtensionCallback(
270 281 hook_name='PRE_PUSH_HOOK',
271 282 kwargs_keys=(
272 283 'server_url', 'config', 'scm', 'username', 'ip', 'action',
273 284 'repository', 'repo_store_path'))
274 285
275 286
276 287 post_push_extension = ExtensionCallback(
277 288 hook_name='PUSH_HOOK',
278 289 kwargs_keys=(
279 290 'server_url', 'config', 'scm', 'username', 'ip', 'action',
280 291 'repository', 'repo_store_path', 'pushed_revs'))
281 292
282 293
283 294 pre_create_user = ExtensionCallback(
284 295 hook_name='PRE_CREATE_USER_HOOK',
285 296 kwargs_keys=(
286 297 'username', 'password', 'email', 'firstname', 'lastname', 'active',
287 298 'admin', 'created_by'))
288 299
289 300
290 301 log_create_pull_request = ExtensionCallback(
291 302 hook_name='CREATE_PULL_REQUEST',
292 303 kwargs_keys=(
293 304 'server_url', 'config', 'scm', 'username', 'ip', 'action',
294 305 'repository', 'pull_request_id', 'url', 'title', 'description',
295 306 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
296 307 'mergeable', 'source', 'target', 'author', 'reviewers'))
297 308
298 309
299 310 log_merge_pull_request = ExtensionCallback(
300 311 hook_name='MERGE_PULL_REQUEST',
301 312 kwargs_keys=(
302 313 'server_url', 'config', 'scm', 'username', 'ip', 'action',
303 314 'repository', 'pull_request_id', 'url', 'title', 'description',
304 315 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
305 316 'mergeable', 'source', 'target', 'author', 'reviewers'))
306 317
307 318
308 319 log_close_pull_request = ExtensionCallback(
309 320 hook_name='CLOSE_PULL_REQUEST',
310 321 kwargs_keys=(
311 322 'server_url', 'config', 'scm', 'username', 'ip', 'action',
312 323 'repository', 'pull_request_id', 'url', 'title', 'description',
313 324 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
314 325 'mergeable', 'source', 'target', 'author', 'reviewers'))
315 326
316 327
317 328 log_review_pull_request = ExtensionCallback(
318 329 hook_name='REVIEW_PULL_REQUEST',
319 330 kwargs_keys=(
320 331 'server_url', 'config', 'scm', 'username', 'ip', 'action',
321 332 'repository', 'pull_request_id', 'url', 'title', 'description',
322 333 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
323 334 'mergeable', 'source', 'target', 'author', 'reviewers'))
324 335
325 336
326 337 log_update_pull_request = ExtensionCallback(
327 338 hook_name='UPDATE_PULL_REQUEST',
328 339 kwargs_keys=(
329 340 'server_url', 'config', 'scm', 'username', 'ip', 'action',
330 341 'repository', 'pull_request_id', 'url', 'title', 'description',
331 342 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
332 343 'mergeable', 'source', 'target', 'author', 'reviewers'))
333 344
334 345
335 346 log_create_user = ExtensionCallback(
336 347 hook_name='CREATE_USER_HOOK',
337 348 kwargs_keys=(
338 349 'username', 'full_name_or_username', 'full_contact', 'user_id',
339 350 'name', 'firstname', 'short_contact', 'admin', 'lastname',
340 351 'ip_addresses', 'extern_type', 'extern_name',
341 352 'email', 'api_key', 'api_keys', 'last_login',
342 353 'full_name', 'active', 'password', 'emails',
343 354 'inherit_default_permissions', 'created_by', 'created_on'))
344 355
345 356
346 357 log_delete_user = ExtensionCallback(
347 358 hook_name='DELETE_USER_HOOK',
348 359 kwargs_keys=(
349 360 'username', 'full_name_or_username', 'full_contact', 'user_id',
350 361 'name', 'firstname', 'short_contact', 'admin', 'lastname',
351 362 'ip_addresses',
352 363 'email', 'api_key', 'last_login',
353 364 'full_name', 'active', 'password', 'emails',
354 365 'inherit_default_permissions', 'deleted_by'))
355 366
356 367
357 368 log_create_repository = ExtensionCallback(
358 369 hook_name='CREATE_REPO_HOOK',
359 370 kwargs_keys=(
360 371 'repo_name', 'repo_type', 'description', 'private', 'created_on',
361 372 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
362 373 'clone_uri', 'fork_id', 'group_id', 'created_by'))
363 374
364 375
365 376 log_delete_repository = ExtensionCallback(
366 377 hook_name='DELETE_REPO_HOOK',
367 378 kwargs_keys=(
368 379 'repo_name', 'repo_type', 'description', 'private', 'created_on',
369 380 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
370 381 'clone_uri', 'fork_id', 'group_id', 'deleted_by', 'deleted_on'))
371 382
372 383
373 384 log_create_repository_group = ExtensionCallback(
374 385 hook_name='CREATE_REPO_GROUP_HOOK',
375 386 kwargs_keys=(
376 387 'group_name', 'group_parent_id', 'group_description',
377 388 'group_id', 'user_id', 'created_by', 'created_on',
378 389 'enable_locking'))
General Comments 0
You need to be logged in to leave comments. Login now