##// END OF EJS Templates
better logging
marcink -
r3545:e2fad0c6 beta
parent child Browse files
Show More
@@ -1,800 +1,800 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import re
28 28 import logging
29 29 import datetime
30 30 import traceback
31 31 import paste
32 32 import beaker
33 33 import tarfile
34 34 import shutil
35 35 import decorator
36 36 import warnings
37 37 from os.path import abspath
38 38 from os.path import dirname as dn, join as jn
39 39
40 40 from paste.script.command import Command, BadCommand
41 41
42 42 from mercurial import ui, config
43 43
44 44 from webhelpers.text import collapse, remove_formatting, strip_tags
45 45
46 46 from rhodecode.lib.vcs import get_backend
47 47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
49 49 from rhodecode.lib.vcs.utils.helpers import get_scm
50 50 from rhodecode.lib.vcs.exceptions import VCSError
51 51
52 52 from rhodecode.lib.caching_query import FromCache
53 53
54 54 from rhodecode.model import meta
55 55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
56 56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
57 57 from rhodecode.model.meta import Session
58 58 from rhodecode.model.repos_group import ReposGroupModel
59 59 from rhodecode.lib.utils2 import safe_str, safe_unicode
60 60 from rhodecode.lib.vcs.utils.fakemod import create_module
61 61
62 62 log = logging.getLogger(__name__)
63 63
64 64 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
65 65
66 66
67 67 def recursive_replace(str_, replace=' '):
68 68 """
69 69 Recursive replace of given sign to just one instance
70 70
71 71 :param str_: given string
72 72 :param replace: char to find and replace multiple instances
73 73
74 74 Examples::
75 75 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
76 76 'Mighty-Mighty-Bo-sstones'
77 77 """
78 78
79 79 if str_.find(replace * 2) == -1:
80 80 return str_
81 81 else:
82 82 str_ = str_.replace(replace * 2, replace)
83 83 return recursive_replace(str_, replace)
84 84
85 85
86 86 def repo_name_slug(value):
87 87 """
88 88 Return slug of name of repository
89 89 This function is called on each creation/modification
90 90 of repository to prevent bad names in repo
91 91 """
92 92
93 93 slug = remove_formatting(value)
94 94 slug = strip_tags(slug)
95 95
96 96 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
97 97 slug = slug.replace(c, '-')
98 98 slug = recursive_replace(slug, '-')
99 99 slug = collapse(slug, '-')
100 100 return slug
101 101
102 102
103 103 def get_repo_slug(request):
104 104 _repo = request.environ['pylons.routes_dict'].get('repo_name')
105 105 if _repo:
106 106 _repo = _repo.rstrip('/')
107 107 return _repo
108 108
109 109
110 110 def get_repos_group_slug(request):
111 111 _group = request.environ['pylons.routes_dict'].get('group_name')
112 112 if _group:
113 113 _group = _group.rstrip('/')
114 114 return _group
115 115
116 116
117 117 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
118 118 """
119 119 Action logger for various actions made by users
120 120
121 121 :param user: user that made this action, can be a unique username string or
122 122 object containing user_id attribute
123 123 :param action: action to log, should be on of predefined unique actions for
124 124 easy translations
125 125 :param repo: string name of repository or object containing repo_id,
126 126 that action was made on
127 127 :param ipaddr: optional ip address from what the action was made
128 128 :param sa: optional sqlalchemy session
129 129
130 130 """
131 131
132 132 if not sa:
133 133 sa = meta.Session()
134 134
135 135 try:
136 136 if hasattr(user, 'user_id'):
137 137 user_obj = User.get(user.user_id)
138 138 elif isinstance(user, basestring):
139 139 user_obj = User.get_by_username(user)
140 140 else:
141 141 raise Exception('You have to provide a user object or a username')
142 142
143 143 if hasattr(repo, 'repo_id'):
144 144 repo_obj = Repository.get(repo.repo_id)
145 145 repo_name = repo_obj.repo_name
146 146 elif isinstance(repo, basestring):
147 147 repo_name = repo.lstrip('/')
148 148 repo_obj = Repository.get_by_repo_name(repo_name)
149 149 else:
150 150 repo_obj = None
151 151 repo_name = ''
152 152
153 153 user_log = UserLog()
154 154 user_log.user_id = user_obj.user_id
155 155 user_log.username = user_obj.username
156 156 user_log.action = safe_unicode(action)
157 157
158 158 user_log.repository = repo_obj
159 159 user_log.repository_name = repo_name
160 160
161 161 user_log.action_date = datetime.datetime.now()
162 162 user_log.user_ip = ipaddr
163 163 sa.add(user_log)
164 164
165 log.info('Logging action %s on %s by %s' %
166 (action, safe_unicode(repo), user_obj))
165 log.info('Logging action:%s on %s by user:%s ip:%s' %
166 (action, safe_unicode(repo), user_obj, ipaddr))
167 167 if commit:
168 168 sa.commit()
169 169 except:
170 170 log.error(traceback.format_exc())
171 171 raise
172 172
173 173
174 174 def get_repos(path, recursive=False, skip_removed_repos=True):
175 175 """
176 176 Scans given path for repos and return (name,(type,path)) tuple
177 177
178 178 :param path: path to scan for repositories
179 179 :param recursive: recursive search and return names with subdirs in front
180 180 """
181 181
182 182 # remove ending slash for better results
183 183 path = path.rstrip(os.sep)
184 184 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
185 185
186 186 def _get_repos(p):
187 187 if not os.access(p, os.W_OK):
188 188 return
189 189 for dirpath in os.listdir(p):
190 190 if os.path.isfile(os.path.join(p, dirpath)):
191 191 continue
192 192 cur_path = os.path.join(p, dirpath)
193 193
194 194 # skip removed repos
195 195 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
196 196 continue
197 197
198 198 #skip .<somethin> dirs
199 199 if dirpath.startswith('.'):
200 200 continue
201 201
202 202 try:
203 203 scm_info = get_scm(cur_path)
204 204 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
205 205 except VCSError:
206 206 if not recursive:
207 207 continue
208 208 #check if this dir containts other repos for recursive scan
209 209 rec_path = os.path.join(p, dirpath)
210 210 if os.path.isdir(rec_path):
211 211 for inner_scm in _get_repos(rec_path):
212 212 yield inner_scm
213 213
214 214 return _get_repos(path)
215 215
216 216 #alias for backward compat
217 217 get_filesystem_repos = get_repos
218 218
219 219
220 220 def is_valid_repo(repo_name, base_path, scm=None):
221 221 """
222 222 Returns True if given path is a valid repository False otherwise.
223 223 If scm param is given also compare if given scm is the same as expected
224 224 from scm parameter
225 225
226 226 :param repo_name:
227 227 :param base_path:
228 228 :param scm:
229 229
230 230 :return True: if given path is a valid repository
231 231 """
232 232 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
233 233
234 234 try:
235 235 scm_ = get_scm(full_path)
236 236 if scm:
237 237 return scm_[0] == scm
238 238 return True
239 239 except VCSError:
240 240 return False
241 241
242 242
243 243 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
244 244 """
245 245 Returns True if given path is a repos group False otherwise
246 246
247 247 :param repo_name:
248 248 :param base_path:
249 249 """
250 250 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
251 251
252 252 # check if it's not a repo
253 253 if is_valid_repo(repos_group_name, base_path):
254 254 return False
255 255
256 256 try:
257 257 # we need to check bare git repos at higher level
258 258 # since we might match branches/hooks/info/objects or possible
259 259 # other things inside bare git repo
260 260 get_scm(os.path.dirname(full_path))
261 261 return False
262 262 except VCSError:
263 263 pass
264 264
265 265 # check if it's a valid path
266 266 if skip_path_check or os.path.isdir(full_path):
267 267 return True
268 268
269 269 return False
270 270
271 271
272 272 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
273 273 while True:
274 274 ok = raw_input(prompt)
275 275 if ok in ('y', 'ye', 'yes'):
276 276 return True
277 277 if ok in ('n', 'no', 'nop', 'nope'):
278 278 return False
279 279 retries = retries - 1
280 280 if retries < 0:
281 281 raise IOError
282 282 print complaint
283 283
284 284 #propagated from mercurial documentation
285 285 ui_sections = ['alias', 'auth',
286 286 'decode/encode', 'defaults',
287 287 'diff', 'email',
288 288 'extensions', 'format',
289 289 'merge-patterns', 'merge-tools',
290 290 'hooks', 'http_proxy',
291 291 'smtp', 'patch',
292 292 'paths', 'profiling',
293 293 'server', 'trusted',
294 294 'ui', 'web', ]
295 295
296 296
297 297 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
298 298 """
299 299 A function that will read python rc files or database
300 300 and make an mercurial ui object from read options
301 301
302 302 :param path: path to mercurial config file
303 303 :param checkpaths: check the path
304 304 :param read_from: read from 'file' or 'db'
305 305 """
306 306
307 307 baseui = ui.ui()
308 308
309 309 # clean the baseui object
310 310 baseui._ocfg = config.config()
311 311 baseui._ucfg = config.config()
312 312 baseui._tcfg = config.config()
313 313
314 314 if read_from == 'file':
315 315 if not os.path.isfile(path):
316 316 log.debug('hgrc file is not present at %s, skipping...' % path)
317 317 return False
318 318 log.debug('reading hgrc from %s' % path)
319 319 cfg = config.config()
320 320 cfg.read(path)
321 321 for section in ui_sections:
322 322 for k, v in cfg.items(section):
323 323 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
324 324 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
325 325
326 326 elif read_from == 'db':
327 327 sa = meta.Session()
328 328 ret = sa.query(RhodeCodeUi)\
329 329 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
330 330 .all()
331 331
332 332 hg_ui = ret
333 333 for ui_ in hg_ui:
334 334 if ui_.ui_active:
335 335 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
336 336 ui_.ui_key, ui_.ui_value)
337 337 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
338 338 safe_str(ui_.ui_value))
339 339 if ui_.ui_key == 'push_ssl':
340 340 # force set push_ssl requirement to False, rhodecode
341 341 # handles that
342 342 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
343 343 False)
344 344 if clear_session:
345 345 meta.Session.remove()
346 346 return baseui
347 347
348 348
349 349 def set_rhodecode_config(config):
350 350 """
351 351 Updates pylons config with new settings from database
352 352
353 353 :param config:
354 354 """
355 355 hgsettings = RhodeCodeSetting.get_app_settings()
356 356
357 357 for k, v in hgsettings.items():
358 358 config[k] = v
359 359
360 360
361 361 def invalidate_cache(cache_key, *args):
362 362 """
363 363 Puts cache invalidation task into db for
364 364 further global cache invalidation
365 365 """
366 366
367 367 from rhodecode.model.scm import ScmModel
368 368
369 369 if cache_key.startswith('get_repo_cached_'):
370 370 name = cache_key.split('get_repo_cached_')[-1]
371 371 ScmModel().mark_for_invalidation(name)
372 372
373 373
374 374 def map_groups(path):
375 375 """
376 376 Given a full path to a repository, create all nested groups that this
377 377 repo is inside. This function creates parent-child relationships between
378 378 groups and creates default perms for all new groups.
379 379
380 380 :param paths: full path to repository
381 381 """
382 382 sa = meta.Session()
383 383 groups = path.split(Repository.url_sep())
384 384 parent = None
385 385 group = None
386 386
387 387 # last element is repo in nested groups structure
388 388 groups = groups[:-1]
389 389 rgm = ReposGroupModel(sa)
390 390 for lvl, group_name in enumerate(groups):
391 391 group_name = '/'.join(groups[:lvl] + [group_name])
392 392 group = RepoGroup.get_by_group_name(group_name)
393 393 desc = '%s group' % group_name
394 394
395 395 # skip folders that are now removed repos
396 396 if REMOVED_REPO_PAT.match(group_name):
397 397 break
398 398
399 399 if group is None:
400 400 log.debug('creating group level: %s group_name: %s' % (lvl,
401 401 group_name))
402 402 group = RepoGroup(group_name, parent)
403 403 group.group_description = desc
404 404 sa.add(group)
405 405 rgm._create_default_perms(group)
406 406 sa.flush()
407 407 parent = group
408 408 return group
409 409
410 410
411 411 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
412 412 install_git_hook=False):
413 413 """
414 414 maps all repos given in initial_repo_list, non existing repositories
415 415 are created, if remove_obsolete is True it also check for db entries
416 416 that are not in initial_repo_list and removes them.
417 417
418 418 :param initial_repo_list: list of repositories found by scanning methods
419 419 :param remove_obsolete: check for obsolete entries in database
420 420 :param install_git_hook: if this is True, also check and install githook
421 421 for a repo if missing
422 422 """
423 423 from rhodecode.model.repo import RepoModel
424 424 from rhodecode.model.scm import ScmModel
425 425 sa = meta.Session()
426 426 rm = RepoModel()
427 427 user = sa.query(User).filter(User.admin == True).first()
428 428 if user is None:
429 429 raise Exception('Missing administrative account!')
430 430 added = []
431 431
432 432 # # clear cache keys
433 433 # log.debug("Clearing cache keys now...")
434 434 # CacheInvalidation.clear_cache()
435 435 # sa.commit()
436 436
437 437 ##creation defaults
438 438 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
439 439 enable_statistics = defs.get('repo_enable_statistics')
440 440 enable_locking = defs.get('repo_enable_locking')
441 441 enable_downloads = defs.get('repo_enable_downloads')
442 442 private = defs.get('repo_private')
443 443
444 444 for name, repo in initial_repo_list.items():
445 445 group = map_groups(name)
446 446 db_repo = rm.get_by_repo_name(name)
447 447 # found repo that is on filesystem not in RhodeCode database
448 448 if not db_repo:
449 449 log.info('repository %s not found, creating now' % name)
450 450 added.append(name)
451 451 desc = (repo.description
452 452 if repo.description != 'unknown'
453 453 else '%s repository' % name)
454 454
455 455 new_repo = rm.create_repo(
456 456 repo_name=name,
457 457 repo_type=repo.alias,
458 458 description=desc,
459 459 repos_group=getattr(group, 'group_id', None),
460 460 owner=user,
461 461 just_db=True,
462 462 enable_locking=enable_locking,
463 463 enable_downloads=enable_downloads,
464 464 enable_statistics=enable_statistics,
465 465 private=private
466 466 )
467 467 # we added that repo just now, and make sure it has githook
468 468 # installed
469 469 if new_repo.repo_type == 'git':
470 470 ScmModel().install_git_hook(new_repo.scm_instance)
471 471 new_repo.update_changeset_cache()
472 472 elif install_git_hook:
473 473 if db_repo.repo_type == 'git':
474 474 ScmModel().install_git_hook(db_repo.scm_instance)
475 475 # during starting install all cache keys for all repositories in the
476 476 # system, this will register all repos and multiple instances
477 477 key, _prefix, _org_key = CacheInvalidation._get_key(name)
478 478 CacheInvalidation.invalidate(name)
479 479 log.debug("Creating a cache key for %s, instance_id %s"
480 480 % (name, _prefix or 'unknown'))
481 481
482 482 sa.commit()
483 483 removed = []
484 484 if remove_obsolete:
485 485 # remove from database those repositories that are not in the filesystem
486 486 for repo in sa.query(Repository).all():
487 487 if repo.repo_name not in initial_repo_list.keys():
488 488 log.debug("Removing non-existing repository found in db `%s`" %
489 489 repo.repo_name)
490 490 try:
491 491 sa.delete(repo)
492 492 sa.commit()
493 493 removed.append(repo.repo_name)
494 494 except:
495 495 #don't hold further removals on error
496 496 log.error(traceback.format_exc())
497 497 sa.rollback()
498 498 return added, removed
499 499
500 500
501 501 # set cache regions for beaker so celery can utilise it
502 502 def add_cache(settings):
503 503 cache_settings = {'regions': None}
504 504 for key in settings.keys():
505 505 for prefix in ['beaker.cache.', 'cache.']:
506 506 if key.startswith(prefix):
507 507 name = key.split(prefix)[1].strip()
508 508 cache_settings[name] = settings[key].strip()
509 509 if cache_settings['regions']:
510 510 for region in cache_settings['regions'].split(','):
511 511 region = region.strip()
512 512 region_settings = {}
513 513 for key, value in cache_settings.items():
514 514 if key.startswith(region):
515 515 region_settings[key.split('.')[1]] = value
516 516 region_settings['expire'] = int(region_settings.get('expire',
517 517 60))
518 518 region_settings.setdefault('lock_dir',
519 519 cache_settings.get('lock_dir'))
520 520 region_settings.setdefault('data_dir',
521 521 cache_settings.get('data_dir'))
522 522
523 523 if 'type' not in region_settings:
524 524 region_settings['type'] = cache_settings.get('type',
525 525 'memory')
526 526 beaker.cache.cache_regions[region] = region_settings
527 527
528 528
529 529 def load_rcextensions(root_path):
530 530 import rhodecode
531 531 from rhodecode.config import conf
532 532
533 533 path = os.path.join(root_path, 'rcextensions', '__init__.py')
534 534 if os.path.isfile(path):
535 535 rcext = create_module('rc', path)
536 536 EXT = rhodecode.EXTENSIONS = rcext
537 537 log.debug('Found rcextensions now loading %s...' % rcext)
538 538
539 539 # Additional mappings that are not present in the pygments lexers
540 540 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
541 541
542 542 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
543 543
544 544 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
545 545 log.debug('settings custom INDEX_EXTENSIONS')
546 546 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
547 547
548 548 #ADDITIONAL MAPPINGS
549 549 log.debug('adding extra into INDEX_EXTENSIONS')
550 550 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
551 551
552 552 # auto check if the module is not missing any data, set to default if is
553 553 # this will help autoupdate new feature of rcext module
554 554 from rhodecode.config import rcextensions
555 555 for k in dir(rcextensions):
556 556 if not k.startswith('_') and not hasattr(EXT, k):
557 557 setattr(EXT, k, getattr(rcextensions, k))
558 558
559 559
560 560 def get_custom_lexer(extension):
561 561 """
562 562 returns a custom lexer if it's defined in rcextensions module, or None
563 563 if there's no custom lexer defined
564 564 """
565 565 import rhodecode
566 566 from pygments import lexers
567 567 #check if we didn't define this extension as other lexer
568 568 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
569 569 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
570 570 return lexers.get_lexer_by_name(_lexer_name)
571 571
572 572
573 573 #==============================================================================
574 574 # TEST FUNCTIONS AND CREATORS
575 575 #==============================================================================
576 576 def create_test_index(repo_location, config, full_index):
577 577 """
578 578 Makes default test index
579 579
580 580 :param config: test config
581 581 :param full_index:
582 582 """
583 583
584 584 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
585 585 from rhodecode.lib.pidlock import DaemonLock, LockHeld
586 586
587 587 repo_location = repo_location
588 588
589 589 index_location = os.path.join(config['app_conf']['index_dir'])
590 590 if not os.path.exists(index_location):
591 591 os.makedirs(index_location)
592 592
593 593 try:
594 594 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
595 595 WhooshIndexingDaemon(index_location=index_location,
596 596 repo_location=repo_location)\
597 597 .run(full_index=full_index)
598 598 l.release()
599 599 except LockHeld:
600 600 pass
601 601
602 602
603 603 def create_test_env(repos_test_path, config):
604 604 """
605 605 Makes a fresh database and
606 606 install test repository into tmp dir
607 607 """
608 608 from rhodecode.lib.db_manage import DbManage
609 609 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
610 610
611 611 # PART ONE create db
612 612 dbconf = config['sqlalchemy.db1.url']
613 613 log.debug('making test db %s' % dbconf)
614 614
615 615 # create test dir if it doesn't exist
616 616 if not os.path.isdir(repos_test_path):
617 617 log.debug('Creating testdir %s' % repos_test_path)
618 618 os.makedirs(repos_test_path)
619 619
620 620 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
621 621 tests=True)
622 622 dbmanage.create_tables(override=True)
623 623 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
624 624 dbmanage.create_default_user()
625 625 dbmanage.admin_prompt()
626 626 dbmanage.create_permissions()
627 627 dbmanage.populate_default_permissions()
628 628 Session().commit()
629 629 # PART TWO make test repo
630 630 log.debug('making test vcs repositories')
631 631
632 632 idx_path = config['app_conf']['index_dir']
633 633 data_path = config['app_conf']['cache_dir']
634 634
635 635 #clean index and data
636 636 if idx_path and os.path.exists(idx_path):
637 637 log.debug('remove %s' % idx_path)
638 638 shutil.rmtree(idx_path)
639 639
640 640 if data_path and os.path.exists(data_path):
641 641 log.debug('remove %s' % data_path)
642 642 shutil.rmtree(data_path)
643 643
644 644 #CREATE DEFAULT TEST REPOS
645 645 cur_dir = dn(dn(abspath(__file__)))
646 646 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
647 647 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
648 648 tar.close()
649 649
650 650 cur_dir = dn(dn(abspath(__file__)))
651 651 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
652 652 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
653 653 tar.close()
654 654
655 655 #LOAD VCS test stuff
656 656 from rhodecode.tests.vcs import setup_package
657 657 setup_package()
658 658
659 659
660 660 #==============================================================================
661 661 # PASTER COMMANDS
662 662 #==============================================================================
663 663 class BasePasterCommand(Command):
664 664 """
665 665 Abstract Base Class for paster commands.
666 666
667 667 The celery commands are somewhat aggressive about loading
668 668 celery.conf, and since our module sets the `CELERY_LOADER`
669 669 environment variable to our loader, we have to bootstrap a bit and
670 670 make sure we've had a chance to load the pylons config off of the
671 671 command line, otherwise everything fails.
672 672 """
673 673 min_args = 1
674 674 min_args_error = "Please provide a paster config file as an argument."
675 675 takes_config_file = 1
676 676 requires_config_file = True
677 677
678 678 def notify_msg(self, msg, log=False):
679 679 """Make a notification to user, additionally if logger is passed
680 680 it logs this action using given logger
681 681
682 682 :param msg: message that will be printed to user
683 683 :param log: logging instance, to use to additionally log this message
684 684
685 685 """
686 686 if log and isinstance(log, logging):
687 687 log(msg)
688 688
689 689 def run(self, args):
690 690 """
691 691 Overrides Command.run
692 692
693 693 Checks for a config file argument and loads it.
694 694 """
695 695 if len(args) < self.min_args:
696 696 raise BadCommand(
697 697 self.min_args_error % {'min_args': self.min_args,
698 698 'actual_args': len(args)})
699 699
700 700 # Decrement because we're going to lob off the first argument.
701 701 # @@ This is hacky
702 702 self.min_args -= 1
703 703 self.bootstrap_config(args[0])
704 704 self.update_parser()
705 705 return super(BasePasterCommand, self).run(args[1:])
706 706
707 707 def update_parser(self):
708 708 """
709 709 Abstract method. Allows for the class's parser to be updated
710 710 before the superclass's `run` method is called. Necessary to
711 711 allow options/arguments to be passed through to the underlying
712 712 celery command.
713 713 """
714 714 raise NotImplementedError("Abstract Method.")
715 715
716 716 def bootstrap_config(self, conf):
717 717 """
718 718 Loads the pylons configuration.
719 719 """
720 720 from pylons import config as pylonsconfig
721 721
722 722 self.path_to_ini_file = os.path.realpath(conf)
723 723 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
724 724 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
725 725
726 726 def _init_session(self):
727 727 """
728 728 Inits SqlAlchemy Session
729 729 """
730 730 logging.config.fileConfig(self.path_to_ini_file)
731 731 from pylons import config
732 732 from rhodecode.model import init_model
733 733 from rhodecode.lib.utils2 import engine_from_config
734 734
735 735 #get to remove repos !!
736 736 add_cache(config)
737 737 engine = engine_from_config(config, 'sqlalchemy.db1.')
738 738 init_model(engine)
739 739
740 740
741 741 def check_git_version():
742 742 """
743 743 Checks what version of git is installed in system, and issues a warning
744 744 if it's too old for RhodeCode to properly work.
745 745 """
746 746 from rhodecode import BACKENDS
747 747 from rhodecode.lib.vcs.backends.git.repository import GitRepository
748 748 from distutils.version import StrictVersion
749 749
750 750 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
751 751 _safe=True)
752 752
753 753 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
754 754 if len(ver.split('.')) > 3:
755 755 #StrictVersion needs to be only 3 element type
756 756 ver = '.'.join(ver.split('.')[:3])
757 757 try:
758 758 _ver = StrictVersion(ver)
759 759 except:
760 760 _ver = StrictVersion('0.0.0')
761 761 stderr = traceback.format_exc()
762 762
763 763 req_ver = '1.7.4'
764 764 to_old_git = False
765 765 if _ver < StrictVersion(req_ver):
766 766 to_old_git = True
767 767
768 768 if 'git' in BACKENDS:
769 769 log.debug('GIT version detected: %s' % stdout)
770 770 if stderr:
771 771 log.warning('Unable to detect git version org error was:%r' % stderr)
772 772 elif to_old_git:
773 773 log.warning('RhodeCode detected git version %s, which is too old '
774 774 'for the system to function properly. Make sure '
775 775 'its version is at least %s' % (ver, req_ver))
776 776 return _ver
777 777
778 778
779 779 @decorator.decorator
780 780 def jsonify(func, *args, **kwargs):
781 781 """Action decorator that formats output for JSON
782 782
783 783 Given a function that will return content, this decorator will turn
784 784 the result into JSON, with a content-type of 'application/json' and
785 785 output it.
786 786
787 787 """
788 788 from pylons.decorators.util import get_pylons
789 789 from rhodecode.lib.ext_json import json
790 790 pylons = get_pylons(args)
791 791 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
792 792 data = func(*args, **kwargs)
793 793 if isinstance(data, (list, tuple)):
794 794 msg = "JSON responses with Array envelopes are susceptible to " \
795 795 "cross-site data leak attacks, see " \
796 796 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
797 797 warnings.warn(msg, Warning, 2)
798 798 log.warning(msg)
799 799 log.debug("Returning JSON wrapped action output")
800 800 return json.dumps(data, encoding='utf-8')
General Comments 0
You need to be logged in to leave comments. Login now