##// END OF EJS Templates
added missing installation of git hooks when doing a fork
marcink -
r2991:578a152f beta
parent child Browse files
Show More
@@ -1,445 +1,448
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 from rhodecode.model.scm import ScmModel
53 54
54 55
55 56 add_cache(config)
56 57
57 58 __all__ = ['whoosh_index', 'get_commits_stats',
58 59 'reset_user_password', 'send_email']
59 60
60 61
61 62 def get_logger(cls):
62 63 if CELERY_ON:
63 64 try:
64 65 log = cls.get_logger()
65 66 except:
66 67 log = logging.getLogger(__name__)
67 68 else:
68 69 log = logging.getLogger(__name__)
69 70
70 71 return log
71 72
72 73
73 74 @task(ignore_result=True)
74 75 @locked_task
75 76 @dbsession
76 77 def whoosh_index(repo_location, full_index):
77 78 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
78 79 log = get_logger(whoosh_index)
79 80 DBS = get_session()
80 81
81 82 index_location = config['index_dir']
82 83 WhooshIndexingDaemon(index_location=index_location,
83 84 repo_location=repo_location, sa=DBS)\
84 85 .run(full_index=full_index)
85 86
86 87
87 88 @task(ignore_result=True)
88 89 @dbsession
89 90 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
90 91 log = get_logger(get_commits_stats)
91 92 DBS = get_session()
92 93 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
93 94 ts_max_y)
94 95 lockkey_path = config['here']
95 96
96 97 log.info('running task with lockkey %s' % lockkey)
97 98
98 99 try:
99 100 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
100 101
101 102 # for js data compatibility cleans the key for person from '
102 103 akc = lambda k: person(k).replace('"', "")
103 104
104 105 co_day_auth_aggr = {}
105 106 commits_by_day_aggregate = {}
106 107 repo = Repository.get_by_repo_name(repo_name)
107 108 if repo is None:
108 109 return True
109 110
110 111 repo = repo.scm_instance
111 112 repo_size = repo.count()
112 113 # return if repo have no revisions
113 114 if repo_size < 1:
114 115 lock.release()
115 116 return True
116 117
117 118 skip_date_limit = True
118 119 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
119 120 last_rev = None
120 121 last_cs = None
121 122 timegetter = itemgetter('time')
122 123
123 124 dbrepo = DBS.query(Repository)\
124 125 .filter(Repository.repo_name == repo_name).scalar()
125 126 cur_stats = DBS.query(Statistics)\
126 127 .filter(Statistics.repository == dbrepo).scalar()
127 128
128 129 if cur_stats is not None:
129 130 last_rev = cur_stats.stat_on_revision
130 131
131 132 if last_rev == repo.get_changeset().revision and repo_size > 1:
132 133 # pass silently without any work if we're not on first revision or
133 134 # current state of parsing revision(from db marker) is the
134 135 # last revision
135 136 lock.release()
136 137 return True
137 138
138 139 if cur_stats:
139 140 commits_by_day_aggregate = OrderedDict(json.loads(
140 141 cur_stats.commit_activity_combined))
141 142 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
142 143
143 144 log.debug('starting parsing %s' % parse_limit)
144 145 lmktime = mktime
145 146
146 147 last_rev = last_rev + 1 if last_rev >= 0 else 0
147 148 log.debug('Getting revisions from %s to %s' % (
148 149 last_rev, last_rev + parse_limit)
149 150 )
150 151 for cs in repo[last_rev:last_rev + parse_limit]:
151 152 log.debug('parsing %s' % cs)
152 153 last_cs = cs # remember last parsed changeset
153 154 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
154 155 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
155 156
156 157 if akc(cs.author) in co_day_auth_aggr:
157 158 try:
158 159 l = [timegetter(x) for x in
159 160 co_day_auth_aggr[akc(cs.author)]['data']]
160 161 time_pos = l.index(k)
161 162 except ValueError:
162 163 time_pos = False
163 164
164 165 if time_pos >= 0 and time_pos is not False:
165 166
166 167 datadict = \
167 168 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
168 169
169 170 datadict["commits"] += 1
170 171 datadict["added"] += len(cs.added)
171 172 datadict["changed"] += len(cs.changed)
172 173 datadict["removed"] += len(cs.removed)
173 174
174 175 else:
175 176 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
176 177
177 178 datadict = {"time": k,
178 179 "commits": 1,
179 180 "added": len(cs.added),
180 181 "changed": len(cs.changed),
181 182 "removed": len(cs.removed),
182 183 }
183 184 co_day_auth_aggr[akc(cs.author)]['data']\
184 185 .append(datadict)
185 186
186 187 else:
187 188 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
188 189 co_day_auth_aggr[akc(cs.author)] = {
189 190 "label": akc(cs.author),
190 191 "data": [{"time":k,
191 192 "commits":1,
192 193 "added":len(cs.added),
193 194 "changed":len(cs.changed),
194 195 "removed":len(cs.removed),
195 196 }],
196 197 "schema": ["commits"],
197 198 }
198 199
199 200 #gather all data by day
200 201 if k in commits_by_day_aggregate:
201 202 commits_by_day_aggregate[k] += 1
202 203 else:
203 204 commits_by_day_aggregate[k] = 1
204 205
205 206 overview_data = sorted(commits_by_day_aggregate.items(),
206 207 key=itemgetter(0))
207 208
208 209 if not co_day_auth_aggr:
209 210 co_day_auth_aggr[akc(repo.contact)] = {
210 211 "label": akc(repo.contact),
211 212 "data": [0, 1],
212 213 "schema": ["commits"],
213 214 }
214 215
215 216 stats = cur_stats if cur_stats else Statistics()
216 217 stats.commit_activity = json.dumps(co_day_auth_aggr)
217 218 stats.commit_activity_combined = json.dumps(overview_data)
218 219
219 220 log.debug('last revison %s' % last_rev)
220 221 leftovers = len(repo.revisions[last_rev:])
221 222 log.debug('revisions to parse %s' % leftovers)
222 223
223 224 if last_rev == 0 or leftovers < parse_limit:
224 225 log.debug('getting code trending stats')
225 226 stats.languages = json.dumps(__get_codes_stats(repo_name))
226 227
227 228 try:
228 229 stats.repository = dbrepo
229 230 stats.stat_on_revision = last_cs.revision if last_cs else 0
230 231 DBS.add(stats)
231 232 DBS.commit()
232 233 except:
233 234 log.error(traceback.format_exc())
234 235 DBS.rollback()
235 236 lock.release()
236 237 return False
237 238
238 239 # final release
239 240 lock.release()
240 241
241 242 # execute another task if celery is enabled
242 243 if len(repo.revisions) > 1 and CELERY_ON:
243 244 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
244 245 return True
245 246 except LockHeld:
246 247 log.info('LockHeld')
247 248 return 'Task with key %s already running' % lockkey
248 249
249 250 @task(ignore_result=True)
250 251 @dbsession
251 252 def send_password_link(user_email):
252 253 from rhodecode.model.notification import EmailNotificationModel
253 254
254 255 log = get_logger(send_password_link)
255 256 DBS = get_session()
256 257
257 258 try:
258 259 user = User.get_by_email(user_email)
259 260 if user:
260 261 log.debug('password reset user found %s' % user)
261 262 link = url('reset_password_confirmation', key=user.api_key,
262 263 qualified=True)
263 264 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
264 265 body = EmailNotificationModel().get_email_tmpl(reg_type,
265 266 **{'user':user.short_contact,
266 267 'reset_url':link})
267 268 log.debug('sending email')
268 269 run_task(send_email, user_email,
269 270 _("password reset link"), body)
270 271 log.info('send new password mail to %s' % user_email)
271 272 else:
272 273 log.debug("password reset email %s not found" % user_email)
273 274 except:
274 275 log.error(traceback.format_exc())
275 276 return False
276 277
277 278 return True
278 279
279 280 @task(ignore_result=True)
280 281 @dbsession
281 282 def reset_user_password(user_email):
282 283 from rhodecode.lib import auth
283 284
284 285 log = get_logger(reset_user_password)
285 286 DBS = get_session()
286 287
287 288 try:
288 289 try:
289 290 user = User.get_by_email(user_email)
290 291 new_passwd = auth.PasswordGenerator().gen_password(8,
291 292 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
292 293 if user:
293 294 user.password = auth.get_crypt_password(new_passwd)
294 295 user.api_key = auth.generate_api_key(user.username)
295 296 DBS.add(user)
296 297 DBS.commit()
297 298 log.info('change password for %s' % user_email)
298 299 if new_passwd is None:
299 300 raise Exception('unable to generate new password')
300 301 except:
301 302 log.error(traceback.format_exc())
302 303 DBS.rollback()
303 304
304 305 run_task(send_email, user_email,
305 306 'Your new password',
306 307 'Your new RhodeCode password:%s' % (new_passwd))
307 308 log.info('send new password mail to %s' % user_email)
308 309
309 310 except:
310 311 log.error('Failed to update user password')
311 312 log.error(traceback.format_exc())
312 313
313 314 return True
314 315
315 316
316 317 @task(ignore_result=True)
317 318 @dbsession
318 319 def send_email(recipients, subject, body, html_body=''):
319 320 """
320 321 Sends an email with defined parameters from the .ini files.
321 322
322 323 :param recipients: list of recipients, it this is empty the defined email
323 324 address from field 'email_to' is used instead
324 325 :param subject: subject of the mail
325 326 :param body: body of the mail
326 327 :param html_body: html version of body
327 328 """
328 329 log = get_logger(send_email)
329 330 DBS = get_session()
330 331
331 332 email_config = config
332 333 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
333 334 if not recipients:
334 335 # if recipients are not defined we send to email_config + all admins
335 336 admins = [u.email for u in User.query()
336 337 .filter(User.admin == True).all()]
337 338 recipients = [email_config.get('email_to')] + admins
338 339
339 340 mail_from = email_config.get('app_email_from', 'RhodeCode')
340 341 user = email_config.get('smtp_username')
341 342 passwd = email_config.get('smtp_password')
342 343 mail_server = email_config.get('smtp_server')
343 344 mail_port = email_config.get('smtp_port')
344 345 tls = str2bool(email_config.get('smtp_use_tls'))
345 346 ssl = str2bool(email_config.get('smtp_use_ssl'))
346 347 debug = str2bool(config.get('debug'))
347 348 smtp_auth = email_config.get('smtp_auth')
348 349
349 350 try:
350 351 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
351 352 mail_port, ssl, tls, debug=debug)
352 353 m.send(recipients, subject, body, html_body)
353 354 except:
354 355 log.error('Mail sending failed')
355 356 log.error(traceback.format_exc())
356 357 return False
357 358 return True
358 359
359 360
360 361 @task(ignore_result=True)
361 362 @dbsession
362 363 def create_repo_fork(form_data, cur_user):
363 364 """
364 365 Creates a fork of repository using interval VCS methods
365 366
366 367 :param form_data:
367 368 :param cur_user:
368 369 """
369 370 from rhodecode.model.repo import RepoModel
370 371 from rhodecode.model.user import UserModel
371 372
372 373 log = get_logger(create_repo_fork)
373 374 DBS = get_session()
374 375
375 376 base_path = Repository.base_path()
376 377 cur_user = UserModel(DBS)._get_user(cur_user)
377 378
378 379 fork_name = form_data['repo_name_full']
379 380 repo_type = form_data['repo_type']
380 381 description = form_data['description']
381 382 owner = cur_user
382 383 private = form_data['private']
383 384 clone_uri = form_data.get('clone_uri')
384 385 repos_group = form_data['repo_group']
385 386 landing_rev = form_data['landing_rev']
386 387 copy_fork_permissions = form_data.get('copy_permissions')
387 388 fork_of = RepoModel(DBS)._get_repo(form_data.get('fork_parent_id'))
388 389
389 390 fork_repo = RepoModel(DBS).create_repo(
390 391 fork_name, repo_type, description, owner, private, clone_uri,
391 392 repos_group, landing_rev, just_db=True, fork_of=fork_of,
392 393 copy_fork_permissions=copy_fork_permissions
393 394 )
394 395
395 396 update_after_clone = form_data['update_after_clone']
396 397
397 398 source_repo_path = os.path.join(base_path, fork_of.repo_name)
398 399 destination_fork_path = os.path.join(base_path, fork_name)
399 400
400 401 log.info('creating fork of %s as %s', source_repo_path,
401 402 destination_fork_path)
402 403 backend = get_backend(repo_type)
403 404
404 405 if repo_type == 'git':
405 backend(safe_str(destination_fork_path), create=True,
406 r = backend(safe_str(destination_fork_path), create=True,
406 407 src_url=safe_str(source_repo_path),
407 408 update_after_clone=update_after_clone,
408 409 bare=True)
409 410 elif repo_type == 'hg':
410 backend(safe_str(destination_fork_path), create=True,
411 r = backend(safe_str(destination_fork_path), create=True,
411 412 src_url=safe_str(source_repo_path),
412 413 update_after_clone=update_after_clone)
413 414 else:
414 415 raise Exception('Unknown backend type %s' % repo_type)
415 416
417 # add rhodecode hook into this repo
418 ScmModel().install_git_hook(repo=r)
416 419 log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
417 420
418 421 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
419 422 fork_of.repo_name, '', DBS)
420 423
421 424 action_logger(cur_user, 'user_created_fork:%s' % fork_name,
422 425 fork_name, '', DBS)
423 426 # finally commit at latest possible stage
424 427 DBS.commit()
425 428
426 429
427 430 def __get_codes_stats(repo_name):
428 431 from rhodecode.config.conf import LANGUAGES_EXTENSIONS_MAP
429 432 repo = Repository.get_by_repo_name(repo_name).scm_instance
430 433
431 434 tip = repo.get_changeset()
432 435 code_stats = {}
433 436
434 437 def aggregate(cs):
435 438 for f in cs[2]:
436 439 ext = lower(f.extension)
437 440 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
438 441 if ext in code_stats:
439 442 code_stats[ext] += 1
440 443 else:
441 444 code_stats[ext] = 1
442 445
443 446 map(aggregate, tip.walk('/'))
444 447
445 448 return code_stats or {}
General Comments 0
You need to be logged in to leave comments. Login now