##// END OF EJS Templates
logging: make 'Creating a cache key for...' more readable
Mads Kiilerich -
r3134:ff315659 beta
parent child Browse files
Show More
@@ -1,755 +1,755 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 165 log.info(
166 166 'Adding user %s, action %s on %s' % (user_obj, action,
167 167 safe_unicode(repo))
168 168 )
169 169 if commit:
170 170 sa.commit()
171 171 except:
172 172 log.error(traceback.format_exc())
173 173 raise
174 174
175 175
176 176 def get_repos(path, recursive=False):
177 177 """
178 178 Scans given path for repos and return (name,(type,path)) tuple
179 179
180 180 :param path: path to scan for repositories
181 181 :param recursive: recursive search and return names with subdirs in front
182 182 """
183 183
184 184 # remove ending slash for better results
185 185 path = path.rstrip(os.sep)
186 186
187 187 def _get_repos(p):
188 188 if not os.access(p, os.W_OK):
189 189 return
190 190 for dirpath in os.listdir(p):
191 191 if os.path.isfile(os.path.join(p, dirpath)):
192 192 continue
193 193 cur_path = os.path.join(p, dirpath)
194 194 try:
195 195 scm_info = get_scm(cur_path)
196 196 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
197 197 except VCSError:
198 198 if not recursive:
199 199 continue
200 200 #check if this dir containts other repos for recursive scan
201 201 rec_path = os.path.join(p, dirpath)
202 202 if os.path.isdir(rec_path):
203 203 for inner_scm in _get_repos(rec_path):
204 204 yield inner_scm
205 205
206 206 return _get_repos(path)
207 207
208 208
209 209 def is_valid_repo(repo_name, base_path, scm=None):
210 210 """
211 211 Returns True if given path is a valid repository False otherwise.
212 212 If scm param is given also compare if given scm is the same as expected
213 213 from scm parameter
214 214
215 215 :param repo_name:
216 216 :param base_path:
217 217 :param scm:
218 218
219 219 :return True: if given path is a valid repository
220 220 """
221 221 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
222 222
223 223 try:
224 224 scm_ = get_scm(full_path)
225 225 if scm:
226 226 return scm_[0] == scm
227 227 return True
228 228 except VCSError:
229 229 return False
230 230
231 231
232 232 def is_valid_repos_group(repos_group_name, base_path):
233 233 """
234 234 Returns True if given path is a repos group False otherwise
235 235
236 236 :param repo_name:
237 237 :param base_path:
238 238 """
239 239 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
240 240
241 241 # check if it's not a repo
242 242 if is_valid_repo(repos_group_name, base_path):
243 243 return False
244 244
245 245 try:
246 246 # we need to check bare git repos at higher level
247 247 # since we might match branches/hooks/info/objects or possible
248 248 # other things inside bare git repo
249 249 get_scm(os.path.dirname(full_path))
250 250 return False
251 251 except VCSError:
252 252 pass
253 253
254 254 # check if it's a valid path
255 255 if os.path.isdir(full_path):
256 256 return True
257 257
258 258 return False
259 259
260 260
261 261 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
262 262 while True:
263 263 ok = raw_input(prompt)
264 264 if ok in ('y', 'ye', 'yes'):
265 265 return True
266 266 if ok in ('n', 'no', 'nop', 'nope'):
267 267 return False
268 268 retries = retries - 1
269 269 if retries < 0:
270 270 raise IOError
271 271 print complaint
272 272
273 273 #propagated from mercurial documentation
274 274 ui_sections = ['alias', 'auth',
275 275 'decode/encode', 'defaults',
276 276 'diff', 'email',
277 277 'extensions', 'format',
278 278 'merge-patterns', 'merge-tools',
279 279 'hooks', 'http_proxy',
280 280 'smtp', 'patch',
281 281 'paths', 'profiling',
282 282 'server', 'trusted',
283 283 'ui', 'web', ]
284 284
285 285
286 286 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
287 287 """
288 288 A function that will read python rc files or database
289 289 and make an mercurial ui object from read options
290 290
291 291 :param path: path to mercurial config file
292 292 :param checkpaths: check the path
293 293 :param read_from: read from 'file' or 'db'
294 294 """
295 295
296 296 baseui = ui.ui()
297 297
298 298 # clean the baseui object
299 299 baseui._ocfg = config.config()
300 300 baseui._ucfg = config.config()
301 301 baseui._tcfg = config.config()
302 302
303 303 if read_from == 'file':
304 304 if not os.path.isfile(path):
305 305 log.debug('hgrc file is not present at %s, skipping...' % path)
306 306 return False
307 307 log.debug('reading hgrc from %s' % path)
308 308 cfg = config.config()
309 309 cfg.read(path)
310 310 for section in ui_sections:
311 311 for k, v in cfg.items(section):
312 312 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
313 313 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
314 314
315 315 elif read_from == 'db':
316 316 sa = meta.Session()
317 317 ret = sa.query(RhodeCodeUi)\
318 318 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
319 319 .all()
320 320
321 321 hg_ui = ret
322 322 for ui_ in hg_ui:
323 323 if ui_.ui_active:
324 324 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
325 325 ui_.ui_key, ui_.ui_value)
326 326 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
327 327 safe_str(ui_.ui_value))
328 328 if ui_.ui_key == 'push_ssl':
329 329 # force set push_ssl requirement to False, rhodecode
330 330 # handles that
331 331 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
332 332 False)
333 333 if clear_session:
334 334 meta.Session.remove()
335 335 return baseui
336 336
337 337
338 338 def set_rhodecode_config(config):
339 339 """
340 340 Updates pylons config with new settings from database
341 341
342 342 :param config:
343 343 """
344 344 hgsettings = RhodeCodeSetting.get_app_settings()
345 345
346 346 for k, v in hgsettings.items():
347 347 config[k] = v
348 348
349 349
350 350 def invalidate_cache(cache_key, *args):
351 351 """
352 352 Puts cache invalidation task into db for
353 353 further global cache invalidation
354 354 """
355 355
356 356 from rhodecode.model.scm import ScmModel
357 357
358 358 if cache_key.startswith('get_repo_cached_'):
359 359 name = cache_key.split('get_repo_cached_')[-1]
360 360 ScmModel().mark_for_invalidation(name)
361 361
362 362
363 363 def map_groups(path):
364 364 """
365 365 Given a full path to a repository, create all nested groups that this
366 366 repo is inside. This function creates parent-child relationships between
367 367 groups and creates default perms for all new groups.
368 368
369 369 :param paths: full path to repository
370 370 """
371 371 sa = meta.Session()
372 372 groups = path.split(Repository.url_sep())
373 373 parent = None
374 374 group = None
375 375
376 376 # last element is repo in nested groups structure
377 377 groups = groups[:-1]
378 378 rgm = ReposGroupModel(sa)
379 379 for lvl, group_name in enumerate(groups):
380 380 group_name = '/'.join(groups[:lvl] + [group_name])
381 381 group = RepoGroup.get_by_group_name(group_name)
382 382 desc = '%s group' % group_name
383 383
384 384 # skip folders that are now removed repos
385 385 if REMOVED_REPO_PAT.match(group_name):
386 386 break
387 387
388 388 if group is None:
389 389 log.debug('creating group level: %s group_name: %s' % (lvl,
390 390 group_name))
391 391 group = RepoGroup(group_name, parent)
392 392 group.group_description = desc
393 393 sa.add(group)
394 394 rgm._create_default_perms(group)
395 395 sa.flush()
396 396 parent = group
397 397 return group
398 398
399 399
400 400 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
401 401 install_git_hook=False):
402 402 """
403 403 maps all repos given in initial_repo_list, non existing repositories
404 404 are created, if remove_obsolete is True it also check for db entries
405 405 that are not in initial_repo_list and removes them.
406 406
407 407 :param initial_repo_list: list of repositories found by scanning methods
408 408 :param remove_obsolete: check for obsolete entries in database
409 409 :param install_git_hook: if this is True, also check and install githook
410 410 for a repo if missing
411 411 """
412 412 from rhodecode.model.repo import RepoModel
413 413 from rhodecode.model.scm import ScmModel
414 414 sa = meta.Session()
415 415 rm = RepoModel()
416 416 user = sa.query(User).filter(User.admin == True).first()
417 417 if user is None:
418 418 raise Exception('Missing administrative account!')
419 419 added = []
420 420
421 421 # # clear cache keys
422 422 # log.debug("Clearing cache keys now...")
423 423 # CacheInvalidation.clear_cache()
424 424 # sa.commit()
425 425
426 426 ##creation defaults
427 427 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
428 428 enable_statistics = defs.get('repo_enable_statistics')
429 429 enable_locking = defs.get('repo_enable_locking')
430 430 enable_downloads = defs.get('repo_enable_downloads')
431 431 private = defs.get('repo_private')
432 432
433 433 for name, repo in initial_repo_list.items():
434 434 group = map_groups(name)
435 435 db_repo = rm.get_by_repo_name(name)
436 436 # found repo that is on filesystem not in RhodeCode database
437 437 if not db_repo:
438 438 log.info('repository %s not found, creating now' % name)
439 439 added.append(name)
440 440 desc = (repo.description
441 441 if repo.description != 'unknown'
442 442 else '%s repository' % name)
443 443
444 444 new_repo = rm.create_repo(
445 445 repo_name=name,
446 446 repo_type=repo.alias,
447 447 description=desc,
448 448 repos_group=getattr(group, 'group_id', None),
449 449 owner=user,
450 450 just_db=True,
451 451 enable_locking=enable_locking,
452 452 enable_downloads=enable_downloads,
453 453 enable_statistics=enable_statistics,
454 454 private=private
455 455 )
456 456 # we added that repo just now, and make sure it has githook
457 457 # installed
458 458 if new_repo.repo_type == 'git':
459 459 ScmModel().install_git_hook(new_repo.scm_instance)
460 460 elif install_git_hook:
461 461 if db_repo.repo_type == 'git':
462 462 ScmModel().install_git_hook(db_repo.scm_instance)
463 463 # during starting install all cache keys for all repositories in the
464 464 # system, this will register all repos and multiple instances
465 465 key, _prefix, _org_key = CacheInvalidation._get_key(name)
466 466 CacheInvalidation.invalidate(name)
467 log.debug("Creating a cache key for %s instance_id=>`%s`"
468 % (name, _prefix or '-'))
467 log.debug("Creating a cache key for %s, instance_id %s"
468 % (name, _prefix or 'unknown'))
469 469
470 470 sa.commit()
471 471 removed = []
472 472 if remove_obsolete:
473 473 # remove from database those repositories that are not in the filesystem
474 474 for repo in sa.query(Repository).all():
475 475 if repo.repo_name not in initial_repo_list.keys():
476 476 log.debug("Removing non-existing repository found in db `%s`" %
477 477 repo.repo_name)
478 478 try:
479 479 sa.delete(repo)
480 480 sa.commit()
481 481 removed.append(repo.repo_name)
482 482 except:
483 483 #don't hold further removals on error
484 484 log.error(traceback.format_exc())
485 485 sa.rollback()
486 486
487 487 return added, removed
488 488
489 489
490 490 # set cache regions for beaker so celery can utilise it
491 491 def add_cache(settings):
492 492 cache_settings = {'regions': None}
493 493 for key in settings.keys():
494 494 for prefix in ['beaker.cache.', 'cache.']:
495 495 if key.startswith(prefix):
496 496 name = key.split(prefix)[1].strip()
497 497 cache_settings[name] = settings[key].strip()
498 498 if cache_settings['regions']:
499 499 for region in cache_settings['regions'].split(','):
500 500 region = region.strip()
501 501 region_settings = {}
502 502 for key, value in cache_settings.items():
503 503 if key.startswith(region):
504 504 region_settings[key.split('.')[1]] = value
505 505 region_settings['expire'] = int(region_settings.get('expire',
506 506 60))
507 507 region_settings.setdefault('lock_dir',
508 508 cache_settings.get('lock_dir'))
509 509 region_settings.setdefault('data_dir',
510 510 cache_settings.get('data_dir'))
511 511
512 512 if 'type' not in region_settings:
513 513 region_settings['type'] = cache_settings.get('type',
514 514 'memory')
515 515 beaker.cache.cache_regions[region] = region_settings
516 516
517 517
518 518 def load_rcextensions(root_path):
519 519 import rhodecode
520 520 from rhodecode.config import conf
521 521
522 522 path = os.path.join(root_path, 'rcextensions', '__init__.py')
523 523 if os.path.isfile(path):
524 524 rcext = create_module('rc', path)
525 525 EXT = rhodecode.EXTENSIONS = rcext
526 526 log.debug('Found rcextensions now loading %s...' % rcext)
527 527
528 528 # Additional mappings that are not present in the pygments lexers
529 529 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
530 530
531 531 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
532 532
533 533 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
534 534 log.debug('settings custom INDEX_EXTENSIONS')
535 535 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
536 536
537 537 #ADDITIONAL MAPPINGS
538 538 log.debug('adding extra into INDEX_EXTENSIONS')
539 539 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
540 540
541 541
542 542 #==============================================================================
543 543 # TEST FUNCTIONS AND CREATORS
544 544 #==============================================================================
545 545 def create_test_index(repo_location, config, full_index):
546 546 """
547 547 Makes default test index
548 548
549 549 :param config: test config
550 550 :param full_index:
551 551 """
552 552
553 553 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
554 554 from rhodecode.lib.pidlock import DaemonLock, LockHeld
555 555
556 556 repo_location = repo_location
557 557
558 558 index_location = os.path.join(config['app_conf']['index_dir'])
559 559 if not os.path.exists(index_location):
560 560 os.makedirs(index_location)
561 561
562 562 try:
563 563 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
564 564 WhooshIndexingDaemon(index_location=index_location,
565 565 repo_location=repo_location)\
566 566 .run(full_index=full_index)
567 567 l.release()
568 568 except LockHeld:
569 569 pass
570 570
571 571
572 572 def create_test_env(repos_test_path, config):
573 573 """
574 574 Makes a fresh database and
575 575 install test repository into tmp dir
576 576 """
577 577 from rhodecode.lib.db_manage import DbManage
578 578 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
579 579
580 580 # PART ONE create db
581 581 dbconf = config['sqlalchemy.db1.url']
582 582 log.debug('making test db %s' % dbconf)
583 583
584 584 # create test dir if it doesn't exist
585 585 if not os.path.isdir(repos_test_path):
586 586 log.debug('Creating testdir %s' % repos_test_path)
587 587 os.makedirs(repos_test_path)
588 588
589 589 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
590 590 tests=True)
591 591 dbmanage.create_tables(override=True)
592 592 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
593 593 dbmanage.create_default_user()
594 594 dbmanage.admin_prompt()
595 595 dbmanage.create_permissions()
596 596 dbmanage.populate_default_permissions()
597 597 Session().commit()
598 598 # PART TWO make test repo
599 599 log.debug('making test vcs repositories')
600 600
601 601 idx_path = config['app_conf']['index_dir']
602 602 data_path = config['app_conf']['cache_dir']
603 603
604 604 #clean index and data
605 605 if idx_path and os.path.exists(idx_path):
606 606 log.debug('remove %s' % idx_path)
607 607 shutil.rmtree(idx_path)
608 608
609 609 if data_path and os.path.exists(data_path):
610 610 log.debug('remove %s' % data_path)
611 611 shutil.rmtree(data_path)
612 612
613 613 #CREATE DEFAULT TEST REPOS
614 614 cur_dir = dn(dn(abspath(__file__)))
615 615 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
616 616 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
617 617 tar.close()
618 618
619 619 cur_dir = dn(dn(abspath(__file__)))
620 620 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
621 621 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
622 622 tar.close()
623 623
624 624 #LOAD VCS test stuff
625 625 from rhodecode.tests.vcs import setup_package
626 626 setup_package()
627 627
628 628
629 629 #==============================================================================
630 630 # PASTER COMMANDS
631 631 #==============================================================================
632 632 class BasePasterCommand(Command):
633 633 """
634 634 Abstract Base Class for paster commands.
635 635
636 636 The celery commands are somewhat aggressive about loading
637 637 celery.conf, and since our module sets the `CELERY_LOADER`
638 638 environment variable to our loader, we have to bootstrap a bit and
639 639 make sure we've had a chance to load the pylons config off of the
640 640 command line, otherwise everything fails.
641 641 """
642 642 min_args = 1
643 643 min_args_error = "Please provide a paster config file as an argument."
644 644 takes_config_file = 1
645 645 requires_config_file = True
646 646
647 647 def notify_msg(self, msg, log=False):
648 648 """Make a notification to user, additionally if logger is passed
649 649 it logs this action using given logger
650 650
651 651 :param msg: message that will be printed to user
652 652 :param log: logging instance, to use to additionally log this message
653 653
654 654 """
655 655 if log and isinstance(log, logging):
656 656 log(msg)
657 657
658 658 def run(self, args):
659 659 """
660 660 Overrides Command.run
661 661
662 662 Checks for a config file argument and loads it.
663 663 """
664 664 if len(args) < self.min_args:
665 665 raise BadCommand(
666 666 self.min_args_error % {'min_args': self.min_args,
667 667 'actual_args': len(args)})
668 668
669 669 # Decrement because we're going to lob off the first argument.
670 670 # @@ This is hacky
671 671 self.min_args -= 1
672 672 self.bootstrap_config(args[0])
673 673 self.update_parser()
674 674 return super(BasePasterCommand, self).run(args[1:])
675 675
676 676 def update_parser(self):
677 677 """
678 678 Abstract method. Allows for the class's parser to be updated
679 679 before the superclass's `run` method is called. Necessary to
680 680 allow options/arguments to be passed through to the underlying
681 681 celery command.
682 682 """
683 683 raise NotImplementedError("Abstract Method.")
684 684
685 685 def bootstrap_config(self, conf):
686 686 """
687 687 Loads the pylons configuration.
688 688 """
689 689 from pylons import config as pylonsconfig
690 690
691 691 self.path_to_ini_file = os.path.realpath(conf)
692 692 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
693 693 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
694 694
695 695
696 696 def check_git_version():
697 697 """
698 698 Checks what version of git is installed in system, and issues a warning
699 699 if it's too old for RhodeCode to properly work.
700 700 """
701 701 import subprocess
702 702 from distutils.version import StrictVersion
703 703 from rhodecode import BACKENDS
704 704
705 705 p = subprocess.Popen('git --version', shell=True,
706 706 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
707 707 stdout, stderr = p.communicate()
708 708 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
709 709 if len(ver.split('.')) > 3:
710 710 #StrictVersion needs to be only 3 element type
711 711 ver = '.'.join(ver.split('.')[:3])
712 712 try:
713 713 _ver = StrictVersion(ver)
714 714 except:
715 715 _ver = StrictVersion('0.0.0')
716 716 stderr = traceback.format_exc()
717 717
718 718 req_ver = '1.7.4'
719 719 to_old_git = False
720 720 if _ver < StrictVersion(req_ver):
721 721 to_old_git = True
722 722
723 723 if 'git' in BACKENDS:
724 724 log.debug('GIT version detected: %s' % stdout)
725 725 if stderr:
726 726 log.warning('Unable to detect git version org error was:%r' % stderr)
727 727 elif to_old_git:
728 728 log.warning('RhodeCode detected git version %s, which is too old '
729 729 'for the system to function properly. Make sure '
730 730 'its version is at least %s' % (ver, req_ver))
731 731 return _ver
732 732
733 733
734 734 @decorator.decorator
735 735 def jsonify(func, *args, **kwargs):
736 736 """Action decorator that formats output for JSON
737 737
738 738 Given a function that will return content, this decorator will turn
739 739 the result into JSON, with a content-type of 'application/json' and
740 740 output it.
741 741
742 742 """
743 743 from pylons.decorators.util import get_pylons
744 744 from rhodecode.lib.ext_json import json
745 745 pylons = get_pylons(args)
746 746 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
747 747 data = func(*args, **kwargs)
748 748 if isinstance(data, (list, tuple)):
749 749 msg = "JSON responses with Array envelopes are susceptible to " \
750 750 "cross-site data leak attacks, see " \
751 751 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
752 752 warnings.warn(msg, Warning, 2)
753 753 log.warning(msg)
754 754 log.debug("Returning JSON wrapped action output")
755 755 return json.dumps(data, encoding='utf-8') No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now