##// END OF EJS Templates
utils: added code for sending test emails.
marcink -
r4439:1d4ce874 default
parent child Browse files
Show More
@@ -1,784 +1,800 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Utilities library for RhodeCode
23 23 """
24 24
25 25 import datetime
26 26 import decorator
27 27 import json
28 28 import logging
29 29 import os
30 30 import re
31 31 import sys
32 32 import shutil
33 import socket
33 34 import tempfile
34 35 import traceback
35 36 import tarfile
36 37 import warnings
37 38 import hashlib
38 39 from os.path import join as jn
39 40
40 41 import paste
41 42 import pkg_resources
42 43 from webhelpers2.text import collapse, remove_formatting
43 44 from mako import exceptions
44 45 from pyramid.threadlocal import get_current_registry
45 46 from rhodecode.lib.request import Request
46 47
47 48 from rhodecode.lib.vcs.backends.base import Config
48 49 from rhodecode.lib.vcs.exceptions import VCSError
49 50 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
50 51 from rhodecode.lib.utils2 import (
51 52 safe_str, safe_unicode, get_current_rhodecode_user, md5, sha1)
52 53 from rhodecode.model import meta
53 54 from rhodecode.model.db import (
54 55 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
55 56 from rhodecode.model.meta import Session
56 57
57 58
58 59 log = logging.getLogger(__name__)
59 60
60 61 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
61 62
62 63 # String which contains characters that are not allowed in slug names for
63 64 # repositories or repository groups. It is properly escaped to use it in
64 65 # regular expressions.
65 66 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
66 67
67 68 # Regex that matches forbidden characters in repo/group slugs.
68 69 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
69 70
70 71 # Regex that matches allowed characters in repo/group slugs.
71 72 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
72 73
73 74 # Regex that matches whole repo/group slugs.
74 75 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
75 76
76 77 _license_cache = None
77 78
78 79
79 80 def repo_name_slug(value):
80 81 """
81 82 Return slug of name of repository
82 83 This function is called on each creation/modification
83 84 of repository to prevent bad names in repo
84 85 """
85 86 replacement_char = '-'
86 87
87 88 slug = remove_formatting(value)
88 89 slug = SLUG_BAD_CHAR_RE.sub('', slug)
89 90 slug = re.sub('[\s]+', '-', slug)
90 91 slug = collapse(slug, replacement_char)
91 92 return slug
92 93
93 94
94 95 #==============================================================================
95 96 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
96 97 #==============================================================================
97 98 def get_repo_slug(request):
98 99 _repo = ''
99 100
100 101 if hasattr(request, 'db_repo'):
101 102 # if our requests has set db reference use it for name, this
102 103 # translates the example.com/_<id> into proper repo names
103 104 _repo = request.db_repo.repo_name
104 105 elif getattr(request, 'matchdict', None):
105 106 # pyramid
106 107 _repo = request.matchdict.get('repo_name')
107 108
108 109 if _repo:
109 110 _repo = _repo.rstrip('/')
110 111 return _repo
111 112
112 113
113 114 def get_repo_group_slug(request):
114 115 _group = ''
115 116 if hasattr(request, 'db_repo_group'):
116 117 # if our requests has set db reference use it for name, this
117 118 # translates the example.com/_<id> into proper repo group names
118 119 _group = request.db_repo_group.group_name
119 120 elif getattr(request, 'matchdict', None):
120 121 # pyramid
121 122 _group = request.matchdict.get('repo_group_name')
122 123
123 124 if _group:
124 125 _group = _group.rstrip('/')
125 126 return _group
126 127
127 128
128 129 def get_user_group_slug(request):
129 130 _user_group = ''
130 131
131 132 if hasattr(request, 'db_user_group'):
132 133 _user_group = request.db_user_group.users_group_name
133 134 elif getattr(request, 'matchdict', None):
134 135 # pyramid
135 136 _user_group = request.matchdict.get('user_group_id')
136 137 _user_group_name = request.matchdict.get('user_group_name')
137 138 try:
138 139 if _user_group:
139 140 _user_group = UserGroup.get(_user_group)
140 141 elif _user_group_name:
141 142 _user_group = UserGroup.get_by_group_name(_user_group_name)
142 143
143 144 if _user_group:
144 145 _user_group = _user_group.users_group_name
145 146 except Exception:
146 147 log.exception('Failed to get user group by id and name')
147 148 # catch all failures here
148 149 return None
149 150
150 151 return _user_group
151 152
152 153
153 154 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
154 155 """
155 156 Scans given path for repos and return (name,(type,path)) tuple
156 157
157 158 :param path: path to scan for repositories
158 159 :param recursive: recursive search and return names with subdirs in front
159 160 """
160 161
161 162 # remove ending slash for better results
162 163 path = path.rstrip(os.sep)
163 164 log.debug('now scanning in %s location recursive:%s...', path, recursive)
164 165
165 166 def _get_repos(p):
166 167 dirpaths = _get_dirpaths(p)
167 168 if not _is_dir_writable(p):
168 169 log.warning('repo path without write access: %s', p)
169 170
170 171 for dirpath in dirpaths:
171 172 if os.path.isfile(os.path.join(p, dirpath)):
172 173 continue
173 174 cur_path = os.path.join(p, dirpath)
174 175
175 176 # skip removed repos
176 177 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
177 178 continue
178 179
179 180 #skip .<somethin> dirs
180 181 if dirpath.startswith('.'):
181 182 continue
182 183
183 184 try:
184 185 scm_info = get_scm(cur_path)
185 186 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
186 187 except VCSError:
187 188 if not recursive:
188 189 continue
189 190 #check if this dir containts other repos for recursive scan
190 191 rec_path = os.path.join(p, dirpath)
191 192 if os.path.isdir(rec_path):
192 193 for inner_scm in _get_repos(rec_path):
193 194 yield inner_scm
194 195
195 196 return _get_repos(path)
196 197
197 198
198 199 def _get_dirpaths(p):
199 200 try:
200 201 # OS-independable way of checking if we have at least read-only
201 202 # access or not.
202 203 dirpaths = os.listdir(p)
203 204 except OSError:
204 205 log.warning('ignoring repo path without read access: %s', p)
205 206 return []
206 207
207 208 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
208 209 # decode paths and suddenly returns unicode objects itself. The items it
209 210 # cannot decode are returned as strings and cause issues.
210 211 #
211 212 # Those paths are ignored here until a solid solution for path handling has
212 213 # been built.
213 214 expected_type = type(p)
214 215
215 216 def _has_correct_type(item):
216 217 if type(item) is not expected_type:
217 218 log.error(
218 219 u"Ignoring path %s since it cannot be decoded into unicode.",
219 220 # Using "repr" to make sure that we see the byte value in case
220 221 # of support.
221 222 repr(item))
222 223 return False
223 224 return True
224 225
225 226 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
226 227
227 228 return dirpaths
228 229
229 230
230 231 def _is_dir_writable(path):
231 232 """
232 233 Probe if `path` is writable.
233 234
234 235 Due to trouble on Cygwin / Windows, this is actually probing if it is
235 236 possible to create a file inside of `path`, stat does not produce reliable
236 237 results in this case.
237 238 """
238 239 try:
239 240 with tempfile.TemporaryFile(dir=path):
240 241 pass
241 242 except OSError:
242 243 return False
243 244 return True
244 245
245 246
246 247 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
247 248 """
248 249 Returns True if given path is a valid repository False otherwise.
249 250 If expect_scm param is given also, compare if given scm is the same
250 251 as expected from scm parameter. If explicit_scm is given don't try to
251 252 detect the scm, just use the given one to check if repo is valid
252 253
253 254 :param repo_name:
254 255 :param base_path:
255 256 :param expect_scm:
256 257 :param explicit_scm:
257 258 :param config:
258 259
259 260 :return True: if given path is a valid repository
260 261 """
261 262 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
262 263 log.debug('Checking if `%s` is a valid path for repository. '
263 264 'Explicit type: %s', repo_name, explicit_scm)
264 265
265 266 try:
266 267 if explicit_scm:
267 268 detected_scms = [get_scm_backend(explicit_scm)(
268 269 full_path, config=config).alias]
269 270 else:
270 271 detected_scms = get_scm(full_path)
271 272
272 273 if expect_scm:
273 274 return detected_scms[0] == expect_scm
274 275 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
275 276 return True
276 277 except VCSError:
277 278 log.debug('path: %s is not a valid repo !', full_path)
278 279 return False
279 280
280 281
281 282 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
282 283 """
283 284 Returns True if given path is a repository group, False otherwise
284 285
285 286 :param repo_name:
286 287 :param base_path:
287 288 """
288 289 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
289 290 log.debug('Checking if `%s` is a valid path for repository group',
290 291 repo_group_name)
291 292
292 293 # check if it's not a repo
293 294 if is_valid_repo(repo_group_name, base_path):
294 295 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
295 296 return False
296 297
297 298 try:
298 299 # we need to check bare git repos at higher level
299 300 # since we might match branches/hooks/info/objects or possible
300 301 # other things inside bare git repo
301 302 maybe_repo = os.path.dirname(full_path)
302 303 if maybe_repo == base_path:
303 304 # skip root level repo check, we know root location CANNOT BE a repo group
304 305 return False
305 306
306 307 scm_ = get_scm(maybe_repo)
307 308 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
308 309 return False
309 310 except VCSError:
310 311 pass
311 312
312 313 # check if it's a valid path
313 314 if skip_path_check or os.path.isdir(full_path):
314 315 log.debug('path: %s is a valid repo group !', full_path)
315 316 return True
316 317
317 318 log.debug('path: %s is not a valid repo group !', full_path)
318 319 return False
319 320
320 321
321 322 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
322 323 while True:
323 324 ok = raw_input(prompt)
324 325 if ok.lower() in ('y', 'ye', 'yes'):
325 326 return True
326 327 if ok.lower() in ('n', 'no', 'nop', 'nope'):
327 328 return False
328 329 retries = retries - 1
329 330 if retries < 0:
330 331 raise IOError
331 332 print(complaint)
332 333
333 334 # propagated from mercurial documentation
334 335 ui_sections = [
335 336 'alias', 'auth',
336 337 'decode/encode', 'defaults',
337 338 'diff', 'email',
338 339 'extensions', 'format',
339 340 'merge-patterns', 'merge-tools',
340 341 'hooks', 'http_proxy',
341 342 'smtp', 'patch',
342 343 'paths', 'profiling',
343 344 'server', 'trusted',
344 345 'ui', 'web', ]
345 346
346 347
347 348 def config_data_from_db(clear_session=True, repo=None):
348 349 """
349 350 Read the configuration data from the database and return configuration
350 351 tuples.
351 352 """
352 353 from rhodecode.model.settings import VcsSettingsModel
353 354
354 355 config = []
355 356
356 357 sa = meta.Session()
357 358 settings_model = VcsSettingsModel(repo=repo, sa=sa)
358 359
359 360 ui_settings = settings_model.get_ui_settings()
360 361
361 362 ui_data = []
362 363 for setting in ui_settings:
363 364 if setting.active:
364 365 ui_data.append((setting.section, setting.key, setting.value))
365 366 config.append((
366 367 safe_str(setting.section), safe_str(setting.key),
367 368 safe_str(setting.value)))
368 369 if setting.key == 'push_ssl':
369 370 # force set push_ssl requirement to False, rhodecode
370 371 # handles that
371 372 config.append((
372 373 safe_str(setting.section), safe_str(setting.key), False))
373 374 log.debug(
374 375 'settings ui from db@repo[%s]: %s',
375 376 repo,
376 377 ','.join(map(lambda s: '[{}] {}={}'.format(*s), ui_data)))
377 378 if clear_session:
378 379 meta.Session.remove()
379 380
380 381 # TODO: mikhail: probably it makes no sense to re-read hooks information.
381 382 # It's already there and activated/deactivated
382 383 skip_entries = []
383 384 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
384 385 if 'pull' not in enabled_hook_classes:
385 386 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
386 387 if 'push' not in enabled_hook_classes:
387 388 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
388 389 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
389 390 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
390 391
391 392 config = [entry for entry in config if entry[:2] not in skip_entries]
392 393
393 394 return config
394 395
395 396
396 397 def make_db_config(clear_session=True, repo=None):
397 398 """
398 399 Create a :class:`Config` instance based on the values in the database.
399 400 """
400 401 config = Config()
401 402 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
402 403 for section, option, value in config_data:
403 404 config.set(section, option, value)
404 405 return config
405 406
406 407
407 408 def get_enabled_hook_classes(ui_settings):
408 409 """
409 410 Return the enabled hook classes.
410 411
411 412 :param ui_settings: List of ui_settings as returned
412 413 by :meth:`VcsSettingsModel.get_ui_settings`
413 414
414 415 :return: a list with the enabled hook classes. The order is not guaranteed.
415 416 :rtype: list
416 417 """
417 418 enabled_hooks = []
418 419 active_hook_keys = [
419 420 key for section, key, value, active in ui_settings
420 421 if section == 'hooks' and active]
421 422
422 423 hook_names = {
423 424 RhodeCodeUi.HOOK_PUSH: 'push',
424 425 RhodeCodeUi.HOOK_PULL: 'pull',
425 426 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
426 427 }
427 428
428 429 for key in active_hook_keys:
429 430 hook = hook_names.get(key)
430 431 if hook:
431 432 enabled_hooks.append(hook)
432 433
433 434 return enabled_hooks
434 435
435 436
436 437 def set_rhodecode_config(config):
437 438 """
438 439 Updates pyramid config with new settings from database
439 440
440 441 :param config:
441 442 """
442 443 from rhodecode.model.settings import SettingsModel
443 444 app_settings = SettingsModel().get_all_settings()
444 445
445 446 for k, v in app_settings.items():
446 447 config[k] = v
447 448
448 449
449 450 def get_rhodecode_realm():
450 451 """
451 452 Return the rhodecode realm from database.
452 453 """
453 454 from rhodecode.model.settings import SettingsModel
454 455 realm = SettingsModel().get_setting_by_name('realm')
455 456 return safe_str(realm.app_settings_value)
456 457
457 458
458 459 def get_rhodecode_base_path():
459 460 """
460 461 Returns the base path. The base path is the filesystem path which points
461 462 to the repository store.
462 463 """
463 464 from rhodecode.model.settings import SettingsModel
464 465 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
465 466 return safe_str(paths_ui.ui_value)
466 467
467 468
468 469 def map_groups(path):
469 470 """
470 471 Given a full path to a repository, create all nested groups that this
471 472 repo is inside. This function creates parent-child relationships between
472 473 groups and creates default perms for all new groups.
473 474
474 475 :param paths: full path to repository
475 476 """
476 477 from rhodecode.model.repo_group import RepoGroupModel
477 478 sa = meta.Session()
478 479 groups = path.split(Repository.NAME_SEP)
479 480 parent = None
480 481 group = None
481 482
482 483 # last element is repo in nested groups structure
483 484 groups = groups[:-1]
484 485 rgm = RepoGroupModel(sa)
485 486 owner = User.get_first_super_admin()
486 487 for lvl, group_name in enumerate(groups):
487 488 group_name = '/'.join(groups[:lvl] + [group_name])
488 489 group = RepoGroup.get_by_group_name(group_name)
489 490 desc = '%s group' % group_name
490 491
491 492 # skip folders that are now removed repos
492 493 if REMOVED_REPO_PAT.match(group_name):
493 494 break
494 495
495 496 if group is None:
496 497 log.debug('creating group level: %s group_name: %s',
497 498 lvl, group_name)
498 499 group = RepoGroup(group_name, parent)
499 500 group.group_description = desc
500 501 group.user = owner
501 502 sa.add(group)
502 503 perm_obj = rgm._create_default_perms(group)
503 504 sa.add(perm_obj)
504 505 sa.flush()
505 506
506 507 parent = group
507 508 return group
508 509
509 510
510 511 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
511 512 """
512 513 maps all repos given in initial_repo_list, non existing repositories
513 514 are created, if remove_obsolete is True it also checks for db entries
514 515 that are not in initial_repo_list and removes them.
515 516
516 517 :param initial_repo_list: list of repositories found by scanning methods
517 518 :param remove_obsolete: check for obsolete entries in database
518 519 """
519 520 from rhodecode.model.repo import RepoModel
520 521 from rhodecode.model.repo_group import RepoGroupModel
521 522 from rhodecode.model.settings import SettingsModel
522 523
523 524 sa = meta.Session()
524 525 repo_model = RepoModel()
525 526 user = User.get_first_super_admin()
526 527 added = []
527 528
528 529 # creation defaults
529 530 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
530 531 enable_statistics = defs.get('repo_enable_statistics')
531 532 enable_locking = defs.get('repo_enable_locking')
532 533 enable_downloads = defs.get('repo_enable_downloads')
533 534 private = defs.get('repo_private')
534 535
535 536 for name, repo in initial_repo_list.items():
536 537 group = map_groups(name)
537 538 unicode_name = safe_unicode(name)
538 539 db_repo = repo_model.get_by_repo_name(unicode_name)
539 540 # found repo that is on filesystem not in RhodeCode database
540 541 if not db_repo:
541 542 log.info('repository %s not found, creating now', name)
542 543 added.append(name)
543 544 desc = (repo.description
544 545 if repo.description != 'unknown'
545 546 else '%s repository' % name)
546 547
547 548 db_repo = repo_model._create_repo(
548 549 repo_name=name,
549 550 repo_type=repo.alias,
550 551 description=desc,
551 552 repo_group=getattr(group, 'group_id', None),
552 553 owner=user,
553 554 enable_locking=enable_locking,
554 555 enable_downloads=enable_downloads,
555 556 enable_statistics=enable_statistics,
556 557 private=private,
557 558 state=Repository.STATE_CREATED
558 559 )
559 560 sa.commit()
560 561 # we added that repo just now, and make sure we updated server info
561 562 if db_repo.repo_type == 'git':
562 563 git_repo = db_repo.scm_instance()
563 564 # update repository server-info
564 565 log.debug('Running update server info')
565 566 git_repo._update_server_info()
566 567
567 568 db_repo.update_commit_cache()
568 569
569 570 config = db_repo._config
570 571 config.set('extensions', 'largefiles', '')
571 572 repo = db_repo.scm_instance(config=config)
572 573 repo.install_hooks()
573 574
574 575 removed = []
575 576 if remove_obsolete:
576 577 # remove from database those repositories that are not in the filesystem
577 578 for repo in sa.query(Repository).all():
578 579 if repo.repo_name not in initial_repo_list.keys():
579 580 log.debug("Removing non-existing repository found in db `%s`",
580 581 repo.repo_name)
581 582 try:
582 583 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
583 584 sa.commit()
584 585 removed.append(repo.repo_name)
585 586 except Exception:
586 587 # don't hold further removals on error
587 588 log.error(traceback.format_exc())
588 589 sa.rollback()
589 590
590 591 def splitter(full_repo_name):
591 592 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
592 593 gr_name = None
593 594 if len(_parts) == 2:
594 595 gr_name = _parts[0]
595 596 return gr_name
596 597
597 598 initial_repo_group_list = [splitter(x) for x in
598 599 initial_repo_list.keys() if splitter(x)]
599 600
600 601 # remove from database those repository groups that are not in the
601 602 # filesystem due to parent child relationships we need to delete them
602 603 # in a specific order of most nested first
603 604 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
604 605 nested_sort = lambda gr: len(gr.split('/'))
605 606 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
606 607 if group_name not in initial_repo_group_list:
607 608 repo_group = RepoGroup.get_by_group_name(group_name)
608 609 if (repo_group.children.all() or
609 610 not RepoGroupModel().check_exist_filesystem(
610 611 group_name=group_name, exc_on_failure=False)):
611 612 continue
612 613
613 614 log.info(
614 615 'Removing non-existing repository group found in db `%s`',
615 616 group_name)
616 617 try:
617 618 RepoGroupModel(sa).delete(group_name, fs_remove=False)
618 619 sa.commit()
619 620 removed.append(group_name)
620 621 except Exception:
621 622 # don't hold further removals on error
622 623 log.exception(
623 624 'Unable to remove repository group `%s`',
624 625 group_name)
625 626 sa.rollback()
626 627 raise
627 628
628 629 return added, removed
629 630
630 631
631 632 def load_rcextensions(root_path):
632 633 import rhodecode
633 634 from rhodecode.config import conf
634 635
635 636 path = os.path.join(root_path)
636 637 sys.path.append(path)
637 638
638 639 try:
639 640 rcextensions = __import__('rcextensions')
640 641 except ImportError:
641 642 if os.path.isdir(os.path.join(path, 'rcextensions')):
642 643 log.warn('Unable to load rcextensions from %s', path)
643 644 rcextensions = None
644 645
645 646 if rcextensions:
646 647 log.debug('Found rcextensions module loaded %s...', rcextensions)
647 648 rhodecode.EXTENSIONS = rcextensions
648 649
649 650 # Additional mappings that are not present in the pygments lexers
650 651 conf.LANGUAGES_EXTENSIONS_MAP.update(
651 652 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
652 653
653 654
654 655 def get_custom_lexer(extension):
655 656 """
656 657 returns a custom lexer if it is defined in rcextensions module, or None
657 658 if there's no custom lexer defined
658 659 """
659 660 import rhodecode
660 661 from pygments import lexers
661 662
662 663 # custom override made by RhodeCode
663 664 if extension in ['mako']:
664 665 return lexers.get_lexer_by_name('html+mako')
665 666
666 667 # check if we didn't define this extension as other lexer
667 668 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
668 669 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
669 670 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
670 671 return lexers.get_lexer_by_name(_lexer_name)
671 672
672 673
673 674 #==============================================================================
674 675 # TEST FUNCTIONS AND CREATORS
675 676 #==============================================================================
676 677 def create_test_index(repo_location, config):
677 678 """
678 679 Makes default test index.
679 680 """
680 681 import rc_testdata
681 682
682 683 rc_testdata.extract_search_index(
683 684 'vcs_search_index', os.path.dirname(config['search.location']))
684 685
685 686
686 687 def create_test_directory(test_path):
687 688 """
688 689 Create test directory if it doesn't exist.
689 690 """
690 691 if not os.path.isdir(test_path):
691 692 log.debug('Creating testdir %s', test_path)
692 693 os.makedirs(test_path)
693 694
694 695
695 696 def create_test_database(test_path, config):
696 697 """
697 698 Makes a fresh database.
698 699 """
699 700 from rhodecode.lib.db_manage import DbManage
700 701
701 702 # PART ONE create db
702 703 dbconf = config['sqlalchemy.db1.url']
703 704 log.debug('making test db %s', dbconf)
704 705
705 706 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
706 707 tests=True, cli_args={'force_ask': True})
707 708 dbmanage.create_tables(override=True)
708 709 dbmanage.set_db_version()
709 710 # for tests dynamically set new root paths based on generated content
710 711 dbmanage.create_settings(dbmanage.config_prompt(test_path))
711 712 dbmanage.create_default_user()
712 713 dbmanage.create_test_admin_and_users()
713 714 dbmanage.create_permissions()
714 715 dbmanage.populate_default_permissions()
715 716 Session().commit()
716 717
717 718
718 719 def create_test_repositories(test_path, config):
719 720 """
720 721 Creates test repositories in the temporary directory. Repositories are
721 722 extracted from archives within the rc_testdata package.
722 723 """
723 724 import rc_testdata
724 725 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
725 726
726 727 log.debug('making test vcs repositories')
727 728
728 729 idx_path = config['search.location']
729 730 data_path = config['cache_dir']
730 731
731 732 # clean index and data
732 733 if idx_path and os.path.exists(idx_path):
733 734 log.debug('remove %s', idx_path)
734 735 shutil.rmtree(idx_path)
735 736
736 737 if data_path and os.path.exists(data_path):
737 738 log.debug('remove %s', data_path)
738 739 shutil.rmtree(data_path)
739 740
740 741 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
741 742 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
742 743
743 744 # Note: Subversion is in the process of being integrated with the system,
744 745 # until we have a properly packed version of the test svn repository, this
745 746 # tries to copy over the repo from a package "rc_testdata"
746 747 svn_repo_path = rc_testdata.get_svn_repo_archive()
747 748 with tarfile.open(svn_repo_path) as tar:
748 749 tar.extractall(jn(test_path, SVN_REPO))
749 750
750 751
751 752 def password_changed(auth_user, session):
752 753 # Never report password change in case of default user or anonymous user.
753 754 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
754 755 return False
755 756
756 757 password_hash = md5(auth_user.password) if auth_user.password else None
757 758 rhodecode_user = session.get('rhodecode_user', {})
758 759 session_password_hash = rhodecode_user.get('password', '')
759 760 return password_hash != session_password_hash
760 761
761 762
762 763 def read_opensource_licenses():
763 764 global _license_cache
764 765
765 766 if not _license_cache:
766 767 licenses = pkg_resources.resource_string(
767 768 'rhodecode', 'config/licenses.json')
768 769 _license_cache = json.loads(licenses)
769 770
770 771 return _license_cache
771 772
772 773
773 774 def generate_platform_uuid():
774 775 """
775 776 Generates platform UUID based on it's name
776 777 """
777 778 import platform
778 779
779 780 try:
780 781 uuid_list = [platform.platform()]
781 782 return hashlib.sha256(':'.join(uuid_list)).hexdigest()
782 783 except Exception as e:
783 784 log.error('Failed to generate host uuid: %s', e)
784 785 return 'UNDEFINED'
786
787
788 def send_test_email(recipients, email_body='TEST EMAIL'):
789 """
790 Simple code for generating test emails.
791 Usage::
792
793 from rhodecode.lib import utils
794 utils.send_test_email()
795 """
796 from rhodecode.lib.celerylib import tasks, run_task
797
798 email_body = email_body_plaintext = email_body
799 subject = 'SUBJECT FROM: {}'.format(socket.gethostname())
800 tasks.send_email(recipients, subject, email_body_plaintext, email_body)
General Comments 0
You need to be logged in to leave comments. Login now