# HG changeset patch # User Leonardo # Date 1363018183 -3600 # Node ID 6e8027c2f49cbaa2273a36cb732454ec944d9457 # Parent c65b440540da15cb167819b2ebbf3faa3f3a8dac# Parent 7174ee850baafb02e35da1ee281c31a06267d4aa Merge diff -r c65b440540da -r 6e8027c2f49c development.ini --- a/development.ini Mon Mar 11 16:43:41 2013 +0100 +++ b/development.ini Mon Mar 11 17:09:43 2013 +0100 @@ -65,6 +65,8 @@ lang = en cache_dir = %(here)s/data index_dir = %(here)s/data/index +# set this path to use archive download cache +#archive_cache_dir = /tmp/rhodecode_tarballcache app_instance_uuid = rc-develop cut_off_limit = 256000 vcs_full_cache = True @@ -154,6 +156,11 @@ ## handling that. Set this variable to 403 to return HTTPForbidden auth_ret_code = +## locking return code. When repository is locked return this HTTP code. 2XX +## codes don't break the transactions while 4XX codes do +lock_ret_code = 423 + + #################################### ### CELERY CONFIG #### #################################### diff -r c65b440540da -r 6e8027c2f49c docs/api/api.rst --- a/docs/api/api.rst Mon Mar 11 16:43:41 2013 +0100 +++ b/docs/api/api.rst Mon Mar 11 17:09:43 2013 +0100 @@ -178,7 +178,8 @@ ---- Set locking state on given repository by given user. If userid param is skipped -, then it is set to id of user whos calling this method. +, then it is set to id of user whos calling this method. If locked param is skipped +then function shows current lock state of given repo. This command can be executed only using api_key belonging to user with admin rights or regular user that have admin or write access to repository. @@ -190,7 +191,7 @@ args : { "repoid" : "" "userid" : "", - "locked" : "" + "locked" : "" } OUTPUT:: diff -r c65b440540da -r 6e8027c2f49c docs/changelog.rst --- a/docs/changelog.rst Mon Mar 11 16:43:41 2013 +0100 +++ b/docs/changelog.rst Mon Mar 11 17:09:43 2013 +0100 @@ -16,6 +16,27 @@ fixes +++++ +1.5.4 (**2013-03-13**) +---------------------- + +news +++++ + + +fixes ++++++ + +- fixed webtest dependency issues +- fixed issues with celery tasks for password reset +- fixed #763 gravatar helper function should fallback into default image + if email is empty +- fixes #762 user global activation flag is also respected for LDAP created + accounts +- use password obfuscate when clonning a remote repo with credentials inside +- fixed issue with renaming repos group together with changing parents +- disallow cloning from file:/// URIs +- handle all cases with multiple IP addresses in proxy headers + 1.5.3 (**2013-02-12**) ---------------------- @@ -139,8 +160,8 @@ When this is used together with mercurial internal translation system it can lead to UnicodeDecodeErrors - fixes #645 Fix git handler when doing delete remote branch -- implements #649 added two seperate method for author and commiter to VCS - changeset class switch author for git backed to be the real author not commiter +- implements #649 added two seperate method for author and committer to VCS + changeset class switch author for git backed to be the real author not committer - fix issue #504 RhodeCode is showing different versions of README on different summary page loads - implemented #658 Changing username in LDAP-Mode should not be allowed. diff -r c65b440540da -r 6e8027c2f49c docs/setup.rst --- a/docs/setup.rst Mon Mar 11 16:43:41 2013 +0100 +++ b/docs/setup.rst Mon Mar 11 17:09:43 2013 +0100 @@ -478,7 +478,7 @@ By default RhodeCode uses utf8 encoding, starting from 1.3 series this can be changed, simply edit default_encoding in .ini file to desired one. -This affects many parts in rhodecode including commiters names, filenames, +This affects many parts in rhodecode including committers names, filenames, encoding of commit messages. In addition RhodeCode can detect if `chardet` library is installed. If `chardet` is detected RhodeCode will fallback to it when there are encode/decode errors. diff -r c65b440540da -r 6e8027c2f49c docs/theme/nature/static/pygments.css --- a/docs/theme/nature/static/pygments.css Mon Mar 11 16:43:41 2013 +0100 +++ b/docs/theme/nature/static/pygments.css Mon Mar 11 17:09:43 2013 +0100 @@ -51,4 +51,4 @@ .vc { color: #ff99ff } /* Name.Variable.Class */ .vg { color: #ff99ff } /* Name.Variable.Global */ .vi { color: #ff99ff } /* Name.Variable.Instance */ -.il { color: #009999 } /* Literal.Number.Integer.Long */ +.il { color: #009999 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff -r c65b440540da -r 6e8027c2f49c production.ini --- a/production.ini Mon Mar 11 16:43:41 2013 +0100 +++ b/production.ini Mon Mar 11 17:09:43 2013 +0100 @@ -65,6 +65,8 @@ lang = en cache_dir = %(here)s/data index_dir = %(here)s/data/index +# set this path to use archive download cache +#archive_cache_dir = /tmp/rhodecode_tarballcache app_instance_uuid = rc-production cut_off_limit = 256000 vcs_full_cache = True @@ -154,6 +156,11 @@ ## handling that. Set this variable to 403 to return HTTPForbidden auth_ret_code = +## locking return code. When repository is locked return this HTTP code. 2XX +## codes don't break the transactions while 4XX codes do +lock_ret_code = 423 + + #################################### ### CELERY CONFIG #### #################################### diff -r c65b440540da -r 6e8027c2f49c rhodecode/config/deployment.ini_tmpl --- a/rhodecode/config/deployment.ini_tmpl Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/config/deployment.ini_tmpl Mon Mar 11 17:09:43 2013 +0100 @@ -65,6 +65,8 @@ lang = en cache_dir = %(here)s/data index_dir = %(here)s/data/index +# set this path to use archive download cache +#archive_cache_dir = /tmp/rhodecode_tarballcache app_instance_uuid = ${app_instance_uuid} cut_off_limit = 256000 vcs_full_cache = True @@ -154,6 +156,11 @@ ## handling that. Set this variable to 403 to return HTTPForbidden auth_ret_code = +## locking return code. When repository is locked return this HTTP code. 2XX +## codes don't break the transactions while 4XX codes do +lock_ret_code = 423 + + #################################### ### CELERY CONFIG #### #################################### diff -r c65b440540da -r 6e8027c2f49c rhodecode/config/middleware.py --- a/rhodecode/config/middleware.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/config/middleware.py Mon Mar 11 17:09:43 2013 +0100 @@ -15,6 +15,7 @@ from rhodecode.lib.middleware.simplegit import SimpleGit from rhodecode.lib.middleware.https_fixup import HttpsFixup from rhodecode.config.environment import load_environment +from rhodecode.lib.middleware.wrapper import RequestWrapper def make_app(global_conf, full_stack=True, static_files=True, **app_conf): @@ -55,7 +56,7 @@ from rhodecode.lib.middleware.sentry import Sentry from rhodecode.lib.middleware.errormator import Errormator - if Errormator: + if Errormator and asbool(config['app_conf'].get('errormator')): app = Errormator(app, config) elif Sentry: app = Sentry(app, config) @@ -67,7 +68,7 @@ # need any pylons stack middleware in them app = SimpleHg(app, config) app = SimpleGit(app, config) - + app = RequestWrapper(app, config) # Display error documents for 401, 403, 404 status codes (and # 500 when debug is disabled) if asbool(config['debug']): diff -r c65b440540da -r 6e8027c2f49c rhodecode/config/routing.py --- a/rhodecode/config/routing.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/config/routing.py Mon Mar 11 17:09:43 2013 +0100 @@ -56,6 +56,18 @@ repos_group_name = match_dict.get('group_name') return is_valid_repos_group(repos_group_name, config['base_path']) + def check_group_skip_path(environ, match_dict): + """ + check for valid repository group for proper 404 handling, but skips + verification of existing path + + :param environ: + :param match_dict: + """ + repos_group_name = match_dict.get('group_name') + return is_valid_repos_group(repos_group_name, config['base_path'], + skip_path_check=True) + def check_int(environ, match_dict): return match_dict.get('id').isdigit() @@ -171,9 +183,10 @@ function=check_group)) m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}", action="delete", conditions=dict(method=["DELETE"], - function=check_group)) + function=check_group_skip_path)) m.connect("edit_repos_group", "/repos_groups/{group_name:.*?}/edit", - action="edit", conditions=dict(method=["GET"],)) + action="edit", conditions=dict(method=["GET"], + function=check_group)) m.connect("formatted_edit_repos_group", "/repos_groups/{group_name:.*?}.{format}/edit", action="edit", conditions=dict(method=["GET"], diff -r c65b440540da -r 6e8027c2f49c rhodecode/controllers/admin/repos_groups.py --- a/rhodecode/controllers/admin/repos_groups.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/controllers/admin/repos_groups.py Mon Mar 11 17:09:43 2013 +0100 @@ -251,31 +251,25 @@ repos = gr.repositories.all() if repos: h.flash(_('This group contains %s repositores and cannot be ' - 'deleted') % len(repos), - category='error') + 'deleted') % len(repos), category='warning') + return redirect(url('repos_groups')) + + children = gr.children.all() + if children: + h.flash(_('This group contains %s subgroups and cannot be deleted' + % (len(children))), category='warning') return redirect(url('repos_groups')) try: ReposGroupModel().delete(group_name) Session().commit() - h.flash(_('removed repos group %s') % gr.group_name, + h.flash(_('removed repos group %s') % group_name, category='success') #TODO: in future action_logger(, '', '', '', self.sa) - except IntegrityError, e: - if str(e.message).find('groups_group_parent_id_fkey') != -1: - log.error(traceback.format_exc()) - h.flash(_('Cannot delete this group it still contains ' - 'subgroups'), - category='warning') - else: - log.error(traceback.format_exc()) - h.flash(_('error occurred during deletion of repos ' - 'group %s') % gr.group_name, category='error') - except Exception: log.error(traceback.format_exc()) h.flash(_('error occurred during deletion of repos ' - 'group %s') % gr.group_name, category='error') + 'group %s') % group_name, category='error') return redirect(url('repos_groups')) diff -r c65b440540da -r 6e8027c2f49c rhodecode/controllers/admin/settings.py --- a/rhodecode/controllers/admin/settings.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/controllers/admin/settings.py Mon Mar 11 17:09:43 2013 +0100 @@ -38,7 +38,7 @@ from rhodecode.lib import helpers as h from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \ HasPermissionAnyDecorator, NotAnonymous, HasPermissionAny,\ - HasReposGroupPermissionAll, HasReposGroupPermissionAny + HasReposGroupPermissionAll, HasReposGroupPermissionAny, AuthUser from rhodecode.lib.base import BaseController, render from rhodecode.lib.celerylib import tasks, run_task from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \ @@ -409,6 +409,8 @@ # url('admin_settings_my_account') c.user = User.get(self.rhodecode_user.user_id) + c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id, + ip_addr=self.ip_addr) c.ldap_dn = c.user.ldap_dn if c.user.username == 'default': @@ -440,6 +442,8 @@ # url('admin_settings_my_account_update', id=ID) uid = self.rhodecode_user.user_id c.user = User.get(self.rhodecode_user.user_id) + c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id, + ip_addr=self.ip_addr) c.ldap_dn = c.user.ldap_dn email = self.rhodecode_user.email _form = UserForm(edit=True, diff -r c65b440540da -r 6e8027c2f49c rhodecode/controllers/api/api.py --- a/rhodecode/controllers/api/api.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/controllers/api/api.py Mon Mar 11 17:09:43 2013 +0100 @@ -27,14 +27,14 @@ import traceback import logging -from pylons.controllers.util import abort from rhodecode.controllers.api import JSONRPCController, JSONRPCError from rhodecode.lib.auth import PasswordGenerator, AuthUser, \ HasPermissionAllDecorator, HasPermissionAnyDecorator, \ HasPermissionAnyApi, HasRepoPermissionAnyApi from rhodecode.lib.utils import map_groups, repo2db_mapper -from rhodecode.lib.utils2 import str2bool +from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_int +from rhodecode.lib import helpers as h from rhodecode.model.meta import Session from rhodecode.model.scm import ScmModel from rhodecode.model.repo import RepoModel @@ -42,6 +42,7 @@ from rhodecode.model.users_group import UserGroupModel from rhodecode.model.permission import PermissionModel from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap +from rhodecode.lib.compat import json log = logging.getLogger(__name__) @@ -229,7 +230,8 @@ 'Error occurred during cache invalidation action' ) - def lock(self, apiuser, repoid, locked, userid=Optional(OAttr('apiuser'))): + def lock(self, apiuser, repoid, locked=Optional(None), + userid=Optional(OAttr('apiuser'))): """ Set locking state on particular repository by given user, if this command is runned by non-admin account userid is set to user @@ -257,21 +259,77 @@ if isinstance(userid, Optional): userid = apiuser.user_id + user = get_user_or_error(userid) - locked = str2bool(locked) - try: - if locked: - Repository.lock(repo, user.user_id) + + if isinstance(locked, Optional): + lockobj = Repository.getlock(repo) + + if lockobj[0] is None: + return ('Repo `%s` not locked. Locked=`False`.' + % (repo.repo_name)) else: - Repository.unlock(repo) + userid, time_ = lockobj + user = get_user_or_error(userid) + + return ('Repo `%s` locked by `%s`. Locked=`True`. ' + 'Locked since: `%s`' + % (repo.repo_name, user.username, + json.dumps(time_to_datetime(time_)))) + + else: + locked = str2bool(locked) + try: + if locked: + Repository.lock(repo, user.user_id) + else: + Repository.unlock(repo) + + return ('User `%s` set lock state for repo `%s` to `%s`' + % (user.username, repo.repo_name, locked)) + except Exception: + log.error(traceback.format_exc()) + raise JSONRPCError( + 'Error occurred locking repository `%s`' % repo.repo_name + ) - return ('User `%s` set lock state for repo `%s` to `%s`' - % (user.username, repo.repo_name, locked)) - except Exception: - log.error(traceback.format_exc()) - raise JSONRPCError( - 'Error occurred locking repository `%s`' % repo.repo_name - ) + def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))): + """ + Get all locks for given userid, if + this command is runned by non-admin account userid is set to user + who is calling this method, thus returning locks for himself + + :param apiuser: + :param userid: + """ + if HasPermissionAnyApi('hg.admin')(user=apiuser): + pass + else: + #make sure normal user does not pass someone else userid, + #he is not allowed to do that + if not isinstance(userid, Optional) and userid != apiuser.user_id: + raise JSONRPCError( + 'userid is not the same as your user' + ) + ret = [] + if isinstance(userid, Optional): + user = None + else: + user = get_user_or_error(userid) + + #show all locks + for r in Repository.getAll(): + userid, time_ = r.locked + if time_: + _api_data = r.get_api_data() + # if we use userfilter just show the locks for this user + if user: + if safe_int(userid) == user.user_id: + ret.append(_api_data) + else: + ret.append(_api_data) + + return ret @HasPermissionAllDecorator('hg.admin') def show_ip(self, apiuser, userid): diff -r c65b440540da -r 6e8027c2f49c rhodecode/controllers/compare.py --- a/rhodecode/controllers/compare.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/controllers/compare.py Mon Mar 11 17:09:43 2013 +0100 @@ -89,11 +89,17 @@ # other_ref will be evaluated in other_repo other_ref = (other_ref_type, other_ref) other_repo = request.GET.get('other_repo', org_repo) + # If merge is True: + # Show what org would get if merged with other: + # List changesets that are ancestors of other but not of org. + # New changesets in org is thus ignored. + # Diff will be from common ancestor, and merges of org to other will thus be ignored. + # If merge is False: + # Make a raw diff from org to other, no matter if related or not. + # Changesets in one and not in the other will be ignored + merge = bool(request.GET.get('merge')) # fulldiff disables cut_off_limit c.fulldiff = request.GET.get('fulldiff') - # only consider this range of changesets - rev_start = request.GET.get('rev_start') - rev_end = request.GET.get('rev_end') # partial uses compare_cs.html template directly partial = request.environ.get('HTTP_X_PARTIAL_XHR') # as_form puts hidden input field with changeset revisions @@ -103,7 +109,8 @@ repo_name=other_repo, org_ref_type=other_ref[0], org_ref=other_ref[1], other_repo=org_repo, - other_ref_type=org_ref[0], other_ref=org_ref[1]) + other_ref_type=org_ref[0], other_ref=org_ref[1], + merge=merge or '') org_repo = Repository.get_by_repo_name(org_repo) other_repo = Repository.get_by_repo_name(other_repo) @@ -133,37 +140,23 @@ c.org_ref_type = org_ref[0] c.other_ref_type = other_ref[0] - if rev_start and rev_end: - # swap revs with cherry picked ones, save them for display - #org_ref = ('rev', rev_start) - #other_ref = ('rev', rev_end) - c.org_ref = rev_start[:12] - c.other_ref = rev_end[:12] - # get parent of - # rev start to include it in the diff - _cs = other_repo.scm_instance.get_changeset(rev_start) - rev_start = _cs.parents[0].raw_id if _cs.parents else EmptyChangeset().raw_id - org_ref = ('rev', rev_start) - other_ref = ('rev', rev_end) - #if we cherry pick it's not remote, make the other_repo org_repo - org_repo = other_repo - - c.cs_ranges, ancestor = PullRequestModel().get_compare_data( - org_repo, org_ref, other_repo, other_ref) + c.cs_ranges, c.ancestor = PullRequestModel().get_compare_data( + org_repo, org_ref, other_repo, other_ref, merge) c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in c.cs_ranges]) if partial: + assert c.ancestor return render('compare/compare_cs.html') - if ancestor and org_repo != other_repo: + if c.ancestor: + assert merge # case we want a simple diff without incoming changesets, # previewing what will be merged. - # Make the diff on the forked repo, with - # revision that is common ancestor + # Make the diff on the other repo (which is known to have other_ref) log.debug('Using ancestor %s as org_ref instead of %s' - % (ancestor, org_ref)) - org_ref = ('rev', ancestor) + % (c.ancestor, org_ref)) + org_ref = ('rev', c.ancestor) org_repo = other_repo diff_limit = self.cut_off_limit if not c.fulldiff else None diff -r c65b440540da -r 6e8027c2f49c rhodecode/controllers/files.py --- a/rhodecode/controllers/files.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/controllers/files.py Mon Mar 11 17:09:43 2013 +0100 @@ -27,6 +27,7 @@ import logging import traceback import tempfile +import shutil from pylons import request, response, tmpl_context as c, url from pylons.i18n.translation import _ @@ -315,7 +316,7 @@ try: self.scm_model.commit_change(repo=c.rhodecode_repo, repo_name=repo_name, cs=c.cs, - user=self.rhodecode_user, + user=self.rhodecode_user.user_id, author=author, message=message, content=content, f_path=f_path) h.flash(_('Successfully committed to %s') % f_path, @@ -378,7 +379,7 @@ try: self.scm_model.create_node(repo=c.rhodecode_repo, repo_name=repo_name, cs=c.cs, - user=self.rhodecode_user, + user=self.rhodecode_user.user_id, author=author, message=message, content=content, f_path=node_path) h.flash(_('Successfully committed to %s') % node_path, @@ -429,11 +430,40 @@ return _('Empty repository') except (ImproperArchiveTypeError, KeyError): return _('Unknown archive type') + # archive cache + from rhodecode import CONFIG + rev_name = cs.raw_id[:12] + archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')), + safe_str(rev_name), ext) - fd, archive = tempfile.mkstemp() - t = open(archive, 'wb') - cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos) - t.close() + use_cached_archive = False # defines if we use cached version of archive + archive_cache_enabled = CONFIG.get('archive_cache_dir') + if not subrepos and archive_cache_enabled: + #check if we it's ok to write + if not os.path.isdir(CONFIG['archive_cache_dir']): + os.makedirs(CONFIG['archive_cache_dir']) + cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name) + if os.path.isfile(cached_archive_path): + log.debug('Found cached archive in %s' % cached_archive_path) + fd, archive = None, cached_archive_path + use_cached_archive = True + else: + log.debug('Archive %s is not yet cached' % (archive_name)) + + if not use_cached_archive: + #generate new archive + try: + fd, archive = tempfile.mkstemp() + t = open(archive, 'wb') + log.debug('Creating new temp archive in %s' % archive) + cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos) + if archive_cache_enabled: + #if we generated the archive and use cache rename that + log.debug('Storing new archive in %s' % cached_archive_path) + shutil.move(archive, cached_archive_path) + archive = cached_archive_path + finally: + t.close() def get_chunked_archive(archive): stream = open(archive, 'rb') @@ -441,13 +471,15 @@ data = stream.read(16 * 1024) if not data: stream.close() - os.close(fd) - os.remove(archive) + if fd: # fd means we used temporary file + os.close(fd) + if not archive_cache_enabled: + log.debug('Destroing temp archive %s' % archive) + os.remove(archive) break yield data - response.content_disposition = str('attachment; filename=%s-%s%s' \ - % (repo_name, revision[:12], ext)) + response.content_disposition = str('attachment; filename=%s' % (archive_name)) response.content_type = str(content_type) return get_chunked_archive(archive) diff -r c65b440540da -r 6e8027c2f49c rhodecode/controllers/pullrequests.py --- a/rhodecode/controllers/pullrequests.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/controllers/pullrequests.py Mon Mar 11 17:09:43 2013 +0100 @@ -52,6 +52,7 @@ from rhodecode.model.comment import ChangesetCommentsModel from rhodecode.model.changeset_status import ChangesetStatusModel from rhodecode.model.forms import PullRequestForm +from mercurial import scmutil log = logging.getLogger(__name__) @@ -67,7 +68,7 @@ c.users_array = repo_model.get_users_js() c.users_groups_array = repo_model.get_users_groups_js() - def _get_repo_refs(self, repo, rev=None): + def _get_repo_refs(self, repo, rev=None, branch_rev=None): """return a structure with repo's interesting changesets, suitable for the selectors in pullrequest.html""" branches = [('branch:%s:%s' % (k, v), k) @@ -83,11 +84,25 @@ tips = [x[1] for x in branches + bookmarks + tags if x[0].endswith(colontip)] selected = 'tag:tip:%s' % tip - special = [(selected, 'tip (%s)' % ', '.join(tips))] + special = [(selected, 'tip: %s' % ', '.join(tips))] if rev: selected = 'rev:%s:%s' % (rev, rev) - special.append((selected, rev)) + special.append((selected, '%s: %s' % (_("Selected"), rev[:12]))) + + # list named branches that has been merged to this named branch - it should probably merge back + if branch_rev: + # not restricting to merge() would also get branch point and be better + # (especially because it would get the branch point) ... but is currently too expensive + revs = ["sort(parents(branch(id('%s')) and merge()) - branch(id('%s')))" % + (branch_rev, branch_rev)] + otherbranches = {} + for i in scmutil.revrange(repo._repo, revs): + cs = repo.get_changeset(i) + otherbranches[cs.branch] = cs.raw_id + for branch, node in otherbranches.iteritems(): + selected = 'branch:%s:%s' % (branch, node) + special.append((selected, '%s: %s' % (_('Peer'), branch))) return [(special, _("Special")), (bookmarks, _("Bookmarks")), @@ -121,18 +136,23 @@ category='warning') redirect(url('summary_home', repo_name=org_repo.repo_name)) + org_rev = request.GET.get('rev_end') + # rev_start is not directly useful - its parent could however be used + # as default for other and thus give a simple compare view + #other_rev = request.POST.get('rev_start') + other_repos_info = {} c.org_repos = [] c.org_repos.append((org_repo.repo_name, org_repo.repo_name)) c.default_org_repo = org_repo.repo_name - c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance) + c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance, org_rev) c.other_repos = [] # add org repo to other so we can open pull request against itself c.other_repos.extend(c.org_repos) c.default_other_repo = org_repo.repo_name - c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance) + c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance, branch_rev=org_rev) usr_data = lambda usr: dict(user_id=usr.user_id, username=usr.username, firstname=usr.firstname, @@ -191,23 +211,12 @@ return redirect(url('pullrequest_home', repo_name=repo_name)) org_repo = _form['org_repo'] - org_ref = _form['org_ref'] + org_ref = 'rev:merge:%s' % _form['merge_rev'] other_repo = _form['other_repo'] - other_ref = _form['other_ref'] + other_ref = 'rev:ancestor:%s' % _form['ancestor_rev'] revisions = _form['revisions'] reviewers = _form['review_members'] - # if we have cherry picked pull request we don't care what is in - # org_ref/other_ref - rev_start = request.POST.get('rev_start') - rev_end = request.POST.get('rev_end') - - if rev_start and rev_end: - # this is swapped to simulate that rev_end is a revision from - # parent of the fork - org_ref = 'rev:%s:%s' % (rev_end, rev_end) - other_ref = 'rev:%s:%s' % (rev_start, rev_start) - title = _form['pullrequest_title'] description = _form['pullrequest_desc'] @@ -265,9 +274,6 @@ :param pull_request: :type pull_request: """ - rev_start = request.GET.get('rev_start') - rev_end = request.GET.get('rev_end') - org_repo = pull_request.org_repo (org_ref_type, org_ref_name, @@ -279,7 +285,7 @@ other_ref_rev) = pull_request.other_ref.split(':') # despite opening revisions for bookmarks/branches/tags, we always - # convert this to rev to prevent changes after book or branch change + # convert this to rev to prevent changes after bookmark or branch change org_ref = ('rev', org_ref_rev) other_ref = ('rev', other_ref_rev) @@ -290,10 +296,6 @@ c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions] - other_ref = ('rev', getattr(c.cs_ranges[0].parents[0] - if c.cs_ranges[0].parents - else EmptyChangeset(), 'raw_id')) - c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges]) c.org_ref = org_ref[1] @@ -394,6 +396,7 @@ c.changeset_statuses = ChangesetStatus.STATUSES c.as_form = False + c.ancestor = None # there is one - but right here we don't know which return render('/pullrequests/pullrequest_show.html') @NotAnonymous() diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/en/LC_MESSAGES/rhodecode.po --- a/rhodecode/i18n/en/LC_MESSAGES/rhodecode.po Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/en/LC_MESSAGES/rhodecode.po Mon Mar 11 17:09:43 2013 +0100 @@ -3730,7 +3730,7 @@ msgstr "" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/fr/LC_MESSAGES/rhodecode.po --- a/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.po Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.po Mon Mar 11 17:09:43 2013 +0100 @@ -3869,7 +3869,7 @@ msgstr "Dernière modification" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "Dernier commiteur" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po --- a/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po Mon Mar 11 17:09:43 2013 +0100 @@ -3743,7 +3743,7 @@ msgstr "最終更新日" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "最後の作成者" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/pl/LC_MESSAGES/rhodecode.po --- a/rhodecode/i18n/pl/LC_MESSAGES/rhodecode.po Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/pl/LC_MESSAGES/rhodecode.po Mon Mar 11 17:09:43 2013 +0100 @@ -3836,7 +3836,7 @@ msgstr "Ostatnio modyfikowany" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "Autor" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po --- a/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po Mon Mar 11 17:09:43 2013 +0100 @@ -3910,7 +3910,7 @@ msgstr "Última alteração" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "Último commiter" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/rhodecode.pot --- a/rhodecode/i18n/rhodecode.pot Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/rhodecode.pot Mon Mar 11 17:09:43 2013 +0100 @@ -3680,7 +3680,7 @@ msgstr "" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po --- a/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po Mon Mar 11 17:09:43 2013 +0100 @@ -3733,7 +3733,7 @@ msgstr "最后修改于" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "最后提交者" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po --- a/rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po Mon Mar 11 17:09:43 2013 +0100 @@ -3860,7 +3860,7 @@ msgstr "最後修改" #: rhodecode/templates/files/files_browser.html:52 -msgid "Last commiter" +msgid "Last committer" msgstr "最後的遞交者" #: rhodecode/templates/files/files_edit.html:19 diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/base.py --- a/rhodecode/lib/base.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/base.py Mon Mar 11 17:09:43 2013 +0100 @@ -43,15 +43,17 @@ ip = environ.get(proxy_key2) if ip: - # HTTP_X_FORWARDED_FOR can have mutliple ips inside - # the left-most being the original client, and each successive proxy - # that passed the request adding the IP address where it received the - # request from. - if ',' in ip: - ip = ip.split(',')[0].strip() return ip ip = environ.get(def_key, '0.0.0.0') + + # HEADERS can have mutliple ips inside + # the left-most being the original client, and each successive proxy + # that passed the request adding the IP address where it received the + # request from. + if ',' in ip: + ip = ip.split(',')[0].strip() + return ip @@ -279,7 +281,6 @@ # WSGIController.__call__ dispatches to the Controller method # the request is routed to. This routing information is # available in environ['pylons.routes_dict'] - start = time.time() try: self.ip_addr = _get_ip_addr(environ) # make sure that we update permissions each time we call controller @@ -300,10 +301,6 @@ ) return WSGIController.__call__(self, environ, start_response) finally: - log.info('IP: %s Request to %s time: %.3fs' % ( - _get_ip_addr(environ), - safe_unicode(_get_access_path(environ)), time.time() - start) - ) meta.Session.remove() diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/celerylib/__init__.py --- a/rhodecode/lib/celerylib/__init__.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/celerylib/__init__.py Mon Mar 11 17:09:43 2013 +0100 @@ -59,6 +59,7 @@ def run_task(task, *args, **kwargs): + global CELERY_ON if CELERY_ON: try: t = task.apply_async(args=args, kwargs=kwargs) @@ -68,7 +69,6 @@ except socket.error, e: if isinstance(e, IOError) and e.errno == 111: log.debug('Unable to connect to celeryd. Sync execution') - global CELERY_ON CELERY_ON = False else: log.error(traceback.format_exc()) diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/exceptions.py --- a/rhodecode/lib/exceptions.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/exceptions.py Mon Mar 11 17:09:43 2013 +0100 @@ -60,12 +60,17 @@ class HTTPLockedRC(HTTPClientError): """ - Special Exception For locked Repos in RhodeCode + Special Exception For locked Repos in RhodeCode, the return code can + be overwritten by _code keyword argument passed into constructors """ code = 423 title = explanation = 'Repository Locked' def __init__(self, reponame, username, *args, **kwargs): + from rhodecode import CONFIG + from rhodecode.lib.utils2 import safe_int + _code = CONFIG.get('lock_ret_code') + self.code = safe_int(_code, self.code) self.title = self.explanation = ('Repository `%s` locked by ' 'user `%s`' % (reponame, username)) super(HTTPLockedRC, self).__init__(*args, **kwargs) diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/helpers.py --- a/rhodecode/lib/helpers.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/helpers.py Mon Mar 11 17:09:43 2013 +0100 @@ -550,13 +550,18 @@ return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name, class_='lazy-cs' if lazy_cs else '') + def _get_op(rev_txt): + _op = None + _name = rev_txt + if len(rev_txt.split('=>')) == 2: + _op, _name = rev_txt.split('=>') + return _op, _name + revs = [] if len(filter(lambda v: v != '', revs_ids)) > 0: repo = None for rev in revs_ids[:revs_top_limit]: - _op = _name = None - if len(rev.split('=>')) == 2: - _op, _name = rev.split('=>') + _op, _name = _get_op(rev) # we want parsed changesets, or new log store format is bad if parse_cs: @@ -583,6 +588,10 @@ [lnk(rev, repo_name) for rev in revs[:revs_limit]] ) ) + _op1, _name1 = _get_op(revs_ids[0]) + _op2, _name2 = _get_op(revs_ids[-1]) + + _rev = '%s...%s' % (_name1, _name2) compare_view = ( '
' @@ -591,7 +600,7 @@ revs_ids[0][:12], revs_ids[-1][:12] ), url('changeset_home', repo_name=repo_name, - revision='%s...%s' % (revs_ids[0], revs_ids[-1]) + revision=_rev ), _('compare view') ) diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/hooks.py --- a/rhodecode/lib/hooks.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/hooks.py Mon Mar 11 17:09:43 2013 +0100 @@ -36,7 +36,7 @@ from rhodecode.lib.vcs.backends.base import EmptyChangeset from rhodecode.lib.compat import json from rhodecode.lib.exceptions import HTTPLockedRC -from rhodecode.lib.utils2 import safe_str, datetime_to_time +from rhodecode.lib.utils2 import safe_str from rhodecode.model.db import Repository, User @@ -113,7 +113,14 @@ usr = User.get_by_username(username) if locked_by[0] and usr.user_id != int(locked_by[0]): locked_by = User.get(locked_by[0]).username - raise HTTPLockedRC(repository, locked_by) + # this exception is interpreted in git/hg middlewares and based + # on that proper return code is server to client + _http_ret = HTTPLockedRC(repository, locked_by) + if str(_http_ret.code).startswith('2'): + #2xx Codes don't raise exceptions + sys.stdout.write(_http_ret.title) + else: + raise _http_ret def pre_pull(ui, repo, **kwargs): @@ -139,7 +146,14 @@ if locked_by[0]: locked_by = User.get(locked_by[0]).username - raise HTTPLockedRC(repository, locked_by) + # this exception is interpreted in git/hg middlewares and based + # on that proper return code is server to client + _http_ret = HTTPLockedRC(repository, locked_by) + if str(_http_ret.code).startswith('2'): + #2xx Codes don't raise exceptions + sys.stdout.write(_http_ret.title) + else: + raise _http_ret def log_pull_action(ui, repo, **kwargs): @@ -159,12 +173,14 @@ repository = extras['repository'] scm = extras['scm'] make_lock = extras['make_lock'] + locked_by = extras['locked_by'] ip = extras['ip'] elif 'username' in rc_extras: username = rc_extras['username'] repository = rc_extras['repository'] scm = rc_extras['scm'] make_lock = rc_extras['make_lock'] + locked_by = rc_extras['locked_by'] ip = rc_extras['ip'] else: raise Exception('Missing data in repo.ui and os.environ') @@ -185,6 +201,12 @@ #msg = 'Made lock on repo `%s`' % repository #sys.stdout.write(msg) + if locked_by[0]: + locked_by = User.get(locked_by[0]).username + _http_ret = HTTPLockedRC(repository, locked_by) + if str(_http_ret.code).startswith('2'): + #2xx Codes don't raise exceptions + sys.stdout.write(_http_ret.title) return 0 @@ -207,15 +229,19 @@ repository = extras['repository'] scm = extras['scm'] make_lock = extras['make_lock'] + locked_by = extras['locked_by'] + action = extras['action'] elif 'username' in rc_extras: username = rc_extras['username'] repository = rc_extras['repository'] scm = rc_extras['scm'] make_lock = rc_extras['make_lock'] + locked_by = rc_extras['locked_by'] + action = extras['action'] else: raise Exception('Missing data in repo.ui and os.environ') - action = 'push' + ':%s' + action = action + ':%s' if scm == 'hg': node = kwargs['node'] @@ -255,6 +281,13 @@ msg = 'Released lock on repo `%s`\n' % repository sys.stdout.write(msg) + if locked_by[0]: + locked_by = User.get(locked_by[0]).username + _http_ret = HTTPLockedRC(repository, locked_by) + if str(_http_ret.code).startswith('2'): + #2xx Codes don't raise exceptions + sys.stdout.write(_http_ret.title) + return 0 diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/middleware/simplegit.py --- a/rhodecode/lib/middleware/simplegit.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/middleware/simplegit.py Mon Mar 11 17:09:43 2013 +0100 @@ -234,7 +234,8 @@ app = self.__make_app(repo_name, repo_path, extras) return app(environ, start_response) except HTTPLockedRC, e: - log.debug('Repository LOCKED ret code 423!') + _code = CONFIG.get('lock_ret_code') + log.debug('Repository LOCKED ret code %s!' % (_code)) return e(environ, start_response) except Exception: log.error(traceback.format_exc()) diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/middleware/simplehg.py --- a/rhodecode/lib/middleware/simplehg.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/middleware/simplehg.py Mon Mar 11 17:09:43 2013 +0100 @@ -199,7 +199,8 @@ if str(e).find('not found') != -1: return HTTPNotFound()(environ, start_response) except HTTPLockedRC, e: - log.debug('Repository LOCKED ret code 423!') + _code = CONFIG.get('lock_ret_code') + log.debug('Repository LOCKED ret code %s!' % (_code)) return e(environ, start_response) except Exception: log.error(traceback.format_exc()) diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/middleware/wrapper.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/lib/middleware/wrapper.py Mon Mar 11 17:09:43 2013 +0100 @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +""" + rhodecode.lib.middleware.wrapper + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + request time mesuring app + + :created_on: May 23, 2013 + :author: marcink + :copyright: (C) 2010-2012 Marcin Kuzminski + :license: GPLv3, see COPYING for more details. +""" +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import time +import logging +from rhodecode.lib.base import _get_ip_addr, _get_access_path +from rhodecode.lib.utils2 import safe_unicode + + +class RequestWrapper(object): + + def __init__(self, app, config): + self.application = app + self.config = config + + def __call__(self, environ, start_response): + start = time.time() + try: + return self.application(environ, start_response) + finally: + log = logging.getLogger('rhodecode.' + self.__class__.__name__) + log.info('IP: %s Request to %s time: %.3fs' % ( + _get_ip_addr(environ), + safe_unicode(_get_access_path(environ)), time.time() - start) + ) diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/utils.py Mon Mar 11 17:09:43 2013 +0100 @@ -240,7 +240,7 @@ return False -def is_valid_repos_group(repos_group_name, base_path): +def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False): """ Returns True if given path is a repos group False otherwise @@ -263,7 +263,7 @@ pass # check if it's a valid path - if os.path.isdir(full_path): + if skip_path_check or os.path.isdir(full_path): return True return False @@ -495,7 +495,6 @@ #don't hold further removals on error log.error(traceback.format_exc()) sa.rollback() - return added, removed diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/utils2.py --- a/rhodecode/lib/utils2.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/utils2.py Mon Mar 11 17:09:43 2013 +0100 @@ -565,11 +565,15 @@ def obfuscate_url_pw(engine): - from sqlalchemy.engine import url - url = url.make_url(engine) - if url.password: - url.password = 'XXXXX' - return str(url) + _url = engine or '' + from sqlalchemy.engine import url as sa_url + try: + _url = sa_url.make_url(engine) + if _url.password: + _url.password = 'XXXXX' + except: + pass + return str(_url) def get_server_url(environ): diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/vcs/backends/base.py --- a/rhodecode/lib/vcs/backends/base.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/vcs/backends/base.py Mon Mar 11 17:09:43 2013 +0100 @@ -9,7 +9,7 @@ :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak. """ - +import datetime from itertools import chain from rhodecode.lib.vcs.utils import author_name, author_email from rhodecode.lib.vcs.utils.lazy import LazyProperty @@ -311,6 +311,27 @@ """ raise NotImplementedError + def inject_ui(self, **extras): + """ + Injects extra parameters into UI object of this repo + """ + required_extras = [ + 'ip', + 'username', + 'action', + 'repository', + 'scm', + 'config', + 'server_url', + 'make_lock', + 'locked_by', + ] + for req in required_extras: + if req not in extras: + raise AttributeError('Missing attribute %s in extras' % (req)) + for k, v in extras.items(): + self._repo.ui.setconfig('rhodecode_extras', k, v) + class BaseChangeset(object): """ @@ -433,28 +454,28 @@ raise NotImplementedError @LazyProperty - def commiter(self): + def committer(self): """ - Returns Commiter for given commit + Returns Committer for given commit """ raise NotImplementedError @LazyProperty - def commiter_name(self): + def committer_name(self): """ Returns Author name for given commit """ - return author_name(self.commiter) + return author_name(self.committer) @LazyProperty - def commiter_email(self): + def committer_email(self): """ Returns Author email address for given commit """ - return author_email(self.commiter) + return author_email(self.committer) @LazyProperty def author(self): @@ -959,12 +980,12 @@ """ def __init__(self, cs='0' * 40, repo=None, requested_revision=None, - alias=None, revision=-1, message='', author='', date=''): + alias=None, revision=-1, message='', author='', date=None): self._empty_cs = cs self.revision = revision self.message = message self.author = author - self.date = date + self.date = date or datetime.datetime.fromtimestamp(0) self.repository = repo self.requested_revision = requested_revision self.alias = alias diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/vcs/backends/git/changeset.py --- a/rhodecode/lib/vcs/backends/git/changeset.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/vcs/backends/git/changeset.py Mon Mar 11 17:09:43 2013 +0100 @@ -17,6 +17,7 @@ from rhodecode.lib.vcs.utils import safe_unicode from rhodecode.lib.vcs.utils import date_fromtimestamp from rhodecode.lib.vcs.utils.lazy import LazyProperty +from rhodecode.lib.utils2 import safe_int class GitChangeset(BaseChangeset): @@ -41,7 +42,7 @@ self._commit = commit self._tree_id = commit.tree - self._commiter_property = 'committer' + self._committer_property = 'committer' self._author_property = 'author' self._date_property = 'commit_time' self._date_tz_property = 'commit_timezone' @@ -53,8 +54,8 @@ self._paths = {} @LazyProperty - def commiter(self): - return safe_unicode(getattr(self._commit, self._commiter_property)) + def committer(self): + return safe_unicode(getattr(self._commit, self._committer_property)) @LazyProperty def author(self): @@ -275,10 +276,9 @@ """ Returns last commit of the file at the given ``path``. """ - node = self.get_node(path) - return node.history[0] + return self.get_file_history(path, limit=1)[0] - def get_file_history(self, path): + def get_file_history(self, path, limit=None): """ Returns history of file as reversed list of ``Changeset`` objects for which file at given ``path`` has been modified. @@ -287,11 +287,16 @@ which is generally not good. Should be replaced with algorithm iterating commits. """ + self._get_filectx(path) - - cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % ( - self.id, path - ) + if limit: + cmd = 'log -n %s --pretty="format: %%H" -s -p %s -- "%s"' % ( + safe_int(limit, 0), self.id, path + ) + else: + cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % ( + self.id, path + ) so, se = self.repository.run_git_command(cmd) ids = re.findall(r'[0-9a-fA-F]{40}', so) return [self.repository.get_changeset(id) for id in ids] diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/vcs/backends/git/repository.py --- a/rhodecode/lib/vcs/backends/git/repository.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/vcs/backends/git/repository.py Mon Mar 11 17:09:43 2013 +0100 @@ -67,14 +67,12 @@ @ThreadLocalLazyProperty def _repo(self): repo = Repo(self.path) - #temporary set that to now at later we will move it to constructor - baseui = None - if baseui is None: + # patch the instance of GitRepo with an "FAKE" ui object to add + # compatibility layer with Mercurial + if not hasattr(repo, 'ui'): from mercurial.ui import ui baseui = ui() - # patch the instance of GitRepo with an "FAKE" ui object to add - # compatibility layer with Mercurial - setattr(repo, 'ui', baseui) + setattr(repo, 'ui', baseui) return repo @property @@ -306,6 +304,15 @@ url = ':///'.join(('file', url)) return url + def get_hook_location(self): + """ + returns absolute path to location where hooks are stored + """ + loc = os.path.join(self.path, 'hooks') + if not self.bare: + loc = os.path.join(self.path, '.git', 'hooks') + return loc + @LazyProperty def name(self): return os.path.basename(self.path) diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/vcs/backends/hg/changeset.py --- a/rhodecode/lib/vcs/backends/hg/changeset.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/vcs/backends/hg/changeset.py Mon Mar 11 17:09:43 2013 +0100 @@ -44,8 +44,8 @@ return safe_unicode(self._ctx.description()) @LazyProperty - def commiter(self): - return safe_unicode(self.auhtor) + def committer(self): + return safe_unicode(self.author) @LazyProperty def author(self): @@ -219,19 +219,23 @@ """ Returns last commit of the file at the given ``path``. """ - node = self.get_node(path) - return node.history[0] + return self.get_file_history(path, limit=1)[0] - def get_file_history(self, path): + def get_file_history(self, path, limit=None): """ Returns history of file as reversed list of ``Changeset`` objects for which file at given ``path`` has been modified. """ fctx = self._get_filectx(path) - nodes = [fctx.filectx(x).node() for x in fctx.filelog()] - changesets = [self.repository.get_changeset(hex(node)) - for node in reversed(nodes)] - return changesets + hist = [] + cnt = 0 + for cs in reversed([x for x in fctx.filelog()]): + cnt += 1 + hist.append(hex(fctx.filectx(cs).node())) + if limit and cnt == limit: + break + + return [self.repository.get_changeset(node) for node in hist] def get_file_annotate(self, path): """ diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/vcs/backends/hg/repository.py --- a/rhodecode/lib/vcs/backends/hg/repository.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/vcs/backends/hg/repository.py Mon Mar 11 17:09:43 2013 +0100 @@ -422,6 +422,12 @@ url = "file:" + urllib.pathname2url(url) return url + def get_hook_location(self): + """ + returns absolute path to location where hooks are stored + """ + return os.path.join(self.path, '.hg', '.hgrc') + def get_changeset(self, revision=None): """ Returns ``MercurialChangeset`` object representing repository's @@ -492,7 +498,7 @@ """ return MercurialWorkdir(self) - def get_config_value(self, section, name, config_file=None): + def get_config_value(self, section, name=None, config_file=None): """ Returns configuration value for a given [``section``] and ``name``. diff -r c65b440540da -r 6e8027c2f49c rhodecode/lib/vcs/utils/lazy.py --- a/rhodecode/lib/vcs/utils/lazy.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/lib/vcs/utils/lazy.py Mon Mar 11 17:09:43 2013 +0100 @@ -1,3 +1,14 @@ +class _Missing(object): + + def __repr__(self): + return 'no value' + + def __reduce__(self): + return '_missing' + +_missing = _Missing() + + class LazyProperty(object): """ Decorator for easier creation of ``property`` from potentially expensive to @@ -24,8 +35,11 @@ def __get__(self, obj, klass=None): if obj is None: return self - result = obj.__dict__[self.__name__] = self._func(obj) - return result + value = obj.__dict__.get(self.__name__, _missing) + if value is _missing: + value = self._func(obj) + obj.__dict__[self.__name__] = value + return value import threading @@ -41,5 +55,8 @@ if not hasattr(obj, '__tl_dict__'): obj.__tl_dict__ = threading.local().__dict__ - result = obj.__tl_dict__[self.__name__] = self._func(obj) - return result + value = obj.__tl_dict__.get(self.__name__, _missing) + if value is _missing: + value = self._func(obj) + obj.__tl_dict__[self.__name__] = value + return value diff -r c65b440540da -r 6e8027c2f49c rhodecode/model/db.py --- a/rhodecode/model/db.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/model/db.py Mon Mar 11 17:09:43 2013 +0100 @@ -47,7 +47,7 @@ from rhodecode.lib.vcs.backends.base import EmptyChangeset from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \ - safe_unicode, remove_suffix, remove_prefix + safe_unicode, remove_suffix, remove_prefix, time_to_datetime from rhodecode.lib.compat import json from rhodecode.lib.caching_query import FromCache @@ -938,15 +938,7 @@ @classmethod def inject_ui(cls, repo, extras={}): - from rhodecode.lib.vcs.backends.hg import MercurialRepository - from rhodecode.lib.vcs.backends.git import GitRepository - required = (MercurialRepository, GitRepository) - if not isinstance(repo, required): - raise Exception('repo must be instance of %s' % required) - - # inject ui extra param to log this action via push logger - for k, v in extras.items(): - repo._repo.ui.setconfig('rhodecode_extras', k, v) + repo.inject_ui(extras) @classmethod def is_valid(cls, repo_name): @@ -980,7 +972,11 @@ enable_statistics=repo.enable_statistics, enable_locking=repo.enable_locking, enable_downloads=repo.enable_downloads, - last_changeset=repo.changeset_cache + last_changeset=repo.changeset_cache, + locked_by=User.get(self.locked[0]).get_api_data() \ + if self.locked[0] else None, + locked_date=time_to_datetime(self.locked[1]) \ + if self.locked[1] else None ) rc_config = RhodeCodeSetting.get_app_settings() repository_fields = str2bool(rc_config.get('rhodecode_repository_fields')) @@ -1002,6 +998,10 @@ Session().add(repo) Session().commit() + @classmethod + def getlock(cls, repo): + return repo.locked + @property def last_db_change(self): return self.updated_on @@ -1341,15 +1341,13 @@ return cnt + children_count(self) - def recursive_groups_and_repos(self): - """ - Recursive return all groups, with repositories in those groups - """ + def _recursive_objects(self, include_repos=True): all_ = [] def _get_members(root_gr): - for r in root_gr.repositories: - all_.append(r) + if include_repos: + for r in root_gr.repositories: + all_.append(r) childs = root_gr.children.all() if childs: for gr in childs: @@ -1359,6 +1357,18 @@ _get_members(self) return [self] + all_ + def recursive_groups_and_repos(self): + """ + Recursive return all groups, with repositories in those groups + """ + return self._recursive_objects() + + def recursive_groups(self): + """ + Returns all children groups for this group including children of children + """ + return self._recursive_objects(include_repos=False) + def get_new_name(self, group_name): """ returns new full group name based on parent and new name @@ -1728,7 +1738,7 @@ for inv_obj in inv_objs: inv_obj.cache_active = False log.debug('marking %s key for invalidation based on key=%s,repo_name=%s' - % (inv_obj, key, repo_name)) + % (inv_obj, key, safe_str(repo_name))) invalidated_keys.append(inv_obj.cache_key) Session().add(inv_obj) Session().commit() diff -r c65b440540da -r 6e8027c2f49c rhodecode/model/forms.py --- a/rhodecode/model/forms.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/model/forms.py Mon Mar 11 17:09:43 2013 +0100 @@ -395,4 +395,7 @@ pullrequest_title = v.UnicodeString(strip=True, required=True, min=3) pullrequest_desc = v.UnicodeString(strip=True, required=False) + ancestor_rev = v.UnicodeString(strip=True, required=True) + merge_rev = v.UnicodeString(strip=True, required=True) + return _PullRequestForm diff -r c65b440540da -r 6e8027c2f49c rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/model/pull_request.py Mon Mar 11 17:09:43 2013 +0100 @@ -161,7 +161,7 @@ pull_request.updated_on = datetime.datetime.now() Session().add(pull_request) - def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref): + def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge): """ Returns a list of changesets that can be merged from org_repo@org_ref to other_repo@other_ref ... and the ancestor that would be used for merge @@ -211,16 +211,21 @@ else: hgrepo = other_repo._repo - revs = ["ancestors(id('%s')) and not ancestors(id('%s'))" % - (other_rev, org_rev)] - changesets = [other_repo.get_changeset(cs) - for cs in scmutil.revrange(hgrepo, revs)] + if merge: + revs = ["ancestors(id('%s')) and not ancestors(id('%s')) and not id('%s')" % + (other_rev, org_rev, org_rev)] - if org_repo != other_repo: ancestors = scmutil.revrange(hgrepo, ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)]) if len(ancestors) == 1: ancestor = hgrepo[ancestors[0]].hex() + else: + # TODO: have both + and - changesets + revs = ["id('%s') :: id('%s') - id('%s')" % + (org_rev, other_rev, org_rev)] + + changesets = [other_repo.get_changeset(cs) + for cs in scmutil.revrange(hgrepo, revs)] elif alias == 'git': assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos @@ -233,7 +238,7 @@ return changesets, ancestor - def get_compare_data(self, org_repo, org_ref, other_repo, other_ref): + def get_compare_data(self, org_repo, org_ref, other_repo, other_ref, merge): """ Returns incoming changesets for mercurial repositories @@ -251,5 +256,6 @@ cs_ranges, ancestor = self._get_changesets(org_repo.scm_instance.alias, org_repo.scm_instance, org_ref, - other_repo.scm_instance, other_ref) + other_repo.scm_instance, other_ref, + merge) return cs_ranges, ancestor diff -r c65b440540da -r 6e8027c2f49c rhodecode/model/repo.py --- a/rhodecode/model/repo.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/model/repo.py Mon Mar 11 17:09:43 2013 +0100 @@ -32,7 +32,7 @@ from rhodecode.lib.vcs.backends import get_backend from rhodecode.lib.compat import json from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\ - remove_prefix + remove_prefix, obfuscate_url_pw from rhodecode.lib.caching_query import FromCache from rhodecode.lib.hooks import log_create_repository, log_delete_repository @@ -42,8 +42,6 @@ RhodeCodeSetting, RepositoryField from rhodecode.lib import helpers as h from rhodecode.lib.auth import HasRepoPermissionAny -from rhodecode.lib.vcs.backends.base import EmptyChangeset - log = logging.getLogger(__name__) @@ -640,7 +638,8 @@ raise Exception('This path %s is a valid group' % repo_path) log.info('creating repo %s in %s @ %s' % ( - repo_name, safe_unicode(repo_path), clone_uri + repo_name, safe_unicode(repo_path), + obfuscate_url_pw(clone_uri) ) ) backend = get_backend(alias) diff -r c65b440540da -r 6e8027c2f49c rhodecode/model/repos_group.py --- a/rhodecode/model/repos_group.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/model/repos_group.py Mon Mar 11 17:09:43 2013 +0100 @@ -249,27 +249,37 @@ # change properties repos_group.group_description = form_data['group_description'] - repos_group.parent_group = RepoGroup.get(form_data['group_parent_id']) repos_group.group_parent_id = form_data['group_parent_id'] repos_group.enable_locking = form_data['enable_locking'] + + repos_group.parent_group = RepoGroup.get(form_data['group_parent_id']) repos_group.group_name = repos_group.get_new_name(form_data['group_name']) new_path = repos_group.full_path - self.sa.add(repos_group) - # iterate over all members of this groups and set the locking ! + # iterate over all members of this groups and do fixes + # set locking if given + # if obj is a repoGroup also fix the name of the group according + # to the parent + # if obj is a Repo fix it's name # this can be potentially heavy operation for obj in repos_group.recursive_groups_and_repos(): #set the value from it's parent obj.enable_locking = repos_group.enable_locking + if isinstance(obj, RepoGroup): + new_name = obj.get_new_name(obj.name) + log.debug('Fixing group %s to new name %s' \ + % (obj.group_name, new_name)) + obj.group_name = new_name + elif isinstance(obj, Repository): + # we need to get all repositories from this new group and + # rename them accordingly to new group path + new_name = obj.get_new_name(obj.just_name) + log.debug('Fixing repo %s to new name %s' \ + % (obj.repo_name, new_name)) + obj.repo_name = new_name self.sa.add(obj) - # we need to get all repositories from this new group and - # rename them accordingly to new group path - for r in repos_group.repositories: - r.repo_name = r.get_new_name(r.just_name) - self.sa.add(r) - self.__rename_group(old_path, new_path) return repos_group diff -r c65b440540da -r 6e8027c2f49c rhodecode/model/scm.py --- a/rhodecode/model/scm.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/model/scm.py Mon Mar 11 17:09:43 2013 +0100 @@ -44,13 +44,14 @@ from rhodecode import BACKENDS from rhodecode.lib import helpers as h -from rhodecode.lib.utils2 import safe_str, safe_unicode +from rhodecode.lib.utils2 import safe_str, safe_unicode, get_server_url from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny from rhodecode.lib.utils import get_filesystem_repos, make_ui, \ action_logger, REMOVED_REPO_PAT from rhodecode.model import BaseModel from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \ UserFollowing, UserLog, User, RepoGroup, PullRequest +from rhodecode.lib.hooks import log_push_action log = logging.getLogger(__name__) @@ -402,6 +403,60 @@ self.sa.add(repo) return repo + def _handle_push(self, repo, username, action, repo_name, revisions): + """ + Triggers push action hooks + + :param repo: SCM repo + :param username: username who pushes + :param action: push/push_loca/push_remote + :param repo_name: name of repo + :param revisions: list of revisions that we pushed + """ + from rhodecode import CONFIG + from rhodecode.lib.base import _get_ip_addr + try: + from pylons import request + environ = request.environ + except TypeError: + # we might use this outside of request context, let's fake the + # environ data + from webob import Request + environ = Request.blank('').environ + + #trigger push hook + extras = { + 'ip': _get_ip_addr(environ), + 'username': username, + 'action': 'push_local', + 'repository': repo_name, + 'scm': repo.alias, + 'config': CONFIG['__file__'], + 'server_url': get_server_url(environ), + 'make_lock': None, + 'locked_by': [None, None] + } + _scm_repo = repo._repo + repo.inject_ui(**extras) + if repo.alias == 'hg': + log_push_action(_scm_repo.ui, _scm_repo, node=revisions[0]) + elif repo.alias == 'git': + log_push_action(_scm_repo.ui, _scm_repo, _git_revs=revisions) + + def _get_IMC_module(self, scm_type): + """ + Returns InMemoryCommit class based on scm_type + + :param scm_type: + """ + if scm_type == 'hg': + from rhodecode.lib.vcs.backends.hg import \ + MercurialInMemoryChangeset as IMC + elif scm_type == 'git': + from rhodecode.lib.vcs.backends.git import \ + GitInMemoryChangeset as IMC + return IMC + def pull_changes(self, repo, username): dbrepo = self.__get_repo(repo) clone_uri = dbrepo.clone_uri @@ -409,26 +464,13 @@ raise Exception("This repository doesn't have a clone uri") repo = dbrepo.scm_instance - from rhodecode import CONFIG + repo_name = dbrepo.repo_name try: - extras = { - 'ip': '', - 'username': username, - 'action': 'push_remote', - 'repository': dbrepo.repo_name, - 'scm': repo.alias, - 'config': CONFIG['__file__'], - 'make_lock': None, - 'locked_by': [None, None] - } - - Repository.inject_ui(repo, extras=extras) - if repo.alias == 'git': repo.fetch(clone_uri) else: repo.pull(clone_uri) - self.mark_for_invalidation(dbrepo.repo_name) + self.mark_for_invalidation(repo_name) except: log.error(traceback.format_exc()) raise @@ -441,13 +483,8 @@ :param repo: SCM instance """ - - if repo.alias == 'hg': - from rhodecode.lib.vcs.backends.hg import \ - MercurialInMemoryChangeset as IMC - elif repo.alias == 'git': - from rhodecode.lib.vcs.backends.git import \ - GitInMemoryChangeset as IMC + user = self._get_user(user) + IMC = self._get_IMC_module(repo.alias) # decoding here will force that we have proper encoded values # in any other case this will throw exceptions and deny commit @@ -463,20 +500,21 @@ author=author, parents=[cs], branch=cs.branch) - action = 'push_local:%s' % tip.raw_id - action_logger(user, action, repo_name) self.mark_for_invalidation(repo_name) + self._handle_push(repo, + username=user.username, + action='push_local', + repo_name=repo_name, + revisions=[tip.raw_id]) return tip def create_node(self, repo, repo_name, cs, user, author, message, content, f_path): - if repo.alias == 'hg': - from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC - elif repo.alias == 'git': - from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC + user = self._get_user(user) + IMC = self._get_IMC_module(repo.alias) + # decoding here will force that we have proper encoded values # in any other case this will throw exceptions and deny commit - if isinstance(content, (basestring,)): content = safe_str(content) elif isinstance(content, (file, cStringIO.OutputType,)): @@ -502,9 +540,12 @@ author=author, parents=parents, branch=cs.branch) - action = 'push_local:%s' % tip.raw_id - action_logger(user, action, repo_name) self.mark_for_invalidation(repo_name) + self._handle_push(repo, + username=user.username, + action='push_local', + repo_name=repo_name, + revisions=[tip.raw_id]) return tip def get_nodes(self, repo_name, revision, root_path='/', flat=True): diff -r c65b440540da -r 6e8027c2f49c rhodecode/model/validators.py --- a/rhodecode/model/validators.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/model/validators.py Mon Mar 11 17:09:43 2013 +0100 @@ -416,6 +416,8 @@ svnremoterepo(ui, url).capabilities elif url.startswith('git+http'): raise NotImplementedError() + else: + raise Exception('clone from URI %s not allowed' % (url)) elif repo_type == 'git': from rhodecode.lib.vcs.backends.git.repository import GitRepository @@ -427,6 +429,8 @@ raise NotImplementedError() elif url.startswith('hg+http'): raise NotImplementedError() + else: + raise Exception('clone from URI %s not allowed' % (url)) class _validator(formencode.validators.FancyValidator): messages = { diff -r c65b440540da -r 6e8027c2f49c rhodecode/public/css/codemirror.css --- a/rhodecode/public/css/codemirror.css Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/public/css/codemirror.css Mon Mar 11 17:09:43 2013 +0100 @@ -171,4 +171,4 @@ visibility: hidden; } -} +} \ No newline at end of file diff -r c65b440540da -r 6e8027c2f49c rhodecode/public/css/pygments.css --- a/rhodecode/public/css/pygments.css Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/public/css/pygments.css Mon Mar 11 17:09:43 2013 +0100 @@ -14,7 +14,7 @@ div.codeblock .code-header { border-bottom: 1px solid #CCCCCC; background: #EEEEEE; - padding:10px 0 10px 0; + padding: 10px 0 10px 0; } div.codeblock .code-header .stats { @@ -26,38 +26,38 @@ } div.codeblock .code-header .stats .left { - float:left; + float: left; } div.codeblock .code-header .stats .left.img { - margin-top:-2px; + margin-top: -2px; } div.codeblock .code-header .stats .left.item { - float:left; + float: left; padding: 0 9px 0 9px; - border-right:1px solid #ccc; + border-right: 1px solid #ccc; } div.codeblock .code-header .stats .left.item pre { } div.codeblock .code-header .stats .left.item.last { - border-right:none; + border-right: none; } div.codeblock .code-header .stats .buttons { - float:right; - padding-right:4px; + float: right; + padding-right: 4px; } div.codeblock .code-header .author { - margin-left:25px; + margin-left: 25px; font-weight: bold; height: 25px; } div.codeblock .code-header .author .user { - padding-top:3px; + padding-top: 3px; } div.codeblock .code-header .commit { - margin-left:25px; + margin-left: 25px; font-weight: normal; - white-space:pre; + white-space: pre; } div.codeblock .code-body table { @@ -90,8 +90,8 @@ display: block; } div.annotatediv { - margin-left:2px; - margin-right:4px; + margin-left: 2px; + margin-right: 4px; } .code-highlight { padding: 0px; @@ -170,4 +170,4 @@ .code-highlight .vc, .codehilite .vc { color: #19177C } /* Name.Variable.Class */ .code-highlight .vg, .codehilite .vg { color: #19177C } /* Name.Variable.Global */ .code-highlight .vi, .codehilite .vi { color: #19177C } /* Name.Variable.Instance */ -.code-highlight .il, .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ +.code-highlight .il, .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff -r c65b440540da -r 6e8027c2f49c rhodecode/public/css/style.css --- a/rhodecode/public/css/style.css Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/public/css/style.css Mon Mar 11 17:09:43 2013 +0100 @@ -4143,11 +4143,11 @@ font-weight: normal; } -div.rst-block { +div.rst-block { background-color: #fafafa; } -div.rst-block { +div.rst-block { clear: both; overflow: hidden; margin: 0; @@ -4420,7 +4420,7 @@ padding: 10px 20px; } -.inline-comments div.rst-block { +.inline-comments div.rst-block { clear: both; overflow: hidden; margin: 0; @@ -4811,4 +4811,4 @@ .lineno:target a { border: solid 2px #ee0 !important; margin: -2px; -} +} \ No newline at end of file diff -r c65b440540da -r 6e8027c2f49c rhodecode/public/js/graph.js --- a/rhodecode/public/js/graph.js Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/public/js/graph.js Mon Mar 11 17:09:43 2013 +0100 @@ -26,7 +26,7 @@ this.canvas = document.getElementById("graph_canvas"); - if (!document.createElement("canvas").getContext) + if (!document.createElement("canvas").getContext) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas); this.ctx = this.canvas.getContext('2d'); this.ctx.strokeStyle = 'rgb(0, 0, 0)'; diff -r c65b440540da -r 6e8027c2f49c rhodecode/public/js/rhodecode.js --- a/rhodecode/public/js/rhodecode.js Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/public/js/rhodecode.js Mon Mar 11 17:09:43 2013 +0100 @@ -86,19 +86,6 @@ } } -var setSelectValue = function(select, val){ - var selection = YUD.get(select); - - // select element - for(var i=0;i{2}'.format(t,new_url,n_hl)); } if(match.length >= matches_max){ - match.push('{0}'.format(_TM['search truncated'])); + match.push('{0}'.format(_TM['Search truncated'])); } } } @@ -1115,7 +1102,7 @@ YUD.setStyle('tbody_filtered','display',''); if (match.length==0){ - match.push('{0}'.format(_TM['no matching files'])); + match.push('{0}'.format(_TM['No matching files'])); } YUD.get('tbody_filtered').innerHTML = match.join(""); @@ -2173,11 +2160,11 @@ console.log(t); if(YUD.hasClass(t, 'hidden')){ YUD.removeClass(t, 'hidden'); - YUD.get(button).innerHTML = "↑ {0} ↑".format(_TM['collapse diff']); + YUD.get(button).innerHTML = "↑ {0} ↑".format(_TM['Collapse diff']); } else if(!YUD.hasClass(t, 'hidden')){ YUD.addClass(t, 'hidden'); - YUD.get(button).innerHTML = "↓ {0} ↓".format(_TM['expand diff']); + YUD.get(button).innerHTML = "↓ {0} ↓".format(_TM['Expand diff']); } }); diff -r c65b440540da -r 6e8027c2f49c rhodecode/templates/admin/users/user_edit.html --- a/rhodecode/templates/admin/users/user_edit.html Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/templates/admin/users/user_edit.html Mon Mar 11 17:09:43 2013 +0100 @@ -43,11 +43,14 @@ ${c.user.api_key}
+ ##show current ip just if we show ourself + %if c.rhodecode_user.username == c.user.username:
${c.perm_user.ip_addr or "?"}
+ %endif
diff -r c65b440540da -r 6e8027c2f49c rhodecode/templates/admin/users/user_edit_my_account_form.html --- a/rhodecode/templates/admin/users/user_edit_my_account_form.html Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/templates/admin/users/user_edit_my_account_form.html Mon Mar 11 17:09:43 2013 +0100 @@ -20,6 +20,11 @@ ${c.user.api_key}
+
+
+ ${c.perm_user.ip_addr or "?"} +
+
diff -r c65b440540da -r 6e8027c2f49c rhodecode/templates/base/root.html --- a/rhodecode/templates/base/root.html Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/templates/base/root.html Mon Mar 11 17:09:43 2013 +0100 @@ -41,21 +41,21 @@ diff -r c65b440540da -r 6e8027c2f49c rhodecode/templates/pullrequests/pullrequest_show.html --- a/rhodecode/templates/pullrequests/pullrequest_show.html Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/templates/pullrequests/pullrequest_show.html Mon Mar 11 17:09:43 2013 +0100 @@ -161,7 +161,7 @@
- ${_('save changes')} + ${_('Save changes')}
%endif
diff -r c65b440540da -r 6e8027c2f49c rhodecode/tests/__init__.py --- a/rhodecode/tests/__init__.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/tests/__init__.py Mon Mar 11 17:09:43 2013 +0100 @@ -47,7 +47,8 @@ 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', 'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO', 'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO', - 'GIT_REMOTE_REPO', 'SCM_TESTS', '_get_repo_create_params' + 'GIT_REMOTE_REPO', 'SCM_TESTS', '_get_repo_create_params', + '_get_group_create_params' ] # Invoke websetup with the current config file @@ -183,3 +184,18 @@ defs.update({'repo_name_full': defs['repo_name']}) return defs + + +def _get_group_create_params(**custom): + defs = dict( + group_name=None, + group_description='DESC', + group_parent_id=None, + perms_updates=[], + perms_new=[], + enable_locking=False, + recursive=False + ) + defs.update(custom) + + return defs diff -r c65b440540da -r 6e8027c2f49c rhodecode/tests/api/api_base.py --- a/rhodecode/tests/api/api_base.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/tests/api/api_base.py Mon Mar 11 17:09:43 2013 +0100 @@ -370,6 +370,17 @@ % (TEST_USER_ADMIN_LOGIN, self.REPO, True)) self._compare_ok(id_, expected, given=response.body) + def test_api_lock_repo_lock_optional_locked(self): + from rhodecode.lib.utils2 import time_to_datetime + _locked_since = json.dumps(time_to_datetime(Repository\ + .get_by_repo_name(self.REPO).locked[1])) + id_, params = _build_data(self.apikey, 'lock', + repoid=self.REPO) + response = api_call(self, params) + expected = ('Repo `%s` locked by `%s`. Locked=`True`. Locked since: `%s`' + % (self.REPO, TEST_USER_ADMIN_LOGIN, _locked_since)) + self._compare_ok(id_, expected, given=response.body) + @mock.patch.object(Repository, 'lock', crash) def test_api_lock_error(self): id_, params = _build_data(self.apikey, 'lock', @@ -381,6 +392,32 @@ expected = 'Error occurred locking repository `%s`' % self.REPO self._compare_error(id_, expected, given=response.body) + def test_api_get_locks_regular_user(self): + id_, params = _build_data(self.apikey_regular, 'get_locks') + response = api_call(self, params) + expected = [] + self._compare_ok(id_, expected, given=response.body) + + def test_api_get_locks_with_userid_regular_user(self): + id_, params = _build_data(self.apikey_regular, 'get_locks', + userid=TEST_USER_ADMIN_LOGIN) + response = api_call(self, params) + expected = 'userid is not the same as your user' + self._compare_error(id_, expected, given=response.body) + + def test_api_get_locks(self): + id_, params = _build_data(self.apikey, 'get_locks') + response = api_call(self, params) + expected = [] + self._compare_ok(id_, expected, given=response.body) + + def test_api_get_locks_with_userid(self): + id_, params = _build_data(self.apikey, 'get_locks', + userid=TEST_USER_REGULAR_LOGIN) + response = api_call(self, params) + expected = [] + self._compare_ok(id_, expected, given=response.body) + def test_api_create_existing_user(self): id_, params = _build_data(self.apikey, 'create_user', username=TEST_USER_ADMIN_LOGIN, diff -r c65b440540da -r 6e8027c2f49c rhodecode/tests/functional/test_compare.py --- a/rhodecode/tests/functional/test_compare.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/tests/functional/test_compare.py Mon Mar 11 17:09:43 2013 +0100 @@ -106,6 +106,7 @@ other_repo=repo2.repo_name, other_ref_type="branch", other_ref=rev1, + merge='1', )) response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1)) @@ -118,9 +119,9 @@ response.mustcontain("""r1:%s""" % (repo2.repo_name, cs1.raw_id, cs1.short_id)) response.mustcontain("""r2:%s""" % (repo2.repo_name, cs2.raw_id, cs2.short_id)) ## files - response.mustcontain("""file1""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) + response.mustcontain("""file1""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) #swap - response.mustcontain("""[swap]""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) + response.mustcontain("""[swap]""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self): self.log_user() @@ -160,6 +161,7 @@ other_repo=repo2.repo_name, other_ref_type="branch", other_ref=rev1, + merge='x', )) response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1)) response.mustcontain("""Showing 2 commits""") @@ -171,9 +173,9 @@ response.mustcontain("""r1:%s""" % (repo2.repo_name, cs1.raw_id, cs1.short_id)) response.mustcontain("""r2:%s""" % (repo2.repo_name, cs2.raw_id, cs2.short_id)) ## files - response.mustcontain("""file1""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) + response.mustcontain("""file1""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) #swap - response.mustcontain("""[swap]""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) + response.mustcontain("""[swap]""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) def test_compare_cherry_pick_changesets_from_bottom(self): @@ -215,20 +217,16 @@ cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n', message='commit6', vcs_type='hg', parent=cs4) - rev1 = 'tip' - rev2 = 'tip' - response = self.app.get(url(controller='compare', action='index', repo_name=repo2.repo_name, - org_ref_type="tag", - org_ref=rev1, + org_ref_type="rev", + org_ref=cs1.short_id, # parent of cs2, in repo2 other_repo=repo1.repo_name, - other_ref_type="tag", - other_ref=rev2, - rev_start=cs2.raw_id, - rev_end=cs4.raw_id, + other_ref_type="rev", + other_ref=cs4.short_id, + merge='True', )) - response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, cs2.short_id, repo1.repo_name, cs4.short_id)) + response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, cs1.short_id, repo1.repo_name, cs4.short_id)) response.mustcontain("""Showing 3 commits""") response.mustcontain("""1 file changed with 3 insertions and 0 deletions""") @@ -280,21 +278,16 @@ message='commit5', vcs_type='hg', parent=cs3) cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n', message='commit6', vcs_type='hg', parent=cs4) - rev1 = 'tip' - rev2 = 'tip' - response = self.app.get(url(controller='compare', action='index', - repo_name=repo2.repo_name, - org_ref_type="tag", - org_ref=rev1, - other_repo=repo1.repo_name, - other_ref_type="tag", - other_ref=rev2, - rev_start=cs3.raw_id, - rev_end=cs5.raw_id, + repo_name=repo1.repo_name, + org_ref_type="rev", + org_ref=cs2.short_id, # parent of cs3, not in repo2 + other_ref_type="rev", + other_ref=cs5.short_id, + merge='1', )) - response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, cs3.short_id, repo1.repo_name, cs5.short_id)) + response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, cs2.short_id, repo1.repo_name, cs5.short_id)) response.mustcontain("""Showing 3 commits""") response.mustcontain("""1 file changed with 3 insertions and 0 deletions""") @@ -330,6 +323,7 @@ other_ref_type="rev", other_ref=rev2, other_repo=HG_FORK, + merge='1', )) response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2)) ## outgoing changesets between those revisions @@ -339,9 +333,9 @@ response.mustcontain("""r6:%s""" % (HG_FORK, rev2)) ## files - response.mustcontain("""vcs/backends/hg.py""" % (HG_REPO, rev1, rev2, HG_FORK)) - response.mustcontain("""vcs/backends/__init__.py""" % (HG_REPO, rev1, rev2, HG_FORK)) - response.mustcontain("""vcs/backends/base.py""" % (HG_REPO, rev1, rev2, HG_FORK)) + response.mustcontain("""vcs/backends/hg.py""" % (HG_REPO, rev1, rev2, HG_FORK)) + response.mustcontain("""vcs/backends/__init__.py""" % (HG_REPO, rev1, rev2, HG_FORK)) + response.mustcontain("""vcs/backends/base.py""" % (HG_REPO, rev1, rev2, HG_FORK)) def test_org_repo_new_commits_after_forking_simple_diff(self): self.log_user() @@ -412,6 +406,7 @@ other_ref_type="branch", other_ref=rev2, other_repo=r1_name, + merge='1', )) response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) response.mustcontain('No files') @@ -436,6 +431,7 @@ other_ref_type="branch", other_ref=rev2, other_repo=r1_name, + merge='1', )) response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) diff -r c65b440540da -r 6e8027c2f49c rhodecode/tests/models/test_repos_groups.py --- a/rhodecode/tests/models/test_repos_groups.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/tests/models/test_repos_groups.py Mon Mar 11 17:09:43 2013 +0100 @@ -21,6 +21,33 @@ return gr +def _update_group(id_, group_name, desc='desc', parent_id=None): + form_data = _get_group_create_params(group_name=group_name, + group_desc=desc, + group_parent_id=parent_id) + gr = ReposGroupModel().update(id_, form_data) + return gr + + +def _make_repo(name, **kwargs): + form_data = _get_repo_create_params(repo_name=name, **kwargs) + cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN) + r = RepoModel().create(form_data, cur_user) + return r + + +def _update_repo(name, **kwargs): + form_data = _get_repo_create_params(**kwargs) + if not 'repo_name' in kwargs: + form_data['repo_name'] = name + if not 'perms_new' in kwargs: + form_data['perms_new'] = [] + if not 'perms_updates' in kwargs: + form_data['perms_updates'] = [] + r = RepoModel().update(name, **form_data) + return r + + class TestReposGroups(unittest.TestCase): def setUp(self): @@ -32,7 +59,7 @@ Session().commit() def tearDown(self): - print 'out' + Session.remove() def __check_path(self, *path): """ @@ -48,21 +75,9 @@ def __delete_group(self, id_): ReposGroupModel().delete(id_) - def __update_group(self, id_, path, desc='desc', parent_id=None): - form_data = dict( - group_name=path, - group_description=desc, - group_parent_id=parent_id, - perms_updates=[], - perms_new=[], - enable_locking=False, - recursive=False - ) - gr = ReposGroupModel().update(id_, form_data) - return gr - def test_create_group(self): g = _make_group('newGroup') + Session().commit() self.assertEqual(g.full_path, 'newGroup') self.assertTrue(self.__check_path('newGroup')) @@ -73,23 +88,27 @@ def test_same_subgroup(self): sg1 = _make_group('sub1', parent_id=self.g1.group_id) + Session().commit() self.assertEqual(sg1.parent_group, self.g1) self.assertEqual(sg1.full_path, 'test1/sub1') self.assertTrue(self.__check_path('test1', 'sub1')) ssg1 = _make_group('subsub1', parent_id=sg1.group_id) + Session().commit() self.assertEqual(ssg1.parent_group, sg1) self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1') self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1')) def test_remove_group(self): sg1 = _make_group('deleteme') + Session().commit() self.__delete_group(sg1.group_id) self.assertEqual(RepoGroup.get(sg1.group_id), None) self.assertFalse(self.__check_path('deteteme')) sg1 = _make_group('deleteme', parent_id=self.g1.group_id) + Session().commit() self.__delete_group(sg1.group_id) self.assertEqual(RepoGroup.get(sg1.group_id), None) @@ -97,24 +116,26 @@ def test_rename_single_group(self): sg1 = _make_group('initial') + Session().commit() - new_sg1 = self.__update_group(sg1.group_id, 'after') + new_sg1 = _update_group(sg1.group_id, 'after') self.assertTrue(self.__check_path('after')) self.assertEqual(RepoGroup.get_by_group_name('initial'), None) def test_update_group_parent(self): sg1 = _make_group('initial', parent_id=self.g1.group_id) + Session().commit() - new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id) + new_sg1 = _update_group(sg1.group_id, 'after', parent_id=self.g1.group_id) self.assertTrue(self.__check_path('test1', 'after')) self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None) - new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id) + new_sg1 = _update_group(sg1.group_id, 'after', parent_id=self.g3.group_id) self.assertTrue(self.__check_path('test3', 'after')) self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None) - new_sg1 = self.__update_group(sg1.group_id, 'hello') + new_sg1 = _update_group(sg1.group_id, 'hello') self.assertTrue(self.__check_path('hello')) self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1) @@ -123,23 +144,17 @@ g1 = _make_group('g1') g2 = _make_group('g2') - + Session().commit() # create new repo - form_data = _get_repo_create_params(repo_name='john') - cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN) - r = RepoModel().create(form_data, cur_user) - + r = _make_repo('john') + Session().commit() self.assertEqual(r.repo_name, 'john') - # put repo into group - form_data = form_data - form_data['repo_group'] = g1.group_id - form_data['perms_new'] = [] - form_data['perms_updates'] = [] - RepoModel().update(r.repo_name, **form_data) + r = _update_repo('john', repo_group=g1.group_id) + Session().commit() self.assertEqual(r.repo_name, 'g1/john') - self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id) + _update_group(g1.group_id, 'g1', parent_id=g2.group_id) self.assertTrue(self.__check_path('g2', 'g1')) # test repo @@ -155,7 +170,7 @@ self.assertEqual(g2.full_path, 't11/t22') self.assertTrue(self.__check_path('t11', 't22')) - g2 = self.__update_group(g2.group_id, 'g22', parent_id=None) + g2 = _update_group(g2.group_id, 'g22', parent_id=None) Session().commit() self.assertEqual(g2.group_name, 'g22') @@ -163,3 +178,65 @@ self.assertEqual(g2.full_path, 'g22') self.assertFalse(self.__check_path('t11', 't22')) self.assertTrue(self.__check_path('g22')) + + def test_rename_top_level_group_in_nested_setup(self): + g1 = _make_group('L1') + Session().commit() + g2 = _make_group('L2', parent_id=g1.group_id) + Session().commit() + g3 = _make_group('L3', parent_id=g2.group_id) + Session().commit() + + r = _make_repo('L1/L2/L3/L3_REPO', repo_group=g3.group_id) + Session().commit() + + ##rename L1 all groups should be now changed + _update_group(g1.group_id, 'L1_NEW') + Session().commit() + self.assertEqual(g1.full_path, 'L1_NEW') + self.assertEqual(g2.full_path, 'L1_NEW/L2') + self.assertEqual(g3.full_path, 'L1_NEW/L2/L3') + self.assertEqual(r.repo_name, 'L1_NEW/L2/L3/L3_REPO') + + def test_change_parent_of_top_level_group_in_nested_setup(self): + g1 = _make_group('R1') + Session().commit() + g2 = _make_group('R2', parent_id=g1.group_id) + Session().commit() + g3 = _make_group('R3', parent_id=g2.group_id) + Session().commit() + + g4 = _make_group('R1_NEW') + Session().commit() + + r = _make_repo('R1/R2/R3/R3_REPO', repo_group=g3.group_id) + Session().commit() + ##rename L1 all groups should be now changed + _update_group(g1.group_id, 'R1', parent_id=g4.group_id) + Session().commit() + self.assertEqual(g1.full_path, 'R1_NEW/R1') + self.assertEqual(g2.full_path, 'R1_NEW/R1/R2') + self.assertEqual(g3.full_path, 'R1_NEW/R1/R2/R3') + self.assertEqual(r.repo_name, 'R1_NEW/R1/R2/R3/R3_REPO') + + def test_change_parent_of_top_level_group_in_nested_setup_with_rename(self): + g1 = _make_group('X1') + Session().commit() + g2 = _make_group('X2', parent_id=g1.group_id) + Session().commit() + g3 = _make_group('X3', parent_id=g2.group_id) + Session().commit() + + g4 = _make_group('X1_NEW') + Session().commit() + + r = _make_repo('X1/X2/X3/X3_REPO', repo_group=g3.group_id) + Session().commit() + + ##rename L1 all groups should be now changed + _update_group(g1.group_id, 'X1_PRIM', parent_id=g4.group_id) + Session().commit() + self.assertEqual(g1.full_path, 'X1_NEW/X1_PRIM') + self.assertEqual(g2.full_path, 'X1_NEW/X1_PRIM/X2') + self.assertEqual(g3.full_path, 'X1_NEW/X1_PRIM/X2/X3') + self.assertEqual(r.repo_name, 'X1_NEW/X1_PRIM/X2/X3/X3_REPO') diff -r c65b440540da -r 6e8027c2f49c rhodecode/tests/scripts/test_vcs_operations.py --- a/rhodecode/tests/scripts/test_vcs_operations.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/tests/scripts/test_vcs_operations.py Mon Mar 11 17:09:43 2013 +0100 @@ -4,9 +4,10 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test suite for making push/pull operations. - Run using:: + Run using after doing paster serve test.ini:: + RC_WHOOSH_TEST_DISABLE=1 RC_NO_TMP_PATH=1 nosetests rhodecode/tests/scripts/test_vcs_operations.py - RC_WHOOSH_TEST_DISABLE=1 RC_NO_TMP_PATH=1 nosetests rhodecode/tests/scripts/test_vcs_operations.py + You must have git > 1.8.1 for tests to work fine :created_on: Dec 30, 2010 :author: marcink @@ -107,13 +108,14 @@ for i in xrange(3): cmd = """echo 'added_line%s' >> %s""" % (i, added_file) Command(cwd).execute(cmd) + author_str = 'Marcin Kuźminski ' if vcs == 'hg': cmd = """hg commit -m 'commited new %s' -u '%s' %s """ % ( - i, 'Marcin Kuźminski ', added_file + i, author_str, added_file ) elif vcs == 'git': cmd = """git commit -m 'commited new %s' --author '%s' %s """ % ( - i, 'Marcin Kuźminski ', added_file + i, author_str, added_file ) Command(cwd).execute(cmd) # PUSH it back @@ -129,7 +131,7 @@ if vcs == 'hg': stdout, stderr = Command(cwd).execute('hg push --verbose', clone_url) elif vcs == 'git': - stdout, stderr = Command(cwd).execute('git push', clone_url + " master") + stdout, stderr = Command(cwd).execute('git push --verbose', clone_url + " master") return stdout, stderr @@ -324,8 +326,7 @@ #pull fails since repo is locked clone_url = _construct_url(GIT_REPO) stdout, stderr = Command('/tmp').execute('git clone', clone_url) - msg = ("""423 Repository `%s` locked by user `%s`""" - % (GIT_REPO, TEST_USER_ADMIN_LOGIN)) + msg = ("""The requested URL returned error: 423""") assert msg in stderr def test_push_on_locked_repo_by_other_user_hg(self): @@ -455,7 +456,8 @@ Session().commit() clone_url = _construct_url(GIT_REPO) stdout, stderr = Command('/tmp').execute('git clone', clone_url) - assert 'error: The requested URL returned error: 403 Forbidden' in stderr + msg = ("""The requested URL returned error: 403""") + assert msg in stderr finally: #release IP restrictions for ip in UserIpMap.getAll(): diff -r c65b440540da -r 6e8027c2f49c rhodecode/tests/vcs/conf.py --- a/rhodecode/tests/vcs/conf.py Mon Mar 11 16:43:41 2013 +0100 +++ b/rhodecode/tests/vcs/conf.py Mon Mar 11 17:09:43 2013 +0100 @@ -7,6 +7,7 @@ import hashlib import tempfile import datetime +import shutil from rhodecode.tests import * from utils import get_normalized_path from os.path import join as jn @@ -58,5 +59,6 @@ PACKAGE_DIR = os.path.abspath(os.path.join( os.path.dirname(__file__), '..')) - -TEST_USER_CONFIG_FILE = jn(THIS, 'aconfig') +_dest = jn(TESTS_TMP_PATH,'aconfig') +shutil.copy(jn(THIS, 'aconfig'), _dest) +TEST_USER_CONFIG_FILE = _dest diff -r c65b440540da -r 6e8027c2f49c setup.py --- a/setup.py Mon Mar 11 16:43:41 2013 +0100 +++ b/setup.py Mon Mar 11 17:09:43 2013 +0100 @@ -62,10 +62,10 @@ requirements.append("argparse") if is_windows: - requirements.append("mercurial==2.5.1") + requirements.append("mercurial==2.5.2") else: requirements.append("py-bcrypt") - requirements.append("mercurial==2.5.1") + requirements.append("mercurial==2.5.2") dependency_links = [ diff -r c65b440540da -r 6e8027c2f49c test.ini --- a/test.ini Mon Mar 11 16:43:41 2013 +0100 +++ b/test.ini Mon Mar 11 17:09:43 2013 +0100 @@ -31,22 +31,24 @@ [server:main] ## PASTE ##nr of threads to spawn -threadpool_workers = 5 +#threadpool_workers = 5 ##max request before thread respawn -threadpool_max_requests = 10 +#threadpool_max_requests = 10 ##option to use threads of process -use_threadpool = true +#use_threadpool = true -use = egg:Paste#http +#use = egg:Paste#http #WAITRESS threads = 5 +#100GB +max_request_body_size = 107374182400 use = egg:waitress#main host = 127.0.0.1 -port = 8001 +port = 5000 [filter:proxy-prefix] # prefix middleware for rc @@ -63,6 +65,8 @@ lang = en cache_dir = /tmp/rc/data index_dir = /tmp/rc/index +# set this path to use archive download cache +#archive_cache_dir = /tmp/rhodecode_tarballcache app_instance_uuid = develop-test cut_off_limit = 256000 vcs_full_cache = False @@ -250,6 +254,87 @@ #beaker.session.cookie_expires = 3600 +############################ +## ERROR HANDLING SYSTEMS ## +############################ + +#################### +### [errormator] ### +#################### + +# Errormator is tailored to work with RhodeCode, see +# http://errormator.com for details how to obtain an account +# you must install python package `errormator_client` to make it work + +# errormator enabled +errormator = true + +errormator.server_url = https://api.errormator.com +errormator.api_key = YOUR_API_KEY + +# TWEAK AMOUNT OF INFO SENT HERE + +# enables 404 error logging (default False) +errormator.report_404 = false + +# time in seconds after request is considered being slow (default 1) +errormator.slow_request_time = 1 + +# record slow requests in application +# (needs to be enabled for slow datastore recording and time tracking) +errormator.slow_requests = true + +# enable hooking to application loggers +# errormator.logging = true + +# minimum log level for log capture +# errormator.logging.level = WARNING + +# send logs only from erroneous/slow requests +# (saves API quota for intensive logging) +errormator.logging_on_error = false + +# list of additonal keywords that should be grabbed from environ object +# can be string with comma separated list of words in lowercase +# (by default client will always send following info: +# 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that +# start with HTTP* this list be extended with additional keywords here +errormator.environ_keys_whitelist = + + +# list of keywords that should be blanked from request object +# can be string with comma separated list of words in lowercase +# (by default client will always blank keys that contain following words +# 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf' +# this list be extended with additional keywords set here +errormator.request_keys_blacklist = + + +# list of namespaces that should be ignores when gathering log entries +# can be string with comma separated list of namespaces +# (by default the client ignores own entries: errormator_client.client) +errormator.log_namespace_blacklist = + + +################ +### [sentry] ### +################ + +# sentry is a alternative open source error aggregator +# you must install python packages `sentry` and `raven` to enable + +sentry.dsn = YOUR_DNS +sentry.servers = +sentry.name = +sentry.key = +sentry.public_key = +sentry.secret_key = +sentry.project = +sentry.site = +sentry.include_paths = +sentry.exclude_paths = + + ################################################################################ ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## @@ -270,7 +355,6 @@ sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.sqlite #sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_test #sqlalchemy.db1.url = mysql://root:qwe@localhost/rhodecode_test - sqlalchemy.db1.echo = false sqlalchemy.db1.pool_recycle = 3600 sqlalchemy.db1.convert_unicode = true