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