# HG changeset patch # User Marcin Kuzminski # Date 2011-12-28 22:01:05 # Node ID a8c66e870bd007e9f9f9a554959f74febade4f38 # Parent 320dec24fb9a375f6694c4f117290e2b8c19d9f7 implements #285: Implemented non changeable urls for clone url, and web views diff --git a/docs/usage/general.rst b/docs/usage/general.rst --- a/docs/usage/general.rst +++ b/docs/usage/general.rst @@ -36,6 +36,31 @@ Compare view is also available from the one changeset +Non changeable repository urls +------------------------------ + +Due to complicated nature of repository grouping, often urls of repositories +can change. + +example:: + + #before + http://server.com/repo_name + # after insertion to test_group group the url will be + http://server.com/test_group/repo_name + +This can be an issue for build systems and any other hardcoded scripts, moving +repository to a group leads to a need for changing external systems. To +overcome this RhodeCode introduces a non changable replacement url. It's +simply an repository ID prefixed with `_` above urls are also accessible as:: + + http://server.com/_ + +Since ID are always the same moving the repository will not affect such url. +the _ syntax can be used anywhere in the system so urls with repo_name +for changelogs, files and other can be exchanged with _ syntax. + + Mailing ------- diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -8,7 +8,6 @@ refer to the routes manual at http://rou from __future__ import with_statement from routes import Mapper - # prefix for non repository related links needs to be prefixed with `/` ADMIN_PREFIX = '/_admin' @@ -30,8 +29,17 @@ def make_map(config): :param environ: :param match_dict: """ + from rhodecode.model.db import Repository + repo_name = match_dict.get('repo_name') - repo_name = match_dict.get('repo_name') + try: + by_id = repo_name.split('_') + if len(by_id) == 2 and by_id[1].isdigit(): + repo_name = Repository.get(by_id[1]).repo_name + match_dict['repo_name'] = repo_name + except: + pass + return is_valid_repo(repo_name, config['base_path']) def check_group(environ, match_dict): diff --git a/rhodecode/controllers/summary.py b/rhodecode/controllers/summary.py --- a/rhodecode/controllers/summary.py +++ b/rhodecode/controllers/summary.py @@ -92,13 +92,20 @@ class SummaryController(BaseRepoControll uri_tmpl = config.get('clone_uri', default_clone_uri) uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s') - uri = uri_tmpl % {'user': username, - 'pass': password, - 'scheme': parsed_url.scheme, - 'netloc': parsed_url.netloc, - 'path':parsed_url.path} + uri_dict = { + 'user': username, + 'pass': password, + 'scheme': parsed_url.scheme, + 'netloc': parsed_url.netloc, + 'path': parsed_url.path + } + uri = uri_tmpl % uri_dict + # generate another clone url by id + uri_dict.update({'path': '/_%s' % c.dbrepo.repo_id}) + uri_id = uri_tmpl % uri_dict c.clone_repo_url = uri + c.clone_repo_url_id = uri_id c.repo_tags = OrderedDict() for name, hash in c.rhodecode_repo.tags.items()[:10]: try: diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py --- a/rhodecode/lib/base.py +++ b/rhodecode/lib/base.py @@ -4,6 +4,7 @@ Provides the BaseController class for su """ import logging import time +import traceback from paste.auth.basic import AuthBasicAuthenticator @@ -26,8 +27,9 @@ from rhodecode.model.scm import ScmModel log = logging.getLogger(__name__) + class BaseVCSController(object): - + def __init__(self, application, config): self.application = application self.config = config @@ -36,15 +38,37 @@ class BaseVCSController(object): #authenticate this mercurial request using authfunc self.authenticate = AuthBasicAuthenticator('', authfunc) self.ipaddr = '0.0.0.0' - + + def _get_by_id(self, repo_name): + """ + Get's a special pattern _ from clone url and tries to replace it + with a repository_name for support of _ non changable urls + + :param repo_name: + """ + try: + data = repo_name.split('/') + if len(data) >= 2: + by_id = data[1].split('_') + if len(by_id) == 2 and by_id[1].isdigit(): + _repo_name = Repository.get(by_id[1]).repo_name + data[1] = _repo_name + except: + log.debug('Failed to extract repo_name from id %s' % ( + traceback.format_exc() + ) + ) + + return '/'.join(data) + def _invalidate_cache(self, repo_name): """ Set's cache for this repository for invalidation on next access - + :param repo_name: full repo name, also a cache key """ invalidate_cache('get_repo_cached_%s' % repo_name) - + def _check_permission(self, action, user, repo_name): """ Checks permissions using action (push/pull) user and repository @@ -68,8 +92,8 @@ class BaseVCSController(object): repo_name): return False - return True - + return True + def __call__(self, environ, start_response): start = time.time() try: diff --git a/rhodecode/lib/middleware/simplegit.py b/rhodecode/lib/middleware/simplegit.py --- a/rhodecode/lib/middleware/simplegit.py +++ b/rhodecode/lib/middleware/simplegit.py @@ -217,6 +217,7 @@ class SimpleGit(BaseVCSController): :param environ: environ where PATH_INFO is stored """ try: + environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO']) repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) if repo_name.endswith('/'): repo_name = repo_name.rstrip('/') diff --git a/rhodecode/lib/middleware/simplehg.py b/rhodecode/lib/middleware/simplehg.py --- a/rhodecode/lib/middleware/simplehg.py +++ b/rhodecode/lib/middleware/simplehg.py @@ -180,7 +180,6 @@ class SimpleHg(BaseVCSController): """ return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui) - def __get_repository(self, environ): """ Get's repository name out of PATH_INFO header @@ -188,6 +187,7 @@ class SimpleHg(BaseVCSController): :param environ: environ where PATH_INFO is stored """ try: + environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO']) repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) if repo_name.endswith('/'): repo_name = repo_name.rstrip('/') diff --git a/rhodecode/public/css/style.css b/rhodecode/public/css/style.css --- a/rhodecode/public/css/style.css +++ b/rhodecode/public/css/style.css @@ -1350,9 +1350,11 @@ tbody .yui-dt-editable { cursor: pointer padding: 7px 7px 6px; } -#content div.box div.form div.fields div.field div.input input#clone_url{ +#content div.box div.form div.fields div.field div.input input#clone_url, +#content div.box div.form div.fields div.field div.input input#clone_url_id +{ font-size: 16px; - padding: 2px 7px 2px; + padding: 2px; } #content div.box div.form div.fields div.field div.file input { @@ -3034,7 +3036,18 @@ div.gravatar img { .ui-btn.xsmall{ padding: 1px 2px 1px 1px; } - +.ui-btn.clone{ + padding: 5px 2px 6px 1px; + margin: 0px -4px 3px 0px; + -webkit-border-radius: 4px 0px 0px 4px !important; + -khtml-border-radius: 4px 0px 0px 4px !important; + -moz-border-radius: 4px 0px 0px 4px !important; + border-radius: 4px 0px 0px 4px !important; + width: 100px; + text-align: center; + float: left; + position: absolute; +} .ui-btn:focus { outline: none; } @@ -3100,7 +3113,8 @@ img, #header #header-inner #quick li a:hover span.normal, #header #header-inner #quick li ul li.last, #content div.box div.form div.fields div.field div.textarea table td table td a, -#clone_url +#clone_url, +#clone_url_id { border: none; } diff --git a/rhodecode/templates/summary/summary.html b/rhodecode/templates/summary/summary.html --- a/rhodecode/templates/summary/summary.html +++ b/rhodecode/templates/summary/summary.html @@ -74,7 +74,7 @@ %endif ##REPO NAME - ${h.repo_link(c.dbrepo.groups_and_repo)} + ${h.repo_link(c.dbrepo.groups_and_repo)} ##FORK %if c.dbrepo.fork: @@ -121,7 +121,10 @@
- + +
${_('Show by ID')}
+ +
@@ -240,6 +243,28 @@ YUE.on(clone_url,'click',function(e){ } }) +YUE.on('clone_by_name','click',function(e){ + // show url by name and hide name button + YUD.setStyle('clone_url','display',''); + YUD.setStyle('clone_by_name','display','none'); + + // hide url by id and show name button + YUD.setStyle('clone_by_id','display',''); + YUD.setStyle('clone_url_id','display','none'); + +}) +YUE.on('clone_by_id','click',function(e){ + + // show url by id and hide id button + YUD.setStyle('clone_by_id','display','none'); + YUD.setStyle('clone_url_id','display',''); + + // hide url by name and show id button + YUD.setStyle('clone_by_name','display',''); + YUD.setStyle('clone_url','display','none'); +}) + + var tmpl_links = {}; %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()): tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}'; diff --git a/rhodecode/tests/_test_concurency.py b/rhodecode/tests/_test_concurency.py --- a/rhodecode/tests/_test_concurency.py +++ b/rhodecode/tests/_test_concurency.py @@ -168,7 +168,6 @@ def test_clone_with_credentials(no_error except OSError: raise - clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ {'user':USER, 'pass':PASS, @@ -191,12 +190,17 @@ if __name__ == '__main__': seq = None import time + try: + METHOD = sys.argv[3] + except: + pass + if METHOD == 'pull': seq = _RandomNameSequence().next() test_clone_with_credentials(repo=sys.argv[1], method='clone', seq=seq) s = time.time() - for i in range(int(sys.argv[2])): + for i in range(1, int(sys.argv[2]) + 1): print 'take', i test_clone_with_credentials(repo=sys.argv[1], method=METHOD, seq=seq) diff --git a/rhodecode/tests/functional/test_summary.py b/rhodecode/tests/functional/test_summary.py --- a/rhodecode/tests/functional/test_summary.py +++ b/rhodecode/tests/functional/test_summary.py @@ -2,27 +2,27 @@ from rhodecode.tests import * from rhodecode.model.db import Repository from rhodecode.lib.utils import invalidate_cache + class TestSummaryController(TestController): def test_index(self): self.log_user() + ID = Repository.get_by_repo_name(HG_REPO).repo_id response = self.app.get(url(controller='summary', - action='index', repo_name=HG_REPO)) + action='index', + repo_name=HG_REPO)) #repo type - self.assertTrue("""Mercurial """ - in response.body) - self.assertTrue("""""") + response.mustcontain("""public """ - in response.body) + """repository" src="/images/icons/lock_open.png"/>""") #codes stats self._enable_stats() - invalidate_cache('get_repo_cached_%s' % HG_REPO) response = self.app.get(url(controller='summary', action='index', repo_name=HG_REPO)) @@ -37,8 +37,23 @@ class TestSummaryController(TestControll in response.body) # clone url... - self.assertTrue("""""" % HG_REPO in response.body) + response.mustcontain("""""") + response.mustcontain("""""") + def test_index_by_id(self): + self.log_user() + ID = Repository.get_by_repo_name(HG_REPO).repo_id + response = self.app.get(url(controller='summary', + action='index', + repo_name='_%s' % ID)) + + #repo type + response.mustcontain("""Mercurial """) + response.mustcontain("""public """) def _enable_stats(self): r = Repository.get_by_repo_name(HG_REPO)