##// END OF EJS Templates
git hook installation should be only executed for git backend
marcink -
r2992:3bc8d2e0 beta
parent child Browse files
Show More
@@ -1,56 +1,55 b''
1 1 .. _git_support:
2 2
3 3 ===========
4 4 GIT support
5 5 ===========
6 6
7 7
8 8 Git support in RhodeCode 1.3 was enabled by default. You need to have a git
9 9 client installed on the machine to make git fully work.
10 10
11 Although There are some limitations on git usage.
11 Although There is one limitation on git usage.
12 12
13 - hooks that are executed on pull/push are not *real* hooks, they are
14 just emulating the behavior, and are executed **BEFORE** action takes place.
15 - large pushes needs http server with chunked encoding support.
13 - large pushes requires a http server with chunked encoding support.
16 14
17 15 if you plan to use git you need to run RhodeCode with some
18 16 http server that supports chunked encoding which git http protocol uses,
19 17 i recommend using waitress_ or gunicorn_ (linux only) for `paste` wsgi app
20 replacement.
18 replacement. Starting from version 1.4 waitress_ is the default wsgi server
19 used in RhodeCode.
21 20
22 21 To use, simply change change the following in the .ini file::
23 22
24 23 use = egg:Paste#http
25 24
26 25 to::
27 26
28 27 use = egg:waitress#main
29 28
30 29 or::
31 30
32 31 use = egg:gunicorn#main
33 32
34 33
35 34 And comment out bellow options::
36 35
37 36 threadpool_workers =
38 37 threadpool_max_requests =
39 38 use_threadpool =
40 39
41 40
42 41 You can simply run `paster serve` as usual.
43 42
44 43
45 44 You can always disable git/hg support by editing a
46 45 file **rhodecode/__init__.py** and commenting out backends
47 46
48 47 .. code-block:: python
49 48
50 49 BACKENDS = {
51 50 'hg': 'Mercurial repository',
52 51 #'git': 'Git repository',
53 52 }
54 53
55 54 .. _waitress: http://pypi.python.org/pypi/waitress
56 55 .. _gunicorn: http://pypi.python.org/pypi/gunicorn No newline at end of file
@@ -1,448 +1,448 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.celerylib.tasks
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode task modules, containing all task that suppose to be run
7 7 by celery daemon
8 8
9 9 :created_on: Oct 6, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 from celery.decorators import task
27 27
28 28 import os
29 29 import traceback
30 30 import logging
31 31 from os.path import join as jn
32 32
33 33 from time import mktime
34 34 from operator import itemgetter
35 35 from string import lower
36 36
37 37 from pylons import config, url
38 38 from pylons.i18n.translation import _
39 39
40 40 from rhodecode.lib.vcs import get_backend
41 41
42 42 from rhodecode import CELERY_ON, CELERY_EAGER
43 43 from rhodecode.lib.utils2 import safe_str
44 44 from rhodecode.lib.celerylib import run_task, locked_task, dbsession, \
45 45 str2bool, __get_lockkey, LockHeld, DaemonLock, get_session
46 46 from rhodecode.lib.helpers import person
47 47 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
48 48 from rhodecode.lib.utils import add_cache, action_logger
49 49 from rhodecode.lib.compat import json, OrderedDict
50 50 from rhodecode.lib.hooks import log_create_repository
51 51
52 52 from rhodecode.model.db import Statistics, Repository, User
53 53 from rhodecode.model.scm import ScmModel
54 54
55 55
56 56 add_cache(config)
57 57
58 58 __all__ = ['whoosh_index', 'get_commits_stats',
59 59 'reset_user_password', 'send_email']
60 60
61 61
62 62 def get_logger(cls):
63 63 if CELERY_ON:
64 64 try:
65 65 log = cls.get_logger()
66 66 except:
67 67 log = logging.getLogger(__name__)
68 68 else:
69 69 log = logging.getLogger(__name__)
70 70
71 71 return log
72 72
73 73
74 74 @task(ignore_result=True)
75 75 @locked_task
76 76 @dbsession
77 77 def whoosh_index(repo_location, full_index):
78 78 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
79 79 log = get_logger(whoosh_index)
80 80 DBS = get_session()
81 81
82 82 index_location = config['index_dir']
83 83 WhooshIndexingDaemon(index_location=index_location,
84 84 repo_location=repo_location, sa=DBS)\
85 85 .run(full_index=full_index)
86 86
87 87
88 88 @task(ignore_result=True)
89 89 @dbsession
90 90 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
91 91 log = get_logger(get_commits_stats)
92 92 DBS = get_session()
93 93 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
94 94 ts_max_y)
95 95 lockkey_path = config['here']
96 96
97 97 log.info('running task with lockkey %s' % lockkey)
98 98
99 99 try:
100 100 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
101 101
102 102 # for js data compatibility cleans the key for person from '
103 103 akc = lambda k: person(k).replace('"', "")
104 104
105 105 co_day_auth_aggr = {}
106 106 commits_by_day_aggregate = {}
107 107 repo = Repository.get_by_repo_name(repo_name)
108 108 if repo is None:
109 109 return True
110 110
111 111 repo = repo.scm_instance
112 112 repo_size = repo.count()
113 113 # return if repo have no revisions
114 114 if repo_size < 1:
115 115 lock.release()
116 116 return True
117 117
118 118 skip_date_limit = True
119 119 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
120 120 last_rev = None
121 121 last_cs = None
122 122 timegetter = itemgetter('time')
123 123
124 124 dbrepo = DBS.query(Repository)\
125 125 .filter(Repository.repo_name == repo_name).scalar()
126 126 cur_stats = DBS.query(Statistics)\
127 127 .filter(Statistics.repository == dbrepo).scalar()
128 128
129 129 if cur_stats is not None:
130 130 last_rev = cur_stats.stat_on_revision
131 131
132 132 if last_rev == repo.get_changeset().revision and repo_size > 1:
133 133 # pass silently without any work if we're not on first revision or
134 134 # current state of parsing revision(from db marker) is the
135 135 # last revision
136 136 lock.release()
137 137 return True
138 138
139 139 if cur_stats:
140 140 commits_by_day_aggregate = OrderedDict(json.loads(
141 141 cur_stats.commit_activity_combined))
142 142 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
143 143
144 144 log.debug('starting parsing %s' % parse_limit)
145 145 lmktime = mktime
146 146
147 147 last_rev = last_rev + 1 if last_rev >= 0 else 0
148 148 log.debug('Getting revisions from %s to %s' % (
149 149 last_rev, last_rev + parse_limit)
150 150 )
151 151 for cs in repo[last_rev:last_rev + parse_limit]:
152 152 log.debug('parsing %s' % cs)
153 153 last_cs = cs # remember last parsed changeset
154 154 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
155 155 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
156 156
157 157 if akc(cs.author) in co_day_auth_aggr:
158 158 try:
159 159 l = [timegetter(x) for x in
160 160 co_day_auth_aggr[akc(cs.author)]['data']]
161 161 time_pos = l.index(k)
162 162 except ValueError:
163 163 time_pos = False
164 164
165 165 if time_pos >= 0 and time_pos is not False:
166 166
167 167 datadict = \
168 168 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
169 169
170 170 datadict["commits"] += 1
171 171 datadict["added"] += len(cs.added)
172 172 datadict["changed"] += len(cs.changed)
173 173 datadict["removed"] += len(cs.removed)
174 174
175 175 else:
176 176 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
177 177
178 178 datadict = {"time": k,
179 179 "commits": 1,
180 180 "added": len(cs.added),
181 181 "changed": len(cs.changed),
182 182 "removed": len(cs.removed),
183 183 }
184 184 co_day_auth_aggr[akc(cs.author)]['data']\
185 185 .append(datadict)
186 186
187 187 else:
188 188 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
189 189 co_day_auth_aggr[akc(cs.author)] = {
190 190 "label": akc(cs.author),
191 191 "data": [{"time":k,
192 192 "commits":1,
193 193 "added":len(cs.added),
194 194 "changed":len(cs.changed),
195 195 "removed":len(cs.removed),
196 196 }],
197 197 "schema": ["commits"],
198 198 }
199 199
200 200 #gather all data by day
201 201 if k in commits_by_day_aggregate:
202 202 commits_by_day_aggregate[k] += 1
203 203 else:
204 204 commits_by_day_aggregate[k] = 1
205 205
206 206 overview_data = sorted(commits_by_day_aggregate.items(),
207 207 key=itemgetter(0))
208 208
209 209 if not co_day_auth_aggr:
210 210 co_day_auth_aggr[akc(repo.contact)] = {
211 211 "label": akc(repo.contact),
212 212 "data": [0, 1],
213 213 "schema": ["commits"],
214 214 }
215 215
216 216 stats = cur_stats if cur_stats else Statistics()
217 217 stats.commit_activity = json.dumps(co_day_auth_aggr)
218 218 stats.commit_activity_combined = json.dumps(overview_data)
219 219
220 220 log.debug('last revison %s' % last_rev)
221 221 leftovers = len(repo.revisions[last_rev:])
222 222 log.debug('revisions to parse %s' % leftovers)
223 223
224 224 if last_rev == 0 or leftovers < parse_limit:
225 225 log.debug('getting code trending stats')
226 226 stats.languages = json.dumps(__get_codes_stats(repo_name))
227 227
228 228 try:
229 229 stats.repository = dbrepo
230 230 stats.stat_on_revision = last_cs.revision if last_cs else 0
231 231 DBS.add(stats)
232 232 DBS.commit()
233 233 except:
234 234 log.error(traceback.format_exc())
235 235 DBS.rollback()
236 236 lock.release()
237 237 return False
238 238
239 239 # final release
240 240 lock.release()
241 241
242 242 # execute another task if celery is enabled
243 243 if len(repo.revisions) > 1 and CELERY_ON:
244 244 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
245 245 return True
246 246 except LockHeld:
247 247 log.info('LockHeld')
248 248 return 'Task with key %s already running' % lockkey
249 249
250 250 @task(ignore_result=True)
251 251 @dbsession
252 252 def send_password_link(user_email):
253 253 from rhodecode.model.notification import EmailNotificationModel
254 254
255 255 log = get_logger(send_password_link)
256 256 DBS = get_session()
257 257
258 258 try:
259 259 user = User.get_by_email(user_email)
260 260 if user:
261 261 log.debug('password reset user found %s' % user)
262 262 link = url('reset_password_confirmation', key=user.api_key,
263 263 qualified=True)
264 264 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
265 265 body = EmailNotificationModel().get_email_tmpl(reg_type,
266 266 **{'user':user.short_contact,
267 267 'reset_url':link})
268 268 log.debug('sending email')
269 269 run_task(send_email, user_email,
270 270 _("password reset link"), body)
271 271 log.info('send new password mail to %s' % user_email)
272 272 else:
273 273 log.debug("password reset email %s not found" % user_email)
274 274 except:
275 275 log.error(traceback.format_exc())
276 276 return False
277 277
278 278 return True
279 279
280 280 @task(ignore_result=True)
281 281 @dbsession
282 282 def reset_user_password(user_email):
283 283 from rhodecode.lib import auth
284 284
285 285 log = get_logger(reset_user_password)
286 286 DBS = get_session()
287 287
288 288 try:
289 289 try:
290 290 user = User.get_by_email(user_email)
291 291 new_passwd = auth.PasswordGenerator().gen_password(8,
292 292 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
293 293 if user:
294 294 user.password = auth.get_crypt_password(new_passwd)
295 295 user.api_key = auth.generate_api_key(user.username)
296 296 DBS.add(user)
297 297 DBS.commit()
298 298 log.info('change password for %s' % user_email)
299 299 if new_passwd is None:
300 300 raise Exception('unable to generate new password')
301 301 except:
302 302 log.error(traceback.format_exc())
303 303 DBS.rollback()
304 304
305 305 run_task(send_email, user_email,
306 306 'Your new password',
307 307 'Your new RhodeCode password:%s' % (new_passwd))
308 308 log.info('send new password mail to %s' % user_email)
309 309
310 310 except:
311 311 log.error('Failed to update user password')
312 312 log.error(traceback.format_exc())
313 313
314 314 return True
315 315
316 316
317 317 @task(ignore_result=True)
318 318 @dbsession
319 319 def send_email(recipients, subject, body, html_body=''):
320 320 """
321 321 Sends an email with defined parameters from the .ini files.
322 322
323 323 :param recipients: list of recipients, it this is empty the defined email
324 324 address from field 'email_to' is used instead
325 325 :param subject: subject of the mail
326 326 :param body: body of the mail
327 327 :param html_body: html version of body
328 328 """
329 329 log = get_logger(send_email)
330 330 DBS = get_session()
331 331
332 332 email_config = config
333 333 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
334 334 if not recipients:
335 335 # if recipients are not defined we send to email_config + all admins
336 336 admins = [u.email for u in User.query()
337 337 .filter(User.admin == True).all()]
338 338 recipients = [email_config.get('email_to')] + admins
339 339
340 340 mail_from = email_config.get('app_email_from', 'RhodeCode')
341 341 user = email_config.get('smtp_username')
342 342 passwd = email_config.get('smtp_password')
343 343 mail_server = email_config.get('smtp_server')
344 344 mail_port = email_config.get('smtp_port')
345 345 tls = str2bool(email_config.get('smtp_use_tls'))
346 346 ssl = str2bool(email_config.get('smtp_use_ssl'))
347 347 debug = str2bool(config.get('debug'))
348 348 smtp_auth = email_config.get('smtp_auth')
349 349
350 350 try:
351 351 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
352 352 mail_port, ssl, tls, debug=debug)
353 353 m.send(recipients, subject, body, html_body)
354 354 except:
355 355 log.error('Mail sending failed')
356 356 log.error(traceback.format_exc())
357 357 return False
358 358 return True
359 359
360 360
361 361 @task(ignore_result=True)
362 362 @dbsession
363 363 def create_repo_fork(form_data, cur_user):
364 364 """
365 365 Creates a fork of repository using interval VCS methods
366 366
367 367 :param form_data:
368 368 :param cur_user:
369 369 """
370 370 from rhodecode.model.repo import RepoModel
371 371 from rhodecode.model.user import UserModel
372 372
373 373 log = get_logger(create_repo_fork)
374 374 DBS = get_session()
375 375
376 376 base_path = Repository.base_path()
377 377 cur_user = UserModel(DBS)._get_user(cur_user)
378 378
379 379 fork_name = form_data['repo_name_full']
380 380 repo_type = form_data['repo_type']
381 381 description = form_data['description']
382 382 owner = cur_user
383 383 private = form_data['private']
384 384 clone_uri = form_data.get('clone_uri')
385 385 repos_group = form_data['repo_group']
386 386 landing_rev = form_data['landing_rev']
387 387 copy_fork_permissions = form_data.get('copy_permissions')
388 388 fork_of = RepoModel(DBS)._get_repo(form_data.get('fork_parent_id'))
389 389
390 390 fork_repo = RepoModel(DBS).create_repo(
391 391 fork_name, repo_type, description, owner, private, clone_uri,
392 392 repos_group, landing_rev, just_db=True, fork_of=fork_of,
393 393 copy_fork_permissions=copy_fork_permissions
394 394 )
395 395
396 396 update_after_clone = form_data['update_after_clone']
397 397
398 398 source_repo_path = os.path.join(base_path, fork_of.repo_name)
399 399 destination_fork_path = os.path.join(base_path, fork_name)
400 400
401 401 log.info('creating fork of %s as %s', source_repo_path,
402 402 destination_fork_path)
403 403 backend = get_backend(repo_type)
404 404
405 405 if repo_type == 'git':
406 406 r = backend(safe_str(destination_fork_path), create=True,
407 407 src_url=safe_str(source_repo_path),
408 408 update_after_clone=update_after_clone,
409 409 bare=True)
410 # add rhodecode hook into this repo
411 ScmModel().install_git_hook(repo=r)
410 412 elif repo_type == 'hg':
411 413 r = backend(safe_str(destination_fork_path), create=True,
412 414 src_url=safe_str(source_repo_path),
413 415 update_after_clone=update_after_clone)
414 416 else:
415 417 raise Exception('Unknown backend type %s' % repo_type)
416 418
417 # add rhodecode hook into this repo
418 ScmModel().install_git_hook(repo=r)
419 419 log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
420 420
421 421 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
422 422 fork_of.repo_name, '', DBS)
423 423
424 424 action_logger(cur_user, 'user_created_fork:%s' % fork_name,
425 425 fork_name, '', DBS)
426 426 # finally commit at latest possible stage
427 427 DBS.commit()
428 428
429 429
430 430 def __get_codes_stats(repo_name):
431 431 from rhodecode.config.conf import LANGUAGES_EXTENSIONS_MAP
432 432 repo = Repository.get_by_repo_name(repo_name).scm_instance
433 433
434 434 tip = repo.get_changeset()
435 435 code_stats = {}
436 436
437 437 def aggregate(cs):
438 438 for f in cs[2]:
439 439 ext = lower(f.extension)
440 440 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
441 441 if ext in code_stats:
442 442 code_stats[ext] += 1
443 443 else:
444 444 code_stats[ext] = 1
445 445
446 446 map(aggregate, tip.walk('/'))
447 447
448 448 return code_stats or {}
General Comments 0
You need to be logged in to leave comments. Login now