merge: merged default changes into new-ui
This commit is contained in:
commit
442096cbde
31 changed files with 698 additions and 208 deletions
41
docs/release-notes/release-notes-4.16.2.rst
Normal file
41
docs/release-notes/release-notes-4.16.2.rst
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
|RCE| 4.16.2 |RNS|
|
||||
------------------
|
||||
|
||||
Release Date
|
||||
^^^^^^^^^^^^
|
||||
|
||||
- 2019-04-02
|
||||
|
||||
|
||||
New Features
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
|
||||
|
||||
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
|
||||
|
||||
Performance
|
||||
^^^^^^^^^^^
|
||||
|
||||
|
||||
|
||||
Fixes
|
||||
^^^^^
|
||||
|
||||
- Integrations: fixed missing template variable for fork reference checks.
|
||||
- Permissions: fixed server error when showing permissions for user groups.
|
||||
- Pull requests: fixed a bug in removal of multiple reviewers at once.
|
||||
|
||||
|
||||
Upgrade notes
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
- Scheduled release addressing problems in 4.16.X releases.
|
||||
|
|
@ -9,6 +9,7 @@ Release Notes
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
release-notes-4.16.2.rst
|
||||
release-notes-4.16.1.rst
|
||||
release-notes-4.16.0.rst
|
||||
release-notes-4.15.2.rst
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
self: super: {
|
||||
"alembic" = super.buildPythonPackage {
|
||||
name = "alembic-1.0.8";
|
||||
name = "alembic-1.0.9";
|
||||
doCheck = false;
|
||||
propagatedBuildInputs = [
|
||||
self."sqlalchemy"
|
||||
|
|
@ -14,8 +14,8 @@ self: super: {
|
|||
self."python-dateutil"
|
||||
];
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/d6/bb/ec1e21f2e303689ad2170eb47fc67df9ad4199ade6759a99474c4d3535c8/alembic-1.0.8.tar.gz";
|
||||
sha256 = "1s34i1j0dsxbflxligwhnkf37a5hvcshsv8ibkcfdjf03ph42pah";
|
||||
url = "https://files.pythonhosted.org/packages/fc/42/8729e2491fa9b8eae160d1cbb429f61712bfc2d779816488c25cfdabf7b8/alembic-1.0.9.tar.gz";
|
||||
sha256 = "0a88rwp7fp0y8ykczj82ivr4ww1kiflcvb882lgfl9azm8csdfa0";
|
||||
};
|
||||
meta = {
|
||||
license = [ pkgs.lib.licenses.mit ];
|
||||
|
|
@ -310,11 +310,11 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"coverage" = super.buildPythonPackage {
|
||||
name = "coverage-4.5.1";
|
||||
name = "coverage-4.5.3";
|
||||
doCheck = false;
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/35/fe/e7df7289d717426093c68d156e0fd9117c8f4872b6588e8a8928a0f68424/coverage-4.5.1.tar.gz";
|
||||
sha256 = "1wbrzpxka3xd4nmmkc6q0ir343d91kymwsm8pbmwa0d2a7q4ir2n";
|
||||
url = "https://files.pythonhosted.org/packages/82/70/2280b5b29a0352519bb95ab0ef1ea942d40466ca71c53a2085bdeff7b0eb/coverage-4.5.3.tar.gz";
|
||||
sha256 = "02f6m073qdispn96rc616hg0rnmw1pgqzw3bgxwiwza4zf9hirlx";
|
||||
};
|
||||
meta = {
|
||||
license = [ pkgs.lib.licenses.asl20 ];
|
||||
|
|
@ -1118,14 +1118,14 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"pexpect" = super.buildPythonPackage {
|
||||
name = "pexpect-4.6.0";
|
||||
name = "pexpect-4.7.0";
|
||||
doCheck = false;
|
||||
propagatedBuildInputs = [
|
||||
self."ptyprocess"
|
||||
];
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/89/43/07d07654ee3e25235d8cea4164cdee0ec39d1fda8e9203156ebe403ffda4/pexpect-4.6.0.tar.gz";
|
||||
sha256 = "1fla85g47iaxxpjhp9vkxdnv4pgc7rplfy6ja491smrrk0jqi3ia";
|
||||
url = "https://files.pythonhosted.org/packages/1c/b1/362a0d4235496cb42c33d1d8732b5e2c607b0129ad5fdd76f5a583b9fcb3/pexpect-4.7.0.tar.gz";
|
||||
sha256 = "1sv2rri15zwhds85a4kamwh9pj49qcxv7m4miyr4jfpfwv81yb4y";
|
||||
};
|
||||
meta = {
|
||||
license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
|
||||
|
|
@ -1160,15 +1160,15 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"plaster-pastedeploy" = super.buildPythonPackage {
|
||||
name = "plaster-pastedeploy-0.6";
|
||||
name = "plaster-pastedeploy-0.7";
|
||||
doCheck = false;
|
||||
propagatedBuildInputs = [
|
||||
self."pastedeploy"
|
||||
self."plaster"
|
||||
];
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/3f/e7/6a6833158d2038ec40085433308a1e164fd1dac595513f6dd556d5669bb8/plaster_pastedeploy-0.6.tar.gz";
|
||||
sha256 = "1bkggk18f4z2bmsmxyxabvf62znvjwbivzh880419r3ap0616cf2";
|
||||
url = "https://files.pythonhosted.org/packages/99/69/2d3bc33091249266a1bd3cf24499e40ab31d54dffb4a7d76fe647950b98c/plaster_pastedeploy-0.7.tar.gz";
|
||||
sha256 = "1zg7gcsvc1kzay1ry5p699rg2qavfsxqwl17mqxzr0gzw6j9679r";
|
||||
};
|
||||
meta = {
|
||||
license = [ pkgs.lib.licenses.mit ];
|
||||
|
|
@ -1186,15 +1186,15 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"prompt-toolkit" = super.buildPythonPackage {
|
||||
name = "prompt-toolkit-1.0.15";
|
||||
name = "prompt-toolkit-1.0.16";
|
||||
doCheck = false;
|
||||
propagatedBuildInputs = [
|
||||
self."six"
|
||||
self."wcwidth"
|
||||
];
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz";
|
||||
sha256 = "05v9h5nydljwpj5nm8n804ms0glajwfy1zagrzqrg91wk3qqi1c5";
|
||||
url = "https://files.pythonhosted.org/packages/f1/03/bb36771dc9fa7553ac4bdc639a9ecdf6fda0ff4176faf940d97e3c16e41d/prompt_toolkit-1.0.16.tar.gz";
|
||||
sha256 = "1d65hm6nf0cbq0q0121m60zzy4s1fpg9fn761s1yxf08dridvkn1";
|
||||
};
|
||||
meta = {
|
||||
license = [ pkgs.lib.licenses.bsdOriginal ];
|
||||
|
|
@ -1212,11 +1212,11 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"psycopg2" = super.buildPythonPackage {
|
||||
name = "psycopg2-2.7.7";
|
||||
name = "psycopg2-2.8.2";
|
||||
doCheck = false;
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/63/54/c039eb0f46f9a9406b59a638415c2012ad7be9b4b97bfddb1f48c280df3a/psycopg2-2.7.7.tar.gz";
|
||||
sha256 = "0zjbabb4qjx9dm07imhf8y5a9rpa06d5zah80myiimgdi83nslpl";
|
||||
url = "https://files.pythonhosted.org/packages/23/7e/93c325482c328619870b6cd09370f6dbe1148283daca65115cd63642e60f/psycopg2-2.8.2.tar.gz";
|
||||
sha256 = "122mn2z3r0zgs8jyq682jjjr6vq5690qmxqf22gj6g41dwdz5b2w";
|
||||
};
|
||||
meta = {
|
||||
license = [ pkgs.lib.licenses.zpl21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
|
||||
|
|
@ -1373,7 +1373,7 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"pyramid" = super.buildPythonPackage {
|
||||
name = "pyramid-1.10.2";
|
||||
name = "pyramid-1.10.4";
|
||||
doCheck = false;
|
||||
propagatedBuildInputs = [
|
||||
self."hupper"
|
||||
|
|
@ -1388,8 +1388,8 @@ self: super: {
|
|||
self."repoze.lru"
|
||||
];
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/bc/0e/73de9b189ff00a963beeedaff90e27b134eedf2806279a1a3fe122fd65b6/pyramid-1.10.2.tar.gz";
|
||||
sha256 = "0gn6sw6ml67ir150ffivc0ad5hd448p43p9z2bkyp12jh2n9n2p7";
|
||||
url = "https://files.pythonhosted.org/packages/c2/43/1ae701c9c6bb3a434358e678a5e72c96e8aa55cf4cb1d2fa2041b5dd38b7/pyramid-1.10.4.tar.gz";
|
||||
sha256 = "0rkxs1ajycg2zh1c94xlmls56mx5m161sn8112skj0amza6cn36q";
|
||||
};
|
||||
meta = {
|
||||
license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
|
||||
|
|
@ -1931,11 +1931,11 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"setuptools" = super.buildPythonPackage {
|
||||
name = "setuptools-40.8.0";
|
||||
name = "setuptools-41.0.0";
|
||||
doCheck = false;
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/c2/f7/c7b501b783e5a74cf1768bc174ee4fb0a8a6ee5af6afa92274ff964703e0/setuptools-40.8.0.zip";
|
||||
sha256 = "0k9hifpgahnw2a26w3cr346iy733k6d3nwh3f7g9m13y6f8fqkkf";
|
||||
url = "https://files.pythonhosted.org/packages/ed/69/c805067de1feedbb98c53174b0f2df44cc05e0e9ee73bb85eebc59e508c6/setuptools-41.0.0.zip";
|
||||
sha256 = "1cfwy2g23qj3262ivj0b1182lgwz7bqqbka35rkqwypynra05lvr";
|
||||
};
|
||||
meta = {
|
||||
license = [ pkgs.lib.licenses.mit ];
|
||||
|
|
@ -2012,14 +2012,14 @@ self: super: {
|
|||
};
|
||||
};
|
||||
"supervisor" = super.buildPythonPackage {
|
||||
name = "supervisor-3.3.5";
|
||||
name = "supervisor-4.0.1";
|
||||
doCheck = false;
|
||||
propagatedBuildInputs = [
|
||||
self."meld3"
|
||||
];
|
||||
src = fetchurl {
|
||||
url = "https://files.pythonhosted.org/packages/ba/65/92575a8757ed576beaee59251f64a3287bde82bdc03964b89df9e1d29e1b/supervisor-3.3.5.tar.gz";
|
||||
sha256 = "1w3ahridzbc6rxfpbyx8lij6pjlcgf2ymzyg53llkjqxalp6sk8v";
|
||||
url = "https://files.pythonhosted.org/packages/96/ec/f8190beeb0c6d29a30aea10389c11d0164b6ff221931ee84093315ecde6a/supervisor-4.0.1.tar.gz";
|
||||
sha256 = "10l3z7v6v1fyv7m5zbazzxciwvli2n9a41pxi27p4kixgsfp0s1j";
|
||||
};
|
||||
meta = {
|
||||
license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ pyparsing==2.3.0
|
|||
pyramid-beaker==0.8
|
||||
pyramid-debugtoolbar==4.5.0
|
||||
pyramid-mako==1.0.2
|
||||
pyramid==1.10.2
|
||||
pyramid==1.10.4
|
||||
pyramid_mailer==0.15.1
|
||||
python-dateutil
|
||||
python-ldap==3.1.0
|
||||
|
|
@ -67,7 +67,7 @@ six==1.11.0
|
|||
sqlalchemy==1.1.18
|
||||
sshpubkeys==3.1.0
|
||||
subprocess32==3.5.3
|
||||
supervisor==3.3.5
|
||||
supervisor==4.0.1
|
||||
translationstring==1.3
|
||||
urllib3==1.24.1
|
||||
urlobject==2.4.3
|
||||
|
|
@ -87,7 +87,7 @@ zope.interface==4.6.0
|
|||
mysql-python==1.2.5
|
||||
pymysql==0.8.1
|
||||
pysqlite==2.8.3
|
||||
psycopg2==2.7.7
|
||||
psycopg2==2.8.2
|
||||
|
||||
# IPYTHON RENDERING
|
||||
# entrypoints backport, pypi version doesn't support egg installs
|
||||
|
|
@ -97,7 +97,7 @@ nbformat==4.4.0
|
|||
jupyter_client==5.0.0
|
||||
|
||||
## cli tools
|
||||
alembic==1.0.8
|
||||
alembic==1.0.9
|
||||
invoke==0.13.0
|
||||
bumpversion==0.5.3
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ gprof2dot==2017.9.19
|
|||
|
||||
mock==1.0.1
|
||||
cov-core==1.15.0
|
||||
coverage==4.5.1
|
||||
coverage==4.5.3
|
||||
|
||||
webtest==2.0.33
|
||||
beautifulsoup4==4.6.3
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ PYRAMID_SETTINGS = {}
|
|||
EXTENSIONS = {}
|
||||
|
||||
__version__ = ('.'.join((str(each) for each in VERSION[:3])))
|
||||
__dbversion__ = 95 # defines current db version for migrations
|
||||
__dbversion__ = 97 # defines current db version for migrations
|
||||
__platform__ = platform.system()
|
||||
__license__ = 'AGPLv3, and Commercial License'
|
||||
__author__ = 'RhodeCode GmbH'
|
||||
|
|
|
|||
|
|
@ -423,6 +423,10 @@ def admin_routes(config):
|
|||
name='repo_groups',
|
||||
pattern='/repo_groups')
|
||||
|
||||
config.add_route(
|
||||
name='repo_groups_data',
|
||||
pattern='/repo_groups_data')
|
||||
|
||||
config.add_route(
|
||||
name='repo_group_new',
|
||||
pattern='/repo_group/new')
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ import pytest
|
|||
|
||||
from rhodecode.apps._base import ADMIN_PREFIX
|
||||
from rhodecode.lib import helpers as h
|
||||
from rhodecode.model.db import Repository, UserRepoToPerm, User
|
||||
from rhodecode.model.db import Repository, UserRepoToPerm, User, RepoGroup
|
||||
from rhodecode.model.meta import Session
|
||||
from rhodecode.model.repo_group import RepoGroupModel
|
||||
from rhodecode.tests import (
|
||||
assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH, TestController)
|
||||
assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH)
|
||||
from rhodecode.tests.fixture import Fixture
|
||||
|
||||
fixture = Fixture()
|
||||
|
|
@ -38,6 +38,7 @@ def route_path(name, params=None, **kwargs):
|
|||
|
||||
base_url = {
|
||||
'repo_groups': ADMIN_PREFIX + '/repo_groups',
|
||||
'repo_groups_data': ADMIN_PREFIX + '/repo_groups_data',
|
||||
'repo_group_new': ADMIN_PREFIX + '/repo_group/new',
|
||||
'repo_group_create': ADMIN_PREFIX + '/repo_group/create',
|
||||
|
||||
|
|
@ -59,13 +60,30 @@ def _get_permission_for_user(user, repo):
|
|||
|
||||
@pytest.mark.usefixtures("app")
|
||||
class TestAdminRepositoryGroups(object):
|
||||
def test_show_repo_groups(self, autologin_user):
|
||||
response = self.app.get(route_path('repo_groups'))
|
||||
response.mustcontain('data: []')
|
||||
|
||||
def test_show_repo_groups_after_creating_group(self, autologin_user):
|
||||
def test_show_repo_groups(self, autologin_user):
|
||||
self.app.get(route_path('repo_groups'))
|
||||
|
||||
def test_show_repo_groups_data(self, autologin_user, xhr_header):
|
||||
response = self.app.get(route_path(
|
||||
'repo_groups_data'), extra_environ=xhr_header)
|
||||
|
||||
all_repo_groups = RepoGroup.query().count()
|
||||
assert response.json['recordsTotal'] == all_repo_groups
|
||||
|
||||
def test_show_repo_groups_data_filtered(self, autologin_user, xhr_header):
|
||||
response = self.app.get(route_path(
|
||||
'repo_groups_data', params={'search[value]': 'empty_search'}),
|
||||
extra_environ=xhr_header)
|
||||
|
||||
all_repo_groups = RepoGroup.query().count()
|
||||
assert response.json['recordsTotal'] == all_repo_groups
|
||||
assert response.json['recordsFiltered'] == 0
|
||||
|
||||
def test_show_repo_groups_after_creating_group(self, autologin_user, xhr_header):
|
||||
fixture.create_repo_group('test_repo_group')
|
||||
response = self.app.get(route_path('repo_groups'))
|
||||
response = self.app.get(route_path(
|
||||
'repo_groups_data'), extra_environ=xhr_header)
|
||||
response.mustcontain('"name_raw": "test_repo_group"')
|
||||
fixture.destroy_repo_group('test_repo_group')
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
# This program is dual-licensed. If you wish to learn more about the
|
||||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import formencode
|
||||
import formencode.htmlfill
|
||||
|
|
@ -30,16 +30,16 @@ from pyramid.response import Response
|
|||
from rhodecode import events
|
||||
from rhodecode.apps._base import BaseAppView, DataGridAppView
|
||||
|
||||
from rhodecode.lib.ext_json import json
|
||||
from rhodecode.lib.auth import (
|
||||
LoginRequired, CSRFRequired, NotAnonymous,
|
||||
HasPermissionAny, HasRepoGroupPermissionAny)
|
||||
from rhodecode.lib import helpers as h, audit_logger
|
||||
from rhodecode.lib.utils2 import safe_int, safe_unicode
|
||||
from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time
|
||||
from rhodecode.model.forms import RepoGroupForm
|
||||
from rhodecode.model.repo_group import RepoGroupModel
|
||||
from rhodecode.model.scm import RepoGroupList
|
||||
from rhodecode.model.db import Session, RepoGroup
|
||||
from rhodecode.model.db import (
|
||||
or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -88,23 +88,169 @@ class AdminRepoGroupsView(BaseAppView, DataGridAppView):
|
|||
return False
|
||||
return False
|
||||
|
||||
# permission check in data loading of
|
||||
# `repo_group_list_data` via RepoGroupList
|
||||
@LoginRequired()
|
||||
@NotAnonymous()
|
||||
# perms check inside
|
||||
@view_config(
|
||||
route_name='repo_groups', request_method='GET',
|
||||
renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
|
||||
def repo_group_list(self):
|
||||
c = self.load_default_context()
|
||||
|
||||
repo_group_list = RepoGroup.get_all_repo_groups()
|
||||
repo_group_list_acl = RepoGroupList(
|
||||
repo_group_list, perm_set=['group.admin'])
|
||||
repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
|
||||
repo_group_list=repo_group_list_acl, admin=True)
|
||||
c.data = json.dumps(repo_group_data)
|
||||
return self._get_template_context(c)
|
||||
|
||||
# permission check inside
|
||||
@LoginRequired()
|
||||
@NotAnonymous()
|
||||
@view_config(
|
||||
route_name='repo_groups_data', request_method='GET',
|
||||
renderer='json_ext', xhr=True)
|
||||
def repo_group_list_data(self):
|
||||
self.load_default_context()
|
||||
column_map = {
|
||||
'name_raw': 'group_name_hash',
|
||||
'desc': 'group_description',
|
||||
'last_change_raw': 'updated_on',
|
||||
'top_level_repos': 'repos_total',
|
||||
'owner': 'user_username',
|
||||
}
|
||||
draw, start, limit = self._extract_chunk(self.request)
|
||||
search_q, order_by, order_dir = self._extract_ordering(
|
||||
self.request, column_map=column_map)
|
||||
|
||||
_render = self.request.get_partial_renderer(
|
||||
'rhodecode:templates/data_table/_dt_elements.mako')
|
||||
c = _render.get_call_context()
|
||||
|
||||
def quick_menu(repo_group_name):
|
||||
return _render('quick_repo_group_menu', repo_group_name)
|
||||
|
||||
def repo_group_lnk(repo_group_name):
|
||||
return _render('repo_group_name', repo_group_name)
|
||||
|
||||
def last_change(last_change):
|
||||
if isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
|
||||
delta = datetime.timedelta(
|
||||
seconds=(datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
|
||||
last_change = last_change + delta
|
||||
return _render("last_change", last_change)
|
||||
|
||||
def desc(desc, personal):
|
||||
return _render(
|
||||
'repo_group_desc', desc, personal, c.visual.stylify_metatags)
|
||||
|
||||
def repo_group_actions(repo_group_id, repo_group_name, gr_count):
|
||||
return _render(
|
||||
'repo_group_actions', repo_group_id, repo_group_name, gr_count)
|
||||
|
||||
def user_profile(username):
|
||||
return _render('user_profile', username)
|
||||
|
||||
auth_repo_group_list = RepoGroupList(
|
||||
RepoGroup.query().all(), perm_set=['group.admin'])
|
||||
|
||||
allowed_ids = [-1]
|
||||
for repo_group in auth_repo_group_list:
|
||||
allowed_ids.append(repo_group.group_id)
|
||||
|
||||
repo_groups_data_total_count = RepoGroup.query()\
|
||||
.filter(or_(
|
||||
# generate multiple IN to fix limitation problems
|
||||
*in_filter_generator(RepoGroup.group_id, allowed_ids)
|
||||
)) \
|
||||
.count()
|
||||
|
||||
repo_groups_data_total_inactive_count = RepoGroup.query()\
|
||||
.filter(RepoGroup.group_id.in_(allowed_ids))\
|
||||
.count()
|
||||
|
||||
repo_count = count(Repository.repo_id)
|
||||
base_q = Session.query(
|
||||
RepoGroup.group_name,
|
||||
RepoGroup.group_name_hash,
|
||||
RepoGroup.group_description,
|
||||
RepoGroup.group_id,
|
||||
RepoGroup.personal,
|
||||
RepoGroup.updated_on,
|
||||
User,
|
||||
repo_count.label('repos_count')
|
||||
) \
|
||||
.filter(or_(
|
||||
# generate multiple IN to fix limitation problems
|
||||
*in_filter_generator(RepoGroup.group_id, allowed_ids)
|
||||
)) \
|
||||
.outerjoin(Repository) \
|
||||
.join(User, User.user_id == RepoGroup.user_id) \
|
||||
.group_by(RepoGroup, User)
|
||||
|
||||
if search_q:
|
||||
like_expression = u'%{}%'.format(safe_unicode(search_q))
|
||||
base_q = base_q.filter(or_(
|
||||
RepoGroup.group_name.ilike(like_expression),
|
||||
))
|
||||
|
||||
repo_groups_data_total_filtered_count = base_q.count()
|
||||
# the inactive isn't really used, but we still make it same as other data grids
|
||||
# which use inactive (users,user groups)
|
||||
repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count
|
||||
|
||||
sort_defined = False
|
||||
if order_by == 'group_name':
|
||||
sort_col = func.lower(RepoGroup.group_name)
|
||||
sort_defined = True
|
||||
elif order_by == 'repos_total':
|
||||
sort_col = repo_count
|
||||
sort_defined = True
|
||||
elif order_by == 'user_username':
|
||||
sort_col = User.username
|
||||
else:
|
||||
sort_col = getattr(RepoGroup, order_by, None)
|
||||
|
||||
if sort_defined or sort_col:
|
||||
if order_dir == 'asc':
|
||||
sort_col = sort_col.asc()
|
||||
else:
|
||||
sort_col = sort_col.desc()
|
||||
|
||||
base_q = base_q.order_by(sort_col)
|
||||
base_q = base_q.offset(start).limit(limit)
|
||||
|
||||
# authenticated access to user groups
|
||||
auth_repo_group_list = base_q.all()
|
||||
|
||||
repo_groups_data = []
|
||||
for repo_gr in auth_repo_group_list:
|
||||
row = {
|
||||
"menu": quick_menu(repo_gr.group_name),
|
||||
"name": repo_group_lnk(repo_gr.group_name),
|
||||
"name_raw": repo_gr.group_name,
|
||||
"last_change": last_change(repo_gr.updated_on),
|
||||
"last_change_raw": datetime_to_time(repo_gr.updated_on),
|
||||
|
||||
"last_changeset": "",
|
||||
"last_changeset_raw": "",
|
||||
|
||||
"desc": desc(repo_gr.group_description, repo_gr.personal),
|
||||
"owner": user_profile(repo_gr.User.username),
|
||||
"top_level_repos": repo_gr.repos_count,
|
||||
"action": repo_group_actions(
|
||||
repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count),
|
||||
|
||||
}
|
||||
|
||||
repo_groups_data.append(row)
|
||||
|
||||
data = ({
|
||||
'draw': draw,
|
||||
'data': repo_groups_data,
|
||||
'recordsTotal': repo_groups_data_total_count,
|
||||
'recordsTotalInactive': repo_groups_data_total_inactive_count,
|
||||
'recordsFiltered': repo_groups_data_total_filtered_count,
|
||||
'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count,
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
@LoginRequired()
|
||||
@NotAnonymous()
|
||||
# perm checks inside
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ from rhodecode.model.forms import UserGroupForm
|
|||
from rhodecode.model.permission import PermissionModel
|
||||
from rhodecode.model.scm import UserGroupList
|
||||
from rhodecode.model.db import (
|
||||
or_, count, User, UserGroup, UserGroupMember)
|
||||
or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
|
||||
from rhodecode.model.meta import Session
|
||||
from rhodecode.model.user_group import UserGroupModel
|
||||
from rhodecode.model.db import true
|
||||
|
|
@ -107,11 +107,17 @@ class AdminUserGroupsView(BaseAppView, DataGridAppView):
|
|||
allowed_ids.append(user_group.users_group_id)
|
||||
|
||||
user_groups_data_total_count = UserGroup.query()\
|
||||
.filter(UserGroup.users_group_id.in_(allowed_ids))\
|
||||
.filter(or_(
|
||||
# generate multiple IN to fix limitation problems
|
||||
*in_filter_generator(UserGroup.users_group_id, allowed_ids)
|
||||
))\
|
||||
.count()
|
||||
|
||||
user_groups_data_total_inactive_count = UserGroup.query()\
|
||||
.filter(UserGroup.users_group_id.in_(allowed_ids))\
|
||||
.filter(or_(
|
||||
# generate multiple IN to fix limitation problems
|
||||
*in_filter_generator(UserGroup.users_group_id, allowed_ids)
|
||||
))\
|
||||
.filter(UserGroup.users_group_active != true()).count()
|
||||
|
||||
member_count = count(UserGroupMember.user_id)
|
||||
|
|
@ -123,11 +129,14 @@ class AdminUserGroupsView(BaseAppView, DataGridAppView):
|
|||
UserGroup.group_data,
|
||||
User,
|
||||
member_count.label('member_count')
|
||||
) \
|
||||
.filter(UserGroup.users_group_id.in_(allowed_ids)) \
|
||||
.outerjoin(UserGroupMember) \
|
||||
.join(User, User.user_id == UserGroup.user_id) \
|
||||
.group_by(UserGroup, User)
|
||||
) \
|
||||
.filter(or_(
|
||||
# generate multiple IN to fix limitation problems
|
||||
*in_filter_generator(UserGroup.users_group_id, allowed_ids)
|
||||
)) \
|
||||
.outerjoin(UserGroupMember) \
|
||||
.join(User, User.user_id == UserGroup.user_id) \
|
||||
.group_by(UserGroup, User)
|
||||
|
||||
base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
|
||||
|
||||
|
|
@ -141,14 +150,16 @@ class AdminUserGroupsView(BaseAppView, DataGridAppView):
|
|||
user_groups_data_total_filtered_count = base_q.count()
|
||||
user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
|
||||
|
||||
sort_defined = False
|
||||
if order_by == 'members_total':
|
||||
sort_col = member_count
|
||||
sort_defined = True
|
||||
elif order_by == 'user_username':
|
||||
sort_col = User.username
|
||||
else:
|
||||
sort_col = getattr(UserGroup, order_by, None)
|
||||
|
||||
if isinstance(sort_col, count) or sort_col:
|
||||
if sort_defined or sort_col:
|
||||
if order_dir == 'asc':
|
||||
sort_col = sort_col.asc()
|
||||
else:
|
||||
|
|
@ -162,7 +173,7 @@ class AdminUserGroupsView(BaseAppView, DataGridAppView):
|
|||
|
||||
user_groups_data = []
|
||||
for user_gr in auth_user_group_list:
|
||||
user_groups_data.append({
|
||||
row = {
|
||||
"users_group_name": user_group_name(user_gr.users_group_name),
|
||||
"name_raw": h.escape(user_gr.users_group_name),
|
||||
"description": h.escape(user_gr.user_group_description),
|
||||
|
|
@ -175,7 +186,8 @@ class AdminUserGroupsView(BaseAppView, DataGridAppView):
|
|||
"owner": user_profile(user_gr.User.username),
|
||||
"action": user_group_actions(
|
||||
user_gr.users_group_id, user_gr.users_group_name)
|
||||
})
|
||||
}
|
||||
user_groups_data.append(row)
|
||||
|
||||
data = ({
|
||||
'draw': draw,
|
||||
|
|
|
|||
|
|
@ -67,12 +67,6 @@ class RepoSettingsView(RepoAppView):
|
|||
.filter(UserFollowing.user_id == c.default_user_id) \
|
||||
.filter(UserFollowing.follows_repository == self.db_repo).scalar()
|
||||
|
||||
c.has_origin_repo_read_perm = False
|
||||
if self.db_repo.fork:
|
||||
c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
|
||||
'repository.write', 'repository.read', 'repository.admin')(
|
||||
self.db_repo.fork.repo_name, 'repo set as fork page')
|
||||
|
||||
c.ver_info_dict = self.rhodecode_vcs_repo.get_hooks_info()
|
||||
|
||||
return self._get_template_context(c)
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import logging
|
||||
import tempfile
|
||||
import textwrap
|
||||
|
||||
import collections
|
||||
from .base import VcsServer
|
||||
from rhodecode.model.db import RhodeCodeUi
|
||||
from rhodecode.model.settings import VcsSettingsModel
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -37,62 +38,46 @@ class MercurialTunnelWrapper(object):
|
|||
self.server = server
|
||||
self.stdin = sys.stdin
|
||||
self.stdout = sys.stdout
|
||||
self.svn_conf_fd, self.svn_conf_path = tempfile.mkstemp()
|
||||
self.hooks_env_fd, self.hooks_env_path = tempfile.mkstemp()
|
||||
self.hooks_env_fd, self.hooks_env_path = tempfile.mkstemp(prefix='hgrc_rhodecode_')
|
||||
|
||||
def create_hooks_env(self):
|
||||
repo_name = self.server.repo_name
|
||||
hg_flags = self.config_to_hgrc(repo_name)
|
||||
|
||||
content = textwrap.dedent(
|
||||
'''
|
||||
# SSH hooks version=1.0.0
|
||||
[hooks]
|
||||
pretxnchangegroup.ssh_auth=python:vcsserver.hooks.pre_push_ssh_auth
|
||||
pretxnchangegroup.ssh=python:vcsserver.hooks.pre_push_ssh
|
||||
changegroup.ssh=python:vcsserver.hooks.post_push_ssh
|
||||
|
||||
preoutgoing.ssh=python:vcsserver.hooks.pre_pull_ssh
|
||||
outgoing.ssh=python:vcsserver.hooks.post_pull_ssh
|
||||
|
||||
# RhodeCode SSH hooks version=2.0.0
|
||||
{custom}
|
||||
'''
|
||||
)
|
||||
).format(custom='\n'.join(hg_flags))
|
||||
|
||||
root = self.server.get_root_store()
|
||||
hgrc_custom = os.path.join(root, repo_name, '.hg', 'hgrc_rhodecode')
|
||||
hgrc_main = os.path.join(root, repo_name, '.hg', 'hgrc')
|
||||
|
||||
# cleanup custom hgrc file
|
||||
if os.path.isfile(hgrc_custom):
|
||||
with open(hgrc_custom, 'wb') as f:
|
||||
f.write('')
|
||||
log.debug('Cleanup custom hgrc file under %s', hgrc_custom)
|
||||
|
||||
# write temp
|
||||
with os.fdopen(self.hooks_env_fd, 'w') as hooks_env_file:
|
||||
hooks_env_file.write(content)
|
||||
root = self.server.get_root_store()
|
||||
|
||||
hgrc_custom = os.path.join(
|
||||
root, self.server.repo_name, '.hg', 'hgrc_rhodecode')
|
||||
log.debug('Wrote custom hgrc file under %s', hgrc_custom)
|
||||
shutil.move(
|
||||
self.hooks_env_path, hgrc_custom)
|
||||
return self.hooks_env_path
|
||||
|
||||
hgrc_main = os.path.join(
|
||||
root, self.server.repo_name, '.hg', 'hgrc')
|
||||
include_marker = '%include hgrc_rhodecode'
|
||||
def remove_configs(self):
|
||||
os.remove(self.hooks_env_path)
|
||||
|
||||
if not os.path.isfile(hgrc_main):
|
||||
os.mknod(hgrc_main)
|
||||
|
||||
with open(hgrc_main, 'rb') as f:
|
||||
data = f.read()
|
||||
has_marker = include_marker in data
|
||||
|
||||
if not has_marker:
|
||||
log.debug('Adding include marker for hooks')
|
||||
with open(hgrc_main, 'wa') as f:
|
||||
f.write(textwrap.dedent('''
|
||||
# added by RhodeCode
|
||||
{}
|
||||
'''.format(include_marker)))
|
||||
|
||||
def command(self):
|
||||
def command(self, hgrc_path):
|
||||
root = self.server.get_root_store()
|
||||
|
||||
command = (
|
||||
"cd {root}; {hg_path} -R {root}{repo_name} "
|
||||
"cd {root}; HGRCPATH={hgrc} {hg_path} -R {root}{repo_name} "
|
||||
"serve --stdio".format(
|
||||
root=root, hg_path=self.server.hg_path,
|
||||
repo_name=self.server.repo_name))
|
||||
repo_name=self.server.repo_name, hgrc=hgrc_path))
|
||||
log.debug("Final CMD: %s", command)
|
||||
return command
|
||||
|
||||
|
|
@ -102,22 +87,61 @@ class MercurialTunnelWrapper(object):
|
|||
action = '?'
|
||||
# permissions are check via `pre_push_ssh_auth` hook
|
||||
self.server.update_environment(action=action, extras=extras)
|
||||
self.create_hooks_env()
|
||||
return os.system(self.command())
|
||||
custom_hgrc_file = self.create_hooks_env()
|
||||
|
||||
try:
|
||||
return os.system(self.command(custom_hgrc_file))
|
||||
finally:
|
||||
self.remove_configs()
|
||||
|
||||
|
||||
class MercurialServer(VcsServer):
|
||||
backend = 'hg'
|
||||
cli_flags = ['phases', 'largefiles', 'extensions', 'experimental', 'hooks']
|
||||
|
||||
def __init__(self, store, ini_path, repo_name,
|
||||
user, user_permissions, config, env):
|
||||
super(MercurialServer, self).\
|
||||
__init__(user, user_permissions, config, env)
|
||||
def __init__(self, store, ini_path, repo_name, user, user_permissions, config, env):
|
||||
super(MercurialServer, self).__init__(user, user_permissions, config, env)
|
||||
|
||||
self.store = store
|
||||
self.ini_path = ini_path
|
||||
self.repo_name = repo_name
|
||||
self._path = self.hg_path = config.get(
|
||||
'app:main', 'ssh.executable.hg')
|
||||
|
||||
self._path = self.hg_path = config.get('app:main', 'ssh.executable.hg')
|
||||
self.tunnel = MercurialTunnelWrapper(server=self)
|
||||
|
||||
def config_to_hgrc(self, repo_name):
|
||||
ui_sections = collections.defaultdict(list)
|
||||
ui = VcsSettingsModel(repo=repo_name).get_ui_settings(section=None, key=None)
|
||||
|
||||
# write default hooks
|
||||
default_hooks = [
|
||||
('pretxnchangegroup.ssh_auth', 'python:vcsserver.hooks.pre_push_ssh_auth'),
|
||||
('pretxnchangegroup.ssh', 'python:vcsserver.hooks.pre_push_ssh'),
|
||||
('changegroup.ssh', 'python:vcsserver.hooks.post_push_ssh'),
|
||||
|
||||
('preoutgoing.ssh', 'python:vcsserver.hooks.pre_pull_ssh'),
|
||||
('outgoing.ssh', 'python:vcsserver.hooks.post_pull_ssh'),
|
||||
]
|
||||
|
||||
for k, v in default_hooks:
|
||||
ui_sections['hooks'].append((k, v))
|
||||
|
||||
for entry in ui:
|
||||
if not entry.active:
|
||||
continue
|
||||
sec = entry.section
|
||||
key = entry.key
|
||||
|
||||
if sec in self.cli_flags:
|
||||
# we want only custom hooks, so we skip builtins
|
||||
if sec == 'hooks' and key in RhodeCodeUi.HOOKS_BUILTIN:
|
||||
continue
|
||||
|
||||
ui_sections[sec].append([key, entry.value])
|
||||
|
||||
flags = []
|
||||
for _sec, key_val in ui_sections.items():
|
||||
flags.append(' ')
|
||||
flags.append('[{}]'.format(_sec))
|
||||
for key, val in key_val:
|
||||
flags.append('{}= {}'.format(key, val))
|
||||
return flags
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import os
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
|
|
@ -68,14 +69,16 @@ def hg_server(app):
|
|||
|
||||
class TestMercurialServer(object):
|
||||
|
||||
def test_command(self, hg_server):
|
||||
def test_command(self, hg_server, tmpdir):
|
||||
server = hg_server.create()
|
||||
custom_hgrc = os.path.join(str(tmpdir), 'hgrc')
|
||||
expected_command = (
|
||||
'cd {root}; {hg_path} -R {root}{repo_name} serve --stdio'.format(
|
||||
root=hg_server.root, hg_path=hg_server.hg_path,
|
||||
'cd {root}; HGRCPATH={custom_hgrc} {hg_path} -R {root}{repo_name} serve --stdio'.format(
|
||||
root=hg_server.root, custom_hgrc=custom_hgrc, hg_path=hg_server.hg_path,
|
||||
repo_name=hg_server.repo_name)
|
||||
)
|
||||
assert expected_command == server.tunnel.command()
|
||||
server_command = server.tunnel.command(custom_hgrc)
|
||||
assert expected_command == server_command
|
||||
|
||||
@pytest.mark.parametrize('permissions, action, code', [
|
||||
({}, 'pull', -2),
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ class IntegrationSettingsViewBase(BaseAppView):
|
|||
_ = self.request.translate
|
||||
c = super(IntegrationSettingsViewBase, self)._get_local_tmpl_context(
|
||||
include_app_defaults=include_app_defaults)
|
||||
|
||||
c.active = 'integrations'
|
||||
|
||||
return c
|
||||
|
|
@ -404,6 +403,11 @@ class RepoIntegrationsView(IntegrationSettingsViewBase):
|
|||
c.repo_name = self.db_repo.repo_name
|
||||
c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
|
||||
|
||||
c.has_origin_repo_read_perm = False
|
||||
if self.db_repo.fork:
|
||||
c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
|
||||
'repository.write', 'repository.read', 'repository.admin')(
|
||||
self.db_repo.fork.repo_name, 'summary fork link')
|
||||
return c
|
||||
|
||||
@LoginRequired()
|
||||
|
|
|
|||
|
|
@ -375,6 +375,27 @@ class DbManage(object):
|
|||
hgevolve.ui_active = False
|
||||
self.sa.add(hgevolve)
|
||||
|
||||
hgevolve = RhodeCodeUi()
|
||||
hgevolve.ui_section = 'experimental'
|
||||
hgevolve.ui_key = 'evolution'
|
||||
hgevolve.ui_value = ''
|
||||
hgevolve.ui_active = False
|
||||
self.sa.add(hgevolve)
|
||||
|
||||
hgevolve = RhodeCodeUi()
|
||||
hgevolve.ui_section = 'experimental'
|
||||
hgevolve.ui_key = 'evolution.exchange'
|
||||
hgevolve.ui_value = ''
|
||||
hgevolve.ui_active = False
|
||||
self.sa.add(hgevolve)
|
||||
|
||||
hgevolve = RhodeCodeUi()
|
||||
hgevolve.ui_section = 'extensions'
|
||||
hgevolve.ui_key = 'topic'
|
||||
hgevolve.ui_value = ''
|
||||
hgevolve.ui_active = False
|
||||
self.sa.add(hgevolve)
|
||||
|
||||
# enable hggit disabled by default
|
||||
hggit = RhodeCodeUi()
|
||||
hggit.ui_section = 'extensions'
|
||||
|
|
|
|||
54
rhodecode/lib/dbmigrate/versions/096_version_4_17_0.py
Normal file
54
rhodecode/lib/dbmigrate/versions/096_version_4_17_0.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from alembic.migration import MigrationContext
|
||||
from alembic.operations import Operations
|
||||
from sqlalchemy import String, Column
|
||||
from sqlalchemy.sql import text
|
||||
|
||||
from rhodecode.lib.dbmigrate.versions import _reset_base
|
||||
from rhodecode.model import meta, init_model_encryption
|
||||
from rhodecode.model.db import RepoGroup
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
"""
|
||||
Upgrade operations go here.
|
||||
Don't create your own engine; bind migrate_engine to your metadata
|
||||
"""
|
||||
_reset_base(migrate_engine)
|
||||
from rhodecode.lib.dbmigrate.schema import db_4_16_0_2
|
||||
|
||||
init_model_encryption(db_4_16_0_2)
|
||||
|
||||
context = MigrationContext.configure(migrate_engine.connect())
|
||||
op = Operations(context)
|
||||
|
||||
repo_group = db_4_16_0_2.RepoGroup.__table__
|
||||
|
||||
with op.batch_alter_table(repo_group.name) as batch_op:
|
||||
batch_op.add_column(
|
||||
Column("repo_group_name_hash", String(1024), nullable=True, unique=False))
|
||||
|
||||
_generate_repo_group_name_hashes(db_4_16_0_2, op, meta.Session)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
pass
|
||||
|
||||
|
||||
def _generate_repo_group_name_hashes(models, op, session):
|
||||
repo_groups = models.RepoGroup.get_all()
|
||||
for repo_group in repo_groups:
|
||||
print(repo_group.group_name)
|
||||
hash_ = RepoGroup.hash_repo_group_name(repo_group.group_name)
|
||||
params = {'hash': hash_, 'id': repo_group.group_id}
|
||||
query = text(
|
||||
'UPDATE groups SET repo_group_name_hash = :hash'
|
||||
' WHERE group_id = :id').bindparams(**params)
|
||||
op.execute(query)
|
||||
session().commit()
|
||||
39
rhodecode/lib/dbmigrate/versions/097_version_4_17_0.py
Normal file
39
rhodecode/lib/dbmigrate/versions/097_version_4_17_0.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from alembic.migration import MigrationContext
|
||||
from alembic.operations import Operations
|
||||
|
||||
from rhodecode.lib.dbmigrate.versions import _reset_base
|
||||
from rhodecode.model import init_model_encryption
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
"""
|
||||
Upgrade operations go here.
|
||||
Don't create your own engine; bind migrate_engine to your metadata
|
||||
"""
|
||||
_reset_base(migrate_engine)
|
||||
from rhodecode.lib.dbmigrate.schema import db_4_16_0_2
|
||||
|
||||
init_model_encryption(db_4_16_0_2)
|
||||
|
||||
context = MigrationContext.configure(migrate_engine.connect())
|
||||
op = Operations(context)
|
||||
|
||||
repo_group = db_4_16_0_2.RepoGroup.__table__
|
||||
|
||||
with op.batch_alter_table(repo_group.name) as batch_op:
|
||||
batch_op.alter_column("repo_group_name_hash", nullable=False)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
pass
|
||||
|
||||
|
||||
def _generate_repo_group_name_hashes(models, op, session):
|
||||
pass
|
||||
|
|
@ -80,7 +80,7 @@ def trigger_log_create_pull_request_hook(username, repo_name, repo_alias,
|
|||
extras = _get_rc_scm_extras(username, repo_name, repo_alias,
|
||||
'create_pull_request')
|
||||
events.trigger(events.PullRequestCreateEvent(pull_request))
|
||||
extras.update(pull_request.get_api_data())
|
||||
extras.update(pull_request.get_api_data(with_merge_state=False))
|
||||
hooks_base.log_create_pull_request(**extras)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -371,7 +371,8 @@ def config_data_from_db(clear_session=True, repo=None):
|
|||
config.append((
|
||||
safe_str(setting.section), safe_str(setting.key), False))
|
||||
log.debug(
|
||||
'settings ui from db: %s',
|
||||
'settings ui from db@repo[%s]: %s',
|
||||
repo,
|
||||
','.join(map(lambda s: '[{}] {}={}'.format(*s), ui_data)))
|
||||
if clear_session:
|
||||
meta.Session.remove()
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ class MergeResponse(object):
|
|||
u'This pull request cannot be merged because the source contains '
|
||||
u'more branches than the target.'),
|
||||
MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS: lazy_ugettext(
|
||||
u'This pull request cannot be merged because the target '
|
||||
u'This pull request cannot be merged because the target `{target_ref.name}` '
|
||||
u'has multiple heads: `{heads}`.'),
|
||||
MergeFailureReason.TARGET_IS_LOCKED: lazy_ugettext(
|
||||
u'This pull request cannot be merged because the target repository is '
|
||||
|
|
@ -309,6 +309,9 @@ class BaseRepository(object):
|
|||
def _remote(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _heads(self, branch=None):
|
||||
return []
|
||||
|
||||
@LazyProperty
|
||||
def EMPTY_COMMIT(self):
|
||||
return EmptyCommit(self.EMPTY_COMMIT_ID)
|
||||
|
|
|
|||
|
|
@ -715,11 +715,16 @@ class MercurialRepository(BaseRepository):
|
|||
|
||||
try:
|
||||
if target_ref.type == 'branch' and len(self._heads(target_ref.name)) != 1:
|
||||
heads = ','.join(self._heads(target_ref.name))
|
||||
heads = '\n,'.join(self._heads(target_ref.name))
|
||||
metadata = {
|
||||
'target_ref': target_ref,
|
||||
'source_ref': source_ref,
|
||||
'heads': heads
|
||||
}
|
||||
return MergeResponse(
|
||||
False, False, None,
|
||||
MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS,
|
||||
metadata={'heads': heads})
|
||||
metadata=metadata)
|
||||
except CommitDoesNotExistError:
|
||||
log.exception('Failure when looking up branch heads on hg target')
|
||||
return MergeResponse(
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Database Models for RhodeCode Enterprise
|
|||
import re
|
||||
import os
|
||||
import time
|
||||
import string
|
||||
import hashlib
|
||||
import logging
|
||||
import datetime
|
||||
|
|
@ -50,6 +51,7 @@ from sqlalchemy.dialects.mysql import LONGTEXT
|
|||
from zope.cachedescriptors.property import Lazy as LazyProperty
|
||||
from pyramid import compat
|
||||
from pyramid.threadlocal import get_current_request
|
||||
from webhelpers.text import collapse, remove_formatting
|
||||
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.lib.vcs import get_vcs_instance
|
||||
|
|
@ -409,6 +411,15 @@ class RhodeCodeUi(Base, BaseModel):
|
|||
HOOK_PUSH = 'changegroup.push_logger'
|
||||
HOOK_PUSH_KEY = 'pushkey.key_push'
|
||||
|
||||
HOOKS_BUILTIN = [
|
||||
HOOK_PRE_PULL,
|
||||
HOOK_PULL,
|
||||
HOOK_PRE_PUSH,
|
||||
HOOK_PRETX_PUSH,
|
||||
HOOK_PUSH,
|
||||
HOOK_PUSH_KEY,
|
||||
]
|
||||
|
||||
# TODO: johbo: Unify way how hooks are configured for git and hg,
|
||||
# git part is currently hardcoded.
|
||||
|
||||
|
|
@ -2469,7 +2480,8 @@ class RepoGroup(Base, BaseModel):
|
|||
CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups
|
||||
|
||||
group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
|
||||
group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
|
||||
_group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
|
||||
group_name_hash = Column("repo_group_name_hash", String(1024), nullable=False, unique=False)
|
||||
group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
|
||||
group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
|
||||
enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
|
||||
|
|
@ -2492,6 +2504,15 @@ class RepoGroup(Base, BaseModel):
|
|||
return u"<%s('id:%s:%s')>" % (
|
||||
self.__class__.__name__, self.group_id, self.group_name)
|
||||
|
||||
@hybrid_property
|
||||
def group_name(self):
|
||||
return self._group_name
|
||||
|
||||
@group_name.setter
|
||||
def group_name(self, value):
|
||||
self._group_name = value
|
||||
self.group_name_hash = self.hash_repo_group_name(value)
|
||||
|
||||
@validates('group_parent_id')
|
||||
def validate_group_parent_id(self, key, val):
|
||||
"""
|
||||
|
|
@ -2507,6 +2528,18 @@ class RepoGroup(Base, BaseModel):
|
|||
from rhodecode.lib import helpers as h
|
||||
return h.escape(self.group_description)
|
||||
|
||||
@classmethod
|
||||
def hash_repo_group_name(cls, repo_group_name):
|
||||
val = remove_formatting(repo_group_name)
|
||||
val = safe_str(val).lower()
|
||||
chars = []
|
||||
for c in val:
|
||||
if c not in string.ascii_letters:
|
||||
c = str(ord(c))
|
||||
chars.append(c)
|
||||
|
||||
return ''.join(chars)
|
||||
|
||||
@classmethod
|
||||
def _generate_choice(cls, repo_group):
|
||||
from webhelpers.html import literal as _literal
|
||||
|
|
@ -2770,6 +2803,13 @@ class RepoGroup(Base, BaseModel):
|
|||
}
|
||||
return data
|
||||
|
||||
def get_dict(self):
|
||||
# Since we transformed `group_name` to a hybrid property, we need to
|
||||
# keep compatibility with the code which uses `group_name` field.
|
||||
result = super(RepoGroup, self).get_dict()
|
||||
result['group_name'] = result.pop('_group_name', None)
|
||||
return result
|
||||
|
||||
|
||||
class Permission(Base, BaseModel):
|
||||
__tablename__ = 'permissions'
|
||||
|
|
|
|||
|
|
@ -1020,6 +1020,9 @@ class PullRequestModel(BaseModel):
|
|||
log.debug("Adding %s reviewers", ids_to_add)
|
||||
log.debug("Removing %s reviewers", ids_to_remove)
|
||||
changed = False
|
||||
added_audit_reviewers = []
|
||||
removed_audit_reviewers = []
|
||||
|
||||
for uid in ids_to_add:
|
||||
changed = True
|
||||
_usr = self._get_user(uid)
|
||||
|
|
@ -1030,29 +1033,37 @@ class PullRequestModel(BaseModel):
|
|||
# NOTE(marcink): mandatory shouldn't be changed now
|
||||
# reviewer.mandatory = reviewers[uid]['reasons']
|
||||
Session().add(reviewer)
|
||||
self._log_audit_action(
|
||||
'repo.pull_request.reviewer.add', {'data': reviewer.get_dict()},
|
||||
user, pull_request)
|
||||
added_audit_reviewers.append(reviewer.get_dict())
|
||||
|
||||
for uid in ids_to_remove:
|
||||
changed = True
|
||||
# NOTE(marcink): we fetch "ALL" reviewers using .all(). This is an edge case
|
||||
# that prevents and fixes cases that we added the same reviewer twice.
|
||||
# this CAN happen due to the lack of DB checks
|
||||
reviewers = PullRequestReviewers.query()\
|
||||
.filter(PullRequestReviewers.user_id == uid,
|
||||
PullRequestReviewers.pull_request == pull_request)\
|
||||
.all()
|
||||
# use .all() in case we accidentally added the same person twice
|
||||
# this CAN happen due to the lack of DB checks
|
||||
|
||||
for obj in reviewers:
|
||||
old_data = obj.get_dict()
|
||||
added_audit_reviewers.append(obj.get_dict())
|
||||
Session().delete(obj)
|
||||
self._log_audit_action(
|
||||
'repo.pull_request.reviewer.delete',
|
||||
{'old_data': old_data}, user, pull_request)
|
||||
|
||||
if changed:
|
||||
Session().expire_all()
|
||||
pull_request.updated_on = datetime.datetime.now()
|
||||
Session().add(pull_request)
|
||||
|
||||
# finally store audit logs
|
||||
for user_data in added_audit_reviewers:
|
||||
self._log_audit_action(
|
||||
'repo.pull_request.reviewer.add', {'data': user_data},
|
||||
user, pull_request)
|
||||
for user_data in removed_audit_reviewers:
|
||||
self._log_audit_action(
|
||||
'repo.pull_request.reviewer.delete', {'old_data': user_data},
|
||||
user, pull_request)
|
||||
|
||||
self.notify_reviewers(pull_request, ids_to_add)
|
||||
return ids_to_add, ids_to_remove
|
||||
|
||||
|
|
@ -1306,8 +1317,16 @@ class PullRequestModel(BaseModel):
|
|||
possible = pull_request.last_merge_status == MergeFailureReason.NONE
|
||||
metadata = {
|
||||
'target_ref': pull_request.target_ref_parts,
|
||||
'source_ref': pull_request.source_ref_parts
|
||||
'source_ref': pull_request.source_ref_parts,
|
||||
}
|
||||
if not possible and target_ref.type == 'branch':
|
||||
# NOTE(marcink): case for mercurial multiple heads on branch
|
||||
heads = target_vcs._heads(target_ref.name)
|
||||
if len(heads) != 1:
|
||||
heads = '\n,'.join(target_vcs._heads(target_ref.name))
|
||||
metadata.update({
|
||||
'heads': heads
|
||||
})
|
||||
merge_state = MergeResponse(
|
||||
possible, False, None, pull_request.last_merge_status, metadata=metadata)
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ class SettingsModel(BaseModel):
|
|||
new_ui.ui_value = val
|
||||
new_ui.ui_active = active
|
||||
|
||||
repository_id = ''
|
||||
if self.repo:
|
||||
repo = self._get_repo(self.repo)
|
||||
repository_id = repo.repo_id
|
||||
|
|
@ -440,26 +441,39 @@ class VcsSettingsModel(object):
|
|||
HOOKS_SETTINGS = (
|
||||
('hooks', 'changegroup.repo_size'),
|
||||
('hooks', 'changegroup.push_logger'),
|
||||
('hooks', 'outgoing.pull_logger'),)
|
||||
('hooks', 'outgoing.pull_logger'),
|
||||
)
|
||||
HG_SETTINGS = (
|
||||
('extensions', 'largefiles'),
|
||||
('phases', 'publish'),
|
||||
('extensions', 'evolve'),)
|
||||
('extensions', 'evolve'),
|
||||
('extensions', 'topic'),
|
||||
('experimental', 'evolution'),
|
||||
('experimental', 'evolution.exchange'),
|
||||
)
|
||||
GIT_SETTINGS = (
|
||||
('vcs_git_lfs', 'enabled'),)
|
||||
('vcs_git_lfs', 'enabled'),
|
||||
)
|
||||
GLOBAL_HG_SETTINGS = (
|
||||
('extensions', 'largefiles'),
|
||||
('largefiles', 'usercache'),
|
||||
('phases', 'publish'),
|
||||
('extensions', 'hgsubversion'),
|
||||
('extensions', 'evolve'),)
|
||||
('extensions', 'evolve'),
|
||||
('extensions', 'topic'),
|
||||
('experimental', 'evolution'),
|
||||
('experimental', 'evolution.exchange'),
|
||||
)
|
||||
|
||||
GLOBAL_GIT_SETTINGS = (
|
||||
('vcs_git_lfs', 'enabled'),
|
||||
('vcs_git_lfs', 'store_location'))
|
||||
('vcs_git_lfs', 'store_location')
|
||||
)
|
||||
|
||||
GLOBAL_SVN_SETTINGS = (
|
||||
('vcs_svn_proxy', 'http_requests_enabled'),
|
||||
('vcs_svn_proxy', 'http_server_url'))
|
||||
('vcs_svn_proxy', 'http_server_url')
|
||||
)
|
||||
|
||||
SVN_BRANCH_SECTION = 'vcs_svn_branch'
|
||||
SVN_TAG_SECTION = 'vcs_svn_tag'
|
||||
|
|
@ -574,12 +588,38 @@ class VcsSettingsModel(object):
|
|||
def create_repo_svn_settings(self, data):
|
||||
return self._create_svn_settings(self.repo_settings, data)
|
||||
|
||||
def _set_evolution(self, settings, is_enabled):
|
||||
if is_enabled:
|
||||
# if evolve is active set evolution=all
|
||||
|
||||
self._create_or_update_ui(
|
||||
settings, *('experimental', 'evolution'), value='all',
|
||||
active=True)
|
||||
self._create_or_update_ui(
|
||||
settings, *('experimental', 'evolution.exchange'), value='yes',
|
||||
active=True)
|
||||
# if evolve is active set topics server support
|
||||
self._create_or_update_ui(
|
||||
settings, *('extensions', 'topic'), value='',
|
||||
active=True)
|
||||
|
||||
else:
|
||||
self._create_or_update_ui(
|
||||
settings, *('experimental', 'evolution'), value='',
|
||||
active=False)
|
||||
self._create_or_update_ui(
|
||||
settings, *('experimental', 'evolution.exchange'), value='no',
|
||||
active=False)
|
||||
self._create_or_update_ui(
|
||||
settings, *('extensions', 'topic'), value='',
|
||||
active=False)
|
||||
|
||||
@assert_repo_settings
|
||||
def create_or_update_repo_hg_settings(self, data):
|
||||
largefiles, phases, evolve = \
|
||||
self.HG_SETTINGS
|
||||
self.HG_SETTINGS[:3]
|
||||
largefiles_key, phases_key, evolve_key = \
|
||||
self._get_settings_keys(self.HG_SETTINGS, data)
|
||||
self._get_settings_keys(self.HG_SETTINGS[:3], data)
|
||||
|
||||
self._create_or_update_ui(
|
||||
self.repo_settings, *largefiles, value='',
|
||||
|
|
@ -587,21 +627,22 @@ class VcsSettingsModel(object):
|
|||
self._create_or_update_ui(
|
||||
self.repo_settings, *evolve, value='',
|
||||
active=data[evolve_key])
|
||||
self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
|
||||
|
||||
self._create_or_update_ui(
|
||||
self.repo_settings, *phases, value=safe_str(data[phases_key]))
|
||||
|
||||
def create_or_update_global_hg_settings(self, data):
|
||||
largefiles, largefiles_store, phases, hgsubversion, evolve \
|
||||
= self.GLOBAL_HG_SETTINGS
|
||||
= self.GLOBAL_HG_SETTINGS[:5]
|
||||
largefiles_key, largefiles_store_key, phases_key, subversion_key, evolve_key \
|
||||
= self._get_settings_keys(self.GLOBAL_HG_SETTINGS, data)
|
||||
= self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:5], data)
|
||||
|
||||
self._create_or_update_ui(
|
||||
self.global_settings, *largefiles, value='',
|
||||
active=data[largefiles_key])
|
||||
self._create_or_update_ui(
|
||||
self.global_settings, *largefiles_store,
|
||||
value=data[largefiles_store_key])
|
||||
self.global_settings, *largefiles_store, value=data[largefiles_store_key])
|
||||
self._create_or_update_ui(
|
||||
self.global_settings, *phases, value=safe_str(data[phases_key]))
|
||||
self._create_or_update_ui(
|
||||
|
|
@ -609,9 +650,10 @@ class VcsSettingsModel(object):
|
|||
self._create_or_update_ui(
|
||||
self.global_settings, *evolve, value='',
|
||||
active=data[evolve_key])
|
||||
self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
|
||||
|
||||
def create_or_update_repo_git_settings(self, data):
|
||||
# NOTE(marcink): # comma make unpack work properly
|
||||
# NOTE(marcink): # comma makes unpack work properly
|
||||
lfs_enabled, \
|
||||
= self.GIT_SETTINGS
|
||||
|
||||
|
|
@ -675,6 +717,7 @@ class VcsSettingsModel(object):
|
|||
def get_repo_ui_settings(self, section=None, key=None):
|
||||
global_uis = self.global_settings.get_ui(section, key)
|
||||
repo_uis = self.repo_settings.get_ui(section, key)
|
||||
|
||||
filtered_repo_uis = self._filter_ui_settings(repo_uis)
|
||||
filtered_repo_uis_keys = [
|
||||
(s.section, s.key) for s in filtered_repo_uis]
|
||||
|
|
|
|||
|
|
@ -40,15 +40,32 @@
|
|||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
var get_datatable_count = function(){
|
||||
var api = $('#group_list_table').dataTable().api();
|
||||
$('#repo_group_count').text(api.page.info().recordsDisplay);
|
||||
};
|
||||
var $repoGroupsListTable = $('#group_list_table');
|
||||
|
||||
// repo group list
|
||||
$('#group_list_table').DataTable({
|
||||
data: ${c.data|n},
|
||||
$repoGroupsListTable.DataTable({
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
ajax: {
|
||||
"url": "${h.route_path('repo_groups_data')}",
|
||||
"dataSrc": function (json) {
|
||||
var filteredCount = json.recordsFiltered;
|
||||
var filteredInactiveCount = json.recordsFilteredInactive;
|
||||
var totalInactive = json.recordsTotalInactive;
|
||||
var total = json.recordsTotal;
|
||||
|
||||
var _text = _gettext(
|
||||
"{0} of {1} repository groups").format(
|
||||
filteredCount, total);
|
||||
|
||||
if (total === filteredCount) {
|
||||
_text = _gettext("{0} repository groups").format(total);
|
||||
}
|
||||
$('#repo_group_count').text(_text);
|
||||
return json.data;
|
||||
},
|
||||
},
|
||||
|
||||
dom: 'rtp',
|
||||
pageLength: ${c.visual.admin_grid_items},
|
||||
order: [[ 0, "asc" ]],
|
||||
|
|
@ -66,36 +83,34 @@ $(document).ready(function() {
|
|||
{ data: {"_": "owner",
|
||||
"sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
|
||||
{ data: {"_": "action",
|
||||
"sort": "action"}, title: "${_('Action')}", className: "td-action" }
|
||||
"sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
|
||||
],
|
||||
language: {
|
||||
paginate: DEFAULT_GRID_PAGINATION,
|
||||
sProcessing: _gettext('loading...'),
|
||||
emptyTable: _gettext("No repository groups available yet.")
|
||||
},
|
||||
"initComplete": function( settings, json ) {
|
||||
get_datatable_count();
|
||||
quick_repo_menu();
|
||||
}
|
||||
});
|
||||
|
||||
// update the counter when doing search
|
||||
$('#group_list_table').on( 'search.dt', function (e,settings) {
|
||||
get_datatable_count();
|
||||
$repoGroupsListTable.on('xhr.dt', function(e, settings, json, xhr){
|
||||
$repoGroupsListTable.css('opacity', 1);
|
||||
});
|
||||
|
||||
// filter, filter both grids
|
||||
$('#q_filter').on( 'keyup', function () {
|
||||
|
||||
var repo_group_api = $('#group_list_table').dataTable().api();
|
||||
repo_group_api
|
||||
.columns(0)
|
||||
.search(this.value)
|
||||
.draw();
|
||||
$repoGroupsListTable.on('preXhr.dt', function(e, settings, data){
|
||||
$repoGroupsListTable.css('opacity', 0.3);
|
||||
});
|
||||
|
||||
// refilter table if page load via back button
|
||||
$("#q_filter").trigger('keyup');
|
||||
// filter
|
||||
$('#q_filter').on('keyup',
|
||||
$.debounce(250, function() {
|
||||
$repoGroupsListTable.DataTable().search(
|
||||
$('#q_filter').val()
|
||||
).draw();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</%def>
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ $(document).ready(function() {
|
|||
{ data: {"_": "state",
|
||||
"sort": "state"}, title: "${_('State')}", className: "td-tags td-state" },
|
||||
{ data: {"_": "action",
|
||||
"sort": "action"}, title: "${_('Action')}", className: "td-action" }
|
||||
"sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
|
||||
],
|
||||
language: {
|
||||
paginate: DEFAULT_GRID_PAGINATION,
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ $(document).ready(function() {
|
|||
// filter
|
||||
$('#q_filter').on('keyup',
|
||||
$.debounce(250, function() {
|
||||
$('#user_group_list_table').DataTable().search(
|
||||
$userGroupsListTable.DataTable().search(
|
||||
$('#q_filter').val()
|
||||
).draw();
|
||||
})
|
||||
|
|
|
|||
|
|
@ -133,13 +133,13 @@
|
|||
|
||||
<div class="checkbox">
|
||||
${h.checkbox('extensions_evolve' + suffix, 'True', **kwargs)}
|
||||
<label for="extensions_evolve${suffix}">${_('Enable evolve extension')}</label>
|
||||
<label for="extensions_evolve${suffix}">${_('Enable Evolve and Topic extension')}</label>
|
||||
</div>
|
||||
<div class="label">
|
||||
% if display_globals:
|
||||
<span class="help-block">${_('Enable evolve extension for all repositories.')}</span>
|
||||
<span class="help-block">${_('Enable Evolve and Topic extensions for all repositories.')}</span>
|
||||
% else:
|
||||
<span class="help-block">${_('Enable evolve extension for this repository.')}</span>
|
||||
<span class="help-block">${_('Enable Evolve and Topic extensions for this repository.')}</span>
|
||||
% endif
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ def route_path(name, params=None, **kwargs):
|
|||
ADMIN_PREFIX + '/repos',
|
||||
'repo_groups':
|
||||
ADMIN_PREFIX + '/repo_groups',
|
||||
'repo_groups_data':
|
||||
ADMIN_PREFIX + '/repo_groups_data',
|
||||
'user_groups':
|
||||
ADMIN_PREFIX + '/user_groups',
|
||||
'user_groups_data':
|
||||
|
|
@ -63,8 +65,9 @@ class TestAdminDelegatedUser(TestController):
|
|||
response = self.app.get(route_path('repos'), status=200)
|
||||
response.mustcontain('data: []')
|
||||
|
||||
response = self.app.get(route_path('repo_groups'), status=200)
|
||||
response.mustcontain('data: []')
|
||||
response = self.app.get(route_path('repo_groups_data'),
|
||||
status=200, extra_environ=xhr_header)
|
||||
assert response.json['data'] == []
|
||||
|
||||
response = self.app.get(route_path('user_groups_data'),
|
||||
status=200, extra_environ=xhr_header)
|
||||
|
|
@ -97,7 +100,8 @@ class TestAdminDelegatedUser(TestController):
|
|||
response = self.app.get(route_path('repos'), status=200)
|
||||
response.mustcontain('"name_raw": "{}"'.format(repo_name))
|
||||
|
||||
response = self.app.get(route_path('repo_groups'), status=200)
|
||||
response = self.app.get(route_path('repo_groups_data'),
|
||||
extra_environ=xhr_header, status=200)
|
||||
response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
|
||||
|
||||
response = self.app.get(route_path('user_groups_data'),
|
||||
|
|
@ -139,7 +143,8 @@ class TestAdminDelegatedUser(TestController):
|
|||
response = self.app.get(route_path('repos'), status=200)
|
||||
response.mustcontain('"name_raw": "{}"'.format(repo_name))
|
||||
|
||||
response = self.app.get(route_path('repo_groups'), status=200)
|
||||
response = self.app.get(route_path('repo_groups_data'),
|
||||
extra_environ=xhr_header, status=200)
|
||||
response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
|
||||
|
||||
response = self.app.get(route_path('user_groups_data'),
|
||||
|
|
|
|||
|
|
@ -501,8 +501,8 @@ class TestCreateOrUpdateUi(object):
|
|||
|
||||
def test_update(self, repo_stub, settings_util):
|
||||
model = VcsSettingsModel(repo=repo_stub.repo_name)
|
||||
|
||||
largefiles, phases, evolve = model.HG_SETTINGS
|
||||
# care about only 3 first settings
|
||||
largefiles, phases, evolve = model.HG_SETTINGS[:3]
|
||||
|
||||
section = 'test-section'
|
||||
key = 'test-key'
|
||||
|
|
@ -531,10 +531,11 @@ class TestCreateOrUpdateRepoHgSettings(object):
|
|||
with mock.patch.object(model, '_create_or_update_ui') as create_mock:
|
||||
model.create_or_update_repo_hg_settings(self.FORM_DATA)
|
||||
expected_calls = [
|
||||
mock.call(model.repo_settings, 'extensions', 'largefiles',
|
||||
active=False, value=''),
|
||||
mock.call(model.repo_settings, 'extensions', 'evolve',
|
||||
active=False, value=''),
|
||||
mock.call(model.repo_settings, 'extensions', 'largefiles', active=False, value=''),
|
||||
mock.call(model.repo_settings, 'extensions', 'evolve', active=False, value=''),
|
||||
mock.call(model.repo_settings, 'experimental', 'evolution', active=False, value=''),
|
||||
mock.call(model.repo_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
|
||||
mock.call(model.repo_settings, 'extensions', 'topic', active=False, value=''),
|
||||
mock.call(model.repo_settings, 'phases', 'publish', value='False'),
|
||||
]
|
||||
assert expected_calls == create_mock.call_args_list
|
||||
|
|
@ -589,17 +590,16 @@ class TestCreateOrUpdateGlobalHgSettings(object):
|
|||
with mock.patch.object(model, '_create_or_update_ui') as create_mock:
|
||||
model.create_or_update_global_hg_settings(self.FORM_DATA)
|
||||
expected_calls = [
|
||||
mock.call(model.global_settings, 'extensions', 'largefiles',
|
||||
active=False, value=''),
|
||||
mock.call(model.global_settings, 'largefiles', 'usercache',
|
||||
value='/example/largefiles-store'),
|
||||
mock.call(model.global_settings, 'phases', 'publish',
|
||||
value='False'),
|
||||
mock.call(model.global_settings, 'extensions', 'hgsubversion',
|
||||
active=False),
|
||||
mock.call(model.global_settings, 'extensions', 'evolve',
|
||||
active=False, value='')
|
||||
mock.call(model.global_settings, 'extensions', 'largefiles', active=False, value=''),
|
||||
mock.call(model.global_settings, 'largefiles', 'usercache', value='/example/largefiles-store'),
|
||||
mock.call(model.global_settings, 'phases', 'publish', value='False'),
|
||||
mock.call(model.global_settings, 'extensions', 'hgsubversion', active=False),
|
||||
mock.call(model.global_settings, 'extensions', 'evolve', active=False, value=''),
|
||||
mock.call(model.global_settings, 'experimental', 'evolution', active=False, value=''),
|
||||
mock.call(model.global_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
|
||||
mock.call(model.global_settings, 'extensions', 'topic', active=False, value=''),
|
||||
]
|
||||
|
||||
assert expected_calls == create_mock.call_args_list
|
||||
|
||||
@pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
|
||||
|
|
@ -625,10 +625,8 @@ class TestCreateOrUpdateGlobalGitSettings(object):
|
|||
with mock.patch.object(model, '_create_or_update_ui') as create_mock:
|
||||
model.create_or_update_global_git_settings(self.FORM_DATA)
|
||||
expected_calls = [
|
||||
mock.call(model.global_settings, 'vcs_git_lfs', 'enabled',
|
||||
active=False, value=False),
|
||||
mock.call(model.global_settings, 'vcs_git_lfs', 'store_location',
|
||||
value='/example/lfs-store'),
|
||||
mock.call(model.global_settings, 'vcs_git_lfs', 'enabled', active=False, value=False),
|
||||
mock.call(model.global_settings, 'vcs_git_lfs', 'store_location', value='/example/lfs-store'),
|
||||
]
|
||||
assert expected_calls == create_mock.call_args_list
|
||||
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ def test_outdated_comments(
|
|||
(MergeFailureReason.HG_SOURCE_HAS_MORE_BRANCHES,
|
||||
'This pull request cannot be merged because the source contains more branches than the target.'),
|
||||
(MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS,
|
||||
'This pull request cannot be merged because the target has multiple heads: `a,b,c`.'),
|
||||
'This pull request cannot be merged because the target `ref_name` has multiple heads: `a,b,c`.'),
|
||||
(MergeFailureReason.TARGET_IS_LOCKED,
|
||||
'This pull request cannot be merged because the target repository is locked by user:123.'),
|
||||
(MergeFailureReason.MISSING_TARGET_REF,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue