##// END OF EJS Templates
release: Merge default into stable for release preparation
milka -
r4622:a884bc27 merge stable
parent child Browse files
Show More
@@ -0,0 +1,54 b''
1 |RCE| 4.23.0 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2021-01-10
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - Artifacts: expose additional headers, and content-disposition for downloads from artifacts exposing the real name of the file.
14 - Token access: allow token in headers not only in GET/URL.
15 - File-store: added a stream upload endpoint, it allows to upload GBs of data into artifact store efficiently.
16 Can be used for backups etc.
17 - Pull requests: expose commit versions in the pull-request commit list.
18
19 General
20 ^^^^^^^
21
22 - Deps: bumped redis to 3.5.3
23 - rcextensions: improve examples
24 - Setup: added optional parameters to apply a default license, or skip re-creation of database at install.
25 - Docs: update headers for NGINX
26 - Beaker cache: remove no longer used beaker cache init
27
28
29 Security
30 ^^^^^^^^
31
32
33
34 Performance
35 ^^^^^^^^^^^
36
37 - Core: speed up cache loading on application startup.
38 - Core: allow loading all auth plugins in once place for CE/EE code.
39 - Application: not use config.scan(), and replace all @add_view decorator into a explicit add_view call for faster app start.
40
41
42 Fixes
43 ^^^^^
44
45 - Svn: don't print exceptions in case of safe calls
46 - Vcsserver: use safer maxfd reporting, some linux systems get a problem with this
47 - Hooks-daemon: fixed problem with lost hooks value from .ini file.
48 - Exceptions: fixed truncated exception text
49
50
51 Upgrade notes
52 ^^^^^^^^^^^^^
53
54 - Scheduled release 4.24.0
@@ -1,6 +1,5 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.23.2
2 current_version = 4.24.0
3 message = release: Bump version {current_version} to {new_version}
3 message = release: Bump version {current_version} to {new_version}
4
4
5 [bumpversion:file:rhodecode/VERSION]
5 [bumpversion:file:rhodecode/VERSION]
6
@@ -5,25 +5,20 b' done = false'
5 done = true
5 done = true
6
6
7 [task:rc_tools_pinned]
7 [task:rc_tools_pinned]
8 done = true
9
8
10 [task:fixes_on_stable]
9 [task:fixes_on_stable]
11 done = true
12
10
13 [task:pip2nix_generated]
11 [task:pip2nix_generated]
14 done = true
15
12
16 [task:changelog_updated]
13 [task:changelog_updated]
17 done = true
18
14
19 [task:generate_api_docs]
15 [task:generate_api_docs]
20 done = true
16
17 [task:updated_translation]
21
18
22 [release]
19 [release]
23 state = prepared
20 state = in_progress
24 version = 4.23.2
21 version = 4.24.0
25
26 [task:updated_translation]
27
22
28 [task:generate_js_routes]
23 [task:generate_js_routes]
29
24
@@ -1,69 +1,98 b''
1 .DEFAULT_GOAL := help
1
2
2 .PHONY: clean docs docs-clean docs-cleanup test test-clean test-only test-only-postgres test-only-mysql web-build generate-pkgs pip-packages
3 # set by: PATH_TO_OUTDATED_PACKAGES=/some/path/outdated_packages.py
4 OUTDATED_PACKAGES = ${PATH_TO_OUTDATED_PACKAGES}
3
5
4 NODE_PATH=./node_modules
6 NODE_PATH=./node_modules
5 WEBPACK=./node_binaries/webpack
7 WEBPACK=./node_binaries/webpack
6 GRUNT=./node_binaries/grunt
8 GRUNT=./node_binaries/grunt
7 # set by: PATH_TO_OUTDATED_PACKAGES=/some/path/outdated_packages.py
8 OUTDATED_PACKAGES = ${PATH_TO_OUTDATED_PACKAGES}
9
9
10 clean:
10 .PHONY: clean
11 clean: ## full clean
11 make test-clean
12 make test-clean
12 find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' -o -iname '*.orig' \) -exec rm '{}' ';'
13 find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' -o -iname '*.orig' \) -exec rm '{}' ';'
13
14
14 test:
15
16 .PHONY: test
17 test: ## run test-clean and tests
15 make test-clean
18 make test-clean
16 make test-only
19 make test-only
17
20
18 test-clean:
21
22 .PHONY:test-clean
23 test-clean: ## run test-clean and tests
19 rm -rf coverage.xml htmlcov junit.xml pylint.log result
24 rm -rf coverage.xml htmlcov junit.xml pylint.log result
20 find . -type d -name "__pycache__" -prune -exec rm -rf '{}' ';'
25 find . -type d -name "__pycache__" -prune -exec rm -rf '{}' ';'
21 find . -type f \( -iname '.coverage.*' \) -exec rm '{}' ';'
26 find . -type f \( -iname '.coverage.*' \) -exec rm '{}' ';'
22
27
23 test-only:
28
29 .PHONY: test-only
30 test-only: ## run tests
24 PYTHONHASHSEED=random \
31 PYTHONHASHSEED=random \
25 py.test -x -vv -r xw -p no:sugar --cov=rhodecode \
32 py.test -x -vv -r xw -p no:sugar \
26 --cov-report=term-missing --cov-report=html \
33 --cov=rhodecode --cov-report=term-missing --cov-report=html \
27 rhodecode
34 rhodecode
28
35
29 test-only-mysql:
36
37 .PHONY: test-only-mysql
38 test-only-mysql: ## run tests against mysql
30 PYTHONHASHSEED=random \
39 PYTHONHASHSEED=random \
31 py.test -x -vv -r xw -p no:sugar --cov=rhodecode \
40 py.test -x -vv -r xw -p no:sugar \
32 --cov-report=term-missing --cov-report=html \
41 --cov=rhodecode --cov-report=term-missing --cov-report=html \
33 --ini-config-override='{"app:main": {"sqlalchemy.db1.url": "mysql://root:qweqwe@localhost/rhodecode_test?charset=utf8"}}' \
42 --ini-config-override='{"app:main": {"sqlalchemy.db1.url": "mysql://root:qweqwe@localhost/rhodecode_test?charset=utf8"}}' \
34 rhodecode
43 rhodecode
35
44
36 test-only-postgres:
45
46 .PHONY: test-only-postgres
47 test-only-postgres: ## run tests against postgres
37 PYTHONHASHSEED=random \
48 PYTHONHASHSEED=random \
38 py.test -x -vv -r xw -p no:sugar --cov=rhodecode \
49 py.test -x -vv -r xw -p no:sugar \
39 --cov-report=term-missing --cov-report=html \
50 --cov=rhodecode --cov-report=term-missing --cov-report=html \
40 --ini-config-override='{"app:main": {"sqlalchemy.db1.url": "postgresql://postgres:qweqwe@localhost/rhodecode_test"}}' \
51 --ini-config-override='{"app:main": {"sqlalchemy.db1.url": "postgresql://postgres:qweqwe@localhost/rhodecode_test"}}' \
41 rhodecode
52 rhodecode
42
53
43
54 .PHONY: docs
44 docs:
55 docs: ## build docs
45 (cd docs; nix-build default.nix -o result; make clean html)
56 (cd docs; nix-build default.nix -o result; make clean html)
46
57
47 docs-clean:
58
59 .PHONY: docs-clean
60 docs-clean: ## Cleanup docs
48 (cd docs; make clean)
61 (cd docs; make clean)
49
62
50 docs-cleanup:
63
64 .PHONY: docs-cleanup
65 docs-cleanup: ## Cleanup docs
51 (cd docs; make cleanup)
66 (cd docs; make cleanup)
52
67
53 web-build:
68
69 .PHONY: web-build
70 web-build: ## Build static/js
54 NODE_PATH=$(NODE_PATH) $(GRUNT)
71 NODE_PATH=$(NODE_PATH) $(GRUNT)
55
72
56 generate-pkgs:
73
74 .PHONY: generate-pkgs
75 generate-pkgs: ## generate new python packages
57 nix-shell pkgs/shell-generate.nix --command "pip2nix generate --licenses"
76 nix-shell pkgs/shell-generate.nix --command "pip2nix generate --licenses"
58
77
59 pip-packages:
78
79 .PHONY: pip-packages
80 pip-packages: ## show outdated packages
60 python ${OUTDATED_PACKAGES}
81 python ${OUTDATED_PACKAGES}
61
82
62 generate-js-pkgs:
83
84 .PHONY: generate-js-pkgs
85 generate-js-pkgs: ## generate js packages
63 rm -rf node_modules && \
86 rm -rf node_modules && \
64 nix-shell pkgs/shell-generate.nix --command "node2nix --input package.json -o pkgs/node-packages.nix -e pkgs/node-env.nix -c pkgs/node-default.nix -d --flatten --nodejs-8" && \
87 nix-shell pkgs/shell-generate.nix --command "node2nix --input package.json -o pkgs/node-packages.nix -e pkgs/node-env.nix -c pkgs/node-default.nix -d --flatten --nodejs-8" && \
65 sed -i -e 's/http:\/\//https:\/\//g' pkgs/node-packages.nix
88 sed -i -e 's/http:\/\//https:\/\//g' pkgs/node-packages.nix
66
89
67 generate-license-meta:
90
91 .PHONY: generate-license-meta
92 generate-license-meta: ## Generate license metadata
68 nix-build pkgs/license-generate.nix -o result-license && \
93 nix-build pkgs/license-generate.nix -o result-license && \
69 cat result-license/licenses.json | python -m json.tool > rhodecode/config/licenses.json No newline at end of file
94 cat result-license/licenses.json | python -m json.tool > rhodecode/config/licenses.json
95
96 .PHONY: help
97 help:
98 @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-24s\033[0m %s\n", $$1, $$2}'
@@ -9,6 +9,7 b' Release Notes'
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.24.0.rst
12 release-notes-4.23.2.rst
13 release-notes-4.23.2.rst
13 release-notes-4.23.1.rst
14 release-notes-4.23.1.rst
14 release-notes-4.23.0.rst
15 release-notes-4.23.0.rst
@@ -1828,11 +1828,11 b' self: super: {'
1828 };
1828 };
1829 };
1829 };
1830 "redis" = super.buildPythonPackage {
1830 "redis" = super.buildPythonPackage {
1831 name = "redis-3.4.1";
1831 name = "redis-3.5.3";
1832 doCheck = false;
1832 doCheck = false;
1833 src = fetchurl {
1833 src = fetchurl {
1834 url = "https://files.pythonhosted.org/packages/ef/2e/2c0f59891db7db087a7eeaa79bc7c7f2c039e71a2b5b0a41391e9d462926/redis-3.4.1.tar.gz";
1834 url = "https://files.pythonhosted.org/packages/b3/17/1e567ff78c83854e16b98694411fe6e08c3426af866ad11397cddceb80d3/redis-3.5.3.tar.gz";
1835 sha256 = "07yaj0j9fs7xdkg5bg926fa990khyigjbp31si8ai20vj8sv7kqd";
1835 sha256 = "0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2";
1836 };
1836 };
1837 meta = {
1837 meta = {
1838 license = [ pkgs.lib.licenses.mit ];
1838 license = [ pkgs.lib.licenses.mit ];
@@ -1883,7 +1883,7 b' self: super: {'
1883 };
1883 };
1884 };
1884 };
1885 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1885 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1886 name = "rhodecode-enterprise-ce-4.23.2";
1886 name = "rhodecode-enterprise-ce-4.24.0";
1887 buildInputs = [
1887 buildInputs = [
1888 self."pytest"
1888 self."pytest"
1889 self."py"
1889 self."py"
@@ -57,7 +57,7 b' tzlocal==1.5.1'
57 pyzmq==14.6.0
57 pyzmq==14.6.0
58 py-gfm==0.1.4
58 py-gfm==0.1.4
59 regex==2020.9.27
59 regex==2020.9.27
60 redis==3.4.1
60 redis==3.5.3
61 repoze.lru==0.7
61 repoze.lru==0.7
62 requests==2.22.0
62 requests==2.22.0
63 routes==2.4.1
63 routes==2.4.1
@@ -1,1 +1,1 b''
1 4.23.2 No newline at end of file
1 4.24.0 No newline at end of file
@@ -23,23 +23,23 b' import pytest'
23 from rhodecode.apps._base import ADMIN_PREFIX
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.model.db import User
24 from rhodecode.model.db import User
25 from rhodecode.tests import (
25 from rhodecode.tests import (
26 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
26 TestController, route_path_generator, assert_session_flash)
27 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.utils import AssertResponse
28 from rhodecode.tests.utils import AssertResponse
30
29
31 fixture = Fixture()
30 fixture = Fixture()
32
31
33
32
34 def route_path(name, **kwargs):
33 def route_path(name, params=None, **kwargs):
35 return {
34 url_defs = {
36 'my_account_auth_tokens':
35 'my_account_auth_tokens':
37 ADMIN_PREFIX + '/my_account/auth_tokens',
36 ADMIN_PREFIX + '/my_account/auth_tokens',
38 'my_account_auth_tokens_add':
37 'my_account_auth_tokens_add':
39 ADMIN_PREFIX + '/my_account/auth_tokens/new',
38 ADMIN_PREFIX + '/my_account/auth_tokens/new',
40 'my_account_auth_tokens_delete':
39 'my_account_auth_tokens_delete':
41 ADMIN_PREFIX + '/my_account/auth_tokens/delete',
40 ADMIN_PREFIX + '/my_account/auth_tokens/delete',
42 }[name].format(**kwargs)
41 }
42 return route_path_generator(url_defs, name=name, params=params, **kwargs)
43
43
44
44
45 class TestMyAccountAuthTokens(TestController):
45 class TestMyAccountAuthTokens(TestController):
@@ -357,6 +357,9 b' class RepoPullRequestsView(RepoAppView, '
357 pull_request_id=pull_request_id))
357 pull_request_id=pull_request_id))
358
358
359 versions = pull_request_display_obj.versions()
359 versions = pull_request_display_obj.versions()
360
361 c.commit_versions = PullRequestModel().pr_commits_versions(versions)
362
360 # used to store per-commit range diffs
363 # used to store per-commit range diffs
361 c.changes = collections.OrderedDict()
364 c.changes = collections.OrderedDict()
362
365
@@ -25,20 +25,20 b' import pytest'
25 from whoosh import query
25 from whoosh import query
26
26
27 from rhodecode.tests import (
27 from rhodecode.tests import (
28 TestController, HG_REPO,
28 TestController, route_path_generator, HG_REPO,
29 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
29 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
30 from rhodecode.tests.utils import AssertResponse
30 from rhodecode.tests.utils import AssertResponse
31
31
32
32
33 def route_path(name, **kwargs):
33 def route_path(name, params=None, **kwargs):
34 from rhodecode.apps._base import ADMIN_PREFIX
34 from rhodecode.apps._base import ADMIN_PREFIX
35 return {
35 url_defs = {
36 'search':
36 'search':
37 ADMIN_PREFIX + '/search',
37 ADMIN_PREFIX + '/search',
38 'search_repo':
38 'search_repo':
39 '/{repo_name}/search',
39 '/{repo_name}/search',
40
40 }
41 }[name].format(**kwargs)
41 return route_path_generator(url_defs, name=name, params=params, **kwargs)
42
42
43
43
44 class TestSearchController(TestController):
44 class TestSearchController(TestController):
@@ -47,7 +47,6 b' from rhodecode.lib.vcs import VCSCommuni'
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.celerylib.loader import configure_celery
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
50 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
51 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
52 from rhodecode.lib.exc_tracking import store_exception
@@ -237,6 +236,7 b' def includeme_first(config):'
237
236
238
237
239 def includeme(config, auth_resources=None):
238 def includeme(config, auth_resources=None):
239 from rhodecode.lib.celerylib.loader import configure_celery
240 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
240 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
241 settings = config.registry.settings
241 settings = config.registry.settings
242 config.set_request_factory(Request)
242 config.set_request_factory(Request)
@@ -66,11 +66,23 b' class DbManage(object):'
66 self.root = root
66 self.root = root
67 self.dburi = dbconf
67 self.dburi = dbconf
68 self.log_sql = log_sql
68 self.log_sql = log_sql
69 self.db_exists = False
70 self.cli_args = cli_args or {}
69 self.cli_args = cli_args or {}
71 self.init_db(SESSION=SESSION)
70 self.init_db(SESSION=SESSION)
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
71 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
73
72
73 def db_exists(self):
74 if not self.sa:
75 self.init_db()
76 try:
77 self.sa.query(RhodeCodeUi)\
78 .filter(RhodeCodeUi.ui_key == '/')\
79 .scalar()
80 return True
81 except Exception:
82 return False
83 finally:
84 self.sa.rollback()
85
74 def get_ask_ok_func(self, param):
86 def get_ask_ok_func(self, param):
75 if param not in [None]:
87 if param not in [None]:
76 # return a function lambda that has a default set to param
88 # return a function lambda that has a default set to param
@@ -120,7 +120,7 b' class ThreadedHookCallbackDaemon(object)'
120 _done = False
120 _done = False
121
121
122 def __init__(self, txn_id=None, host=None, port=None):
122 def __init__(self, txn_id=None, host=None, port=None):
123 self._prepare(txn_id=txn_id, host=None, port=port)
123 self._prepare(txn_id=txn_id, host=host, port=port)
124
124
125 def __enter__(self):
125 def __enter__(self):
126 log.debug('Running `%s` callback daemon', self.__class__.__name__)
126 log.debug('Running `%s` callback daemon', self.__class__.__name__)
@@ -60,14 +60,24 b' log = logging.getLogger(__name__)'
60 default=None,
60 default=None,
61 help='Enable public access on this installation. '
61 help='Enable public access on this installation. '
62 'Default is public access enabled.')
62 'Default is public access enabled.')
63 @click.option(
64 '--skip-existing-db',
65 default=False,
66 is_flag=True,
67 help='Do not destroy and re-initialize the database if it already exist.')
68 @click.option(
69 '--apply-license-key',
70 default=False,
71 is_flag=True,
72 help='Get the license key from a license file or ENV and apply during DB creation.')
63 def main(ini_path, force_yes, user, email, password, api_key, repos,
73 def main(ini_path, force_yes, user, email, password, api_key, repos,
64 public_access):
74 public_access, skip_existing_db, apply_license_key):
65 return command(ini_path, force_yes, user, email, password, api_key,
75 return command(ini_path, force_yes, user, email, password, api_key,
66 repos, public_access)
76 repos, public_access, skip_existing_db, apply_license_key)
67
77
68
78
69 def command(ini_path, force_yes, user, email, password, api_key, repos,
79 def command(ini_path, force_yes, user, email, password, api_key, repos,
70 public_access):
80 public_access, skip_existing_db, apply_license_key):
71 # mapping of old parameters to new CLI from click
81 # mapping of old parameters to new CLI from click
72 options = dict(
82 options = dict(
73 username=user,
83 username=user,
@@ -85,6 +95,9 b' def command(ini_path, force_yes, user, e'
85 db_uri = config['sqlalchemy.db1.url']
95 db_uri = config['sqlalchemy.db1.url']
86 dbmanage = DbManage(log_sql=True, dbconf=db_uri, root='.',
96 dbmanage = DbManage(log_sql=True, dbconf=db_uri, root='.',
87 tests=False, cli_args=options)
97 tests=False, cli_args=options)
98 if skip_existing_db and dbmanage.db_exists():
99 return
100
88 dbmanage.create_tables(override=True)
101 dbmanage.create_tables(override=True)
89 dbmanage.set_db_version()
102 dbmanage.set_db_version()
90 opts = dbmanage.config_prompt(None)
103 opts = dbmanage.config_prompt(None)
@@ -93,6 +106,13 b' def command(ini_path, force_yes, user, e'
93 dbmanage.create_admin_and_prompt()
106 dbmanage.create_admin_and_prompt()
94 dbmanage.create_permissions()
107 dbmanage.create_permissions()
95 dbmanage.populate_default_permissions()
108 dbmanage.populate_default_permissions()
109 if apply_license_key:
110 try:
111 from rc_license.models import apply_trial_license_if_missing
112 apply_trial_license_if_missing(force=True)
113 except ImportError:
114 pass
115
96 Session().commit()
116 Session().commit()
97
117
98 with bootstrap(ini_path, env={'RC_CMD_SETUP_RC': '1'}) as env:
118 with bootstrap(ini_path, env={'RC_CMD_SETUP_RC': '1'}) as env:
@@ -3821,8 +3821,12 b' class ChangesetComment(Base, BaseModel):'
3821 return q.all()
3821 return q.all()
3822
3822
3823 @classmethod
3823 @classmethod
3824 def get_index_from_version(cls, pr_version, versions):
3824 def get_index_from_version(cls, pr_version, versions=None, num_versions=None):
3825 num_versions = [x.pull_request_version_id for x in versions]
3825
3826 if versions is not None:
3827 num_versions = [x.pull_request_version_id for x in versions]
3828
3829 num_versions = num_versions or []
3826 try:
3830 try:
3827 return num_versions.index(pr_version) + 1
3831 return num_versions.index(pr_version) + 1
3828 except (IndexError, ValueError):
3832 except (IndexError, ValueError):
@@ -610,6 +610,20 b' class PullRequestModel(BaseModel):'
610 return _org_pull_request_obj, pull_request_obj, \
610 return _org_pull_request_obj, pull_request_obj, \
611 pull_request_display_obj, at_version
611 pull_request_display_obj, at_version
612
612
613 def pr_commits_versions(self, versions):
614 """
615 Maps the pull-request commits into all known PR versions. This way we can obtain
616 each pr version the commit was introduced in.
617 """
618 commit_versions = collections.defaultdict(list)
619 num_versions = [x.pull_request_version_id for x in versions]
620 for ver in versions:
621 for commit_id in ver.revisions:
622 ver_idx = ChangesetComment.get_index_from_version(
623 ver.pull_request_version_id, num_versions=num_versions)
624 commit_versions[commit_id].append(ver_idx)
625 return commit_versions
626
613 def create(self, created_by, source_repo, source_ref, target_repo,
627 def create(self, created_by, source_repo, source_ref, target_repo,
614 target_ref, revisions, reviewers, observers, title, description=None,
628 target_ref, revisions, reviewers, observers, title, description=None,
615 common_ancestor_id=None,
629 common_ancestor_id=None,
@@ -12,6 +12,9 b''
12 <input type="hidden" name="__start__" value="revisions:sequence">
12 <input type="hidden" name="__start__" value="revisions:sequence">
13 <table class="rctable compare_view_commits">
13 <table class="rctable compare_view_commits">
14 <tr>
14 <tr>
15 % if hasattr(c, 'commit_versions'):
16 <th>ver</th>
17 % endif
15 <th>${_('Time')}</th>
18 <th>${_('Time')}</th>
16 <th>${_('Author')}</th>
19 <th>${_('Author')}</th>
17 <th>${_('Commit')}</th>
20 <th>${_('Commit')}</th>
@@ -30,6 +33,11 b''
30 class="compare_select"
33 class="compare_select"
31 style="${'display: none' if c.collapse_all_commits else ''}"
34 style="${'display: none' if c.collapse_all_commits else ''}"
32 >
35 >
36 % if hasattr(c, 'commit_versions'):
37 <td class="tooltip" title="${_('Pull request version this commit was introduced')}">
38 <code>${('v{}'.format(c.commit_versions[commit.raw_id][0]) if c.commit_versions[commit.raw_id] else 'latest')}</code>
39 </td>
40 % endif
33 <td class="td-time">
41 <td class="td-time">
34 ${h.age_component(commit.date)}
42 ${h.age_component(commit.date)}
35 </td>
43 </td>
@@ -33,15 +33,15 b' import pytest'
33 from rhodecode.model.db import User
33 from rhodecode.model.db import User
34 from rhodecode.lib import auth
34 from rhodecode.lib import auth
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.helpers import flash, link_to
36 from rhodecode.lib.helpers import flash
37 from rhodecode.lib.utils2 import safe_str
37 from rhodecode.lib.utils2 import safe_str
38
38
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42 __all__ = [
42 __all__ = [
43 'get_new_dir', 'TestController',
43 'get_new_dir', 'TestController', 'route_path_generator',
44 'link_to', 'clear_cache_regions',
44 'clear_cache_regions',
45 'assert_session_flash', 'login_user', 'no_newline_id_generator',
45 'assert_session_flash', 'login_user', 'no_newline_id_generator',
46 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'SVN_REPO',
46 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'SVN_REPO',
47 'NEW_HG_REPO', 'NEW_GIT_REPO',
47 'NEW_HG_REPO', 'NEW_GIT_REPO',
@@ -243,3 +243,13 b' def no_newline_id_generator(test_name):'
243 .replace(' ', '_S')
243 .replace(' ', '_S')
244
244
245 return test_name or 'test-with-empty-name'
245 return test_name or 'test-with-empty-name'
246
247
248 def route_path_generator(url_defs, name, params=None, **kwargs):
249 import urllib
250
251 base_url = url_defs[name].format(**kwargs)
252
253 if params:
254 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
255 return base_url
General Comments 0
You need to be logged in to leave comments. Login now