Show More
@@ -0,0 +1,76 b'' | |||||
|
1 | |RCE| 4.25.0 |RNS| | |||
|
2 | ------------------ | |||
|
3 | ||||
|
4 | Release Date | |||
|
5 | ^^^^^^^^^^^^ | |||
|
6 | ||||
|
7 | - 2021-04-02 | |||
|
8 | ||||
|
9 | ||||
|
10 | New Features | |||
|
11 | ^^^^^^^^^^^^ | |||
|
12 | ||||
|
13 | - SSH: allow clone by ID via SSH operations. | |||
|
14 | - Artifacts: added an admin panel to manage artifacts. | |||
|
15 | - Redmine: added option to add note to a ticket without changing its status in Redmine integration. | |||
|
16 | ||||
|
17 | ||||
|
18 | General | |||
|
19 | ^^^^^^^ | |||
|
20 | ||||
|
21 | - Git: change lookups logic. Prioritize reference names over numerical ids. | |||
|
22 | Numerical ids are supported as a fallback if ref matching is unsuccessful. | |||
|
23 | - Permissions: changed fork permission help text to reflect the actual state on how it works. | |||
|
24 | - Permissions: flush permissions on owner changes for repo and repo groups. This | |||
|
25 | would fix problems when owner of repository changes then the new owner lacked permissions | |||
|
26 | until cache expired. | |||
|
27 | - Artifacts: added API function to remove artifacts. | |||
|
28 | - Archives: use a special name for non-hashed archives to fix caching issues. | |||
|
29 | - Packaging: fixed few packages requirements for a proper builds. | |||
|
30 | - Packaging: fix rhodecode-tools for docker builds. | |||
|
31 | - Packaging: fixed some problem after latest setuptools-scm release. | |||
|
32 | - Packaging: added setuptools-scm to packages for build. | |||
|
33 | - Packaging: fix jira package for reproducible builds. | |||
|
34 | - Packaging: fix zipp package patches. | |||
|
35 | ||||
|
36 | ||||
|
37 | Security | |||
|
38 | ^^^^^^^^ | |||
|
39 | ||||
|
40 | - Comments: forbid removal of comments by anyone except the owners. | |||
|
41 | Previously admins of a repository could remove them if they would construct a special url with data. | |||
|
42 | - Pull requests: fixed some xss problems when a deleted file with special characters were commented on. | |||
|
43 | ||||
|
44 | ||||
|
45 | Performance | |||
|
46 | ^^^^^^^^^^^ | |||
|
47 | ||||
|
48 | - License: skip channelstream connect on license checks logic to reduce calls handling times. | |||
|
49 | - Core: optimize some calls to skip license/scm detection on them. Each license check is expensive | |||
|
50 | and we don't need them on each call. | |||
|
51 | ||||
|
52 | ||||
|
53 | Fixes | |||
|
54 | ^^^^^ | |||
|
55 | ||||
|
56 | - Branch-permissions: fixed ce view. Fixes #5656 | |||
|
57 | - Feed: fix errors on feed access of empty repositories. | |||
|
58 | - Archives: if implicit ref name was used (e.g master) to obtain archive, we now | |||
|
59 | redirect to explicit commit sha so we can have the proper caching for references names. | |||
|
60 | - rcextensions: fixed pre-files extractor return code support. | |||
|
61 | - Svn: fix subprocess problems on some of the calls for file checking. | |||
|
62 | - Pull requests: fixed multiple repetitions of referenced tickets in pull requests summary sidebar. | |||
|
63 | - Maintenance: fixed bad routes def | |||
|
64 | - clone-uri: fixed the problems with key mismatch that caused errors on summary page. | |||
|
65 | - Largefiles: added fix for downloading largefiles which had no extension in file name. | |||
|
66 | - Compare: fix referenced commits bug. | |||
|
67 | - Git: fix for unicode branches | |||
|
68 | ||||
|
69 | ||||
|
70 | Upgrade notes | |||
|
71 | ^^^^^^^^^^^^^ | |||
|
72 | ||||
|
73 | - Scheduled release 4.25.0. | |||
|
74 | ||||
|
75 | ||||
|
76 |
@@ -0,0 +1,13 b'' | |||||
|
1 | diff -rup channelstream-0.6.14-orig/setup.py channelstream-0.6.14/setup.py | |||
|
2 | ||||
|
3 | --- channelstream-0.6.14/setup-orig.py 2021-03-11 12:34:45.000000000 +0100 | |||
|
4 | +++ channelstream-0.6.14/setup.py 2021-03-11 12:34:56.000000000 +0100 | |||
|
5 | @@ -52,7 +52,7 @@ setup( | |||
|
6 | include_package_data=True, | |||
|
7 | install_requires=requires, | |||
|
8 | python_requires=">=2.7", | |||
|
9 | - setup_requires=["pytest-runner"], | |||
|
10 | + setup_requires=["pytest-runner==5.1.0"], | |||
|
11 | extras_require={ | |||
|
12 | "dev": ["coverage", "pytest", "pyramid", "tox", "mock", "webtest"], | |||
|
13 | "lint": ["black"], |
@@ -0,0 +1,10 b'' | |||||
|
1 | diff -rup configparser-4.0.2-orig/pyproject.toml configparser-4.0.2/pyproject.toml | |||
|
2 | --- configparser-4.0.2-orig/pyproject.toml 2021-03-22 21:28:11.000000000 +0100 | |||
|
3 | +++ configparser-4.0.2/pyproject.toml 2021-03-22 21:28:11.000000000 +0100 | |||
|
4 | @@ -1,5 +1,5 @@ | |||
|
5 | [build-system] | |||
|
6 | -requires = ["setuptools>=40.7", "wheel", "setuptools_scm>=1.15"] | |||
|
7 | +requires = ["setuptools<=42.0", "wheel", "setuptools_scm<6.0.0"] | |||
|
8 | build-backend = "setuptools.build_meta" | |||
|
9 | ||||
|
10 | [tool.black] |
@@ -0,0 +1,7 b'' | |||||
|
1 | diff -rup importlib-metadata-1.6.0-orig/yproject.toml importlib-metadata-1.6.0/pyproject.toml | |||
|
2 | --- importlib-metadata-1.6.0-orig/yproject.toml 2021-03-22 22:10:33.000000000 +0100 | |||
|
3 | +++ importlib-metadata-1.6.0/pyproject.toml 2021-03-22 22:11:09.000000000 +0100 | |||
|
4 | @@ -1,3 +1,3 @@ | |||
|
5 | [build-system] | |||
|
6 | -requires = ["setuptools>=30.3", "wheel", "setuptools_scm"] | |||
|
7 | +requires = ["setuptools<42.0", "wheel", "setuptools_scm<6.0.0"] |
@@ -0,0 +1,12 b'' | |||||
|
1 | diff -rup pyramid-apispec-0.3.2-orig/setup.py pyramid-apispec-0.3.2/setup.py | |||
|
2 | --- pyramid-apispec-0.3.2-orig/setup.py 2021-03-11 11:19:26.000000000 +0100 | |||
|
3 | +++ pyramid-apispec-0.3.2/setup.py 2021-03-11 11:19:51.000000000 +0100 | |||
|
4 | @@ -44,7 +44,7 @@ setup( | |||
|
5 | packages=find_packages(exclude=["contrib", "docs", "tests"]), | |||
|
6 | package_data={"pyramid_apispec": ["static/*.*"], "": ["LICENSE"]}, | |||
|
7 | install_requires=["apispec[yaml]==1.0.0"], | |||
|
8 | - setup_requires=["pytest-runner"], | |||
|
9 | + setup_requires=["pytest-runner==5.1"], | |||
|
10 | extras_require={ | |||
|
11 | "dev": ["coverage", "pytest", "pyramid", "tox", "webtest"], | |||
|
12 | "demo": ["marshmallow==2.15.3", "pyramid", "apispec", "webtest"], No newline at end of file |
@@ -0,0 +1,12 b'' | |||||
|
1 | diff -rup rhodecode-tools-1.4.0-orig/setup.py rhodecode-tools-1.4.0/setup.py | |||
|
2 | --- rhodecode-tools-1.4.0/setup-orig.py 2021-03-11 12:34:45.000000000 +0100 | |||
|
3 | +++ rhodecode-tools-1.4.0/setup.py 2021-03-11 12:34:56.000000000 +0100 | |||
|
4 | @@ -69,7 +69,7 @@ def _get_requirements(req_filename, excl | |||
|
5 | ||||
|
6 | ||||
|
7 | # requirements extract | |||
|
8 | -setup_requirements = ['pytest-runner'] | |||
|
9 | +setup_requirements = ['pytest-runner==5.1.0'] | |||
|
10 | install_requirements = _get_requirements( | |||
|
11 | 'requirements.txt', exclude=['setuptools']) | |||
|
12 | test_requirements = _get_requirements('requirements_test.txt') No newline at end of file |
@@ -0,0 +1,10 b'' | |||||
|
1 | diff -rup zip-1.2.0-orig/pyproject.toml zip-1.2.0/pyproject.toml | |||
|
2 | --- zip-1.2.0-orig/pyproject.toml 2021-03-23 10:55:37.000000000 +0100 | |||
|
3 | +++ zip-1.2.0/pyproject.toml 2021-03-23 10:56:05.000000000 +0100 | |||
|
4 | @@ -1,5 +1,5 @@ | |||
|
5 | [build-system] | |||
|
6 | -requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"] | |||
|
7 | +requires = ["setuptools<42.0", "wheel", "setuptools_scm<6.0.0"] | |||
|
8 | build-backend = "setuptools.build_meta" | |||
|
9 | ||||
|
10 | [tool.black] |
@@ -0,0 +1,74 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2010-2020 RhodeCode GmbH | |||
|
4 | # | |||
|
5 | # This program is free software: you can redistribute it and/or modify | |||
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU Affero General Public License | |||
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | import mock | |||
|
22 | import pytest | |||
|
23 | ||||
|
24 | from rhodecode.lib.utils2 import str2bool | |||
|
25 | from rhodecode.lib.vcs.exceptions import RepositoryRequirementError | |||
|
26 | from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User | |||
|
27 | from rhodecode.model.meta import Session | |||
|
28 | from rhodecode.tests import ( | |||
|
29 | TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, assert_session_flash) | |||
|
30 | from rhodecode.tests.fixture import Fixture | |||
|
31 | ||||
|
32 | fixture = Fixture() | |||
|
33 | ||||
|
34 | ||||
|
35 | def route_path(name, params=None, **kwargs): | |||
|
36 | import urllib | |||
|
37 | ||||
|
38 | base_url = { | |||
|
39 | 'edit_repo_maintenance': '/{repo_name}/settings/maintenance', | |||
|
40 | 'edit_repo_maintenance_execute': '/{repo_name}/settings/maintenance/execute', | |||
|
41 | ||||
|
42 | }[name].format(**kwargs) | |||
|
43 | ||||
|
44 | if params: | |||
|
45 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |||
|
46 | return base_url | |||
|
47 | ||||
|
48 | ||||
|
49 | def _get_permission_for_user(user, repo): | |||
|
50 | perm = UserRepoToPerm.query()\ | |||
|
51 | .filter(UserRepoToPerm.repository == | |||
|
52 | Repository.get_by_repo_name(repo))\ | |||
|
53 | .filter(UserRepoToPerm.user == User.get_by_username(user))\ | |||
|
54 | .all() | |||
|
55 | return perm | |||
|
56 | ||||
|
57 | ||||
|
58 | @pytest.mark.usefixtures('autologin_user', 'app') | |||
|
59 | class TestAdminRepoMaintenance(object): | |||
|
60 | @pytest.mark.parametrize('urlname', [ | |||
|
61 | 'edit_repo_maintenance', | |||
|
62 | ]) | |||
|
63 | def test_show_page(self, urlname, app, backend): | |||
|
64 | app.get(route_path(urlname, repo_name=backend.repo_name), status=200) | |||
|
65 | ||||
|
66 | def test_execute_maintenance_for_repo_hg(self, app, backend_hg, autologin_user, xhr_header): | |||
|
67 | repo_name = backend_hg.repo_name | |||
|
68 | ||||
|
69 | response = app.get( | |||
|
70 | route_path('edit_repo_maintenance_execute', | |||
|
71 | repo_name=repo_name,), | |||
|
72 | extra_environ=xhr_header) | |||
|
73 | ||||
|
74 | assert "HG Verify repo" in ''.join(response.json) |
@@ -1,6 +1,5 b'' | |||||
1 | [bumpversion] |
|
1 | [bumpversion] | |
2 |
current_version = 4.2 |
|
2 | current_version = 4.25.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 = |
|
20 | state = in_progress | |
24 |
version = 4.2 |
|
21 | version = 4.25.0 | |
25 |
|
||||
26 | [task:updated_translation] |
|
|||
27 |
|
22 | |||
28 | [task:generate_js_routes] |
|
23 | [task:generate_js_routes] | |
29 |
|
24 |
@@ -130,6 +130,24 b' file_store_get_info (EE only)' | |||||
130 | error : null |
|
130 | error : null | |
131 |
|
131 | |||
132 |
|
132 | |||
|
133 | file_store_delete (EE only) | |||
|
134 | --------------------------- | |||
|
135 | ||||
|
136 | .. py:function:: file_store_delete(apiuser, store_fid) | |||
|
137 | ||||
|
138 | Delete an artifact based on the secret uuid. | |||
|
139 | ||||
|
140 | Example output: | |||
|
141 | ||||
|
142 | .. code-block:: bash | |||
|
143 | ||||
|
144 | id : <id_given_in_input> | |||
|
145 | result: { | |||
|
146 | "artifact" : {"uid": "some uid", "removed": true} | |||
|
147 | } | |||
|
148 | error : null | |||
|
149 | ||||
|
150 | ||||
133 | file_store_add_metadata (EE only) |
|
151 | file_store_add_metadata (EE only) | |
134 | --------------------------------- |
|
152 | --------------------------------- | |
135 |
|
153 |
@@ -9,6 +9,7 b' Release Notes' | |||||
9 | .. toctree:: |
|
9 | .. toctree:: | |
10 | :maxdepth: 1 |
|
10 | :maxdepth: 1 | |
11 |
|
11 | |||
|
12 | release-notes-4.25.0.rst | |||
12 | release-notes-4.24.1.rst |
|
13 | release-notes-4.24.1.rst | |
13 | release-notes-4.24.0.rst |
|
14 | release-notes-4.24.0.rst | |
14 | release-notes-4.23.2.rst |
|
15 | release-notes-4.23.2.rst |
@@ -6,7 +6,7 b' diff -rup pytest-4.6.5-orig/setup.py pyt' | |||||
6 | setup( |
|
6 | setup( | |
7 | use_scm_version={"write_to": "src/_pytest/_version.py"}, |
|
7 | use_scm_version={"write_to": "src/_pytest/_version.py"}, | |
8 | - setup_requires=["setuptools-scm", "setuptools>=40.0"], |
|
8 | - setup_requires=["setuptools-scm", "setuptools>=40.0"], | |
9 | + setup_requires=["setuptools-scm", "setuptools<=42.0"], |
|
9 | + setup_requires=["setuptools-scm<6.0.0", "setuptools<=42.0"], | |
10 | package_dir={"": "src"}, |
|
10 | package_dir={"": "src"}, | |
11 | # fmt: off |
|
11 | # fmt: off | |
12 | extras_require={ No newline at end of file |
|
12 | extras_require={ |
@@ -280,6 +280,72 b' self: super: {' | |||||
280 | ]; |
|
280 | ]; | |
281 | }); |
|
281 | }); | |
282 |
|
282 | |||
|
283 | "pytest-runner" = super."pytest-runner".override (attrs: { | |||
|
284 | propagatedBuildInputs = [ | |||
|
285 | self."setuptools-scm" | |||
|
286 | ]; | |||
|
287 | }); | |||
|
288 | ||||
|
289 | "py" = super."py".override (attrs: { | |||
|
290 | propagatedBuildInputs = [ | |||
|
291 | self."setuptools-scm" | |||
|
292 | ]; | |||
|
293 | }); | |||
|
294 | ||||
|
295 | "python-dateutil" = super."python-dateutil".override (attrs: { | |||
|
296 | propagatedBuildInputs = attrs.propagatedBuildInputs ++ [ | |||
|
297 | self."setuptools-scm" | |||
|
298 | ]; | |||
|
299 | }); | |||
|
300 | ||||
|
301 | "configparser" = super."configparser".override (attrs: { | |||
|
302 | patches = [ | |||
|
303 | ./patches/configparser/pyproject.patch | |||
|
304 | ]; | |||
|
305 | propagatedBuildInputs = [ | |||
|
306 | self."setuptools-scm" | |||
|
307 | ]; | |||
|
308 | }); | |||
|
309 | ||||
|
310 | "importlib-metadata" = super."importlib-metadata".override (attrs: { | |||
|
311 | ||||
|
312 | patches = [ | |||
|
313 | ./patches/importlib_metadata/pyproject.patch | |||
|
314 | ]; | |||
|
315 | ||||
|
316 | propagatedBuildInputs = attrs.propagatedBuildInputs ++ [ | |||
|
317 | self."setuptools-scm" | |||
|
318 | ]; | |||
|
319 | ||||
|
320 | }); | |||
|
321 | ||||
|
322 | "zipp" = super."zipp".override (attrs: { | |||
|
323 | patches = [ | |||
|
324 | ./patches/zipp/pyproject.patch | |||
|
325 | ]; | |||
|
326 | propagatedBuildInputs = attrs.propagatedBuildInputs ++ [ | |||
|
327 | self."setuptools-scm" | |||
|
328 | ]; | |||
|
329 | }); | |||
|
330 | ||||
|
331 | "pyramid-apispec" = super."pyramid-apispec".override (attrs: { | |||
|
332 | patches = [ | |||
|
333 | ./patches/pyramid_apispec/setuptools.patch | |||
|
334 | ]; | |||
|
335 | }); | |||
|
336 | ||||
|
337 | "channelstream" = super."channelstream".override (attrs: { | |||
|
338 | patches = [ | |||
|
339 | ./patches/channelstream/setuptools.patch | |||
|
340 | ]; | |||
|
341 | }); | |||
|
342 | ||||
|
343 | "rhodecode-tools" = super."rhodecode-tools".override (attrs: { | |||
|
344 | patches = [ | |||
|
345 | ./patches/rhodecode_tools/setuptools.patch | |||
|
346 | ]; | |||
|
347 | }); | |||
|
348 | ||||
283 | # Avoid that base packages screw up the build process |
|
349 | # Avoid that base packages screw up the build process | |
284 | inherit (basePythonPackages) |
|
350 | inherit (basePythonPackages) | |
285 | setuptools; |
|
351 | setuptools; |
@@ -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.2 |
|
1886 | name = "rhodecode-enterprise-ce-4.25.0"; | |
1887 | buildInputs = [ |
|
1887 | buildInputs = [ | |
1888 | self."pytest" |
|
1888 | self."pytest" | |
1889 | self."py" |
|
1889 | self."py" | |
@@ -2092,6 +2092,17 b' self: super: {' | |||||
2092 | license = [ pkgs.lib.licenses.mit ]; |
|
2092 | license = [ pkgs.lib.licenses.mit ]; | |
2093 | }; |
|
2093 | }; | |
2094 | }; |
|
2094 | }; | |
|
2095 | "setuptools-scm" = super.buildPythonPackage { | |||
|
2096 | name = "setuptools-scm-3.5.0"; | |||
|
2097 | doCheck = false; | |||
|
2098 | src = fetchurl { | |||
|
2099 | url = "https://files.pythonhosted.org/packages/b2/f7/60a645aae001a2e06cf4b8db2fba9d9f36b8fd378f10647e3e218b61b74b/setuptools_scm-3.5.0.tar.gz"; | |||
|
2100 | sha256 = "5bdf21a05792903cafe7ae0c9501182ab52497614fa6b1750d9dbae7b60c1a87"; | |||
|
2101 | }; | |||
|
2102 | meta = { | |||
|
2103 | license = [ pkgs.lib.licenses.psfl ]; | |||
|
2104 | }; | |||
|
2105 | }; | |||
2095 | "simplegeneric" = super.buildPythonPackage { |
|
2106 | "simplegeneric" = super.buildPythonPackage { | |
2096 | name = "simplegeneric-0.8.1"; |
|
2107 | name = "simplegeneric-0.8.1"; | |
2097 | doCheck = false; |
|
2108 | doCheck = false; |
@@ -1212,7 +1212,7 b' def fork_repo(request, apiuser, repoid, ' | |||||
1212 | validate_repo_permissions(apiuser, repoid, repo, _perms) |
|
1212 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |
1213 |
|
1213 | |||
1214 | # check if the regular user has at least fork permissions as well |
|
1214 | # check if the regular user has at least fork permissions as well | |
1215 |
if not HasPermissionAnyApi( |
|
1215 | if not HasPermissionAnyApi(PermissionModel.FORKING_ENABLED)(user=apiuser): | |
1216 | raise JSONRPCForbidden() |
|
1216 | raise JSONRPCForbidden() | |
1217 |
|
1217 | |||
1218 | # check if user can set owner parameter |
|
1218 | # check if user can set owner parameter |
@@ -255,7 +255,7 b' class LocalFileStorage(object):' | |||||
255 |
|
255 | |||
256 | return filename, metadata |
|
256 | return filename, metadata | |
257 |
|
257 | |||
258 | def get_metadata(self, filename): |
|
258 | def get_metadata(self, filename, ignore_missing=False): | |
259 | """ |
|
259 | """ | |
260 | Reads JSON stored metadata for a file |
|
260 | Reads JSON stored metadata for a file | |
261 |
|
261 | |||
@@ -264,6 +264,7 b' class LocalFileStorage(object):' | |||||
264 | """ |
|
264 | """ | |
265 | filename = self.store_path(filename) |
|
265 | filename = self.store_path(filename) | |
266 | filename_meta = filename + '.meta' |
|
266 | filename_meta = filename + '.meta' | |
267 |
|
267 | if ignore_missing and not os.path.isfile(filename_meta): | ||
|
268 | return {} | |||
268 | with open(filename_meta, "rb") as source_meta: |
|
269 | with open(filename_meta, "rb") as source_meta: | |
269 | return json.loads(source_meta.read()) |
|
270 | return json.loads(source_meta.read()) |
@@ -932,8 +932,8 b' def includeme(config):' | |||||
932 | name='edit_repo_perms_branch', |
|
932 | name='edit_repo_perms_branch', | |
933 | pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True) |
|
933 | pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True) | |
934 | config.add_view( |
|
934 | config.add_view( | |
935 | RepoBranchesView, |
|
935 | RepoSettingsBranchPermissionsView, | |
936 | attr='branches', |
|
936 | attr='branch_permissions', | |
937 | route_name='edit_repo_perms_branch', request_method='GET', |
|
937 | route_name='edit_repo_perms_branch', request_method='GET', | |
938 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') |
|
938 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
939 |
|
939 | |||
@@ -950,8 +950,8 b' def includeme(config):' | |||||
950 | config.add_view( |
|
950 | config.add_view( | |
951 | RepoMaintenanceView, |
|
951 | RepoMaintenanceView, | |
952 | attr='repo_maintenance', |
|
952 | attr='repo_maintenance', | |
953 |
route_name='edit_repo_maintenance |
|
953 | route_name='edit_repo_maintenance', request_method='GET', | |
954 | renderer='json', xhr=True) |
|
954 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
955 |
|
955 | |||
956 | config.add_route( |
|
956 | config.add_route( | |
957 | name='edit_repo_maintenance_execute', |
|
957 | name='edit_repo_maintenance_execute', | |
@@ -959,8 +959,8 b' def includeme(config):' | |||||
959 | config.add_view( |
|
959 | config.add_view( | |
960 | RepoMaintenanceView, |
|
960 | RepoMaintenanceView, | |
961 | attr='repo_maintenance_execute', |
|
961 | attr='repo_maintenance_execute', | |
962 | route_name='edit_repo_maintenance', request_method='GET', |
|
962 | route_name='edit_repo_maintenance_execute', request_method='GET', | |
963 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') |
|
963 | renderer='json', xhr=True) | |
964 |
|
964 | |||
965 | # Fields |
|
965 | # Fields | |
966 | config.add_route( |
|
966 | config.add_route( |
@@ -542,6 +542,28 b' class TestRepositoryArchival(object):' | |||||
542 | for header in headers: |
|
542 | for header in headers: | |
543 | assert header in response.headers.items() |
|
543 | assert header in response.headers.items() | |
544 |
|
544 | |||
|
545 | def test_archival_no_hash(self, backend): | |||
|
546 | backend.enable_downloads() | |||
|
547 | commit = backend.repo.get_commit(commit_idx=173) | |||
|
548 | for a_type, content_type, extension in settings.ARCHIVE_SPECS: | |||
|
549 | ||||
|
550 | short = 'plain' + extension | |||
|
551 | fname = commit.raw_id + extension | |||
|
552 | filename = '%s-%s' % (backend.repo_name, short) | |||
|
553 | response = self.app.get( | |||
|
554 | route_path('repo_archivefile', | |||
|
555 | repo_name=backend.repo_name, | |||
|
556 | fname=fname, params={'with_hash': 0})) | |||
|
557 | ||||
|
558 | assert response.status == '200 OK' | |||
|
559 | headers = [ | |||
|
560 | ('Content-Disposition', 'attachment; filename=%s' % filename), | |||
|
561 | ('Content-Type', '%s' % content_type), | |||
|
562 | ] | |||
|
563 | ||||
|
564 | for header in headers: | |||
|
565 | assert header in response.headers.items() | |||
|
566 | ||||
545 | @pytest.mark.parametrize('arch_ext',[ |
|
567 | @pytest.mark.parametrize('arch_ext',[ | |
546 | 'tar', 'rar', 'x', '..ax', '.zipz', 'tar.gz.tar']) |
|
568 | 'tar', 'rar', 'x', '..ax', '.zipz', 'tar.gz.tar']) | |
547 | def test_archival_wrong_ext(self, backend, arch_ext): |
|
569 | def test_archival_wrong_ext(self, backend, arch_ext): |
@@ -34,7 +34,7 b' from rhodecode.lib.auth import (' | |||||
34 | from rhodecode.lib.ext_json import json |
|
34 | from rhodecode.lib.ext_json import json | |
35 | from rhodecode.lib.graphmod import _colored, _dagwalker |
|
35 | from rhodecode.lib.graphmod import _colored, _dagwalker | |
36 | from rhodecode.lib.helpers import RepoPage |
|
36 | from rhodecode.lib.helpers import RepoPage | |
37 | from rhodecode.lib.utils2 import safe_int, safe_str, str2bool |
|
37 | from rhodecode.lib.utils2 import safe_int, safe_str, str2bool, safe_unicode | |
38 | from rhodecode.lib.vcs.exceptions import ( |
|
38 | from rhodecode.lib.vcs.exceptions import ( | |
39 | RepositoryError, CommitDoesNotExistError, |
|
39 | RepositoryError, CommitDoesNotExistError, | |
40 | CommitError, NodeDoesNotExistError, EmptyRepositoryError) |
|
40 | CommitError, NodeDoesNotExistError, EmptyRepositoryError) | |
@@ -110,7 +110,7 b' class RepoChangelogView(RepoAppView):' | |||||
110 |
|
110 | |||
111 | def _check_if_valid_branch(self, branch_name, repo_name, f_path): |
|
111 | def _check_if_valid_branch(self, branch_name, repo_name, f_path): | |
112 | if branch_name not in self.rhodecode_vcs_repo.branches_all: |
|
112 | if branch_name not in self.rhodecode_vcs_repo.branches_all: | |
113 | h.flash('Branch {} is not found.'.format(h.escape(branch_name)), |
|
113 | h.flash(u'Branch {} is not found.'.format(h.escape(safe_unicode(branch_name))), | |
114 | category='warning') |
|
114 | category='warning') | |
115 | redirect_url = h.route_path( |
|
115 | redirect_url = h.route_path( | |
116 | 'repo_commits_file', repo_name=repo_name, |
|
116 | 'repo_commits_file', repo_name=repo_name, |
@@ -674,6 +674,10 b' class RepoCommitsView(RepoAppView):' | |||||
674 | is_repo_comment = comment.repo.repo_id == self.db_repo.repo_id |
|
674 | is_repo_comment = comment.repo.repo_id == self.db_repo.repo_id | |
675 | comment_repo_admin = is_repo_admin and is_repo_comment |
|
675 | comment_repo_admin = is_repo_admin and is_repo_comment | |
676 |
|
676 | |||
|
677 | if comment.draft and not comment_owner: | |||
|
678 | # We never allow to delete draft comments for other than owners | |||
|
679 | raise HTTPNotFound() | |||
|
680 | ||||
677 | if super_admin or comment_owner or comment_repo_admin: |
|
681 | if super_admin or comment_owner or comment_repo_admin: | |
678 | CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user) |
|
682 | CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user) | |
679 | Session().commit() |
|
683 | Session().commit() |
@@ -104,6 +104,9 b' class RepoFeedView(RepoAppView):' | |||||
104 |
|
104 | |||
105 | def _get_commits(self): |
|
105 | def _get_commits(self): | |
106 | pre_load = ['author', 'branch', 'date', 'message', 'parents'] |
|
106 | pre_load = ['author', 'branch', 'date', 'message', 'parents'] | |
|
107 | if self.rhodecode_vcs_repo.is_empty(): | |||
|
108 | return [] | |||
|
109 | ||||
107 | collection = self.rhodecode_vcs_repo.get_commits( |
|
110 | collection = self.rhodecode_vcs_repo.get_commits( | |
108 | branch_name=None, show_hidden=False, pre_load=pre_load, |
|
111 | branch_name=None, show_hidden=False, pre_load=pre_load, | |
109 | translate_tags=False) |
|
112 | translate_tags=False) | |
@@ -137,6 +140,7 b' class RepoFeedView(RepoAppView):' | |||||
137 | language=self.language, |
|
140 | language=self.language, | |
138 | ttl=self.ttl |
|
141 | ttl=self.ttl | |
139 | ) |
|
142 | ) | |
|
143 | ||||
140 | for commit in reversed(self._get_commits()): |
|
144 | for commit in reversed(self._get_commits()): | |
141 | date = self._set_timezone(commit.date) |
|
145 | date = self._set_timezone(commit.date) | |
142 | feed.add_item( |
|
146 | feed.add_item( |
@@ -325,17 +325,18 b' class RepoFilesView(RepoAppView):' | |||||
325 |
|
325 | |||
326 | return lf_enabled |
|
326 | return lf_enabled | |
327 |
|
327 | |||
328 | def _get_archive_name(self, db_repo_name, commit_sha, ext, subrepos=False, path_sha=''): |
|
328 | def _get_archive_name(self, db_repo_name, commit_sha, ext, subrepos=False, path_sha='', with_hash=True): | |
329 | # original backward compat name of archive |
|
329 | # original backward compat name of archive | |
330 | clean_name = safe_str(db_repo_name.replace('/', '_')) |
|
330 | clean_name = safe_str(db_repo_name.replace('/', '_')) | |
331 |
|
331 | |||
332 | # e.g vcsserver.zip |
|
332 | # e.g vcsserver.zip | |
333 | # e.g vcsserver-abcdefgh.zip |
|
333 | # e.g vcsserver-abcdefgh.zip | |
334 | # e.g vcsserver-abcdefgh-defghijk.zip |
|
334 | # e.g vcsserver-abcdefgh-defghijk.zip | |
335 | archive_name = '{}{}{}{}{}'.format( |
|
335 | archive_name = '{}{}{}{}{}{}'.format( | |
336 | clean_name, |
|
336 | clean_name, | |
337 | '-sub' if subrepos else '', |
|
337 | '-sub' if subrepos else '', | |
338 | commit_sha, |
|
338 | commit_sha, | |
|
339 | '-{}'.format('plain') if not with_hash else '', | |||
339 | '-{}'.format(path_sha) if path_sha else '', |
|
340 | '-{}'.format(path_sha) if path_sha else '', | |
340 | ext) |
|
341 | ext) | |
341 | return archive_name |
|
342 | return archive_name | |
@@ -372,6 +373,11 b' class RepoFilesView(RepoAppView):' | |||||
372 | except EmptyRepositoryError: |
|
373 | except EmptyRepositoryError: | |
373 | return Response(_('Empty repository')) |
|
374 | return Response(_('Empty repository')) | |
374 |
|
375 | |||
|
376 | # we used a ref, or a shorter version, lets redirect client ot use explicit hash | |||
|
377 | if commit_id != commit.raw_id: | |||
|
378 | fname='{}{}'.format(commit.raw_id, ext) | |||
|
379 | raise HTTPFound(self.request.current_route_path(fname=fname)) | |||
|
380 | ||||
375 | try: |
|
381 | try: | |
376 | at_path = commit.get_node(at_path).path or default_at_path |
|
382 | at_path = commit.get_node(at_path).path or default_at_path | |
377 | except Exception: |
|
383 | except Exception: | |
@@ -385,7 +391,7 b' class RepoFilesView(RepoAppView):' | |||||
385 | # used for cache etc |
|
391 | # used for cache etc | |
386 | archive_name = self._get_archive_name( |
|
392 | archive_name = self._get_archive_name( | |
387 | self.db_repo_name, commit_sha=short_sha, ext=ext, subrepos=subrepos, |
|
393 | self.db_repo_name, commit_sha=short_sha, ext=ext, subrepos=subrepos, | |
388 | path_sha=path_sha) |
|
394 | path_sha=path_sha, with_hash=with_hash) | |
389 |
|
395 | |||
390 | if not with_hash: |
|
396 | if not with_hash: | |
391 | short_sha = '' |
|
397 | short_sha = '' | |
@@ -394,7 +400,7 b' class RepoFilesView(RepoAppView):' | |||||
394 | # what end client gets served |
|
400 | # what end client gets served | |
395 | response_archive_name = self._get_archive_name( |
|
401 | response_archive_name = self._get_archive_name( | |
396 | self.db_repo_name, commit_sha=short_sha, ext=ext, subrepos=subrepos, |
|
402 | self.db_repo_name, commit_sha=short_sha, ext=ext, subrepos=subrepos, | |
397 | path_sha=path_sha) |
|
403 | path_sha=path_sha, with_hash=with_hash) | |
398 | # remove extension from our archive directory name |
|
404 | # remove extension from our archive directory name | |
399 | archive_dir_name = response_archive_name[:-len(ext)] |
|
405 | archive_dir_name = response_archive_name[:-len(ext)] | |
400 |
|
406 | |||
@@ -404,9 +410,10 b' class RepoFilesView(RepoAppView):' | |||||
404 | cached_archive_path = None |
|
410 | cached_archive_path = None | |
405 |
|
411 | |||
406 | if archive_cache_enabled: |
|
412 | if archive_cache_enabled: | |
407 | # check if we it's ok to write |
|
413 | # check if we it's ok to write, and re-create the archive cache | |
408 | if not os.path.isdir(CONFIG['archive_cache_dir']): |
|
414 | if not os.path.isdir(CONFIG['archive_cache_dir']): | |
409 | os.makedirs(CONFIG['archive_cache_dir']) |
|
415 | os.makedirs(CONFIG['archive_cache_dir']) | |
|
416 | ||||
410 | cached_archive_path = os.path.join( |
|
417 | cached_archive_path = os.path.join( | |
411 | CONFIG['archive_cache_dir'], archive_name) |
|
418 | CONFIG['archive_cache_dir'], archive_name) | |
412 | if os.path.isfile(cached_archive_path): |
|
419 | if os.path.isfile(cached_archive_path): |
@@ -165,7 +165,7 b' class RepoForksView(RepoAppView, DataGri' | |||||
165 |
|
165 | |||
166 | @LoginRequired() |
|
166 | @LoginRequired() | |
167 | @NotAnonymous() |
|
167 | @NotAnonymous() | |
168 |
@HasPermissionAnyDecorator('hg.admin', |
|
168 | @HasPermissionAnyDecorator('hg.admin', PermissionModel.FORKING_ENABLED) | |
169 | @HasRepoPermissionAnyDecorator( |
|
169 | @HasRepoPermissionAnyDecorator( | |
170 | 'repository.read', 'repository.write', 'repository.admin') |
|
170 | 'repository.read', 'repository.write', 'repository.admin') | |
171 | def repo_fork_new(self): |
|
171 | def repo_fork_new(self): | |
@@ -191,7 +191,7 b' class RepoForksView(RepoAppView, DataGri' | |||||
191 |
|
191 | |||
192 | @LoginRequired() |
|
192 | @LoginRequired() | |
193 | @NotAnonymous() |
|
193 | @NotAnonymous() | |
194 |
@HasPermissionAnyDecorator('hg.admin', |
|
194 | @HasPermissionAnyDecorator('hg.admin', PermissionModel.FORKING_ENABLED) | |
195 | @HasRepoPermissionAnyDecorator( |
|
195 | @HasRepoPermissionAnyDecorator( | |
196 | 'repository.read', 'repository.write', 'repository.admin') |
|
196 | 'repository.read', 'repository.write', 'repository.admin') | |
197 | @CSRFRequired() |
|
197 | @CSRFRequired() |
@@ -1748,6 +1748,10 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1748 | is_repo_comment = comment.repo.repo_name == self.db_repo_name |
|
1748 | is_repo_comment = comment.repo.repo_name == self.db_repo_name | |
1749 | comment_repo_admin = is_repo_admin and is_repo_comment |
|
1749 | comment_repo_admin = is_repo_admin and is_repo_comment | |
1750 |
|
1750 | |||
|
1751 | if comment.draft and not comment_owner: | |||
|
1752 | # We never allow to delete draft comments for other than owners | |||
|
1753 | raise HTTPNotFound() | |||
|
1754 | ||||
1751 | if super_admin or comment_owner or comment_repo_admin: |
|
1755 | if super_admin or comment_owner or comment_repo_admin: | |
1752 | old_calculated_status = comment.pull_request.calculated_review_status() |
|
1756 | old_calculated_status = comment.pull_request.calculated_review_status() | |
1753 | CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user) |
|
1757 | CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user) |
@@ -89,6 +89,14 b' class SshWrapper(object):' | |||||
89 |
|
89 | |||
90 | return conn |
|
90 | return conn | |
91 |
|
91 | |||
|
92 | def maybe_translate_repo_uid(self, repo_name): | |||
|
93 | if repo_name.startswith('_'): | |||
|
94 | from rhodecode.model.repo import RepoModel | |||
|
95 | by_id_match = RepoModel().get_repo_by_id(repo_name) | |||
|
96 | if by_id_match: | |||
|
97 | repo_name = by_id_match.repo_name | |||
|
98 | return repo_name | |||
|
99 | ||||
92 | def get_repo_details(self, mode): |
|
100 | def get_repo_details(self, mode): | |
93 | vcs_type = mode if mode in ['svn', 'hg', 'git'] else None |
|
101 | vcs_type = mode if mode in ['svn', 'hg', 'git'] else None | |
94 | repo_name = None |
|
102 | repo_name = None | |
@@ -97,14 +105,14 b' class SshWrapper(object):' | |||||
97 | hg_match = re.match(hg_pattern, self.command) |
|
105 | hg_match = re.match(hg_pattern, self.command) | |
98 | if hg_match is not None: |
|
106 | if hg_match is not None: | |
99 | vcs_type = 'hg' |
|
107 | vcs_type = 'hg' | |
100 | repo_name = hg_match.group(1).strip('/') |
|
108 | repo_name = self.maybe_translate_repo_uid(hg_match.group(1).strip('/')) | |
101 | return vcs_type, repo_name, mode |
|
109 | return vcs_type, repo_name, mode | |
102 |
|
110 | |||
103 | git_pattern = r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$' |
|
111 | git_pattern = r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$' | |
104 | git_match = re.match(git_pattern, self.command) |
|
112 | git_match = re.match(git_pattern, self.command) | |
105 | if git_match is not None: |
|
113 | if git_match is not None: | |
106 | vcs_type = 'git' |
|
114 | vcs_type = 'git' | |
107 | repo_name = git_match.group(2).strip('/') |
|
115 | repo_name = self.maybe_translate_repo_uid(git_match.group(2).strip('/')) | |
108 | mode = git_match.group(1) |
|
116 | mode = git_match.group(1) | |
109 | return vcs_type, repo_name, mode |
|
117 | return vcs_type, repo_name, mode | |
110 |
|
118 |