@@ -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/_<ID>
Since ID are always the same moving the repository will not affect such url.
the _<ID> syntax can be used anywhere in the system so urls with repo_name
for changelogs, files and other can be exchanged with _<ID> syntax.
Mailing
-------
@@ -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')
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):
@@ -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,
'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]:
@@ -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 _<ID> from clone url and tries to replace it
with a repository_name for support of _<ID> non changable urls
:param repo_name:
data = repo_name.split('/')
if len(data) >= 2:
by_id = data[1].split('_')
_repo_name = Repository.get(by_id[1]).repo_name
data[1] = _repo_name
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
def __call__(self, environ, start_response):
start = time.time()
@@ -217,6 +217,7 @@ class SimpleGit(BaseVCSController):
:param environ: environ where PATH_INFO is stored
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('/')
@@ -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):
@@ -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;
@@ -74,7 +74,7 @@
%endif
##REPO NAME
<span class="repo_name">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
<span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
##FORK
%if c.dbrepo.fork:
@@ -121,7 +121,10 @@
<label>${_('Clone url')}:</label>
</div>
<div class="input ${summary(c.show_stats)}">
<input type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}" size="70"/>
<div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
<div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
<input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
<input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_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')}';
@@ -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
METHOD = sys.argv[3]
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,
@@ -2,27 +2,27 @@ from rhodecode.tests import *
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("""<img style="margin-bottom:2px" class="icon" """
response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
"""title="Mercurial repository" alt="Mercurial """
"""repository" src="/images/icons/hgicon.png"/>"""
in response.body)
"""repository" src="/images/icons/hgicon.png"/>""")
"""title="public repository" alt="public """
"""repository" src="/images/icons/lock_open.png"/>"""
"""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',
@@ -37,8 +37,23 @@ class TestSummaryController(TestControll
# clone url...
self.assertTrue("""<input type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO in response.body)
response.mustcontain("""<input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/vcs_test_hg"/>""")
response.mustcontain("""<input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_1"/>""")
def test_index_by_id(self):
repo_name='_%s' % ID))
def _enable_stats(self):
r = Repository.get_by_repo_name(HG_REPO)
Status change: