From ebd0ba9561050c68996934f668b6fa0414ad92e4 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 13:51:36 +0200 Subject: [PATCH 01/99] release: Bump version 5.3.0 to 5.4.0 --- .bumpversion.cfg | 2 +- rhodecode/VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 622f9b2f..db9105cf 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 5.3.0 +current_version = 5.4.0 message = release: Bump version {current_version} to {new_version} [bumpversion:file:rhodecode/VERSION] diff --git a/rhodecode/VERSION b/rhodecode/VERSION index e230c839..1e20ec35 100644 --- a/rhodecode/VERSION +++ b/rhodecode/VERSION @@ -1 +1 @@ -5.3.0 \ No newline at end of file +5.4.0 \ No newline at end of file From 61cebd4f25e9897e3112b04b87cbe91df92ff2f1 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 13:53:54 +0200 Subject: [PATCH 02/99] deps: bumped zope.interface==7.1.0 --- requirements.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index d3c228b9..f72a757f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,7 +33,7 @@ channelstream==0.7.1 gevent==24.2.1 greenlet==3.0.3 zope.event==5.0.0 - zope.interface==7.0.3 + zope.interface==7.1.0 itsdangerous==1.1.0 marshmallow==2.18.0 pyramid==2.0.2 @@ -46,7 +46,7 @@ channelstream==0.7.1 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==7.0.3 + zope.interface==7.1.0 pyramid-jinja2==2.10 jinja2==3.1.2 markupsafe==2.1.2 @@ -61,7 +61,7 @@ channelstream==0.7.1 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==7.0.3 + zope.interface==7.1.0 zope.deprecation==5.0.0 python-dateutil==2.8.2 six==1.16.0 @@ -93,7 +93,7 @@ gunicorn==23.0.0 gevent==24.2.1 greenlet==3.0.3 zope.event==5.0.0 - zope.interface==7.0.3 + zope.interface==7.1.0 ipython==8.26.0 decorator==5.1.1 jedi==0.19.1 @@ -201,13 +201,13 @@ pyramid-mailer==0.15.1 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==7.0.3 + zope.interface==7.1.0 repoze.sendmail==4.4.1 transaction==5.0.0 - zope.interface==7.0.3 - zope.interface==7.0.3 + zope.interface==7.1.0 + zope.interface==7.1.0 transaction==5.0.0 - zope.interface==7.0.3 + zope.interface==7.1.0 pyramid-mako==1.1.0 mako==1.2.4 markupsafe==2.1.2 @@ -221,7 +221,7 @@ pyramid-mako==1.1.0 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==7.0.3 + zope.interface==7.1.0 python-ldap==3.4.3 pyasn1==0.4.8 pyasn1-modules==0.2.8 From 9153c3cd7c941c298cbf1991b67c3dea95543715 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 13:56:41 +0200 Subject: [PATCH 03/99] deps: bumped beaker==1.13.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f72a757f..cb154a83 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ alembic==1.13.1 typing_extensions==4.12.2 async-timeout==4.0.3 babel==2.12.1 -beaker==1.12.1 +beaker==1.13.0 celery==5.3.6 billiard==4.2.0 click==8.1.3 From 32e1072f99f51a850dbb47d316f097c1129916e8 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 13:59:40 +0200 Subject: [PATCH 04/99] deps: bumped gevent and greenlet --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index cb154a83..aa294553 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ alembic==1.13.1 mako==1.2.4 markupsafe==2.1.2 sqlalchemy==1.4.52 - greenlet==3.0.3 + greenlet==3.1.1 typing_extensions==4.12.2 async-timeout==4.0.3 babel==2.12.1 @@ -30,8 +30,8 @@ celery==5.3.6 tzdata==2024.1 vine==5.1.0 channelstream==0.7.1 - gevent==24.2.1 - greenlet==3.0.3 + gevent==24.10.3 + greenlet==3.1.1 zope.event==5.0.0 zope.interface==7.1.0 itsdangerous==1.1.0 @@ -90,8 +90,8 @@ formencode==2.1.0 fsspec==2024.9.0 gunicorn==23.0.0 packaging==24.1 -gevent==24.2.1 - greenlet==3.0.3 +gevent==24.10.3 + greenlet==3.1.1 zope.event==5.0.0 zope.interface==7.1.0 ipython==8.26.0 @@ -278,7 +278,7 @@ sshpubkeys==3.3.1 ecdsa==0.18.0 six==1.16.0 sqlalchemy==1.4.52 - greenlet==3.0.3 + greenlet==3.1.1 typing_extensions==4.12.2 supervisor==4.2.5 tzlocal==4.3 From aea3e2d9eff4d23b7dbdaf0dd78761e82bffbdf1 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 14:00:17 +0200 Subject: [PATCH 05/99] deps: bumped webob==1.8.8 --- requirements.txt | 8 ++++---- requirements_test.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index aa294553..c756a186 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,7 +44,7 @@ channelstream==0.7.1 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.7 + webob==1.8.8 zope.deprecation==5.0.0 zope.interface==7.1.0 pyramid-jinja2==2.10 @@ -59,7 +59,7 @@ channelstream==0.7.1 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.7 + webob==1.8.8 zope.deprecation==5.0.0 zope.interface==7.1.0 zope.deprecation==5.0.0 @@ -199,7 +199,7 @@ pyramid-mailer==0.15.1 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.7 + webob==1.8.8 zope.deprecation==5.0.0 zope.interface==7.1.0 repoze.sendmail==4.4.1 @@ -219,7 +219,7 @@ pyramid-mako==1.1.0 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.7 + webob==1.8.8 zope.deprecation==5.0.0 zope.interface==7.1.0 python-ldap==3.4.3 diff --git a/requirements_test.txt b/requirements_test.txt index 1e7cbcc6..5376b367 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -41,7 +41,7 @@ webtest==3.0.0 beautifulsoup4==4.12.3 soupsieve==2.5 waitress==3.0.0 - webob==1.8.7 + webob==1.8.8 # RhodeCode test-data rc_testdata @ https://code.rhodecode.com/upstream/rc-testdata-dist/raw/77378e9097f700b4c1b9391b56199fe63566b5c9/rc_testdata-0.11.0.tar.gz#egg=rc_testdata From 7a7a07d0b1117c26244ba647dd2d0b51a05046cd Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 14:50:32 +0200 Subject: [PATCH 06/99] fix(svn): cache path check similar like for git --- rhodecode/lib/vcs/client_http.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py index f81ce2c0..195d854e 100644 --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -289,7 +289,8 @@ class RemoteRepo(object): 'revision', 'tree_items', 'ctx_list', 'ctx_branch', 'ctx_description', 'bulk_request', - 'assert_correct_path' + 'assert_correct_path', + 'is_path_valid_repository', ] if local_cache_on and name in cache_methods: From 35a099d995104fb23ee7d27dc4a71e808a117991 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 15:42:29 +0200 Subject: [PATCH 07/99] fix: version, ensure no newlines are present --- rhodecode/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py index 881b10b9..8754f00a 100644 --- a/rhodecode/__init__.py +++ b/rhodecode/__init__.py @@ -30,7 +30,7 @@ log.debug(f'{now} Starting RhodeCode imports...') VERSION = tuple(open(os.path.join( - os.path.dirname(__file__), 'VERSION')).read().split('.')) + os.path.dirname(__file__), 'VERSION')).read().strip().split('.')) BACKENDS = collections.OrderedDict() From 7ac0cc7bce33a797e4c037572501dea6f787b5c6 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 21 Oct 2024 16:09:52 +0200 Subject: [PATCH 08/99] docs: added 5.3.1 release info --- docs/release-notes/release-notes-5.3.1.rst | 41 ++++++++++++++++++++++ docs/release-notes/release-notes.rst | 1 + 2 files changed, 42 insertions(+) create mode 100644 docs/release-notes/release-notes-5.3.1.rst diff --git a/docs/release-notes/release-notes-5.3.1.rst b/docs/release-notes/release-notes-5.3.1.rst new file mode 100644 index 00000000..3602500a --- /dev/null +++ b/docs/release-notes/release-notes-5.3.1.rst @@ -0,0 +1,41 @@ +|RCE| 5.3.1 |RNS| +----------------- + +Release Date +^^^^^^^^^^^^ + +- 2024-09-21 + + +New Features +^^^^^^^^^^^^ + + + +General +^^^^^^^ + + + +Security +^^^^^^^^ + + + +Performance +^^^^^^^^^^^ + +- svn: small performance bump by restoring path cache in previous builds + + +Fixes +^^^^^ + +- Fixed problems with incorrect version string +- Fixed accidentally removed svn path check cache + + +Upgrade notes +^^^^^^^^^^^^^ + +- RhodeCode 5.3.1 is unscheduled security release to address some build issues with 5.X images diff --git a/docs/release-notes/release-notes.rst b/docs/release-notes/release-notes.rst index 2f65863c..fad8bad0 100644 --- a/docs/release-notes/release-notes.rst +++ b/docs/release-notes/release-notes.rst @@ -10,6 +10,7 @@ Release Notes .. toctree:: :maxdepth: 1 + release-notes-5.3.1.rst release-notes-5.3.0.rst release-notes-5.2.1.rst release-notes-5.2.0.rst From b99ad6fb8213db6f6cb9740f1c0799bad7f58dda Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 22 Oct 2024 09:11:42 +0200 Subject: [PATCH 09/99] core: setuptools, switched from deprecated setup.py to pyproject.toml --- .bumpversion.cfg | 5 -- pyproject.toml | 135 +++++++++++++++++++++++++++++++ setup.py | 201 ----------------------------------------------- 3 files changed, 135 insertions(+), 206 deletions(-) delete mode 100644 .bumpversion.cfg create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index db9105cf..00000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[bumpversion] -current_version = 5.4.0 -message = release: Bump version {current_version} to {new_version} - -[bumpversion:file:rhodecode/VERSION] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..84e5acbd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,135 @@ +[build-system] +requires = ["setuptools>=61.0.0", "wheel", "pastescript"] +build-backend = "setuptools.build_meta" + +[project] +name = "rhodecode-enterprise-ce" +description = "Enterprise Source Code Management Platform" +authors = [ + {name = "RhodeCode GmbH", email = "support@rhodecode.com"}, +] +keywords = [ + 'rhodecode', 'mercurial', 'git', 'svn', + 'code review', + 'repo groups', 'ldap', 'repository management', 'hgweb', + 'hgwebdir', 'gitweb', 'serving hgweb', +] +license = {text = "GPL V3"} +requires-python = ">=3.10" +dynamic = ["version", "readme", "dependencies", "optional-dependencies"] +classifiers = [ + 'Development Status :: 6 - Mature', + 'Intended Audience :: Developers', + 'Operating System :: OS Independent', + 'Topic :: Software Development :: Version Control', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + 'Programming Language :: Python :: 3.10', +] + +[project.entry-points."paste.app_factory"] +main = "rhodecode.config.middleware:make_pyramid_app" + +[project.entry-points."pyramid.pshell_runner"] +ipython = "rhodecode.lib.pyramid_shell:ipython_shell_runner" + +[project.entry-points."beaker.backends"] +memorylru_base="rhodecode.lib.memory_lru_dict:MemoryLRUNamespaceManagerBase" +memorylru_debug="rhodecode.lib.memory_lru_dict:MemoryLRUNamespaceManagerDebug" + +[project.scripts] + +rc-setup-app = "rhodecode.lib.rc_commands.setup_rc:main" +rc-upgrade-db = "rhodecode.lib.rc_commands.upgrade_db:main" +rc-ishell = "rhodecode.lib.rc_commands.ishell:main" +rc-add-artifact = "rhodecode.lib.rc_commands.add_artifact:main" +rc-migrate-artifact = "rhodecode.lib.rc_commands.migrate_artifact:main" +rc-ssh-wrapper = "rhodecode.apps.ssh_support.lib.ssh_wrapper_v1:main" +rc-ssh-wrapper-v2 = "rhodecode.apps.ssh_support.lib.ssh_wrapper_v2:main" + +[tool.setuptools] +packages = ["rhodecode"] +include-package-data = true + +[tools.setuptools.package-data] +"" = ['*.txt', '*.rst'] +"configs" = ['*.ini'] +"rhodecode" = ['VERSION', 'i18n/*/LC_MESSAGES/*.mo', ] + +[tool.setuptools.exclude-package-data] +"rhodecode" = ["__pycache__"] + +[tool.setuptools.dynamic] +readme = {file = ["README.rst"], content-type = "text/rst"} +version = {file = "rhodecode/VERSION"} +dependencies = {file = ["requirements.txt"]} +optional-dependencies.tests = {file = ["requirements_test.txt"]} + +[tool.ruff] + +select = [ + # Pyflakes + "F", + # Pycodestyle + "E", + "W", + # isort + "I001" +] + +ignore = [ + "E501", # line too long, handled by black +] + +# Same as Black. +line-length = 120 + +[tool.ruff.isort] + +known-first-party = ["rhodecode"] + +[tool.ruff.format] + +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + + +[tool.bumpversion] +current_version = "5.4.0" +parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" +serialize = ["{major}.{minor}.{patch}"] +search = "{current_version}" +replace = "{new_version}" +regex = false +ignore_missing_version = false +ignore_missing_files = false +tag = false +sign_tags = false +tag_name = "v{new_version}" +tag_message = "release(version-bump): {current_version} → {new_version}" +allow_dirty = false +commit = false +message = "release(version-bump): {current_version} → {new_version}" +commit_args = "" +setup_hooks = [] +pre_commit_hooks = [] +post_commit_hooks = [] + + +# message_extractors={ +# 'rhodecode': [ +# ('**.py', 'python', None), +# ('**.js', 'javascript', None), +# ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}), +# ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}), +# ('public/**', 'ignore', None), +# ] +# }, diff --git a/setup.py b/setup.py deleted file mode 100644 index b0165a4e..00000000 --- a/setup.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3 -# (only), as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# 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 early to make sure things are patched up properly -from setuptools import setup, find_packages, Extension - -import os -import re -import sys -import pkgutil -import platform -import codecs - -import pip - -pip_major_version = int(pip.__version__.split(".")[0]) -if pip_major_version >= 20: - from pip._internal.req import parse_requirements - from pip._internal.network.session import PipSession -elif pip_major_version >= 10: - from pip._internal.req import parse_requirements - from pip._internal.download import PipSession -else: - from pip.req import parse_requirements - from pip.download import PipSession - - -def get_package_name(req_object): - package_name = None - try: - from pip._internal.req.constructors import install_req_from_parsed_requirement - except ImportError: - install_req_from_parsed_requirement = None - - # In 20.1 of pip, the requirements object changed - if hasattr(req_object, 'req'): - package_name = req_object.req.name - - if package_name is None: - if install_req_from_parsed_requirement: - package = install_req_from_parsed_requirement(req_object) - package_name = package.req.name - - if package_name is None: - # fallback for older pip - package_name = re.split('===|<=|!=|==|>=|~=|<|>', req_object.requirement)[0] - - return package_name - - -if sys.version_info < (3, 10): - raise Exception('RhodeCode requires Python 3.10 or later') - -here = os.path.abspath(os.path.dirname(__file__)) - -# defines current platform -__platform__ = platform.system() -__license__ = 'AGPLv3, and Commercial License' -__author__ = 'RhodeCode GmbH' -__url__ = 'https://code.rhodecode.com' -is_windows = __platform__ in ('Windows',) - - -def _get_requirements(req_filename, exclude=None, extras=None): - extras = extras or [] - exclude = exclude or [] - - try: - parsed = parse_requirements( - os.path.join(here, req_filename), session=PipSession()) - except TypeError: - # try pip < 6.0.0, that doesn't support session - parsed = parse_requirements(os.path.join(here, req_filename)) - - requirements = [] - for int_req in parsed: - req_name = get_package_name(int_req) - if req_name not in exclude: - requirements.append(req_name) - return requirements + extras - - -# requirements extract -setup_requirements = ['PasteScript'] -install_requirements = _get_requirements( - 'requirements.txt', exclude=['setuptools', 'entrypoints']) -test_requirements = _get_requirements( - 'requirements_test.txt') - - -def get_version(): - here = os.path.abspath(os.path.dirname(__file__)) - ver_file = os.path.join(here, "rhodecode", "VERSION") - with open(ver_file, "rt") as f: - version = f.read().strip() - - return version - - -# additional files that goes into package itself -package_data = { - '': ['*.txt', '*.rst'], - 'configs': ['*.ini'], - 'rhodecode': ['VERSION', 'i18n/*/LC_MESSAGES/*.mo', ], -} - -description = 'Source Code Management Platform' -keywords = ' '.join([ - 'rhodecode', 'mercurial', 'git', 'svn', - 'code review', - 'repo groups', 'ldap', 'repository management', 'hgweb', - 'hgwebdir', 'gitweb', 'serving hgweb', -]) - - -# README/DESCRIPTION generation -readme_file = 'README.rst' -changelog_file = 'CHANGES.rst' -try: - long_description = codecs.open(readme_file).read() + '\n\n' + \ - codecs.open(changelog_file).read() -except IOError as err: - sys.stderr.write( - "[WARNING] Cannot find file specified as long_description (%s)\n " - "or changelog (%s) skipping that file" % (readme_file, changelog_file)) - long_description = description - - -setup( - name='rhodecode-enterprise-ce', - version=get_version(), - description=description, - long_description=long_description, - keywords=keywords, - license=__license__, - author=__author__, - author_email='support@rhodecode.com', - url=__url__, - setup_requires=setup_requirements, - install_requires=install_requirements, - tests_require=test_requirements, - zip_safe=False, - packages=find_packages(exclude=["docs", "tests*"]), - package_data=package_data, - include_package_data=True, - classifiers=[ - 'Development Status :: 6 - Mature', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'Operating System :: OS Independent', - 'Topic :: Software Development :: Version Control', - 'License :: OSI Approved :: Affero GNU General Public License v3 or later (AGPLv3+)', - 'Programming Language :: Python :: 3.10', - ], - message_extractors={ - 'rhodecode': [ - ('**.py', 'python', None), - ('**.js', 'javascript', None), - ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}), - ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}), - ('public/**', 'ignore', None), - ] - }, - - entry_points={ - 'paste.app_factory': [ - 'main=rhodecode.config.middleware:make_pyramid_app', - ], - 'pyramid.pshell_runner': [ - 'ipython = rhodecode.lib.pyramid_shell:ipython_shell_runner', - ], - 'console_scripts': [ - 'rc-setup-app=rhodecode.lib.rc_commands.setup_rc:main', - 'rc-upgrade-db=rhodecode.lib.rc_commands.upgrade_db:main', - 'rc-ishell=rhodecode.lib.rc_commands.ishell:main', - 'rc-add-artifact=rhodecode.lib.rc_commands.add_artifact:main', - 'rc-migrate-artifact=rhodecode.lib.rc_commands.migrate_artifact:main', - 'rc-ssh-wrapper=rhodecode.apps.ssh_support.lib.ssh_wrapper_v1:main', - 'rc-ssh-wrapper-v2=rhodecode.apps.ssh_support.lib.ssh_wrapper_v2:main', - ], - 'beaker.backends': [ - 'memorylru_base=rhodecode.lib.memory_lru_dict:MemoryLRUNamespaceManagerBase', - 'memorylru_debug=rhodecode.lib.memory_lru_dict:MemoryLRUNamespaceManagerDebug' - ] - }, -) From 0800cfc3fe5904b0041d5405a422e85d55accd94 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 22 Oct 2024 12:12:52 +0200 Subject: [PATCH 10/99] caches: allow force re-init of cache region for re-creating the cache --- rhodecode/lib/rc_cache/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rhodecode/lib/rc_cache/utils.py b/rhodecode/lib/rc_cache/utils.py index 5d0c39f4..9a8a9baf 100644 --- a/rhodecode/lib/rc_cache/utils.py +++ b/rhodecode/lib/rc_cache/utils.py @@ -174,7 +174,7 @@ def backend_key_generator(backend): return wrapper -def get_or_create_region(region_name, region_namespace: str = None, use_async_runner=False): +def get_or_create_region(region_name, region_namespace: str = None, use_async_runner=False, force=False): from .backends import FileNamespaceBackend from . import async_creation_runner @@ -191,7 +191,7 @@ def get_or_create_region(region_name, region_namespace: str = None, use_async_ru raise ValueError(f'{FileNamespaceBackend} used requires to specify region_namespace param') region_exist = region_meta.dogpile_cache_regions.get(region_namespace) - if region_exist: + if region_exist and not force: log.debug('Using already configured region: %s', region_namespace) return region_exist From a36bd6781b4d2278f0fe83d8b543432b5cdedc78 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 22 Oct 2024 21:56:08 +0200 Subject: [PATCH 11/99] fix(configs): fixed some config bad default setting --- configs/development.ini | 2 +- configs/production.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/development.ini b/configs/development.ini index b0f38a8d..5d4100b7 100644 --- a/configs/development.ini +++ b/configs/development.ini @@ -547,7 +547,7 @@ rc_cache.cache_repo.expiration_time = 2592000 ; types are file, ext:redis, ext:database, ext:memcached ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session #beaker.session.type = file -#beaker.session.data_dir = %(here)s/data/sessions +#beaker.session.data_dir = /var/opt/rhodecode_data/sessions ; Redis based sessions beaker.session.type = ext:redis diff --git a/configs/production.ini b/configs/production.ini index 3dfd209f..8a2713cf 100644 --- a/configs/production.ini +++ b/configs/production.ini @@ -515,7 +515,7 @@ rc_cache.cache_repo.expiration_time = 2592000 ; types are file, ext:redis, ext:database, ext:memcached ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session #beaker.session.type = file -#beaker.session.data_dir = %(here)s/data/sessions +#beaker.session.data_dir = /var/opt/rhodecode_data/sessions ; Redis based sessions beaker.session.type = ext:redis From a019d888c3ce6e0a9d9f002bd90552beee5b2a59 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 22 Oct 2024 21:56:30 +0200 Subject: [PATCH 12/99] fix(tests): fixed few tests --- rhodecode/tests/models/test_permissions.py | 87 ++++++++++------------ rhodecode/tests/rhodecode.ini | 4 +- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/rhodecode/tests/models/test_permissions.py b/rhodecode/tests/models/test_permissions.py index 1954ac3f..2efb6475 100644 --- a/rhodecode/tests/models/test_permissions.py +++ b/rhodecode/tests/models/test_permissions.py @@ -54,21 +54,21 @@ class TestPermissions(object): # TODO: User creation is a duplicate of test_nofitications, check # if that can be unified self.u1 = UserModel().create_or_update( - username=u'u1', password=u'qweqwe', - email=u'u1@rhodecode.org', firstname=u'u1', lastname=u'u1' + username='u1', password='qweqwe', + email='u1@rhodecode.org', firstname='u1', lastname='u1' ) self.u2 = UserModel().create_or_update( - username=u'u2', password=u'qweqwe', - email=u'u2@rhodecode.org', firstname=u'u2', lastname=u'u2' + username='u2', password='qweqwe', + email='u2@rhodecode.org', firstname='u2', lastname='u2' ) self.u3 = UserModel().create_or_update( - username=u'u3', password=u'qweqwe', - email=u'u3@rhodecode.org', firstname=u'u3', lastname=u'u3' + username='u3', password='qweqwe', + email='u3@rhodecode.org', firstname='u3', lastname='u3' ) self.anon = User.get_default_user() self.a1 = UserModel().create_or_update( - username=u'a1', password=u'qweqwe', - email=u'a1@rhodecode.org', firstname=u'a1', lastname=u'a1', + username='a1', password='qweqwe', + email='a1@rhodecode.org', firstname='a1', lastname='a1', admin=True ) Session().commit() @@ -180,12 +180,11 @@ class TestPermissions(object): repo_type='hg', repo_group=self.g1, cur_user=self.u1,) - assert repo_perms(self.anon) == { - repo_name_in_group: 'repository.read', - 'vcs_test_git': 'repository.read', - 'vcs_test_hg': 'repository.read', - 'vcs_test_svn': 'repository.read', - } + assert repo_perms(self.anon)[repo_name_in_group] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_git'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_hg'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_svn'] == 'repository.read' + # Now change default user permissions new_perm = 'repository.write' perm_updates = [ @@ -195,12 +194,10 @@ class TestPermissions(object): repo_group=self.g1, perm_updates=perm_updates, recursive='all') Session().commit() - assert repo_perms(self.anon) == { - repo_name_in_group: new_perm, - 'vcs_test_git': 'repository.read', - 'vcs_test_hg': 'repository.read', - 'vcs_test_svn': 'repository.read', - } + assert repo_perms(self.anon)[repo_name_in_group] == new_perm + assert repo_perms(self.anon)['vcs_test_git'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_hg'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_svn'] == 'repository.read' # NOW MARK repo as private changes = { @@ -211,12 +208,10 @@ class TestPermissions(object): Session().commit() # Private repo sets 'none' permission for default user - assert repo_perms(self.anon) == { - repo_name_in_group: 'repository.none', - 'vcs_test_git': 'repository.read', - 'vcs_test_hg': 'repository.read', - 'vcs_test_svn': 'repository.read', - } + assert repo_perms(self.anon)[repo_name_in_group] == 'repository.none' + assert repo_perms(self.anon)['vcs_test_git'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_hg'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_svn'] == 'repository.read' # apply same logic of "updated" recursive, but now the anon permissions should be not be impacted new_perm = 'repository.write' @@ -227,12 +222,10 @@ class TestPermissions(object): repo_group=self.g1, perm_updates=perm_updates, recursive='all') Session().commit() - assert repo_perms(self.anon) == { - repo_name_in_group: 'repository.none', - 'vcs_test_git': 'repository.read', - 'vcs_test_hg': 'repository.read', - 'vcs_test_svn': 'repository.read', - } + assert repo_perms(self.anon)[repo_name_in_group] == 'repository.none' + assert repo_perms(self.anon)['vcs_test_git'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_hg'] == 'repository.read' + assert repo_perms(self.anon)['vcs_test_svn'] == 'repository.read' def test_propagated_permission_from_users_group_by_explicit_perms_exist( self, repo_name): @@ -301,10 +294,10 @@ class TestPermissions(object): self.g2 = fixture.create_repo_group('group2', skip_if_exists=True) # both perms should be read ! assert group_perms(self.u1) == \ - {u'group1': u'group.read', u'group2': u'group.read'} + {'group1': 'group.read', 'group2': 'group.read'} assert group_perms(self.anon) == \ - {u'group1': u'group.read', u'group2': u'group.read'} + {'group1': 'group.read', 'group2': 'group.read'} # Change perms to none for both groups RepoGroupModel().grant_user_permission( @@ -313,9 +306,9 @@ class TestPermissions(object): repo_group=self.g2, user=self.anon, perm='group.none') assert group_perms(self.u1) == \ - {u'group1': u'group.none', u'group2': u'group.none'} + {'group1': 'group.none', 'group2': 'group.none'} assert group_perms(self.anon) == \ - {u'group1': u'group.none', u'group2': u'group.none'} + {'group1': 'group.none', 'group2': 'group.none'} # add repo to group name = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm']) @@ -325,9 +318,9 @@ class TestPermissions(object): cur_user=self.u1,) assert group_perms(self.u1) == \ - {u'group1': u'group.none', u'group2': u'group.none'} + {'group1': 'group.none', 'group2': 'group.none'} assert group_perms(self.anon) == \ - {u'group1': u'group.none', u'group2': u'group.none'} + {'group1': 'group.none', 'group2': 'group.none'} # grant permission for u2 ! RepoGroupModel().grant_user_permission( @@ -339,16 +332,16 @@ class TestPermissions(object): # u1 and anon should have not change perms while u2 should ! assert group_perms(self.u1) == \ - {u'group1': u'group.none', u'group2': u'group.none'} + {'group1': 'group.none', 'group2': 'group.none'} assert group_perms(self.u2) == \ - {u'group1': u'group.read', u'group2': u'group.read'} + {'group1': 'group.read', 'group2': 'group.read'} assert group_perms(self.anon) == \ - {u'group1': u'group.none', u'group2': u'group.none'} + {'group1': 'group.none', 'group2': 'group.none'} def test_repo_group_user_as_user_group_member(self): # create Group1 self.g1 = fixture.create_repo_group('group1', skip_if_exists=True) - assert group_perms(self.anon) == {u'group1': u'group.read'} + assert group_perms(self.anon) == {'group1': 'group.read'} # set default permission to none RepoGroupModel().grant_user_permission( @@ -368,8 +361,8 @@ class TestPermissions(object): # add some user to that group # check his permissions - assert group_perms(self.anon) == {u'group1': u'group.none'} - assert group_perms(self.u1) == {u'group1': u'group.none'} + assert group_perms(self.anon) == {'group1': 'group.none'} + assert group_perms(self.u1) == {'group1': 'group.none'} # grant ug1 read permissions for RepoGroupModel().grant_user_group_permission( @@ -383,8 +376,8 @@ class TestPermissions(object): .scalar() assert obj.permission.permission_name == 'group.read' - assert group_perms(self.anon) == {u'group1': u'group.none'} - assert group_perms(self.u1) == {u'group1': u'group.read'} + assert group_perms(self.anon) == {'group1': 'group.none'} + assert group_perms(self.u1) == {'group1': 'group.read'} def test_inherited_permissions_from_default_on_user_enabled(self): # enable fork and create on default user @@ -598,7 +591,7 @@ class TestPermissions(object): # Make him owner of the repo group self.g1.user = self.u1 - assert group_perms(self.u1) == {u'test1': 'group.admin'} + assert group_perms(self.u1) == {'test1': 'group.admin'} def assert_user_perm_equal( self, user, change_factor=0, compare_keys=None): diff --git a/rhodecode/tests/rhodecode.ini b/rhodecode/tests/rhodecode.ini index 6e269065..ab57657f 100644 --- a/rhodecode/tests/rhodecode.ini +++ b/rhodecode/tests/rhodecode.ini @@ -229,7 +229,7 @@ license_token = abra-cada-bra1-rce3 license.hide_license_info = false ; Import EE license from this license path -#license.import_path = %(here)s/rhodecode_enterprise.license +license.import_path = %(here)s/rhodecode_enterprise.license ; import license 'if-missing' or 'force' (always override) ; if-missing means apply license if it doesn't exist. 'force' option always overrides it @@ -645,7 +645,7 @@ vcs.scm_app_implementation = http ; `http` - use http-rpc backend (default) ; `celery` - use celery based hooks #DEPRECATED:vcs.hooks.protocol = http -vcs.hooks.protocol = http +vcs.hooks.protocol.v2 = celery ; Host on which this instance is listening for hooks. vcsserver will call this host to pull/push hooks so it should be ; accessible via network. From 6cf678911a942534d302b09d07bab23233cd7d58 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 22 Oct 2024 22:15:23 +0200 Subject: [PATCH 13/99] fix(tests): fixed more permissions tests --- rhodecode/tests/models/common.py | 24 +++++++++---------- .../test_user_permissions_on_repo_groups.py | 6 +++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/rhodecode/tests/models/common.py b/rhodecode/tests/models/common.py index d8726770..f41c4aab 100644 --- a/rhodecode/tests/models/common.py +++ b/rhodecode/tests/models/common.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -69,9 +68,9 @@ def _create_project_tree(): """ test_u1 = UserModel().create_or_update( - username=u'test_u1', password=u'qweqwe', - email=u'test_u1@rhodecode.org', firstname=u'test_u1', - lastname=u'test_u1') + username='test_u1', password='qweqwe', + email='test_u1@rhodecode.org', firstname='test_u1', + lastname='test_u1') fixture.create_repo_group('g0') g0_1 = fixture.create_repo_group('g0/g0_1') @@ -84,8 +83,7 @@ def _create_project_tree(): fixture.create_repo('g0/g0_2/g0_2_r2', repo_group=g0_2) g0_3 = fixture.create_repo_group('g0/g0_3') fixture.create_repo('g0/g0_3/g0_3_r1', repo_group=g0_3) - fixture.create_repo( - 'g0/g0_3/g0_3_r1_private', repo_group=g0_3, repo_private=True) + fixture.create_repo('g0/g0_3/g0_3_r1_private', repo_group=g0_3, repo_private=True) return test_u1 @@ -100,14 +98,14 @@ def expected_count(group_name, objects=False): def _check_expected_count(items, repo_items, expected): should_be = len(items + repo_items) there_are = len(expected) - assert should_be == there_are, ( - '%s != %s' % ((items + repo_items), expected)) + assert should_be == there_are, f'{(items + repo_items)} != {expected}' -def check_tree_perms(obj_name, repo_perm, prefix, expected_perm): - assert repo_perm == expected_perm, ( - 'obj:`%s` got perm:`%s` should:`%s`' % ( - obj_name, repo_perm, expected_perm)) +def check_tree_perms(obj_name, repo_perm, prefix, expected_perm, default_user=False): + if 'r1_private' in obj_name and default_user: + # private repo must be private even if "recursive" permissions apply + expected_perm = 'repository.none' + assert repo_perm == expected_perm, f'obj:`{obj_name}` got perm:`{repo_perm}` should:`{expected_perm}`' def _get_perms(filter_='', recursive=None, key=None, test_u1_id=None): diff --git a/rhodecode/tests/models/test_user_permissions_on_repo_groups.py b/rhodecode/tests/models/test_user_permissions_on_repo_groups.py index 8f0e55ac..4cc81319 100644 --- a/rhodecode/tests/models/test_user_permissions_on_repo_groups.py +++ b/rhodecode/tests/models/test_user_permissions_on_repo_groups.py @@ -139,6 +139,8 @@ def test_user_permissions_on_group_with_recursive_mode_for_default_user(): # set permission to g0 recursive mode, all children including # other repos and groups should have this permission now set ! + # except the PRIVATE repo which must remain repository.none permissions + # this is exclusive to the default user recursive = 'all' group = 'g0' default_user_id = User.get_default_user_id() @@ -156,10 +158,10 @@ def test_user_permissions_on_group_with_recursive_mode_for_default_user(): _check_expected_count(items, repo_items, expected_count(group, True)) for name, perm in repo_items: - check_tree_perms(name, perm, group, 'repository.write') + check_tree_perms(name, perm, group, 'repository.write', default_user=True) for name, perm in items: - check_tree_perms(name, perm, group, 'group.write') + check_tree_perms(name, perm, group, 'group.write', default_user=True) def test_user_permissions_on_group_with_recursive_mode_inner_group(): From 47a4437420e60c04d19988f1ffe43364de917c71 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 22 Oct 2024 22:15:46 +0200 Subject: [PATCH 14/99] fix(sqlalchemy): fixed deprecated methods for sqlalchemy --- rhodecode/model/db.py | 8 ++++---- rhodecode/model/user.py | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py index 8c2466e3..4d687678 100644 --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -284,7 +284,7 @@ class BaseModel(object): @classmethod def get(cls, id_): if id_: - return cls.query().get(id_) + return Session().get(cls, id_) @classmethod def get_or_404(cls, id_): @@ -1077,11 +1077,11 @@ class User(Base, BaseModel): if not user_id: return - user = cls.query() + q = cls.select().where(cls.user_id == user_id) if cache: - user = user.options( + q = q.options( FromCache("sql_cache_short", f"get_users_{user_id}")) - return user.get(user_id) + return cls.execute(q).scalar_one_or_none() @classmethod def get_by_username(cls, username, case_insensitive=False, diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py index 44ebe36b..852e8aad 100644 --- a/rhodecode/model/user.py +++ b/rhodecode/model/user.py @@ -54,11 +54,12 @@ class UserModel(BaseModel): cls = User def get(self, user_id, cache=False): - user = self.sa.query(User) + cls = self.cls + q = cls.select().where(cls.user_id == user_id) if cache: - user = user.options( - FromCache("sql_cache_short", f"get_user_{user_id}")) - return user.get(user_id) + q = q.options( + FromCache("sql_cache_short", f"get_users_{user_id}")) + return cls.execute(q).scalar_one_or_none() def get_user(self, user): return self._get_user(user) From c712ce33c0a46d18acc23f3ea42454c6de6a151d Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 22 Oct 2024 22:25:31 +0200 Subject: [PATCH 15/99] deps: msgpack==1.1.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c756a186..a4c45cca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -113,7 +113,7 @@ ipython==8.26.0 traitlets==5.14.3 typing_extensions==4.12.2 markdown==3.4.3 -msgpack==1.0.8 +msgpack==1.1.0 mysqlclient==2.1.1 nbconvert==7.7.3 beautifulsoup4==4.12.3 From f096d65f319cde35c58801fc62bdf14624014d55 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 23 Oct 2024 11:31:17 +0200 Subject: [PATCH 16/99] caches: improve logging --- rhodecode/lib/rc_cache/utils.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rhodecode/lib/rc_cache/utils.py b/rhodecode/lib/rc_cache/utils.py index 9a8a9baf..44766a91 100644 --- a/rhodecode/lib/rc_cache/utils.py +++ b/rhodecode/lib/rc_cache/utils.py @@ -69,19 +69,17 @@ class RhodeCodeCacheRegion(CacheRegion): def get_or_create_for_user_func(func_key_generator, user_func, *arg, **kw): if not condition: - log.debug('Calling un-cached method:%s', user_func.__name__) + log.debug('Calling un-cached method:`%s`', user_func.__name__) start = time.time() result = user_func(*arg, **kw) total = time.time() - start - log.debug('un-cached method:%s took %.4fs', user_func.__name__, total) + log.debug('Call for un-cached method:`%s` took %.4fs', user_func.__name__, total) return result key = func_key_generator(*arg, **kw) + timeout = expiration_time() if expiration_time_is_callable else expiration_time + log.debug('Calling cached (timeout=%s) method:`%s`', timeout, user_func.__name__) - timeout = expiration_time() if expiration_time_is_callable \ - else expiration_time - - log.debug('Calling cached method:`%s`', user_func.__name__) return self.get_or_create(key, user_func, timeout, should_cache_fn, (arg, kw)) def cache_decorator(user_func): From 67674fcb3d6fba21e033059dd9d8198bcbb7fe4d Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 23 Oct 2024 11:32:20 +0200 Subject: [PATCH 17/99] fix(caches): actually obey wire cache flag on local method caching --- rhodecode/lib/vcs/client_http.py | 5 ++++- rhodecode/model/scm.py | 6 ++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py index 195d854e..b331b167 100644 --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -293,7 +293,9 @@ class RemoteRepo(object): 'is_path_valid_repository', ] - if local_cache_on and name in cache_methods: + wire_cache = self._wire['cache'] + + if local_cache_on and wire_cache and name in cache_methods: cache_on = True repo_state_uid = self._wire['repo_state_uid'] call_args = [a for a in args] @@ -303,6 +305,7 @@ class RemoteRepo(object): @exceptions.map_vcs_exceptions def _call(self, name, *args, **kwargs): + context_uid, payload = self._base_call(name, *args, **kwargs) url = self.url diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py index 30228e6b..e4e5e564 100644 --- a/rhodecode/model/scm.py +++ b/rhodecode/model/scm.py @@ -213,12 +213,10 @@ class ScmModel(BaseModel): try: if name in repos: - raise RepositoryError('Duplicate repository name %s ' - 'found in %s' % (name, path)) + raise RepositoryError(f'Duplicate repository name {name} found in {path}') elif path[0] in rhodecode.BACKENDS: backend = get_backend(path[0]) - repos[name] = backend(path[1], config=config, - with_wire={"cache": False}) + repos[name] = backend(path[1], config=config, with_wire={"cache": False}) except OSError: continue except RepositoryError: From 465e9ede180aca2c22a60e69ca238fc91168cc30 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Thu, 24 Oct 2024 13:25:37 +0200 Subject: [PATCH 18/99] fix(mercurial): actually use assert_path instead of always initializing repo object. This makes it consistent with how things work on git and svn sides --- rhodecode/lib/vcs/backends/hg/repository.py | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/rhodecode/lib/vcs/backends/hg/repository.py b/rhodecode/lib/vcs/backends/hg/repository.py index 8b2330e1..7cc7ac38 100644 --- a/rhodecode/lib/vcs/backends/hg/repository.py +++ b/rhodecode/lib/vcs/backends/hg/repository.py @@ -368,26 +368,32 @@ class MercurialRepository(BaseRepository): be created. If `src_url` is given, would try to clone repository from the - location at given clone_point. Additionally it'll make update to + location at given clone_point. Additionally, it'll make update to working copy accordingly to `do_workspace_checkout` flag. """ if create and os.path.exists(self.path): raise RepositoryError( f"Cannot create repository at {self.path}, location already exist") - if src_url: - url = str(self._get_url(src_url)) - MercurialRepository.check_url(url, self.config) - - self._remote.clone(url, self.path, do_workspace_checkout) - - # Don't try to create if we've already cloned repo - create = False - if create: - os.makedirs(self.path, mode=0o755) + if src_url: + url = str(self._get_url(src_url)) + MercurialRepository.check_url(url, self.config) - self._remote.localrepository(create) + self._remote.clone(url, self.path, do_workspace_checkout) + + # Don't try to create if we've already cloned repo + create = False + self._remote.localrepository(create) + else: + os.makedirs(self.path, mode=0o755) + create = True + self._remote.localrepository(create) + + else: + if not self._remote.assert_correct_path(): + raise RepositoryError( + f'Path "{self.path}" does not contain a Mercurial repository') @LazyProperty def in_memory_commit(self): From 7d196811c46dcbf329b3f7fd67933d1a2ddb6322 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 23 Oct 2024 22:11:27 +0200 Subject: [PATCH 19/99] feat(redis): added ability to specify a redis key prefix for all cache regions of dogpile that use redis backend --- configs/development.ini | 12 ++++++++++++ configs/production.ini | 12 ++++++++++++ rhodecode/lib/rc_cache/backends.py | 5 ++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/configs/development.ini b/configs/development.ini index 5d4100b7..096ebc45 100644 --- a/configs/development.ini +++ b/configs/development.ini @@ -485,6 +485,10 @@ rc_cache.cache_general.expiration_time = 43200 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_general.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_general.arguments.key_prefix = custom-prefix- + + ; ************************************************* ; `cache_perms` cache for permission tree, auth TTL ; for simplicity use rc.file_namespace backend, @@ -512,6 +516,10 @@ rc_cache.cache_perms.expiration_time = 3600 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_perms.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_perms.arguments.key_prefix = custom-prefix- + + ; *************************************************** ; `cache_repo` cache for file tree, Readme, RSS FEEDS ; for simplicity use rc.file_namespace backend, @@ -539,6 +547,10 @@ rc_cache.cache_repo.expiration_time = 2592000 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_repo.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_repo.arguments.key_prefix = custom-prefix- + + ; ############## ; BEAKER SESSION ; ############## diff --git a/configs/production.ini b/configs/production.ini index 8a2713cf..073943ca 100644 --- a/configs/production.ini +++ b/configs/production.ini @@ -453,6 +453,10 @@ rc_cache.cache_general.expiration_time = 43200 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_general.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_general.arguments.key_prefix = custom-prefix- + + ; ************************************************* ; `cache_perms` cache for permission tree, auth TTL ; for simplicity use rc.file_namespace backend, @@ -480,6 +484,10 @@ rc_cache.cache_perms.expiration_time = 3600 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_perms.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_perms.arguments.key_prefix = custom-prefix- + + ; *************************************************** ; `cache_repo` cache for file tree, Readme, RSS FEEDS ; for simplicity use rc.file_namespace backend, @@ -507,6 +515,10 @@ rc_cache.cache_repo.expiration_time = 2592000 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_repo.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_repo.arguments.key_prefix = custom-prefix- + + ; ############## ; BEAKER SESSION ; ############## diff --git a/rhodecode/lib/rc_cache/backends.py b/rhodecode/lib/rc_cache/backends.py index b4f64aa5..04d4f724 100644 --- a/rhodecode/lib/rc_cache/backends.py +++ b/rhodecode/lib/rc_cache/backends.py @@ -216,6 +216,9 @@ class BaseRedisBackend(redis_backend.RedisBackend): self._lock_timeout = self.lock_timeout self._lock_auto_renewal = str2bool(arguments.pop("lock_auto_renewal", True)) + self._store_key_prefix = arguments.pop('key_prefix', '') + self.key_prefix = f'{self._store_key_prefix}{self.key_prefix}' + if self._lock_auto_renewal and not self._lock_timeout: # set default timeout for auto_renewal self._lock_timeout = 30 @@ -275,7 +278,7 @@ class BaseRedisBackend(redis_backend.RedisBackend): def get_mutex(self, key): if self.distributed_lock: - lock_key = f'_lock_{safe_str(key)}' + lock_key = f'{self._store_key_prefix}_lock_{safe_str(key)}' return get_mutex_lock( self.writer_client, lock_key, self._lock_timeout, From 3aec79cd174ea6dcc8b68df5a33442e196be1874 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Thu, 24 Oct 2024 14:53:47 +0200 Subject: [PATCH 20/99] fix(styling): fixed white background for markdown block codes --- rhodecode/public/css/legacy_code_styles.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhodecode/public/css/legacy_code_styles.less b/rhodecode/public/css/legacy_code_styles.less index 8134be7e..0cb4e074 100644 --- a/rhodecode/public/css/legacy_code_styles.less +++ b/rhodecode/public/css/legacy_code_styles.less @@ -287,7 +287,7 @@ div.markdown-block #message { font-family: @text-monospace; font-size: 11px; .border-radius(@border-radius); - background-color: white; + background-color: transparent; color: @grey3; } From 17f3b8d89583b4354b78de58dff6b3a967a47eb8 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Thu, 24 Oct 2024 14:55:30 +0200 Subject: [PATCH 21/99] fix(css): distinguish code/pre from #message --- rhodecode/public/css/legacy_code_styles.less | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rhodecode/public/css/legacy_code_styles.less b/rhodecode/public/css/legacy_code_styles.less index 0cb4e074..c7a4d5b8 100644 --- a/rhodecode/public/css/legacy_code_styles.less +++ b/rhodecode/public/css/legacy_code_styles.less @@ -287,10 +287,18 @@ div.markdown-block #message { font-family: @text-monospace; font-size: 11px; .border-radius(@border-radius); - background-color: transparent; color: @grey3; } +div.markdown-block code, +div.markdown-block pre { + background-color: transparent; +} + +div.markdown-block #ws, +div.markdown-block #message { + background-color: white; +} div.markdown-block code { border: @border-thickness solid @grey6; From 9e9b09f082b65bed81a95e0b956dac8a9dc52419 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Thu, 24 Oct 2024 14:57:31 +0200 Subject: [PATCH 22/99] fix(emails): fixed white background in email templates --- rhodecode/templates/email_templates/base.mako | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rhodecode/templates/email_templates/base.mako b/rhodecode/templates/email_templates/base.mako index 53cd1b48..d88989a6 100644 --- a/rhodecode/templates/email_templates/base.mako +++ b/rhodecode/templates/email_templates/base.mako @@ -409,10 +409,19 @@ ${self.plaintext_footer()} -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - background-color: #FFFFFF; color: #7E7F7F } + div.markdown-block code, + div.markdown-block pre { + background-color: transparent; + } + + div.markdown-block #ws, + div.markdown-block #message { + background-color: #FFFFFF; + } + div.markdown-block code { border: 1px solid #7E7F7F; margin: 0 2px; From 36f01dd3c965effce3b55eafe13b789285ee1ac9 Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Wed, 6 Nov 2024 09:36:08 +0200 Subject: [PATCH 23/99] fix: fixed problems on UI in case of long commit messages. Fixes: RCCE-140 --- rhodecode/templates/changeset/changeset.mako | 47 +++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/rhodecode/templates/changeset/changeset.mako b/rhodecode/templates/changeset/changeset.mako index ee306309..86070542 100644 --- a/rhodecode/templates/changeset/changeset.mako +++ b/rhodecode/templates/changeset/changeset.mako @@ -128,7 +128,7 @@
-
+
${_('Show More')}
@@ -416,6 +416,51 @@ var channel = '${c.commit_broadcast_channel}'; new ReviewerPresenceController(channel) + function breakLongCommitMessage() { + const commitElements = document.querySelectorAll(".left-content-message .commit"); + const maxAllowedWidth = window.innerWidth * 0.9; + + commitElements.forEach(commitElement => { + const originalText = commitElement.textContent; + const lines = originalText.split("\n"); + const brokenLines = []; + + commitElement.style.whiteSpace = "nowrap"; + + for (let line of lines) { + let brokenLine = ""; + let words = line.split(" "); + let currentLine = ""; + + words.forEach(word => { + const testLine = currentLine.length > 0 ? currentLine + " " + word : word; + + commitElement.textContent = testLine; + const testLineWidth = commitElement.offsetWidth; + + if (testLineWidth > maxAllowedWidth) { + brokenLine += currentLine + "\n"; + currentLine = word; + } else { + currentLine = testLine; + } + }); + + brokenLine += currentLine; + brokenLines.push(brokenLine.trim()); + } + + commitElement.textContent = brokenLines.join("\n"); + + commitElement.style.whiteSpace = "pre-wrap"; + }); + } + + window.addEventListener("load", function () { + const button = document.getElementById("break-button"); + button.addEventListener("click", breakLongCommitMessage); + + }); }) From c630cf0a6096a15d631490d511756a9c63f1a54e Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Thu, 7 Nov 2024 09:32:01 +0200 Subject: [PATCH 24/99] fix: added release notes. Fixes: RCCE-140 --- docs/release-notes/release-notes-5.4.0.rst | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/release-notes/release-notes-5.4.0.rst diff --git a/docs/release-notes/release-notes-5.4.0.rst b/docs/release-notes/release-notes-5.4.0.rst new file mode 100644 index 00000000..379d2fb9 --- /dev/null +++ b/docs/release-notes/release-notes-5.4.0.rst @@ -0,0 +1,40 @@ +|RCE| 5.4.0 |RNS| +----------------- + +Release Date +^^^^^^^^^^^^ + + + + +New Features +^^^^^^^^^^^^ + + + +General +^^^^^^^ + + + +Security +^^^^^^^^ + + + +Performance +^^^^^^^^^^^ + + + + +Fixes +^^^^^ + +- Fixed UI visualization issues with long commit messages + + +Upgrade notes +^^^^^^^^^^^^^ + + From 89af1f74b3125a5dda2c46a5865137451e9ddd9c Mon Sep 17 00:00:00 2001 From: Andrii Verbytskyi Date: Fri, 8 Nov 2024 17:38:08 +0100 Subject: [PATCH 25/99] bug: fixed docs not building by adding release notes to index --- docs/release-notes/release-notes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/release-notes.rst b/docs/release-notes/release-notes.rst index fad8bad0..634084b2 100644 --- a/docs/release-notes/release-notes.rst +++ b/docs/release-notes/release-notes.rst @@ -10,6 +10,7 @@ Release Notes .. toctree:: :maxdepth: 1 + release-notes-5.4.0.rst release-notes-5.3.1.rst release-notes-5.3.0.rst release-notes-5.2.1.rst From 6a667624ae42ab9876809e015ddc55f732d517f3 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 25 Oct 2024 10:34:22 +0200 Subject: [PATCH 26/99] fix(tests): fixed tests for PR celery hooks deamon --- rhodecode/lib/hook_daemon/base.py | 9 +++++++-- rhodecode/lib/hook_daemon/celery_hooks_deamon.py | 10 ++++++---- rhodecode/tests/fixture_mods/fixture_utils.py | 2 +- rhodecode/tests/lib/test_hooks_daemon.py | 1 + rhodecode/tests/models/test_pullrequest.py | 5 +++-- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/rhodecode/lib/hook_daemon/base.py b/rhodecode/lib/hook_daemon/base.py index d29dbec4..705926f4 100644 --- a/rhodecode/lib/hook_daemon/base.py +++ b/rhodecode/lib/hook_daemon/base.py @@ -20,7 +20,7 @@ import os import time import logging -from rhodecode.lib.config_utils import get_config +from rhodecode.lib.config_utils import get_app_config_lightweight from rhodecode.lib.svn_txn_utils import get_txn_id_from_store @@ -68,7 +68,12 @@ def prepare_callback_daemon(extras, protocol, host, txn_id=None): txn_id=txn_id, host=host, port=port) case 'celery': from rhodecode.lib.hook_daemon.celery_hooks_deamon import CeleryHooksCallbackDaemon - callback_daemon = CeleryHooksCallbackDaemon(get_config(extras['config'])) + + config = get_app_config_lightweight(extras['config']) + task_queue = config.get('celery.broker_url') + task_backend = config.get('celery.result_backend') + + callback_daemon = CeleryHooksCallbackDaemon(task_queue, task_backend) case 'local': from rhodecode.lib.hook_daemon.hook_module import Hooks callback_daemon = HooksModuleCallbackDaemon(Hooks.__module__) diff --git a/rhodecode/lib/hook_daemon/celery_hooks_deamon.py b/rhodecode/lib/hook_daemon/celery_hooks_deamon.py index 636f27ad..245a6bf3 100644 --- a/rhodecode/lib/hook_daemon/celery_hooks_deamon.py +++ b/rhodecode/lib/hook_daemon/celery_hooks_deamon.py @@ -24,10 +24,12 @@ class CeleryHooksCallbackDaemon(BaseHooksCallbackDaemon): Context manger for achieving a compatibility with celery backend """ - def __init__(self, config): - # TODO: replace this with settings bootstrapped... - self.task_queue = config.get('app:main', 'celery.broker_url') - self.task_backend = config.get('app:main', 'celery.result_backend') + def __init__(self, task_queue, task_backend): + self.task_queue = task_queue + self.task_backend = task_backend + + def __repr__(self): + return f'CeleryHooksCallbackDaemon(task_queue={self.task_queue}, task_backend={self.task_backend})' def __repr__(self): return f'CeleryHooksCallbackDaemon(task_queue={self.task_queue}, task_backend={self.task_backend})' diff --git a/rhodecode/tests/fixture_mods/fixture_utils.py b/rhodecode/tests/fixture_mods/fixture_utils.py index 3351c104..9b95ceeb 100644 --- a/rhodecode/tests/fixture_mods/fixture_utils.py +++ b/rhodecode/tests/fixture_mods/fixture_utils.py @@ -177,7 +177,7 @@ def baseapp(ini_config, vcsserver, http_environ_session): from rhodecode.lib.config_utils import get_app_config from rhodecode.config.middleware import make_pyramid_app - log.info("Using the RhodeCode configuration:{}".format(ini_config)) + log.info("Using the RhodeCode configuration:%s", ini_config) pyramid.paster.setup_logging(ini_config) settings = get_app_config(ini_config) diff --git a/rhodecode/tests/lib/test_hooks_daemon.py b/rhodecode/tests/lib/test_hooks_daemon.py index 08f2738f..cf6b6500 100644 --- a/rhodecode/tests/lib/test_hooks_daemon.py +++ b/rhodecode/tests/lib/test_hooks_daemon.py @@ -322,6 +322,7 @@ class TestPrepareHooksDaemon(object): 'invalid', 'Http', 'HTTP', + 'celerY' )) def test_raises_on_invalid_protocol(self, protocol): expected_extras = { diff --git a/rhodecode/tests/models/test_pullrequest.py b/rhodecode/tests/models/test_pullrequest.py index f1d07c3c..0df90b8c 100644 --- a/rhodecode/tests/models/test_pullrequest.py +++ b/rhodecode/tests/models/test_pullrequest.py @@ -567,10 +567,11 @@ def test_merge_response_message(mr_type, expected_msg): @pytest.fixture() -def merge_extras(user_regular): +def merge_extras(request, user_regular): """ Context for the vcs operation when running a merge. """ + extras = { 'ip': '127.0.0.1', 'username': user_regular.username, @@ -578,7 +579,7 @@ def merge_extras(user_regular): 'action': 'push', 'repository': 'fake_target_repo_name', 'scm': 'git', - 'config': 'fake_config_ini_path', + 'config': request.config.getini('pyramid_config'), 'repo_store': '', 'make_lock': None, 'locked_by': [None, None, None], From 38d5e39b5752aad089ebcb64805a36ba15647bb6 Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Mon, 28 Oct 2024 12:35:44 +0200 Subject: [PATCH 27/99] feat(task-for-automatic-pr-merge): added f-ty to support pull request auto merge for EE. Fixes: RCCE-67 --- .../tests/test_repo_vcs_settings.py | 3 +- rhodecode/config/middleware.py | 3 +- rhodecode/model/forms.py | 1 + rhodecode/model/pull_request.py | 4 +++ rhodecode/model/settings.py | 1 + rhodecode/subscribers.py | 28 +++++++++++++++++++ rhodecode/templates/base/vcs_settings.mako | 17 +++++++++++ .../models/settings/test_vcs_settings.py | 2 ++ 8 files changed, 57 insertions(+), 2 deletions(-) diff --git a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py index cd41d528..59b5434d 100644 --- a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py +++ b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py @@ -48,6 +48,7 @@ class TestVcsSettings(object): 'extensions_evolve': False, 'phases_publish': 'False', 'rhodecode_pr_merge_enabled': False, + 'rhodecode_auto_merge_enabled': False, 'rhodecode_use_outdated_comments': False, 'new_svn_branch': '', 'new_svn_tag': '' @@ -59,7 +60,7 @@ class TestVcsSettings(object): response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name)) expected_settings = ( - 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled', + 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled', 'rhodecode_auto_merge_enabled', 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger', 'hooks_outgoing_pull_logger' ) diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py index c82395cd..a37f941a 100644 --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -49,7 +49,7 @@ from rhodecode.lib.plugins.utils import register_rhodecode_plugin from rhodecode.lib.utils2 import AttributeDict from rhodecode.lib.exc_tracking import store_exception, format_exc from rhodecode.subscribers import ( - scan_repositories_if_enabled, write_js_routes_if_enabled, + auto_merge_pr_if_needed, scan_repositories_if_enabled, write_js_routes_if_enabled, write_metadata_if_needed, write_usage_data, import_license_if_present) from rhodecode.lib.statsd_client import StatsdClient @@ -392,6 +392,7 @@ def includeme(config, auth_resources=None): # Add subscribers. if load_all: log.debug('Adding subscribers...') + config.add_subscriber(auto_merge_pr_if_needed, rhodecode.events.PullRequestReviewEvent) config.add_subscriber(scan_repositories_if_enabled, pyramid.events.ApplicationCreated) config.add_subscriber(write_metadata_if_needed, diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py index bc82d930..cfe62448 100644 --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -445,6 +445,7 @@ class _BaseVcsSettingsForm(formencode.Schema): # PR/Code-review rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False) + rhodecode_auto_merge_enabled = v.StringBoolean(if_missing=False) rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False) # hg diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py index 0848a826..94a03326 100644 --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -2125,6 +2125,10 @@ class PullRequestModel(BaseModel): return self._get_general_setting( pull_request, 'rhodecode_pr_merge_enabled') + def is_automatic_merge_enabled(self, pull_request): + return self._get_general_setting( + pull_request, 'rhodecode_auto_merge_enabled') + def _use_rebase_for_merging(self, pull_request): repo_type = pull_request.target_repo.repo_type if repo_type == 'hg': diff --git a/rhodecode/model/settings.py b/rhodecode/model/settings.py index 275cd868..5d396425 100644 --- a/rhodecode/model/settings.py +++ b/rhodecode/model/settings.py @@ -461,6 +461,7 @@ class VcsSettingsModel(object): GENERAL_SETTINGS = ( 'use_outdated_comments', 'pr_merge_enabled', + 'auto_merge_enabled', 'hg_use_rebase_for_merging', 'hg_close_branch_before_merging', 'git_use_rebase_for_merging', diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py index 4bf7f2b8..461572da 100644 --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -57,6 +57,34 @@ def add_renderer_globals(event): event['h'] = helpers +def auto_merge_pr_if_needed(event): + from rhodecode.model.db import PullRequest + from rhodecode.model.pull_request import ( + PullRequestModel, ChangesetStatus, MergeCheck + ) + + pr_event_data = event.as_dict()['pullrequest'] + pull_request = PullRequest.get(pr_event_data['pull_request_id']) + calculated_status = pr_event_data['status'] + if (calculated_status == ChangesetStatus.STATUS_APPROVED + and PullRequestModel().is_automatic_merge_enabled(pull_request)): + user = pull_request.author.AuthUser() + + merge_check = MergeCheck.validate( + pull_request, user, translator=lambda x: x, fail_early=True + ) + if merge_check.merge_possible: + from rhodecode.lib.base import vcs_operation_context + extras = vcs_operation_context( + event.request.environ, repo_name=pull_request.target_repo.repo_name, + username=user.username, action='push', + scm=pull_request.target_repo.repo_type) + from rc_ee.lib.celerylib.tasks import auto_merge_repo + auto_merge_repo.apply_async( + args=(pull_request.pull_request_id, extras) + ) + + def set_user_lang(event): request = event.request cur_user = getattr(request, 'user', None) diff --git a/rhodecode/templates/base/vcs_settings.mako b/rhodecode/templates/base/vcs_settings.mako index 33ca7e91..6bd45548 100644 --- a/rhodecode/templates/base/vcs_settings.mako +++ b/rhodecode/templates/base/vcs_settings.mako @@ -210,6 +210,23 @@
${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}
+ %if c.rhodecode_edition_id != 'EE': +
+ + +
+
+ ${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='sales@rhodecode.com')|n} +
+ %else: +
+ ${h.checkbox('rhodecode_auto_merge_enabled' + suffix, 'True', **kwargs)} + +
+
+ ${_('When this is enabled, the pull request will be merged once it has at least one reviewer and is approved.')} +
+ %endif
${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)} diff --git a/rhodecode/tests/models/settings/test_vcs_settings.py b/rhodecode/tests/models/settings/test_vcs_settings.py index 9ca4653a..e19aee37 100644 --- a/rhodecode/tests/models/settings/test_vcs_settings.py +++ b/rhodecode/tests/models/settings/test_vcs_settings.py @@ -38,6 +38,7 @@ SVN_FORM_DATA = { GENERAL_FORM_DATA = { 'rhodecode_pr_merge_enabled': True, + 'rhodecode_auto_merge_enabled': True, 'rhodecode_use_outdated_comments': True, 'rhodecode_hg_use_rebase_for_merging': True, 'rhodecode_hg_close_branch_before_merging': True, @@ -989,6 +990,7 @@ class TestCreateOrUpdateRepoSettings(object): 'vcs_git_lfs_enabled': False, 'phases_publish': 'False', 'rhodecode_pr_merge_enabled': False, + 'rhodecode_auto_merge_enabled': False, 'rhodecode_use_outdated_comments': False, 'new_svn_branch': '', 'new_svn_tag': '' From 9dbc076c280bc6e4985870efb9ead63f739e4c35 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:53:05 +0100 Subject: [PATCH 28/99] deps: updated test deps --- requirements_test.txt | 59 ++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index 5376b367..f615a94a 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,42 +1,49 @@ # test related requirements + mock==5.1.0 -pytest-cov==4.1.0 - coverage==7.4.3 - pytest==8.1.1 - iniconfig==2.0.0 - packaging==24.1 - pluggy==1.4.0 -pytest-env==1.1.3 - pytest==8.1.1 - iniconfig==2.0.0 - packaging==24.1 - pluggy==1.4.0 -pytest-profiling==1.7.0 - gprof2dot==2022.7.29 - pytest==8.1.1 - iniconfig==2.0.0 - packaging==24.1 - pluggy==1.4.0 - six==1.16.0 -pytest-rerunfailures==13.0 + +pytest==8.3.3 + iniconfig==2.0.0 packaging==24.1 - pytest==8.1.1 + pluggy==1.5.0 +pytest-cov==5.0.0 + coverage==7.6.4 + pytest==8.3.3 iniconfig==2.0.0 packaging==24.1 - pluggy==1.4.0 + pluggy==1.5.0 +pytest-env==1.1.5 + pytest==8.3.3 + iniconfig==2.0.0 + packaging==24.1 + pluggy==1.5.0 +pytest-profiling==1.7.0 + gprof2dot==2024.6.6 + pytest==8.3.3 + iniconfig==2.0.0 + packaging==24.1 + pluggy==1.5.0 + six==1.16.0 +pytest-rerunfailures==14.0 + packaging==24.1 + pytest==8.3.3 + iniconfig==2.0.0 + packaging==24.1 + pluggy==1.5.0 pytest-runner==6.0.1 pytest-sugar==1.0.0 packaging==24.1 - pytest==8.1.1 + pytest==8.3.3 iniconfig==2.0.0 packaging==24.1 - pluggy==1.4.0 - termcolor==2.4.0 + pluggy==1.5.0 + termcolor==2.5.0 pytest-timeout==2.3.1 - pytest==8.1.1 + pytest==8.3.3 iniconfig==2.0.0 packaging==24.1 - pluggy==1.4.0 + pluggy==1.5.0 + webtest==3.0.0 beautifulsoup4==4.12.3 soupsieve==2.5 From d332e94a0296520c2c149bba8a3688adec50ad87 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:53:44 +0100 Subject: [PATCH 29/99] deps: bumped celery==5.4.0 --- requirements.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index a4c45cca..db3188c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,19 +9,18 @@ alembic==1.13.1 async-timeout==4.0.3 babel==2.12.1 beaker==1.13.0 -celery==5.3.6 - billiard==4.2.0 - click==8.1.3 - click-didyoumean==0.3.0 - click==8.1.3 +celery==5.4.0 + billiard==4.2.1 + click==8.1.7 + click-didyoumean==0.3.1 + click==8.1.7 click-plugins==1.1.1 click==8.1.3 click-repl==0.2.0 click==8.1.3 prompt_toolkit==3.0.47 wcwidth==0.2.13 - six==1.16.0 - kombu==5.3.5 + kombu==5.4.2 amqp==5.2.0 vine==5.1.0 vine==5.1.0 From 1a4c2fac2affd72d39c7d3d6b5ae8a7d5d81bb96 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:54:02 +0100 Subject: [PATCH 30/99] deps: bumped rhodecode-tools==4.0.0 --- requirements_rc_tools.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_rc_tools.txt b/requirements_rc_tools.txt index c6c0eb11..fc3d7f6f 100644 --- a/requirements_rc_tools.txt +++ b/requirements_rc_tools.txt @@ -1,3 +1,3 @@ ## rhodecode-tools, special case, use file://PATH.tar.gz#egg=rhodecode-tools==X.Y.Z, to test local version -rhodecode-tools @ https://code.rhodecode.com/_file_store/download/0-36d03a63-36f7-47af-a33c-d81c39f88251.0.0.tar.gz#egg=rhodecode-tools -rhodecode-tools==3.0.0 +rhodecode-tools @ https://code.rhodecode.com/_file_store/download/0-a47f315d-1289-41bf-af77-ea052f6a9f77.0.0.tar.gz#egg=rhodecode-tools +rhodecode-tools==4.0.0 From 20774df66b1b0eb7a3c549cd4bcdc9a29f106acd Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:54:41 +0100 Subject: [PATCH 31/99] deps: bumped mako dependency --- requirements.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index db3188c2..895c5efe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ # deps, generated via pipdeptree --exclude setuptools,wheel,pipdeptree,pip -f | tr '[:upper:]' '[:lower:]' alembic==1.13.1 - mako==1.2.4 - markupsafe==2.1.2 + mako==1.3.6 + markupsafe==3.0.2 sqlalchemy==1.4.52 greenlet==3.1.1 typing_extensions==4.12.2 @@ -48,8 +48,8 @@ channelstream==0.7.1 zope.interface==7.1.0 pyramid-jinja2==2.10 jinja2==3.1.2 - markupsafe==2.1.2 - markupsafe==2.1.2 + markupsafe==3.0.2 + markupsafe==3.0.2 pyramid==2.0.2 hupper==1.12 plaster==1.1.2 @@ -122,12 +122,12 @@ nbconvert==7.7.3 webencodings==0.5.1 defusedxml==0.7.1 jinja2==3.1.2 - markupsafe==2.1.2 + markupsafe==3.0.2 jupyter_core==5.3.1 platformdirs==3.10.0 traitlets==5.14.3 jupyterlab-pygments==0.2.2 - markupsafe==2.1.2 + markupsafe==3.0.2 mistune==2.0.5 nbclient==0.8.0 jupyter_client==8.3.0 @@ -208,8 +208,8 @@ pyramid-mailer==0.15.1 transaction==5.0.0 zope.interface==7.1.0 pyramid-mako==1.1.0 - mako==1.2.4 - markupsafe==2.1.2 + mako==1.3.6 + markupsafe==3.0.2 pyramid==2.0.2 hupper==1.12 plaster==1.1.2 @@ -288,7 +288,7 @@ unidecode==1.3.6 urlobject==2.4.3 waitress==3.0.0 webhelpers2==2.1 - markupsafe==2.1.2 + markupsafe==3.0.2 six==1.16.0 whoosh==2.7.4 zope.cachedescriptors==5.0.0 From 8d72eaa810a94aba63e9b44a1befaf8cc7fb73fe Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:55:17 +0100 Subject: [PATCH 32/99] deps: bumped tzdata==2024.2 --- requirements.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 895c5efe..8832c57d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,10 +23,11 @@ celery==5.4.0 kombu==5.4.2 amqp==5.2.0 vine==5.1.0 + tzdata==2024.2 vine==5.1.0 python-dateutil==2.8.2 six==1.16.0 - tzdata==2024.1 + tzdata==2024.2 vine==5.1.0 channelstream==0.7.1 gevent==24.10.3 @@ -282,7 +283,7 @@ sqlalchemy==1.4.52 supervisor==4.2.5 tzlocal==4.3 pytz-deprecation-shim==0.1.0.post0 - tzdata==2024.1 + tzdata==2024.2 tempita==0.5.2 unidecode==1.3.6 urlobject==2.4.3 From f43be6da5d909983a86166818978d48be2516b2c Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:55:52 +0100 Subject: [PATCH 33/99] deps: bumped zope.interface==7.1.1 --- requirements.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8832c57d..283a5e55 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,7 +33,7 @@ channelstream==0.7.1 gevent==24.10.3 greenlet==3.1.1 zope.event==5.0.0 - zope.interface==7.1.0 + zope.interface==7.1.1 itsdangerous==1.1.0 marshmallow==2.18.0 pyramid==2.0.2 @@ -46,7 +46,7 @@ channelstream==0.7.1 venusian==3.0.0 webob==1.8.8 zope.deprecation==5.0.0 - zope.interface==7.1.0 + zope.interface==7.1.1 pyramid-jinja2==2.10 jinja2==3.1.2 markupsafe==3.0.2 @@ -61,7 +61,7 @@ channelstream==0.7.1 venusian==3.0.0 webob==1.8.8 zope.deprecation==5.0.0 - zope.interface==7.1.0 + zope.interface==7.1.1 zope.deprecation==5.0.0 python-dateutil==2.8.2 six==1.16.0 @@ -93,7 +93,7 @@ gunicorn==23.0.0 gevent==24.10.3 greenlet==3.1.1 zope.event==5.0.0 - zope.interface==7.1.0 + zope.interface==7.1.1 ipython==8.26.0 decorator==5.1.1 jedi==0.19.1 @@ -201,13 +201,13 @@ pyramid-mailer==0.15.1 venusian==3.0.0 webob==1.8.8 zope.deprecation==5.0.0 - zope.interface==7.1.0 + zope.interface==7.1.1 repoze.sendmail==4.4.1 transaction==5.0.0 - zope.interface==7.1.0 - zope.interface==7.1.0 + zope.interface==7.1.1 + zope.interface==7.1.1 transaction==5.0.0 - zope.interface==7.1.0 + zope.interface==7.1.1 pyramid-mako==1.1.0 mako==1.3.6 markupsafe==3.0.2 @@ -221,7 +221,7 @@ pyramid-mako==1.1.0 venusian==3.0.0 webob==1.8.8 zope.deprecation==5.0.0 - zope.interface==7.1.0 + zope.interface==7.1.1 python-ldap==3.4.3 pyasn1==0.4.8 pyasn1-modules==0.2.8 From df93caae59dcb3f4d0f722087b97ba68846bfcdb Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:56:24 +0100 Subject: [PATCH 34/99] deps: bumped python-dateutil==2.9.0.post0 --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 283a5e55..2dbe67ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,7 +25,7 @@ celery==5.4.0 vine==5.1.0 tzdata==2024.2 vine==5.1.0 - python-dateutil==2.8.2 + python-dateutil==2.9.0.post0 six==1.16.0 tzdata==2024.2 vine==5.1.0 @@ -63,7 +63,7 @@ channelstream==0.7.1 zope.deprecation==5.0.0 zope.interface==7.1.1 zope.deprecation==5.0.0 - python-dateutil==2.8.2 + python-dateutil==2.9.0.post0 six==1.16.0 requests==2.28.2 certifi==2022.12.7 @@ -135,7 +135,7 @@ nbconvert==7.7.3 jupyter_core==5.3.1 platformdirs==3.10.0 traitlets==5.14.3 - python-dateutil==2.8.2 + python-dateutil==2.9.0.post0 six==1.16.0 pyzmq==25.0.0 tornado==6.2 @@ -256,7 +256,7 @@ s3fs==2024.9.0 aioitertools==0.11.0 botocore==1.34.106 jmespath==1.0.1 - python-dateutil==2.8.2 + python-dateutil==2.9.0.post0 six==1.16.0 urllib3==1.26.14 wrapt==1.16.0 From d9de88a130078cddc9ba17ed40e8f348d2ec7d8a Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:56:39 +0100 Subject: [PATCH 35/99] deps: bumped click==8.1.7 --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2dbe67ed..f02bfb95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,10 +15,10 @@ celery==5.4.0 click-didyoumean==0.3.1 click==8.1.7 click-plugins==1.1.1 - click==8.1.3 - click-repl==0.2.0 - click==8.1.3 - prompt_toolkit==3.0.47 + click==8.1.7 + click-repl==0.3.0 + click==8.1.7 + prompt_toolkit==3.0.48 wcwidth==0.2.13 kombu==5.4.2 amqp==5.2.0 From 3e06518205970895b30ff9c33e4b261716c662ef Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:57:01 +0100 Subject: [PATCH 36/99] deps: bumped cryptography==43.0.3 --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index f02bfb95..4996f0de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -183,8 +183,8 @@ psutil==5.9.8 psycopg2==2.9.9 py-bcrypt==0.4 pycmarkgfm==1.2.0 - cffi==1.16.0 - pycparser==2.21 + cffi==1.17.1 + pycparser==2.22 pycryptodome==3.17 pycurl==7.45.3 pymysql==1.0.3 @@ -272,10 +272,10 @@ s3fs==2024.9.0 fsspec==2024.9.0 simplejson==3.19.2 sshpubkeys==3.3.1 - cryptography==40.0.2 - cffi==1.16.0 - pycparser==2.21 - ecdsa==0.18.0 + cryptography==43.0.3 + cffi==1.17.1 + pycparser==2.22 + ecdsa==0.19.0 six==1.16.0 sqlalchemy==1.4.52 greenlet==3.1.1 From daa9625ae8e6f83d2e2bc09b3e078678008086bd Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:57:32 +0100 Subject: [PATCH 37/99] deps: bumped python-memcached==1.62 --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4996f0de..49c1cf9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -226,8 +226,7 @@ python-ldap==3.4.3 pyasn1==0.4.8 pyasn1-modules==0.2.8 pyasn1==0.4.8 -python-memcached==1.59 - six==1.16.0 +python-memcached==1.62 python-pam==2.0.2 python3-saml==1.16.0 isodate==0.6.1 From 5378235614f99487ad392bf6de5c92cc1d4cf886 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 30 Oct 2024 10:57:43 +0100 Subject: [PATCH 38/99] deps: bumped prompt_toolkit==3.0.48 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 49c1cf9a..c96364ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -102,7 +102,7 @@ ipython==8.26.0 traitlets==5.14.3 pexpect==4.9.0 ptyprocess==0.7.0 - prompt_toolkit==3.0.47 + prompt_toolkit==3.0.48 wcwidth==0.2.13 pygments==2.18.0 stack-data==0.6.3 From 17bc7d5c020b05d8eb1aa6feb1b3fc0e0e4decb0 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 8 Nov 2024 15:16:31 +0100 Subject: [PATCH 39/99] tests: makefile update for new test suite --- Makefile | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 46cded38..d20aca27 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ clean: # test: run test-clean and tests test: make test-clean - unset RC_SQLALCHEMY_DB1_URL && unset RC_DB_URL && make test-only + make test-only .PHONY: test-clean @@ -71,7 +71,18 @@ test-only: PYTHONHASHSEED=random \ py.test -x -vv -r xw -p no:sugar \ --cov-report=term-missing --cov-report=html \ - --cov=rhodecode rhodecode + --cov=rhodecode rhodecode \ + --ignore=rhodecode/tests/vcs_operations \ + --ignore=rhodecode/tests/database + + PYTHONHASHSEED=random \ + py.test -x -vv -r xw -p no:sugar \ + rhodecode/tests/vcs_operations + + PYTHONHASHSEED=random \ + py.test -x -vv -r xw -p no:sugar \ + rhodecode/tests/database + # >>> Docs commands From fa816afe7b46b740013e0820877380687b629da4 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 8 Nov 2024 15:17:36 +0100 Subject: [PATCH 40/99] fix: settings, no longer create lfs dirs, and disable hgsubversion from old installations --- rhodecode/apps/admin/views/settings.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py index 4f226076..6c20c46b 100644 --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -77,13 +77,16 @@ class AdminSettingsView(BaseAppView): raise Exception('Could not get application ui settings !') settings = { # legacy param that needs to be kept - 'web_push_ssl': False + 'web_push_ssl': False, + 'extensions_hgsubversion': False } for each in ret: k = each.ui_key v = each.ui_value + section = each.ui_section + # skip some options if they are defined - if k in ['push_ssl']: + if f"{section}_{k}" in ['web_push_ssl', 'extensions_hgsubversion']: continue if k == '/': @@ -98,7 +101,7 @@ class AdminSettingsView(BaseAppView): if each.ui_section in ['hooks', 'extensions']: v = each.ui_active - settings[each.ui_section + '_' + k] = v + settings[section + '_' + k] = v return settings @@ -125,8 +128,6 @@ class AdminSettingsView(BaseAppView): c.svn_config_path = rhodecode.ConfigGet().get_str(config_keys.config_file_path) defaults = self._form_defaults() - model.create_largeobjects_dirs_if_needed(defaults['paths_root_path']) - data = render('rhodecode:templates/admin/settings/settings.mako', self._get_template_context(c), self.request) html = formencode.htmlfill.render( From 2bbd831d7c01407a4602cf8c2e2a4bff3343e241 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 8 Nov 2024 15:18:23 +0100 Subject: [PATCH 41/99] fix(ssh-backend): fix accidentally added rhodecode_tools import --- rhodecode/apps/ssh_support/lib/backends/svn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhodecode/apps/ssh_support/lib/backends/svn.py b/rhodecode/apps/ssh_support/lib/backends/svn.py index ca27890f..4e54e53a 100644 --- a/rhodecode/apps/ssh_support/lib/backends/svn.py +++ b/rhodecode/apps/ssh_support/lib/backends/svn.py @@ -25,7 +25,7 @@ import tempfile from subprocess import Popen, PIPE import urllib.parse -from rhodecode_tools.lib.utils import safe_str +from rhodecode.lib.str_utils import safe_str from .base import SshVcsServer log = logging.getLogger(__name__) From bc0998866f131a68206ec0b37cbab3f4508af97f Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 8 Nov 2024 15:19:10 +0100 Subject: [PATCH 42/99] feat(config-maker): ensure dirs for LFS objects are set and existing --- rhodecode/config/config_maker.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rhodecode/config/config_maker.py b/rhodecode/config/config_maker.py index 64cd3a72..9155c472 100644 --- a/rhodecode/config/config_maker.py +++ b/rhodecode/config/config_maker.py @@ -122,9 +122,12 @@ def sanitize_settings_and_apply_defaults(global_config, settings): settings_maker.make_setting('vcs.start_server', 'false', parser='bool') settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list') settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int') - settings_maker.make_setting('vcs.git.lfs.storage_location', '/var/opt/rhodecode_repo_store/.cache/git_lfs_store') + settings_maker.make_setting('vcs.git.lfs.storage_location', + '/var/opt/rhodecode_repo_store/.cache/git_lfs_store', + parser='dir:ensured', default_when_empty=True) settings_maker.make_setting('vcs.hg.largefiles.storage_location', - '/var/opt/rhodecode_repo_store/.cache/hg_largefiles_store') + '/var/opt/rhodecode_repo_store/.cache/hg_largefiles_store', + parser='dir:ensured', default_when_empty=True) settings_maker.make_setting('vcs.methods.cache', True, parser='bool') From aa45596cef67e6556e28d56979ea58a20a26c67a Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 8 Nov 2024 15:20:20 +0100 Subject: [PATCH 43/99] fix(db-bootstrap): don't store LFS/largefiles data into DB as they are controlled now via .ini files --- rhodecode/lib/db_manage.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py index 93c95ef1..c09155af 100644 --- a/rhodecode/lib/db_manage.py +++ b/rhodecode/lib/db_manage.py @@ -328,8 +328,6 @@ class DbManage(object): and disables dotencode """ settings_model = SettingsModel(sa=self.sa) - from rhodecode.lib.vcs.backends.hg import largefiles_store - from rhodecode.lib.vcs.backends.git import lfs_store # Build HOOKS hooks = [ @@ -360,24 +358,6 @@ class DbManage(object): largefiles.ui_value = '' self.sa.add(largefiles) - # set default largefiles cache dir, defaults to - # /repo_store_location/.cache/largefiles - largefiles = RhodeCodeUi() - largefiles.ui_section = 'largefiles' - largefiles.ui_key = 'usercache' - largefiles.ui_value = largefiles_store(repo_store_path) - - self.sa.add(largefiles) - - # set default lfs cache dir, defaults to - # /repo_store_location/.cache/lfs_store - lfsstore = RhodeCodeUi() - lfsstore.ui_section = 'vcs_git_lfs' - lfsstore.ui_key = 'store_location' - lfsstore.ui_value = lfs_store(repo_store_path) - - self.sa.add(lfsstore) - # enable hgevolve disabled by default hgevolve = RhodeCodeUi() hgevolve.ui_section = 'extensions' From 840634c16f56c88c82d84d5895a7a44902c06340 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 8 Nov 2024 16:17:03 +0100 Subject: [PATCH 44/99] fix(mercurial): fixed httppostargs logic --- rhodecode/lib/middleware/simplehg.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/rhodecode/lib/middleware/simplehg.py b/rhodecode/lib/middleware/simplehg.py index 5100f589..35f1f75a 100644 --- a/rhodecode/lib/middleware/simplehg.py +++ b/rhodecode/lib/middleware/simplehg.py @@ -22,6 +22,7 @@ SimpleHG middleware for handling mercurial protocol request (push/clone etc.). It's implemented with basic auth function """ +import copy import logging import urllib.parse import urllib.request @@ -32,6 +33,7 @@ from rhodecode.lib import utils from rhodecode.lib.ext_json import json from rhodecode.lib.middleware import simplevcs from rhodecode.lib.middleware.utils import get_path_info +from rhodecode.lib.str_utils import safe_str log = logging.getLogger(__name__) @@ -95,7 +97,7 @@ class SimpleHg(simplevcs.SimpleVCS): i = 1 chunks = [] # gather chunks stored in multiple 'hgarg_N' while True: - head = environ.get('HTTP_X_HGARG_{}'.format(i)) + head = environ.get(f'HTTP_X_HGARG_{i}') if not head: break i += 1 @@ -118,8 +120,18 @@ class SimpleHg(simplevcs.SimpleVCS): """ default = 'push' batch_cmds = [] + try: - cmds = cls._get_xarg_headers(environ) + httppostargs_enabled = True + post_args_size = environ.get('HTTP_X_HGARGS_POST') + if post_args_size and httppostargs_enabled: + # a new proto when httppostargs is enabled + response_data = copy.copy(environ['wsgi.input']) + cmds = [safe_str(response_data.read(post_args_size))] + else: + # old way... from headers + cmds = cls._get_xarg_headers(environ) + for pair in cmds: parts = pair.split(' ', 1) if len(parts) != 2: From 1d5d2c01558214fad12600b44a6d27b8ff3a1973 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 8 Nov 2024 16:17:57 +0100 Subject: [PATCH 45/99] tests: fixed test suite for celery adoption --- conftest.py | 7 +- pyproject.toml | 10 +- pytest.ini | 6 +- rhodecode/api/tests/test_add_field_to_repo.py | 3 +- rhodecode/api/tests/test_create_gist.py | 2 +- rhodecode/api/tests/test_create_repo.py | 2 +- rhodecode/api/tests/test_create_repo_group.py | 2 +- rhodecode/api/tests/test_create_user.py | 2 +- rhodecode/api/tests/test_create_user_group.py | 2 +- rhodecode/api/tests/test_fork_repo.py | 2 +- rhodecode/api/tests/test_update_repo.py | 4 +- .../apps/admin/tests/test_admin_audit_logs.py | 2 +- .../apps/admin/tests/test_admin_main_views.py | 2 +- .../apps/admin/tests/test_admin_repos.py | 2 +- .../tests/test_admin_repository_groups.py | 2 +- .../admin/tests/test_admin_user_groups.py | 2 +- .../apps/admin/tests/test_admin_users.py | 2 +- .../admin/tests/test_admin_users_ssh_keys.py | 2 +- .../home/tests/test_get_goto_switched_data.py | 2 +- .../home/tests/test_get_repo_list_data.py | 2 +- .../apps/home/tests/test_get_user_data.py | 2 +- .../home/tests/test_get_user_group_data.py | 2 +- rhodecode/apps/home/tests/test_home.py | 2 +- rhodecode/apps/login/tests/test_2fa.py | 2 +- rhodecode/apps/login/tests/test_login.py | 2 +- .../apps/login/tests/test_password_reset.py | 2 +- .../tests/test_my_account_auth_tokens.py | 2 +- .../tests/test_my_account_emails.py | 2 +- .../tests/test_my_account_notifications.py | 2 +- .../tests/test_my_account_password.py | 2 +- .../tests/test_my_account_profile.py | 2 +- .../tests/test_my_account_simple_views.py | 2 +- .../tests/test_my_account_ssh_keys.py | 2 +- .../tests/test_repo_compare_on_single_file.py | 15 +- .../apps/repository/tests/test_repo_files.py | 2 +- .../apps/repository/tests/test_repo_forks.py | 2 +- .../tests/test_repo_maintainance.py | 2 +- .../tests/test_repo_pullrequests.py | 76 +- .../repository/tests/test_repo_settings.py | 2 +- .../tests/test_repo_settings_advanced.py | 2 +- .../repository/tests/test_repo_summary.py | 2 +- .../tests/test_repo_vcs_settings.py | 2 +- .../repository/tests/test_vcs_settings.py | 9 +- .../apps/ssh_support/lib/backends/base.py | 6 +- .../apps/ssh_support/tests/test_server_git.py | 43 +- .../apps/ssh_support/tests/test_server_hg.py | 42 +- .../apps/ssh_support/tests/test_server_svn.py | 58 +- .../apps/user_group/tests/test_user_groups.py | 2 +- .../tests/test_user_group.py | 2 +- .../apps/user_profile/tests/test_users.py | 2 +- rhodecode/config/environment.py | 5 +- rhodecode/config/middleware.py | 3 + rhodecode/config/rcextensions/__init__.py | 2 +- .../validate_pushed_files_name_and_size.py | 2 +- .../rcextensions/helpers/extra_fields.py | 5 +- .../helpers/extract_post_commits.py | 1 - .../helpers/extract_pre_commits.py | 7 +- .../rcextensions/helpers/extract_pre_files.py | 5 +- .../config/rcextensions/helpers/http_call.py | 1 - rhodecode/config/rcextensions/utils.py | 16 +- rhodecode/config/routing_links.py | 8 +- rhodecode/config/utils.py | 27 +- rhodecode/lib/exceptions.py | 19 +- rhodecode/lib/hook_daemon/base.py | 120 +- .../lib/hook_daemon/celery_hooks_deamon.py | 16 +- rhodecode/lib/hook_daemon/hook_module.py | 84 +- .../lib/hook_daemon/http_hooks_deamon.py | 287 ----- rhodecode/lib/hook_daemon/utils.py | 61 + rhodecode/lib/hooks_base.py | 240 ++-- rhodecode/lib/middleware/simplegit.py | 12 +- rhodecode/lib/middleware/simplevcs.py | 309 +++-- rhodecode/lib/utils.py | 69 +- rhodecode/lib/vcs/__init__.py | 2 +- rhodecode/lib/vcs/backends/git/__init__.py | 7 - rhodecode/lib/vcs/backends/hg/__init__.py | 7 - rhodecode/lib/vcs/client_http.py | 2 +- rhodecode/model/comment.py | 12 +- rhodecode/model/pull_request.py | 6 +- rhodecode/model/settings.py | 24 - rhodecode/tests/__init__.py | 13 +- rhodecode/tests/auth_external_test.py | 5 +- rhodecode/tests/config/test_routing_links.py | 4 +- rhodecode/tests/conftest.py | 117 +- rhodecode/tests/conftest_common.py | 20 +- rhodecode/tests/database/__init__.py | 17 + rhodecode/tests/database/conftest.py | 139 +-- rhodecode/tests/database/test_creation.py | 1 - rhodecode/tests/database/test_migration.py | 46 +- .../tests/fixture_mods/fixture_pyramid.py | 226 ---- .../{fixture_mods => fixtures}/__init__.py | 0 .../diff_with_diff_data.diff | 0 .../git_diff_binary_and_normal.diff | 0 .../git_diff_binary_special_files.diff | 0 .../git_diff_binary_special_files_2.diff | 0 .../{ => diff_fixtures}/git_diff_chmod.diff | 0 .../git_diff_js_chars.diff | 0 .../git_diff_mod_single_binary_file.diff | 0 .../git_diff_rename_file.diff | 0 .../git_diff_rename_file_with_spaces.diff | 0 .../git_node_history_response.json | 0 .../hg_diff_add_single_binary_file.diff | 0 .../hg_diff_binary_and_normal.diff | 0 .../{ => diff_fixtures}/hg_diff_chmod.diff | 0 ...diff_chmod_and_mod_single_binary_file.diff | 0 .../hg_diff_copy_and_chmod_file.diff | 0 .../hg_diff_copy_and_modify_file.diff | 0 .../hg_diff_copy_chmod_and_edit_file.diff | 0 .../hg_diff_copy_file.diff | 0 .../hg_diff_copy_file_with_spaces.diff | 0 .../hg_diff_del_single_binary_file.diff | 0 ...iff_double_file_change_double_newline.diff | 0 .../hg_diff_double_file_change_newline.diff | 0 .../hg_diff_four_file_change_newline.diff | 0 .../hg_diff_mixed_filename_encodings.diff | 0 .../hg_diff_mod_file_and_rename.diff | 0 .../hg_diff_mod_single_binary_file.diff | 0 ..._mod_single_file_and_rename_and_chmod.diff | 0 .../hg_diff_no_newline.diff | 0 .../hg_diff_rename_and_chmod_file.diff | 0 .../hg_diff_rename_file.diff | 0 .../hg_diff_rename_file_with_spaces.diff | 0 .../hg_diff_single_file_change_newline.diff | 0 .../hg_node_history_response.json | 0 .../{ => diff_fixtures}/journal_dump.csv | 0 .../{ => diff_fixtures}/large_diff.diff | 0 .../svn_diff_binary_add_file.diff | 0 .../svn_diff_multiple_changes.diff | 0 .../svn_node_history_branches.json | 0 .../svn_node_history_response.json | 0 rhodecode/tests/fixtures/fixture_pyramid.py | 308 +++++ .../fixture_utils.py | 775 ++++++------- .../tests/fixtures/function_scoped_baseapp.py | 52 + .../tests/fixtures/module_scoped_baseapp.py | 49 + .../{fixture.py => fixtures/rc_fixture.py} | 198 ++-- .../tests/fixtures/rcextensions_fixtures.py | 157 +++ .../tests/functional/test_delegated_admin.py | 2 +- rhodecode/tests/integrations/__init__.py | 17 + .../tests/integrations/test_integration.py | 2 +- rhodecode/tests/lib/auth_modules/__init__.py | 17 + .../tests/lib/middleware/test_simplehg.py | 6 +- .../tests/lib/middleware/test_simplevcs.py | 80 +- .../lib/middleware/utils/test_scm_app_http.py | 8 +- rhodecode/tests/lib/test_auth.py | 3 +- rhodecode/tests/lib/test_diffs.py | 2 +- rhodecode/tests/lib/test_hooks_daemon.py | 348 +----- rhodecode/tests/lib/test_utils.py | 11 +- rhodecode/tests/load/http_performance.py | 6 +- rhodecode/tests/load/profile-mem.py | 10 - rhodecode/tests/load/profile.py | 1 - rhodecode/tests/load/vcs_performance.py | 2 - rhodecode/tests/models/common.py | 2 +- rhodecode/tests/models/test_notifications.py | 2 +- rhodecode/tests/models/test_permissions.py | 2 +- rhodecode/tests/models/test_pullrequest.py | 781 ++++++------- rhodecode/tests/models/test_repo_groups.py | 2 +- ...t_user_group_permissions_on_repo_groups.py | 2 +- rhodecode/tests/models/test_user_groups.py | 2 +- rhodecode/tests/models/test_users.py | 2 +- rhodecode/tests/other/test_validators.py | 4 +- rhodecode/tests/rhodecode.ini | 36 +- rhodecode/tests/run_full_tests.sh | 17 - rhodecode/tests/server_utils.py | 100 +- rhodecode/tests/test_vcs_operations.sh | 27 - rhodecode/tests/utils.py | 94 +- rhodecode/tests/vcs/__init__.py | 1 - rhodecode/tests/vcs/conftest.py | 79 +- rhodecode/tests/vcs/test_archives.py | 130 +-- rhodecode/tests/vcs/test_branches.py | 20 +- rhodecode/tests/vcs/test_client_http.py | 44 +- rhodecode/tests/vcs/test_commits.py | 244 ++-- rhodecode/tests/vcs/test_config.py | 24 +- rhodecode/tests/vcs/test_diff.py | 169 ++- rhodecode/tests/vcs/test_exceptions.py | 6 +- .../tests/vcs/test_filenodes_unicode_path.py | 14 +- rhodecode/tests/vcs/test_getitem.py | 25 +- rhodecode/tests/vcs/test_getslice.py | 38 +- rhodecode/tests/vcs/test_git.py | 1011 ++++++++--------- rhodecode/tests/vcs/test_hg.py | 986 ++++++++-------- .../test_hg_vcsserver_cache_invalidation.py | 76 +- rhodecode/tests/vcs/test_inmemory.py | 183 ++- rhodecode/tests/vcs/test_nodes.py | 152 ++- rhodecode/tests/vcs/test_repository.py | 40 +- rhodecode/tests/vcs/test_svn.py | 92 +- rhodecode/tests/vcs/test_tags.py | 27 +- rhodecode/tests/vcs/test_utils.py | 126 +- rhodecode/tests/vcs/test_vcs.py | 34 +- rhodecode/tests/vcs/utils.py | 25 +- rhodecode/tests/vcs_operations/__init__.py | 4 +- rhodecode/tests/vcs_operations/conftest.py | 235 ++-- .../test_vcs_calls_custom_auth_code_403.py | 33 +- .../test_vcs_calls_custom_auth_code_404.py | 33 +- ...est_vcs_calls_custom_auth_code_bad_code.py | 30 +- .../test_vcs_calls_small_post_buffer.py | 58 +- .../test_vcs_operations_bad_client.py | 89 ++ .../test_vcs_operations_branch_protection.py | 19 +- .../test_vcs_operations_by_auth_tokens.py | 58 +- .../test_vcs_operations_clone.py | 63 + .../test_vcs_operations_force_push.py | 31 +- .../vcs_operations/test_vcs_operations_git.py | 120 +- .../vcs_operations/test_vcs_operations_hg.py | 125 +- ...est_vcs_operations_integrations_trigger.py | 53 +- .../test_vcs_operations_locking.py | 150 +-- ...test_vcs_operations_locking_custom_code.py | 94 +- .../test_vcs_operations_new_branch_push.py | 24 +- .../test_vcs_operations_rcextensions_push.py | 81 ++ .../test_vcs_operations_special.py | 70 +- .../vcs_operations/test_vcs_operations_svn.py | 115 +- .../test_vcs_operations_tag_push.py | 59 +- rhodecode/tests/vcsserver_http.ini | 4 +- 209 files changed, 5248 insertions(+), 5275 deletions(-) delete mode 100644 rhodecode/lib/hook_daemon/http_hooks_deamon.py create mode 100644 rhodecode/lib/hook_daemon/utils.py create mode 100644 rhodecode/tests/database/__init__.py delete mode 100644 rhodecode/tests/fixture_mods/fixture_pyramid.py rename rhodecode/tests/{fixture_mods => fixtures}/__init__.py (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/diff_with_diff_data.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_binary_and_normal.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_binary_special_files.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_binary_special_files_2.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_chmod.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_js_chars.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_mod_single_binary_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_rename_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_diff_rename_file_with_spaces.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/git_node_history_response.json (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_add_single_binary_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_binary_and_normal.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_chmod.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_chmod_and_mod_single_binary_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_copy_and_chmod_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_copy_and_modify_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_copy_chmod_and_edit_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_copy_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_copy_file_with_spaces.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_del_single_binary_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_double_file_change_double_newline.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_double_file_change_newline.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_four_file_change_newline.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_mixed_filename_encodings.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_mod_file_and_rename.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_mod_single_binary_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_mod_single_file_and_rename_and_chmod.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_no_newline.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_rename_and_chmod_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_rename_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_rename_file_with_spaces.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_diff_single_file_change_newline.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/hg_node_history_response.json (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/journal_dump.csv (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/large_diff.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/svn_diff_binary_add_file.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/svn_diff_multiple_changes.diff (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/svn_node_history_branches.json (100%) rename rhodecode/tests/fixtures/{ => diff_fixtures}/svn_node_history_response.json (100%) create mode 100644 rhodecode/tests/fixtures/fixture_pyramid.py rename rhodecode/tests/{fixture_mods => fixtures}/fixture_utils.py (69%) create mode 100644 rhodecode/tests/fixtures/function_scoped_baseapp.py create mode 100644 rhodecode/tests/fixtures/module_scoped_baseapp.py rename rhodecode/tests/{fixture.py => fixtures/rc_fixture.py} (68%) create mode 100644 rhodecode/tests/fixtures/rcextensions_fixtures.py create mode 100644 rhodecode/tests/integrations/__init__.py create mode 100644 rhodecode/tests/lib/auth_modules/__init__.py delete mode 100755 rhodecode/tests/run_full_tests.sh delete mode 100755 rhodecode/tests/test_vcs_operations.sh create mode 100644 rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py create mode 100644 rhodecode/tests/vcs_operations/test_vcs_operations_clone.py create mode 100644 rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py diff --git a/conftest.py b/conftest.py index f731dbc6..0ac472d3 100644 --- a/conftest.py +++ b/conftest.py @@ -27,8 +27,11 @@ from rhodecode.tests.conftest_common import ( # noqa pytest_plugins = [ - "rhodecode.tests.fixture_mods.fixture_pyramid", - "rhodecode.tests.fixture_mods.fixture_utils", + "rhodecode.tests.fixtures.fixture_pyramid", + "rhodecode.tests.fixtures.fixture_utils", + "rhodecode.tests.fixtures.function_scoped_baseapp", + "rhodecode.tests.fixtures.module_scoped_baseapp", + "rhodecode.tests.fixtures.rcextensions_fixtures", ] diff --git a/pyproject.toml b/pyproject.toml index 84e5acbd..599ef4f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,8 +65,7 @@ dependencies = {file = ["requirements.txt"]} optional-dependencies.tests = {file = ["requirements_test.txt"]} [tool.ruff] - -select = [ +lint.select = [ # Pyflakes "F", # Pycodestyle @@ -75,16 +74,13 @@ select = [ # isort "I001" ] - -ignore = [ +lint.ignore = [ "E501", # line too long, handled by black ] - # Same as Black. line-length = 120 -[tool.ruff.isort] - +[tool.ruff.lint.isort] known-first-party = ["rhodecode"] [tool.ruff.format] diff --git a/pytest.ini b/pytest.ini index e1bd2486..6afc1d2f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -4,8 +4,10 @@ norecursedirs = rhodecode/public rhodecode/templates tests/scripts cache_dir = /tmp/.pytest_cache pyramid_config = rhodecode/tests/rhodecode.ini -vcsserver_protocol = http -vcsserver_config_http = rhodecode/tests/vcsserver_http.ini + +vcsserver_config = rhodecode/tests/vcsserver_http.ini +rhodecode_config = rhodecode/tests/rhodecode.ini +celery_config = rhodecode/tests/rhodecode.ini addopts = --pdbcls=IPython.terminal.debugger:TerminalPdb diff --git a/rhodecode/api/tests/test_add_field_to_repo.py b/rhodecode/api/tests/test_add_field_to_repo.py index c12ae440..3b9f1fc8 100644 --- a/rhodecode/api/tests/test_add_field_to_repo.py +++ b/rhodecode/api/tests/test_add_field_to_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_gist.py b/rhodecode/api/tests/test_create_gist.py index 41b4bf77..c440fb9e 100644 --- a/rhodecode/api/tests/test_create_gist.py +++ b/rhodecode/api/tests/test_create_gist.py @@ -24,7 +24,7 @@ from rhodecode.model.db import Gist from rhodecode.model.gist import GistModel from rhodecode.api.tests.utils import ( build_data, api_call, assert_error, assert_ok, crash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture @pytest.mark.usefixtures("testuser_api", "app") diff --git a/rhodecode/api/tests/test_create_repo.py b/rhodecode/api/tests/test_create_repo.py index be373a42..872d62e7 100644 --- a/rhodecode/api/tests/test_create_repo.py +++ b/rhodecode/api/tests/test_create_repo.py @@ -27,7 +27,7 @@ from rhodecode.model.user import UserModel from rhodecode.tests import TEST_USER_ADMIN_LOGIN from rhodecode.api.tests.utils import ( build_data, api_call, assert_ok, assert_error, crash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.lib.ext_json import json from rhodecode.lib.str_utils import safe_str diff --git a/rhodecode/api/tests/test_create_repo_group.py b/rhodecode/api/tests/test_create_repo_group.py index 72e7ee56..a72b2180 100644 --- a/rhodecode/api/tests/test_create_repo_group.py +++ b/rhodecode/api/tests/test_create_repo_group.py @@ -26,7 +26,7 @@ from rhodecode.model.user import UserModel from rhodecode.tests import TEST_USER_ADMIN_LOGIN from rhodecode.api.tests.utils import ( build_data, api_call, assert_ok, assert_error, crash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture fixture = Fixture() diff --git a/rhodecode/api/tests/test_create_user.py b/rhodecode/api/tests/test_create_user.py index 9b1e6797..6b612232 100644 --- a/rhodecode/api/tests/test_create_user.py +++ b/rhodecode/api/tests/test_create_user.py @@ -26,7 +26,7 @@ from rhodecode.tests import ( TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL) from rhodecode.api.tests.utils import ( build_data, api_call, assert_ok, assert_error, jsonify, crash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.model.db import RepoGroup diff --git a/rhodecode/api/tests/test_create_user_group.py b/rhodecode/api/tests/test_create_user_group.py index 14035458..fb101f0d 100644 --- a/rhodecode/api/tests/test_create_user_group.py +++ b/rhodecode/api/tests/test_create_user_group.py @@ -25,7 +25,7 @@ from rhodecode.model.user import UserModel from rhodecode.model.user_group import UserGroupModel from rhodecode.api.tests.utils import ( build_data, api_call, assert_error, assert_ok, crash, jsonify) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture @pytest.mark.usefixtures("testuser_api", "app") diff --git a/rhodecode/api/tests/test_fork_repo.py b/rhodecode/api/tests/test_fork_repo.py index f24b1a90..bb4cb554 100644 --- a/rhodecode/api/tests/test_fork_repo.py +++ b/rhodecode/api/tests/test_fork_repo.py @@ -28,7 +28,7 @@ from rhodecode.model.user import UserModel from rhodecode.tests import TEST_USER_ADMIN_LOGIN from rhodecode.api.tests.utils import ( build_data, api_call, assert_error, assert_ok, crash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture fixture = Fixture() diff --git a/rhodecode/api/tests/test_update_repo.py b/rhodecode/api/tests/test_update_repo.py index f978b9aa..64f98f7c 100644 --- a/rhodecode/api/tests/test_update_repo.py +++ b/rhodecode/api/tests/test_update_repo.py @@ -25,8 +25,8 @@ from rhodecode.model.scm import ScmModel from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN from rhodecode.api.tests.utils import ( build_data, api_call, assert_error, assert_ok, crash, jsonify) -from rhodecode.tests.fixture import Fixture -from rhodecode.tests.fixture_mods.fixture_utils import plain_http_host_only_stub +from rhodecode.tests.fixtures.rc_fixture import Fixture +from rhodecode.tests.fixtures.fixture_utils import plain_http_host_only_stub fixture = Fixture() diff --git a/rhodecode/apps/admin/tests/test_admin_audit_logs.py b/rhodecode/apps/admin/tests/test_admin_audit_logs.py index da127e3d..fe1475c3 100644 --- a/rhodecode/apps/admin/tests/test_admin_audit_logs.py +++ b/rhodecode/apps/admin/tests/test_admin_audit_logs.py @@ -26,7 +26,7 @@ import pytest from rhodecode.lib.str_utils import safe_str from rhodecode.tests import * from rhodecode.tests.routes import route_path -from rhodecode.tests.fixture import FIXTURES +from rhodecode.tests.fixtures.rc_fixture import FIXTURES from rhodecode.model.db import UserLog from rhodecode.model.meta import Session diff --git a/rhodecode/apps/admin/tests/test_admin_main_views.py b/rhodecode/apps/admin/tests/test_admin_main_views.py index b0b75eab..1520cc51 100644 --- a/rhodecode/apps/admin/tests/test_admin_main_views.py +++ b/rhodecode/apps/admin/tests/test_admin_main_views.py @@ -20,7 +20,7 @@ import pytest from rhodecode.tests import TestController -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/admin/tests/test_admin_repos.py b/rhodecode/apps/admin/tests/test_admin_repos.py index 014a79b7..ab09b219 100644 --- a/rhodecode/apps/admin/tests/test_admin_repos.py +++ b/rhodecode/apps/admin/tests/test_admin_repos.py @@ -37,7 +37,7 @@ from rhodecode.model.user import UserModel from rhodecode.tests import ( login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) -from rhodecode.tests.fixture import Fixture, error_function +from rhodecode.tests.fixtures.rc_fixture import Fixture, error_function from rhodecode.tests.utils import repo_on_filesystem from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/admin/tests/test_admin_repository_groups.py b/rhodecode/apps/admin/tests/test_admin_repository_groups.py index 1f94bce2..9d168f39 100644 --- a/rhodecode/apps/admin/tests/test_admin_repository_groups.py +++ b/rhodecode/apps/admin/tests/test_admin_repository_groups.py @@ -27,7 +27,7 @@ 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) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/admin/tests/test_admin_user_groups.py b/rhodecode/apps/admin/tests/test_admin_user_groups.py index c6dbe1d0..61d37d10 100644 --- a/rhodecode/apps/admin/tests/test_admin_user_groups.py +++ b/rhodecode/apps/admin/tests/test_admin_user_groups.py @@ -24,7 +24,7 @@ from rhodecode.model.meta import Session from rhodecode.tests import ( TestController, assert_session_flash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/admin/tests/test_admin_users.py b/rhodecode/apps/admin/tests/test_admin_users.py index 39b83e21..095d84a0 100644 --- a/rhodecode/apps/admin/tests/test_admin_users.py +++ b/rhodecode/apps/admin/tests/test_admin_users.py @@ -28,7 +28,7 @@ from rhodecode.model.user import UserModel from rhodecode.tests import ( TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py b/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py index 1d1e09b3..33518b74 100644 --- a/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py +++ b/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py @@ -22,7 +22,7 @@ import pytest from rhodecode.model.db import User, UserSshKeys from rhodecode.tests import TestController, assert_session_flash -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/home/tests/test_get_goto_switched_data.py b/rhodecode/apps/home/tests/test_get_goto_switched_data.py index b7d6e269..551fcbe9 100644 --- a/rhodecode/apps/home/tests/test_get_goto_switched_data.py +++ b/rhodecode/apps/home/tests/test_get_goto_switched_data.py @@ -27,7 +27,7 @@ from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.db import Session, Repository, RepoGroup from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/home/tests/test_get_repo_list_data.py b/rhodecode/apps/home/tests/test_get_repo_list_data.py index 52139aed..bf6b7d31 100644 --- a/rhodecode/apps/home/tests/test_get_repo_list_data.py +++ b/rhodecode/apps/home/tests/test_get_repo_list_data.py @@ -22,7 +22,7 @@ from rhodecode.model.db import Repository from rhodecode.lib.ext_json import json from rhodecode.tests import TestController -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/home/tests/test_get_user_data.py b/rhodecode/apps/home/tests/test_get_user_data.py index d1128c5c..2113b360 100644 --- a/rhodecode/apps/home/tests/test_get_user_data.py +++ b/rhodecode/apps/home/tests/test_get_user_data.py @@ -20,7 +20,7 @@ import pytest from rhodecode.lib.ext_json import json from rhodecode.tests import TestController -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/home/tests/test_get_user_group_data.py b/rhodecode/apps/home/tests/test_get_user_group_data.py index dea6573f..9fdb613c 100644 --- a/rhodecode/apps/home/tests/test_get_user_group_data.py +++ b/rhodecode/apps/home/tests/test_get_user_group_data.py @@ -40,7 +40,7 @@ import pytest from rhodecode.lib.ext_json import json from rhodecode.tests import TestController -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/home/tests/test_home.py b/rhodecode/apps/home/tests/test_home.py index 9b855285..fdcfb634 100644 --- a/rhodecode/apps/home/tests/test_home.py +++ b/rhodecode/apps/home/tests/test_home.py @@ -24,7 +24,7 @@ from rhodecode.model.db import Repository, RepoGroup, User from rhodecode.model.meta import Session from rhodecode.model.settings import SettingsModel from rhodecode.tests import TestController -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/login/tests/test_2fa.py b/rhodecode/apps/login/tests/test_2fa.py index 3c3ec102..0e193253 100644 --- a/rhodecode/apps/login/tests/test_2fa.py +++ b/rhodecode/apps/login/tests/test_2fa.py @@ -3,7 +3,7 @@ import mock from rhodecode.lib.type_utils import AttributeDict from rhodecode.model.meta import Session -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path from rhodecode.model.settings import SettingsModel diff --git a/rhodecode/apps/login/tests/test_login.py b/rhodecode/apps/login/tests/test_login.py index 424cfabf..22c9ac8e 100644 --- a/rhodecode/apps/login/tests/test_login.py +++ b/rhodecode/apps/login/tests/test_login.py @@ -31,7 +31,7 @@ from rhodecode.model.meta import Session from rhodecode.tests import ( assert_session_flash, HG_REPO, TEST_USER_ADMIN_LOGIN, no_newline_id_generator) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/login/tests/test_password_reset.py b/rhodecode/apps/login/tests/test_password_reset.py index 3d95cc2d..d4d26b4f 100644 --- a/rhodecode/apps/login/tests/test_password_reset.py +++ b/rhodecode/apps/login/tests/test_password_reset.py @@ -22,7 +22,7 @@ from rhodecode.lib import helpers as h from rhodecode.tests import ( TestController, clear_cache_regions, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.utils import AssertResponse from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py b/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py index 5923c751..162221dc 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py +++ b/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py @@ -22,7 +22,7 @@ from rhodecode.apps._base import ADMIN_PREFIX from rhodecode.model.db import User from rhodecode.tests import ( TestController, assert_session_flash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/my_account/tests/test_my_account_emails.py b/rhodecode/apps/my_account/tests/test_my_account_emails.py index ba4f542b..4ca87b60 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_emails.py +++ b/rhodecode/apps/my_account/tests/test_my_account_emails.py @@ -23,7 +23,7 @@ from rhodecode.model.db import User, UserEmailMap from rhodecode.tests import ( TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL, assert_session_flash, TEST_USER_REGULAR_PASS) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/my_account/tests/test_my_account_notifications.py b/rhodecode/apps/my_account/tests/test_my_account_notifications.py index f6746159..a8259dd7 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_notifications.py +++ b/rhodecode/apps/my_account/tests/test_my_account_notifications.py @@ -21,7 +21,7 @@ import pytest from rhodecode.tests import ( TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path from rhodecode.model.db import Notification, User diff --git a/rhodecode/apps/my_account/tests/test_my_account_password.py b/rhodecode/apps/my_account/tests/test_my_account_password.py index a1841e1b..23f5bdfa 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_password.py +++ b/rhodecode/apps/my_account/tests/test_my_account_password.py @@ -24,7 +24,7 @@ from rhodecode.lib.auth import check_password from rhodecode.model.meta import Session from rhodecode.model.user import UserModel from rhodecode.tests import assert_session_flash, TestController -from rhodecode.tests.fixture import Fixture, error_function +from rhodecode.tests.fixtures.rc_fixture import Fixture, error_function from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/my_account/tests/test_my_account_profile.py b/rhodecode/apps/my_account/tests/test_my_account_profile.py index 79117835..fe8d9ac8 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_profile.py +++ b/rhodecode/apps/my_account/tests/test_my_account_profile.py @@ -20,7 +20,7 @@ from rhodecode.tests import ( TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/my_account/tests/test_my_account_simple_views.py b/rhodecode/apps/my_account/tests/test_my_account_simple_views.py index 154f4768..1fb74f8c 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_simple_views.py +++ b/rhodecode/apps/my_account/tests/test_my_account_simple_views.py @@ -19,7 +19,7 @@ from rhodecode.model.db import User, Repository, UserFollowing from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py b/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py index 7e769eda..cf853330 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py +++ b/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py @@ -21,7 +21,7 @@ from rhodecode.model.db import User, UserSshKeys from rhodecode.tests import TestController, assert_session_flash -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py index 8ba459eb..2d81aaad 100644 --- a/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py +++ b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py @@ -22,7 +22,7 @@ import pytest from rhodecode.apps.repository.tests.test_repo_compare import ComparePage from rhodecode.lib.vcs import nodes from rhodecode.lib.vcs.backends.base import EmptyCommit -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.utils import commit_change from rhodecode.tests.routes import route_path @@ -166,14 +166,15 @@ class TestSideBySideDiff(object): response.mustcontain('Collapse 2 commits') response.mustcontain('123 file changed') - response.mustcontain( - 'r%s:%s...r%s:%s' % ( - commit1.idx, commit1.short_id, commit2.idx, commit2.short_id)) + response.mustcontain(f'r{commit1.idx}:{commit1.short_id}...r{commit2.idx}:{commit2.short_id}') response.mustcontain(f_path) - @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)') + #@pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)') def test_diff_side_by_side_from_0_commit_with_file_filter(self, app, backend, backend_stub): + if backend.alias == 'git': + pytest.skip('GIT does not handle empty commit compare correct (missing 1 commit)') + f_path = b'test_sidebyside_file.py' commit1_content = b'content-25d7e49c18b159446c\n' commit2_content = b'content-603d6c72c46d953420\n' @@ -200,9 +201,7 @@ class TestSideBySideDiff(object): response.mustcontain('Collapse 2 commits') response.mustcontain('1 file changed') - response.mustcontain( - 'r%s:%s...r%s:%s' % ( - commit1.idx, commit1.short_id, commit2.idx, commit2.short_id)) + response.mustcontain(f'r{commit1.idx}:{commit1.short_id}...r{commit2.idx}:{commit2.short_id}') response.mustcontain(f_path) diff --git a/rhodecode/apps/repository/tests/test_repo_files.py b/rhodecode/apps/repository/tests/test_repo_files.py index 01301c8f..c59aed27 100644 --- a/rhodecode/apps/repository/tests/test_repo_files.py +++ b/rhodecode/apps/repository/tests/test_repo_files.py @@ -33,7 +33,7 @@ from rhodecode.lib.vcs.conf import settings from rhodecode.model.db import Session, Repository from rhodecode.tests import assert_session_flash -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/repository/tests/test_repo_forks.py b/rhodecode/apps/repository/tests/test_repo_forks.py index 978b86ff..e52dc880 100644 --- a/rhodecode/apps/repository/tests/test_repo_forks.py +++ b/rhodecode/apps/repository/tests/test_repo_forks.py @@ -21,7 +21,7 @@ import pytest from rhodecode.tests import TestController, assert_session_flash, HG_FORK, GIT_FORK -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.lib import helpers as h from rhodecode.model.db import Repository diff --git a/rhodecode/apps/repository/tests/test_repo_maintainance.py b/rhodecode/apps/repository/tests/test_repo_maintainance.py index 8b812084..86e0ad49 100644 --- a/rhodecode/apps/repository/tests/test_repo_maintainance.py +++ b/rhodecode/apps/repository/tests/test_repo_maintainance.py @@ -21,7 +21,7 @@ import pytest from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/repository/tests/test_repo_pullrequests.py b/rhodecode/apps/repository/tests/test_repo_pullrequests.py index 1df532a3..ad41a2cb 100644 --- a/rhodecode/apps/repository/tests/test_repo_pullrequests.py +++ b/rhodecode/apps/repository/tests/test_repo_pullrequests.py @@ -15,6 +15,9 @@ # 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 logging +import os + import mock import pytest @@ -41,7 +44,7 @@ from rhodecode.tests import ( TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, ) -from rhodecode.tests.fixture_mods.fixture_utils import PRTestUtility +from rhodecode.tests.fixtures.fixture_utils import PRTestUtility from rhodecode.tests.routes import route_path @@ -1050,7 +1053,6 @@ class TestPullrequestsView(object): ) assert len(notifications.all()) == 2 - @pytest.mark.xfail(reason="unable to fix this test after python3 migration") def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token): commits = [ { @@ -1125,20 +1127,38 @@ class TestPullrequestsView(object): response.mustcontain(no=["content_of_ancestor-child"]) response.mustcontain("content_of_change") - def test_merge_pull_request_enabled(self, pr_util, csrf_token): - # Clear any previous calls to rcextensions - rhodecode.EXTENSIONS.calls.clear() + def test_merge_pull_request_enabled(self, pr_util, csrf_token, rcextensions_modification): pull_request = pr_util.create_pull_request(approved=True, mergeable=True) pull_request_id = pull_request.pull_request_id - repo_name = (pull_request.target_repo.scm_instance().name,) + repo_name = pull_request.target_repo.scm_instance().name url = route_path( "pullrequest_merge", - repo_name=str(repo_name[0]), + repo_name=repo_name, pull_request_id=pull_request_id, ) - response = self.app.post(url, params={"csrf_token": csrf_token}).follow() + + rcstack_location = os.path.dirname(self.app._pyramid_registry.settings['__file__']) + rc_ext_location = os.path.join(rcstack_location, 'rcextension-output.txt') + + + mods = [ + ('_push_hook', + f""" + import os + action = kwargs['action'] + commit_ids = kwargs['commit_ids'] + with open('{rc_ext_location}', 'w') as f: + f.write('test-execution'+os.linesep) + f.write(f'{{action}}'+os.linesep) + f.write(f'{{commit_ids}}'+os.linesep) + return HookResponse(0, 'HOOK_TEST') + """) + ] + # Add the hook + with rcextensions_modification(rcstack_location, mods, create_if_missing=True, force_create=True): + response = self.app.post(url, params={"csrf_token": csrf_token}).follow() pull_request = PullRequest.get(pull_request_id) @@ -1162,12 +1182,39 @@ class TestPullrequestsView(object): assert actions[-1].action == "user.push" assert actions[-1].action_data["commit_ids"] == pr_commit_ids - # Check post_push rcextension was really executed - push_calls = rhodecode.EXTENSIONS.calls["_push_hook"] - assert len(push_calls) == 1 - unused_last_call_args, last_call_kwargs = push_calls[0] - assert last_call_kwargs["action"] == "push" - assert last_call_kwargs["commit_ids"] == pr_commit_ids + with open(rc_ext_location) as f: + f_data = f.read() + assert 'test-execution' in f_data + for commit_id in pr_commit_ids: + assert f'{commit_id}' in f_data + + def test_merge_pull_request_forbidden_by_pre_push_hook(self, pr_util, csrf_token, rcextensions_modification, caplog): + caplog.set_level(logging.WARNING, logger="rhodecode.model.pull_request") + + pull_request = pr_util.create_pull_request(approved=True, mergeable=True) + pull_request_id = pull_request.pull_request_id + repo_name = pull_request.target_repo.scm_instance().name + + url = route_path( + "pullrequest_merge", + repo_name=repo_name, + pull_request_id=pull_request_id, + ) + + rcstack_location = os.path.dirname(self.app._pyramid_registry.settings['__file__']) + + mods = [ + ('_pre_push_hook', + f""" + return HookResponse(1, 'HOOK_TEST_FORBIDDEN') + """) + ] + # Add the hook + with rcextensions_modification(rcstack_location, mods, create_if_missing=True, force_create=True): + self.app.post(url, params={"csrf_token": csrf_token}) + + assert 'Merge failed, not updating the pull request.' in [r[2] for r in caplog.record_tuples] + def test_merge_pull_request_disabled(self, pr_util, csrf_token): pull_request = pr_util.create_pull_request(mergeable=False) @@ -1523,7 +1570,6 @@ class TestPullrequestsView(object): assert pull_request.revisions == [commit_ids["change-rebased"]] - def test_remove_pull_request_branch(self, backend_git, csrf_token): branch_name = "development" commits = [ diff --git a/rhodecode/apps/repository/tests/test_repo_settings.py b/rhodecode/apps/repository/tests/test_repo_settings.py index 14b7281c..d20fe3cb 100644 --- a/rhodecode/apps/repository/tests/test_repo_settings.py +++ b/rhodecode/apps/repository/tests/test_repo_settings.py @@ -26,7 +26,7 @@ from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User from rhodecode.model.meta import Session from rhodecode.tests import ( TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, assert_session_flash) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py index 1d3cdc80..dc8e8c6a 100644 --- a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py +++ b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py @@ -24,7 +24,7 @@ from rhodecode.model.db import Repository from rhodecode.model.repo import RepoModel from rhodecode.tests import ( HG_REPO, GIT_REPO, assert_session_flash, no_newline_id_generator) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.utils import repo_on_filesystem from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/repository/tests/test_repo_summary.py b/rhodecode/apps/repository/tests/test_repo_summary.py index 8fb75b34..a2a3ab92 100644 --- a/rhodecode/apps/repository/tests/test_repo_summary.py +++ b/rhodecode/apps/repository/tests/test_repo_summary.py @@ -31,7 +31,7 @@ from rhodecode.model.meta import Session from rhodecode.model.repo import RepoModel from rhodecode.model.scm import ScmModel from rhodecode.tests import assert_session_flash -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.utils import AssertResponse, repo_on_filesystem from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py index cd41d528..63accea0 100644 --- a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py +++ b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py @@ -30,7 +30,7 @@ from rhodecode.model.user import UserModel from rhodecode.tests import ( login_user_session, logout_user_session, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.utils import AssertResponse from rhodecode.tests.routes import route_path diff --git a/rhodecode/apps/repository/tests/test_vcs_settings.py b/rhodecode/apps/repository/tests/test_vcs_settings.py index 72d9a07c..0b05c64a 100644 --- a/rhodecode/apps/repository/tests/test_vcs_settings.py +++ b/rhodecode/apps/repository/tests/test_vcs_settings.py @@ -32,16 +32,13 @@ class TestAdminRepoVcsSettings(object): @pytest.mark.parametrize('setting_name, setting_backends', [ ('hg_use_rebase_for_merging', ['hg']), ]) - def test_labs_settings_visible_if_enabled( - self, setting_name, setting_backends, backend): + def test_labs_settings_visible_if_enabled(self, setting_name, setting_backends, backend): if backend.alias not in setting_backends: pytest.skip('Setting not available for backend {}'.format(backend)) - vcs_settings_url = route_path( - 'edit_repo_vcs', repo_name=backend.repo.repo_name) + vcs_settings_url = route_path('edit_repo_vcs', repo_name=backend.repo.repo_name) - with mock.patch.dict( - rhodecode.CONFIG, {'labs_settings_active': 'true'}): + with mock.patch.dict(rhodecode.CONFIG, {'labs_settings_active': 'true'}): response = self.app.get(vcs_settings_url) assertr = response.assert_response() diff --git a/rhodecode/apps/ssh_support/lib/backends/base.py b/rhodecode/apps/ssh_support/lib/backends/base.py index ede5ad3a..639a810e 100644 --- a/rhodecode/apps/ssh_support/lib/backends/base.py +++ b/rhodecode/apps/ssh_support/lib/backends/base.py @@ -20,7 +20,7 @@ import os import sys import logging -from rhodecode.lib.hook_daemon.base import prepare_callback_daemon +from rhodecode.lib.hook_daemon.utils import prepare_callback_daemon from rhodecode.lib.ext_json import sjson as json from rhodecode.lib.vcs.conf import settings as vcs_settings from rhodecode.lib.api_utils import call_service_api @@ -162,9 +162,7 @@ class SshVcsServer(object): extras = {} extras.update(tunnel_extras) - callback_daemon, extras = prepare_callback_daemon( - extras, protocol=self.hooks_protocol, - host=vcs_settings.HOOKS_HOST) + callback_daemon, extras = prepare_callback_daemon(extras, protocol=self.hooks_protocol) with callback_daemon: try: diff --git a/rhodecode/apps/ssh_support/tests/test_server_git.py b/rhodecode/apps/ssh_support/tests/test_server_git.py index 843bec16..fc721340 100644 --- a/rhodecode/apps/ssh_support/tests/test_server_git.py +++ b/rhodecode/apps/ssh_support/tests/test_server_git.py @@ -33,19 +33,24 @@ class GitServerCreator(object): 'app:main': { 'ssh.executable.git': git_path, 'vcs.hooks.protocol.v2': 'celery', + 'app.service_api.host': 'http://localhost', + 'app.service_api.token': 'secret4', + 'rhodecode.api.url': '/_admin/api', } } repo_name = 'test_git' repo_mode = 'receive-pack' user = plain_dummy_user() - def __init__(self): - pass + def __init__(self, service_api_url, ini_file): + self.service_api_url = service_api_url + self.ini_file = ini_file def create(self, **kwargs): + self.config_data['app:main']['app.service_api.host'] = self.service_api_url parameters = { 'store': self.root, - 'ini_path': '', + 'ini_path': self.ini_file, 'user': self.user, 'repo_name': self.repo_name, 'repo_mode': self.repo_mode, @@ -60,12 +65,30 @@ class GitServerCreator(object): return server -@pytest.fixture() -def git_server(app): - return GitServerCreator() +@pytest.fixture(scope='module') +def git_server(request, module_app, rhodecode_factory, available_port_factory): + ini_file = module_app._pyramid_settings['__file__'] + vcsserver_host = module_app._pyramid_settings['vcs.server'] + + store_dir = os.path.dirname(ini_file) + + # start rhodecode for service API + rc = rhodecode_factory( + request, + store_dir=store_dir, + port=available_port_factory(), + overrides=( + {'handler_console': {'level': 'DEBUG'}}, + {'app:main': {'vcs.server': vcsserver_host}}, + {'app:main': {'repo_store.path': store_dir}} + )) + + service_api_url = f'http://{rc.bind_addr}' + + return GitServerCreator(service_api_url, ini_file) -class TestGitServer(object): +class TestGitServer: def test_command(self, git_server): server = git_server.create() @@ -102,14 +125,14 @@ class TestGitServer(object): assert result is value def test_run_returns_executes_command(self, git_server): - server = git_server.create() from rhodecode.apps.ssh_support.lib.backends.git import GitTunnelWrapper + server = git_server.create() os.environ['SSH_CLIENT'] = '127.0.0.1' with mock.patch.object(GitTunnelWrapper, 'create_hooks_env') as _patch: _patch.return_value = 0 with mock.patch.object(GitTunnelWrapper, 'command', return_value='date'): - exit_code = server.run() + exit_code = server.run(tunnel_extras={'config': server.ini_path}) assert exit_code == (0, False) @@ -135,7 +158,7 @@ class TestGitServer(object): 'action': action, 'ip': '10.10.10.10', 'locked_by': [None, None], - 'config': '', + 'config': git_server.ini_file, 'repo_store': store, 'server_url': None, 'hooks': ['push', 'pull'], diff --git a/rhodecode/apps/ssh_support/tests/test_server_hg.py b/rhodecode/apps/ssh_support/tests/test_server_hg.py index 9765eb52..e9585eb5 100644 --- a/rhodecode/apps/ssh_support/tests/test_server_hg.py +++ b/rhodecode/apps/ssh_support/tests/test_server_hg.py @@ -17,6 +17,7 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import os + import mock import pytest @@ -32,22 +33,27 @@ class MercurialServerCreator(object): 'app:main': { 'ssh.executable.hg': hg_path, 'vcs.hooks.protocol.v2': 'celery', + 'app.service_api.host': 'http://localhost', + 'app.service_api.token': 'secret4', + 'rhodecode.api.url': '/_admin/api', } } repo_name = 'test_hg' user = plain_dummy_user() - def __init__(self): - pass + def __init__(self, service_api_url, ini_file): + self.service_api_url = service_api_url + self.ini_file = ini_file def create(self, **kwargs): + self.config_data['app:main']['app.service_api.host'] = self.service_api_url parameters = { 'store': self.root, - 'ini_path': '', + 'ini_path': self.ini_file, 'user': self.user, 'repo_name': self.repo_name, 'user_permissions': { - 'test_hg': 'repository.admin' + self.repo_name: 'repository.admin' }, 'settings': self.config_data['app:main'], 'env': plain_dummy_env() @@ -57,12 +63,30 @@ class MercurialServerCreator(object): return server -@pytest.fixture() -def hg_server(app): - return MercurialServerCreator() +@pytest.fixture(scope='module') +def hg_server(request, module_app, rhodecode_factory, available_port_factory): + ini_file = module_app._pyramid_settings['__file__'] + vcsserver_host = module_app._pyramid_settings['vcs.server'] + + store_dir = os.path.dirname(ini_file) + + # start rhodecode for service API + rc = rhodecode_factory( + request, + store_dir=store_dir, + port=available_port_factory(), + overrides=( + {'handler_console': {'level': 'DEBUG'}}, + {'app:main': {'vcs.server': vcsserver_host}}, + {'app:main': {'repo_store.path': store_dir}} + )) + + service_api_url = f'http://{rc.bind_addr}' + + return MercurialServerCreator(service_api_url, ini_file) -class TestMercurialServer(object): +class TestMercurialServer: def test_command(self, hg_server, tmpdir): server = hg_server.create() @@ -107,7 +131,7 @@ class TestMercurialServer(object): with mock.patch.object(MercurialTunnelWrapper, 'create_hooks_env') as _patch: _patch.return_value = 0 with mock.patch.object(MercurialTunnelWrapper, 'command', return_value='date'): - exit_code = server.run() + exit_code = server.run(tunnel_extras={'config': server.ini_path}) assert exit_code == (0, False) diff --git a/rhodecode/apps/ssh_support/tests/test_server_svn.py b/rhodecode/apps/ssh_support/tests/test_server_svn.py index d8e1d1f0..e78d3149 100644 --- a/rhodecode/apps/ssh_support/tests/test_server_svn.py +++ b/rhodecode/apps/ssh_support/tests/test_server_svn.py @@ -15,7 +15,9 @@ # 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 os + import mock import pytest @@ -26,39 +28,62 @@ from rhodecode.apps.ssh_support.tests.conftest import plain_dummy_env, plain_dum class SubversionServerCreator(object): root = '/tmp/repo/path/' svn_path = '/usr/local/bin/svnserve' + config_data = { 'app:main': { 'ssh.executable.svn': svn_path, 'vcs.hooks.protocol.v2': 'celery', + 'app.service_api.host': 'http://localhost', + 'app.service_api.token': 'secret4', + 'rhodecode.api.url': '/_admin/api', } } repo_name = 'test-svn' user = plain_dummy_user() - def __init__(self): - pass + def __init__(self, service_api_url, ini_file): + self.service_api_url = service_api_url + self.ini_file = ini_file def create(self, **kwargs): + self.config_data['app:main']['app.service_api.host'] = self.service_api_url parameters = { 'store': self.root, - 'repo_name': self.repo_name, - 'ini_path': '', + 'ini_path': self.ini_file, 'user': self.user, + 'repo_name': self.repo_name, 'user_permissions': { self.repo_name: 'repository.admin' }, 'settings': self.config_data['app:main'], 'env': plain_dummy_env() } - parameters.update(kwargs) server = SubversionServer(**parameters) return server -@pytest.fixture() -def svn_server(app): - return SubversionServerCreator() +@pytest.fixture(scope='module') +def svn_server(request, module_app, rhodecode_factory, available_port_factory): + ini_file = module_app._pyramid_settings['__file__'] + vcsserver_host = module_app._pyramid_settings['vcs.server'] + + store_dir = os.path.dirname(ini_file) + + # start rhodecode for service API + rc = rhodecode_factory( + request, + store_dir=store_dir, + port=available_port_factory(), + overrides=( + {'handler_console': {'level': 'DEBUG'}}, + {'app:main': {'vcs.server': vcsserver_host}}, + {'app:main': {'repo_store.path': store_dir}} + )) + + service_api_url = f'http://{rc.bind_addr}' + + return SubversionServerCreator(service_api_url, ini_file) class TestSubversionServer(object): @@ -168,8 +193,9 @@ class TestSubversionServer(object): assert repo_name == expected_match def test_run_returns_executes_command(self, svn_server): - server = svn_server.create() from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper + + server = svn_server.create() os.environ['SSH_CLIENT'] = '127.0.0.1' with mock.patch.object( SubversionTunnelWrapper, 'get_first_client_response', @@ -184,20 +210,18 @@ class TestSubversionServer(object): SubversionTunnelWrapper, 'command', return_value=['date']): - exit_code = server.run() + exit_code = server.run(tunnel_extras={'config': server.ini_path}) # SVN has this differently configured, and we get in our mock env # None as return code assert exit_code == (None, False) def test_run_returns_executes_command_that_cannot_extract_repo_name(self, svn_server): - server = svn_server.create() from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper - with mock.patch.object( - SubversionTunnelWrapper, 'command', - return_value=['date']): - with mock.patch.object( - SubversionTunnelWrapper, 'get_first_client_response', + + server = svn_server.create() + with mock.patch.object(SubversionTunnelWrapper, 'command', return_value=['date']): + with mock.patch.object(SubversionTunnelWrapper, 'get_first_client_response', return_value=None): - exit_code = server.run() + exit_code = server.run(tunnel_extras={'config': server.ini_path}) assert exit_code == (1, False) diff --git a/rhodecode/apps/user_group/tests/test_user_groups.py b/rhodecode/apps/user_group/tests/test_user_groups.py index b2b5cae4..5d5fd018 100644 --- a/rhodecode/apps/user_group/tests/test_user_groups.py +++ b/rhodecode/apps/user_group/tests/test_user_groups.py @@ -22,7 +22,7 @@ from rhodecode.tests import ( TestController, assert_session_flash, TEST_USER_ADMIN_LOGIN) from rhodecode.model.db import UserGroup from rhodecode.model.meta import Session -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/user_group_profile/tests/test_user_group.py b/rhodecode/apps/user_group_profile/tests/test_user_group.py index 51096e70..4740375f 100644 --- a/rhodecode/apps/user_group_profile/tests/test_user_group.py +++ b/rhodecode/apps/user_group_profile/tests/test_user_group.py @@ -18,7 +18,7 @@ from rhodecode.model.user_group import UserGroupModel from rhodecode.tests import ( TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path fixture = Fixture() diff --git a/rhodecode/apps/user_profile/tests/test_users.py b/rhodecode/apps/user_profile/tests/test_users.py index c471adef..79b348c7 100644 --- a/rhodecode/apps/user_profile/tests/test_users.py +++ b/rhodecode/apps/user_profile/tests/test_users.py @@ -22,7 +22,7 @@ from rhodecode.model.db import User from rhodecode.tests import ( TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.utils import AssertResponse from rhodecode.tests.routes import route_path diff --git a/rhodecode/config/environment.py b/rhodecode/config/environment.py index 1c699e65..acc2a76f 100644 --- a/rhodecode/config/environment.py +++ b/rhodecode/config/environment.py @@ -30,7 +30,7 @@ from rhodecode.lib.vcs import connect_vcs log = logging.getLogger(__name__) -def propagate_rhodecode_config(global_config, settings, config): +def propagate_rhodecode_config(global_config, settings, config, full=True): # Store the settings to make them available to other modules. settings_merged = global_config.copy() settings_merged.update(settings) @@ -40,7 +40,7 @@ def propagate_rhodecode_config(global_config, settings, config): rhodecode.PYRAMID_SETTINGS = settings_merged rhodecode.CONFIG = settings_merged - if 'default_user_id' not in rhodecode.CONFIG: + if full and 'default_user_id' not in rhodecode.CONFIG: rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id() log.debug('set rhodecode.CONFIG data') @@ -93,6 +93,7 @@ def load_pyramid_environment(global_config, settings): # first run, to store data... propagate_rhodecode_config(global_config, settings, {}) + if vcs_server_enabled: connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings)) else: diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py index c82395cd..8e34137c 100644 --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -101,6 +101,9 @@ def make_pyramid_app(global_config, **settings): patches.inspect_getargspec() patches.repoze_sendmail_lf_fix() + # first init, so load_pyramid_enviroment, can access some critical data, like __file__ + propagate_rhodecode_config(global_config, {}, {}, full=False) + load_pyramid_environment(global_config, settings) # Static file view comes first diff --git a/rhodecode/config/rcextensions/__init__.py b/rhodecode/config/rcextensions/__init__.py index a4b661c4..ddb69559 100644 --- a/rhodecode/config/rcextensions/__init__.py +++ b/rhodecode/config/rcextensions/__init__.py @@ -17,7 +17,7 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ """ -rcextensions module, please edit `hooks.py` to over write hooks logic +rcextensions module, please edit `hooks.py` to over-write hooks logic """ from .hooks import ( diff --git a/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py b/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py index 1631291c..fbf1d72e 100644 --- a/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py +++ b/rhodecode/config/rcextensions/examples/validate_pushed_files_name_and_size.py @@ -85,7 +85,7 @@ def _pre_push_hook(*args, **kwargs): # check files names if forbidden_files: - reason = 'File {} is forbidden to be pushed'.format(file_name) + reason = f'File {file_name} is forbidden to be pushed' for forbidden_pattern in forbid_files: # here we can also filter for operation, e.g if check for only ADDED files # if operation == 'A': diff --git a/rhodecode/config/rcextensions/helpers/extra_fields.py b/rhodecode/config/rcextensions/helpers/extra_fields.py index 093de20d..3bf746d2 100644 --- a/rhodecode/config/rcextensions/helpers/extra_fields.py +++ b/rhodecode/config/rcextensions/helpers/extra_fields.py @@ -1,4 +1,3 @@ - # Copyright (C) 2016-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -55,7 +54,7 @@ def run(*args, **kwargs): return fields -class _Undefined(object): +class _Undefined: pass @@ -67,7 +66,7 @@ def get_field(extra_fields_data, key, default=_Undefined(), convert_type=True): if key not in extra_fields_data: if isinstance(default, _Undefined): - raise ValueError('key {} not present in extra_fields'.format(key)) + raise ValueError(f'key {key} not present in extra_fields') return default # NOTE(dan): from metadata we get field_label, field_value, field_desc, field_type diff --git a/rhodecode/config/rcextensions/helpers/extract_post_commits.py b/rhodecode/config/rcextensions/helpers/extract_post_commits.py index 84ca7e69..9b2d0d9f 100644 --- a/rhodecode/config/rcextensions/helpers/extract_post_commits.py +++ b/rhodecode/config/rcextensions/helpers/extract_post_commits.py @@ -1,4 +1,3 @@ - # Copyright (C) 2016-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify diff --git a/rhodecode/config/rcextensions/helpers/extract_pre_commits.py b/rhodecode/config/rcextensions/helpers/extract_pre_commits.py index b2c1df2b..40702027 100644 --- a/rhodecode/config/rcextensions/helpers/extract_pre_commits.py +++ b/rhodecode/config/rcextensions/helpers/extract_pre_commits.py @@ -1,4 +1,3 @@ - # Copyright (C) 2016-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -52,7 +51,7 @@ def get_git_commits(repo, refs): cmd = [ 'log', '--pretty=format:{"commit_id": "%H", "author": "%aN <%aE>", "date": "%ad", "message": "%s"}', - '{}...{}'.format(old_rev, new_rev) + f'{old_rev}...{new_rev}' ] stdout, stderr = repo.run_git_command(cmd, extra_env=git_env) @@ -80,12 +79,12 @@ def run(*args, **kwargs): if vcs_type == 'git': for rev_data in kwargs['commit_ids']: - new_environ = dict((k, v) for k, v in rev_data['git_env']) + new_environ = {k: v for k, v in rev_data['git_env']} commits = get_git_commits(vcs_repo, kwargs['commit_ids']) if vcs_type == 'hg': for rev_data in kwargs['commit_ids']: - new_environ = dict((k, v) for k, v in rev_data['hg_env']) + new_environ = {k: v for k, v in rev_data['hg_env']} commits = get_hg_commits(vcs_repo, kwargs['commit_ids']) return commits diff --git a/rhodecode/config/rcextensions/helpers/extract_pre_files.py b/rhodecode/config/rcextensions/helpers/extract_pre_files.py index 3d517165..9eb625c8 100644 --- a/rhodecode/config/rcextensions/helpers/extract_pre_files.py +++ b/rhodecode/config/rcextensions/helpers/extract_pre_files.py @@ -1,4 +1,3 @@ - # Copyright (C) 2016-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -133,12 +132,12 @@ def run(*args, **kwargs): if vcs_type == 'git': for rev_data in kwargs['commit_ids']: - new_environ = dict((k, v) for k, v in rev_data['git_env']) + new_environ = {k: v for k, v in rev_data['git_env']} files = get_git_files(repo, vcs_repo, kwargs['commit_ids']) if vcs_type == 'hg': for rev_data in kwargs['commit_ids']: - new_environ = dict((k, v) for k, v in rev_data['hg_env']) + new_environ = {k: v for k, v in rev_data['hg_env']} files = get_hg_files(repo, vcs_repo, kwargs['commit_ids']) if vcs_type == 'svn': diff --git a/rhodecode/config/rcextensions/helpers/http_call.py b/rhodecode/config/rcextensions/helpers/http_call.py index 0bbe2434..b06dd9a0 100644 --- a/rhodecode/config/rcextensions/helpers/http_call.py +++ b/rhodecode/config/rcextensions/helpers/http_call.py @@ -1,4 +1,3 @@ - # Copyright (C) 2016-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify diff --git a/rhodecode/config/rcextensions/utils.py b/rhodecode/config/rcextensions/utils.py index 2f31a873..d7f4b48d 100644 --- a/rhodecode/config/rcextensions/utils.py +++ b/rhodecode/config/rcextensions/utils.py @@ -28,7 +28,7 @@ import urllib.error log = logging.getLogger('rhodecode.' + __name__) -class HookResponse(object): +class HookResponse: def __init__(self, status, output): self.status = status self.output = output @@ -44,6 +44,11 @@ class HookResponse(object): def __bool__(self): return self.status == 0 + def to_json(self): + return {'status': self.status, 'output': self.output} + + def __repr__(self): + return self.to_json().__repr__() class DotDict(dict): @@ -91,8 +96,8 @@ class DotDict(dict): def __repr__(self): keys = list(self.keys()) keys.sort() - args = ', '.join(['%s=%r' % (key, self[key]) for key in keys]) - return '%s(%s)' % (self.__class__.__name__, args) + args = ', '.join(['{}={!r}'.format(key, self[key]) for key in keys]) + return '{}({})'.format(self.__class__.__name__, args) @staticmethod def fromDict(d): @@ -110,7 +115,7 @@ def serialize(x): def unserialize(x): if isinstance(x, dict): - return dict((k, unserialize(v)) for k, v in x.items()) + return {k: unserialize(v) for k, v in x.items()} elif isinstance(x, (list, tuple)): return type(x)(unserialize(v) for v in x) else: @@ -161,7 +166,8 @@ def str2bool(_str) -> bool: string into boolean :param _str: string value to translate into boolean - :returns: bool from given string + :rtype: boolean + :returns: boolean from given string """ if _str is None: return False diff --git a/rhodecode/config/routing_links.py b/rhodecode/config/routing_links.py index 4a782b15..6a569383 100644 --- a/rhodecode/config/routing_links.py +++ b/rhodecode/config/routing_links.py @@ -49,22 +49,22 @@ link_config = [ { "name": "enterprise_docs", "target": "https://rhodecode.com/r1/enterprise/docs/", - "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/", + "external_target": "https://docs.rhodecode.com/4.x/rce/index.html", }, { "name": "enterprise_log_file_locations", "target": "https://rhodecode.com/r1/enterprise/docs/admin-system-overview/", - "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/system-overview.html#log-files", + "external_target": "https://docs.rhodecode.com/4.x/rce/admin/system-overview.html#log-files", }, { "name": "enterprise_issue_tracker_settings", "target": "https://rhodecode.com/r1/enterprise/docs/issue-trackers-overview/", - "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/issue-trackers/issue-trackers.html", + "external_target": "https://docs.rhodecode.com/4.x/rce/issue-trackers/issue-trackers.html", }, { "name": "enterprise_svn_setup", "target": "https://rhodecode.com/r1/enterprise/docs/svn-setup/", - "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/svn-http.html", + "external_target": "https://docs.rhodecode.com/4.x/rce/admin/svn-http.html", }, { "name": "enterprise_license_convert_from_old", diff --git a/rhodecode/config/utils.py b/rhodecode/config/utils.py index 17236b25..e3d48d1c 100644 --- a/rhodecode/config/utils.py +++ b/rhodecode/config/utils.py @@ -19,6 +19,8 @@ import os import platform +from rhodecode.lib.type_utils import str2bool + DEFAULT_USER = 'default' @@ -48,28 +50,23 @@ def initialize_database(config): engine = engine_from_config(config, 'sqlalchemy.db1.') init_model(engine, encryption_key=get_encryption_key(config)) +def initialize_test_environment(settings): + skip_test_env = str2bool(os.environ.get('RC_NO_TEST_ENV')) + if skip_test_env: + return -def initialize_test_environment(settings, test_env=None): - if test_env is None: - test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0)) + repo_store_path = os.environ.get('RC_TEST_ENV_REPO_STORE') or settings['repo_store.path'] from rhodecode.lib.utils import ( create_test_directory, create_test_database, create_test_repositories, create_test_index) - from rhodecode.tests import TESTS_TMP_PATH - from rhodecode.lib.vcs.backends.hg import largefiles_store - from rhodecode.lib.vcs.backends.git import lfs_store + create_test_directory(repo_store_path) + + create_test_database(repo_store_path, settings) # test repos - if test_env: - create_test_directory(TESTS_TMP_PATH) - # large object stores - create_test_directory(largefiles_store(TESTS_TMP_PATH)) - create_test_directory(lfs_store(TESTS_TMP_PATH)) - - create_test_database(TESTS_TMP_PATH, settings) - create_test_repositories(TESTS_TMP_PATH, settings) - create_test_index(TESTS_TMP_PATH, settings) + create_test_repositories(repo_store_path, settings) + create_test_index(repo_store_path, settings) def get_vcs_server_protocol(config): diff --git a/rhodecode/lib/exceptions.py b/rhodecode/lib/exceptions.py index d4cd6c17..301e629f 100644 --- a/rhodecode/lib/exceptions.py +++ b/rhodecode/lib/exceptions.py @@ -20,8 +20,7 @@ Set of custom exceptions used in RhodeCode """ -from webob.exc import HTTPClientError -from pyramid.httpexceptions import HTTPBadGateway +from pyramid.httpexceptions import HTTPBadGateway, HTTPClientError class LdapUsernameError(Exception): @@ -102,12 +101,7 @@ class HTTPRequirementError(HTTPClientError): self.args = (message, ) -class ClientNotSupportedError(HTTPRequirementError): - title = explanation = 'Client Not Supported' - reason = None - - -class HTTPLockedRC(HTTPClientError): +class HTTPLockedRepo(HTTPClientError): """ Special Exception For locked Repos in RhodeCode, the return code can be overwritten by _code keyword argument passed into constructors @@ -131,14 +125,13 @@ class HTTPBranchProtected(HTTPClientError): Special Exception For Indicating that branch is protected in RhodeCode, the return code can be overwritten by _code keyword argument passed into constructors """ - code = 403 title = explanation = 'Branch Protected' reason = None - def __init__(self, message, *args, **kwargs): - self.title = self.explanation = message - super().__init__(*args, **kwargs) - self.args = (message, ) + +class ClientNotSupported(HTTPRequirementError): + title = explanation = 'Client Not Supported' + reason = None class IMCCommitError(Exception): diff --git a/rhodecode/lib/hook_daemon/base.py b/rhodecode/lib/hook_daemon/base.py index 705926f4..d0629edb 100644 --- a/rhodecode/lib/hook_daemon/base.py +++ b/rhodecode/lib/hook_daemon/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -16,13 +16,14 @@ # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ -import os -import time import logging +import traceback -from rhodecode.lib.config_utils import get_app_config_lightweight +from rhodecode.model import meta +from rhodecode.lib import hooks_base +from rhodecode.lib.utils2 import AttributeDict +from rhodecode.lib.exceptions import HTTPLockedRepo, HTTPBranchProtected -from rhodecode.lib.svn_txn_utils import get_txn_id_from_store log = logging.getLogger(__name__) @@ -42,53 +43,82 @@ class BaseHooksCallbackDaemon: log.debug('Exiting `%s` callback daemon', self.__class__.__name__) -class HooksModuleCallbackDaemon(BaseHooksCallbackDaemon): +class Hooks(object): + """ + Exposes the hooks module for calling them using the local HooksModuleCallbackDaemon + """ + def __init__(self, request=None, log_prefix=''): + self.log_prefix = log_prefix + self.request = request - def __init__(self, module): - super().__init__() - self.hooks_module = module + def repo_size(self, extras): + log.debug("%sCalled repo_size of %s object", self.log_prefix, self) + return self._call_hook(hooks_base.repo_size, extras) - def __repr__(self): - return f'HooksModuleCallbackDaemon(hooks_module={self.hooks_module})' + def pre_pull(self, extras): + log.debug("%sCalled pre_pull of %s object", self.log_prefix, self) + return self._call_hook(hooks_base.pre_pull, extras) + def post_pull(self, extras): + log.debug("%sCalled post_pull of %s object", self.log_prefix, self) + return self._call_hook(hooks_base.post_pull, extras) -def prepare_callback_daemon(extras, protocol, host, txn_id=None): + def pre_push(self, extras): + log.debug("%sCalled pre_push of %s object", self.log_prefix, self) + return self._call_hook(hooks_base.pre_push, extras) - match protocol: - case 'http': - from rhodecode.lib.hook_daemon.http_hooks_deamon import HttpHooksCallbackDaemon - port = 0 - if txn_id: - # read txn-id to re-use the PORT for callback daemon - repo_path = os.path.join(extras['repo_store'], extras['repository']) - txn_details = get_txn_id_from_store(repo_path, txn_id) - port = txn_details.get('port', 0) + def post_push(self, extras): + log.debug("%sCalled post_push of %s object", self.log_prefix, self) + return self._call_hook(hooks_base.post_push, extras) - callback_daemon = HttpHooksCallbackDaemon( - txn_id=txn_id, host=host, port=port) - case 'celery': - from rhodecode.lib.hook_daemon.celery_hooks_deamon import CeleryHooksCallbackDaemon + def _call_hook(self, hook, extras): + extras = AttributeDict(extras) + _server_url = extras['server_url'] - config = get_app_config_lightweight(extras['config']) - task_queue = config.get('celery.broker_url') - task_backend = config.get('celery.result_backend') + extras.request = self.request + try: + result = hook(extras) + if result is None: + raise Exception(f'Failed to obtain hook result from func: {hook}') + except HTTPBranchProtected as error: + # Those special cases don't need error reporting. It's a case of + # locked repo or protected branch + result = AttributeDict({ + 'status': error.code, + 'output': error.explanation + }) + except HTTPLockedRepo as error: + # Those special cases don't need error reporting. It's a case of + # locked repo or protected branch + result = AttributeDict({ + 'status': error.code, + 'output': error.explanation + }) + except Exception as error: + # locked needs different handling since we need to also + # handle PULL operations + log.exception('%sException when handling hook %s', self.log_prefix, hook) + exc_tb = traceback.format_exc() + error_args = error.args + return { + 'status': 128, + 'output': '', + 'exception': type(error).__name__, + 'exception_traceback': exc_tb, + 'exception_args': error_args, + } + finally: + meta.Session.remove() - callback_daemon = CeleryHooksCallbackDaemon(task_queue, task_backend) - case 'local': - from rhodecode.lib.hook_daemon.hook_module import Hooks - callback_daemon = HooksModuleCallbackDaemon(Hooks.__module__) - case _: - log.error('Unsupported callback daemon protocol "%s"', protocol) - raise Exception('Unsupported callback daemon protocol.') + log.debug('%sGot hook call response %s', self.log_prefix, result) + return { + 'status': result.status, + 'output': result.output, + } - extras['hooks_uri'] = getattr(callback_daemon, 'hooks_uri', '') - extras['task_queue'] = getattr(callback_daemon, 'task_queue', '') - extras['task_backend'] = getattr(callback_daemon, 'task_backend', '') - extras['hooks_protocol'] = protocol - extras['time'] = time.time() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass - # register txn_id - extras['txn_id'] = txn_id - log.debug('Prepared a callback daemon: %s', - callback_daemon.__class__.__name__) - return callback_daemon, extras diff --git a/rhodecode/lib/hook_daemon/celery_hooks_deamon.py b/rhodecode/lib/hook_daemon/celery_hooks_deamon.py index 245a6bf3..fc67b3cb 100644 --- a/rhodecode/lib/hook_daemon/celery_hooks_deamon.py +++ b/rhodecode/lib/hook_daemon/celery_hooks_deamon.py @@ -22,14 +22,16 @@ from rhodecode.lib.hook_daemon.base import BaseHooksCallbackDaemon class CeleryHooksCallbackDaemon(BaseHooksCallbackDaemon): """ Context manger for achieving a compatibility with celery backend + It is calling a call to vcsserver, where it uses HooksCeleryClient to actually call a task from + + f'rhodecode.lib.celerylib.tasks.{method}' + """ - def __init__(self, task_queue, task_backend): - self.task_queue = task_queue - self.task_backend = task_backend + def __init__(self, broker_url, result_backend): + super().__init__() + self.broker_url = broker_url + self.result_backend = result_backend def __repr__(self): - return f'CeleryHooksCallbackDaemon(task_queue={self.task_queue}, task_backend={self.task_backend})' - - def __repr__(self): - return f'CeleryHooksCallbackDaemon(task_queue={self.task_queue}, task_backend={self.task_backend})' + return f'CeleryHooksCallbackDaemon(broker_url={self.broker_url}, result_backend={self.result_backend})' diff --git a/rhodecode/lib/hook_daemon/hook_module.py b/rhodecode/lib/hook_daemon/hook_module.py index c6e88c0b..1a7e90e0 100644 --- a/rhodecode/lib/hook_daemon/hook_module.py +++ b/rhodecode/lib/hook_daemon/hook_module.py @@ -17,88 +17,18 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import logging -import traceback -from rhodecode.model import meta - -from rhodecode.lib import hooks_base -from rhodecode.lib.exceptions import HTTPLockedRC, HTTPBranchProtected -from rhodecode.lib.utils2 import AttributeDict +from rhodecode.lib.hook_daemon.base import BaseHooksCallbackDaemon log = logging.getLogger(__name__) -class Hooks(object): - """ - Exposes the hooks for remote callbacks - """ - def __init__(self, request=None, log_prefix=''): - self.log_prefix = log_prefix - self.request = request +class HooksModuleCallbackDaemon(BaseHooksCallbackDaemon): - def repo_size(self, extras): - log.debug("%sCalled repo_size of %s object", self.log_prefix, self) - return self._call_hook(hooks_base.repo_size, extras) + def __init__(self, module): + super().__init__() + self.hooks_module = module - def pre_pull(self, extras): - log.debug("%sCalled pre_pull of %s object", self.log_prefix, self) - return self._call_hook(hooks_base.pre_pull, extras) + def __repr__(self): + return f'HooksModuleCallbackDaemon(hooks_module={self.hooks_module})' - def post_pull(self, extras): - log.debug("%sCalled post_pull of %s object", self.log_prefix, self) - return self._call_hook(hooks_base.post_pull, extras) - - def pre_push(self, extras): - log.debug("%sCalled pre_push of %s object", self.log_prefix, self) - return self._call_hook(hooks_base.pre_push, extras) - - def post_push(self, extras): - log.debug("%sCalled post_push of %s object", self.log_prefix, self) - return self._call_hook(hooks_base.post_push, extras) - - def _call_hook(self, hook, extras): - extras = AttributeDict(extras) - _server_url = extras['server_url'] - - extras.request = self.request - - try: - result = hook(extras) - if result is None: - raise Exception(f'Failed to obtain hook result from func: {hook}') - except HTTPBranchProtected as error: - # Those special cases don't need error reporting. It's a case of - # locked repo or protected branch - result = AttributeDict({ - 'status': error.code, - 'output': error.explanation - }) - except (HTTPLockedRC, Exception) as error: - # locked needs different handling since we need to also - # handle PULL operations - exc_tb = '' - if not isinstance(error, HTTPLockedRC): - exc_tb = traceback.format_exc() - log.exception('%sException when handling hook %s', self.log_prefix, hook) - error_args = error.args - return { - 'status': 128, - 'output': '', - 'exception': type(error).__name__, - 'exception_traceback': exc_tb, - 'exception_args': error_args, - } - finally: - meta.Session.remove() - - log.debug('%sGot hook call response %s', self.log_prefix, result) - return { - 'status': result.status, - 'output': result.output, - } - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - pass diff --git a/rhodecode/lib/hook_daemon/http_hooks_deamon.py b/rhodecode/lib/hook_daemon/http_hooks_deamon.py deleted file mode 100644 index bc86d0cb..00000000 --- a/rhodecode/lib/hook_daemon/http_hooks_deamon.py +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3 -# (only), as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# 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 os -import logging -import traceback -import threading -import socket -import msgpack -import gevent - -from http.server import BaseHTTPRequestHandler -from socketserver import TCPServer - -from rhodecode.model import meta -from rhodecode.lib.ext_json import json -from rhodecode.lib import rc_cache -from rhodecode.lib.svn_txn_utils import get_txn_id_data_key -from rhodecode.lib.hook_daemon.hook_module import Hooks - -log = logging.getLogger(__name__) - - -class HooksHttpHandler(BaseHTTPRequestHandler): - - JSON_HOOKS_PROTO = 'json.v1' - MSGPACK_HOOKS_PROTO = 'msgpack.v1' - # starting with RhodeCode 5.0.0 MsgPack is the default, prior it used json - DEFAULT_HOOKS_PROTO = MSGPACK_HOOKS_PROTO - - @classmethod - def serialize_data(cls, data, proto=DEFAULT_HOOKS_PROTO): - if proto == cls.MSGPACK_HOOKS_PROTO: - return msgpack.packb(data) - return json.dumps(data) - - @classmethod - def deserialize_data(cls, data, proto=DEFAULT_HOOKS_PROTO): - if proto == cls.MSGPACK_HOOKS_PROTO: - return msgpack.unpackb(data) - return json.loads(data) - - def do_POST(self): - hooks_proto, method, extras = self._read_request() - log.debug('Handling HooksHttpHandler %s with %s proto', method, hooks_proto) - - txn_id = getattr(self.server, 'txn_id', None) - if txn_id: - log.debug('Computing TXN_ID based on `%s`:`%s`', - extras['repository'], extras['txn_id']) - computed_txn_id = rc_cache.utils.compute_key_from_params( - extras['repository'], extras['txn_id']) - if txn_id != computed_txn_id: - raise Exception( - 'TXN ID fail: expected {} got {} instead'.format( - txn_id, computed_txn_id)) - - request = getattr(self.server, 'request', None) - try: - hooks = Hooks(request=request, log_prefix='HOOKS: {} '.format(self.server.server_address)) - result = self._call_hook_method(hooks, method, extras) - - except Exception as e: - exc_tb = traceback.format_exc() - result = { - 'exception': e.__class__.__name__, - 'exception_traceback': exc_tb, - 'exception_args': e.args - } - self._write_response(hooks_proto, result) - - def _read_request(self): - length = int(self.headers['Content-Length']) - # respect sent headers, fallback to OLD proto for compatability - hooks_proto = self.headers.get('rc-hooks-protocol') or self.JSON_HOOKS_PROTO - if hooks_proto == self.MSGPACK_HOOKS_PROTO: - # support for new vcsserver msgpack based protocol hooks - body = self.rfile.read(length) - data = self.deserialize_data(body) - else: - body = self.rfile.read(length) - data = self.deserialize_data(body) - - return hooks_proto, data['method'], data['extras'] - - def _write_response(self, hooks_proto, result): - self.send_response(200) - if hooks_proto == self.MSGPACK_HOOKS_PROTO: - self.send_header("Content-type", "application/msgpack") - self.end_headers() - data = self.serialize_data(result) - self.wfile.write(data) - else: - self.send_header("Content-type", "text/json") - self.end_headers() - data = self.serialize_data(result) - self.wfile.write(data) - - def _call_hook_method(self, hooks, method, extras): - try: - result = getattr(hooks, method)(extras) - finally: - meta.Session.remove() - return result - - def log_message(self, format, *args): - """ - This is an overridden method of BaseHTTPRequestHandler which logs using - a logging library instead of writing directly to stderr. - """ - - message = format % args - - log.debug( - "HOOKS: client=%s - - [%s] %s", self.client_address, - self.log_date_time_string(), message) - - -class ThreadedHookCallbackDaemon(object): - - _callback_thread = None - _daemon = None - _done = False - use_gevent = False - - def __init__(self, txn_id=None, host=None, port=None): - self._prepare(txn_id=txn_id, host=host, port=port) - if self.use_gevent: - self._run_func = self._run_gevent - self._stop_func = self._stop_gevent - else: - self._run_func = self._run - self._stop_func = self._stop - - def __enter__(self): - log.debug('Running `%s` callback daemon', self.__class__.__name__) - self._run_func() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - log.debug('Exiting `%s` callback daemon', self.__class__.__name__) - self._stop_func() - - def _prepare(self, txn_id=None, host=None, port=None): - raise NotImplementedError() - - def _run(self): - raise NotImplementedError() - - def _stop(self): - raise NotImplementedError() - - def _run_gevent(self): - raise NotImplementedError() - - def _stop_gevent(self): - raise NotImplementedError() - - -class HttpHooksCallbackDaemon(ThreadedHookCallbackDaemon): - """ - Context manager which will run a callback daemon in a background thread. - """ - - hooks_uri = None - - # From Python docs: Polling reduces our responsiveness to a shutdown - # request and wastes cpu at all other times. - POLL_INTERVAL = 0.01 - - use_gevent = False - - def __repr__(self): - return f'HttpHooksCallbackDaemon(hooks_uri={self.hooks_uri})' - - @property - def _hook_prefix(self): - return f'HOOKS: {self.hooks_uri} ' - - def get_hostname(self): - return socket.gethostname() or '127.0.0.1' - - def get_available_port(self, min_port=20000, max_port=65535): - from rhodecode.lib.utils2 import get_available_port as _get_port - return _get_port(min_port, max_port) - - def _prepare(self, txn_id=None, host=None, port=None): - from pyramid.threadlocal import get_current_request - - if not host or host == "*": - host = self.get_hostname() - if not port: - port = self.get_available_port() - - server_address = (host, port) - self.hooks_uri = f'{host}:{port}' - self.txn_id = txn_id - self._done = False - - log.debug( - "%s Preparing HTTP callback daemon registering hook object: %s", - self._hook_prefix, HooksHttpHandler) - - self._daemon = TCPServer(server_address, HooksHttpHandler) - # inject transaction_id for later verification - self._daemon.txn_id = self.txn_id - - # pass the WEB app request into daemon - self._daemon.request = get_current_request() - - def _run(self): - log.debug("Running thread-based loop of callback daemon in background") - callback_thread = threading.Thread( - target=self._daemon.serve_forever, - kwargs={'poll_interval': self.POLL_INTERVAL}) - callback_thread.daemon = True - callback_thread.start() - self._callback_thread = callback_thread - - def _run_gevent(self): - log.debug("Running gevent-based loop of callback daemon in background") - # create a new greenlet for the daemon's serve_forever method - callback_greenlet = gevent.spawn( - self._daemon.serve_forever, - poll_interval=self.POLL_INTERVAL) - - # store reference to greenlet - self._callback_greenlet = callback_greenlet - - # switch to this greenlet - gevent.sleep(0.01) - - def _stop(self): - log.debug("Waiting for background thread to finish.") - self._daemon.shutdown() - self._callback_thread.join() - self._daemon = None - self._callback_thread = None - if self.txn_id: - #TODO: figure out the repo_path... - repo_path = '' - txn_id_file = get_txn_id_data_key(repo_path, self.txn_id) - log.debug('Cleaning up TXN ID %s', txn_id_file) - if os.path.isfile(txn_id_file): - os.remove(txn_id_file) - - log.debug("Background thread done.") - - def _stop_gevent(self): - log.debug("Waiting for background greenlet to finish.") - - # if greenlet exists and is running - if self._callback_greenlet and not self._callback_greenlet.dead: - # shutdown daemon if it exists - if self._daemon: - self._daemon.shutdown() - - # kill the greenlet - self._callback_greenlet.kill() - - self._daemon = None - self._callback_greenlet = None - - if self.txn_id: - #TODO: figure out the repo_path... - repo_path = '' - txn_id_file = get_txn_id_data_key(repo_path, self.txn_id) - log.debug('Cleaning up TXN ID %s', txn_id_file) - if os.path.isfile(txn_id_file): - os.remove(txn_id_file) - - log.debug("Background greenlet done.") diff --git a/rhodecode/lib/hook_daemon/utils.py b/rhodecode/lib/hook_daemon/utils.py new file mode 100644 index 00000000..b2938e21 --- /dev/null +++ b/rhodecode/lib/hook_daemon/utils.py @@ -0,0 +1,61 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 time +import logging + +from rhodecode.lib.config_utils import get_app_config_lightweight + +from rhodecode.lib.hook_daemon.base import Hooks +from rhodecode.lib.hook_daemon.hook_module import HooksModuleCallbackDaemon +from rhodecode.lib.hook_daemon.celery_hooks_deamon import CeleryHooksCallbackDaemon +from rhodecode.lib.type_utils import str2bool + +log = logging.getLogger(__name__) + + + +def prepare_callback_daemon(extras, protocol: str, txn_id=None): + hooks_config = {} + match protocol: + case 'celery': + config = get_app_config_lightweight(extras['config']) + + broker_url = config.get('celery.broker_url') + result_backend = config.get('celery.result_backend') + + hooks_config = { + 'broker_url': broker_url, + 'result_backend': result_backend, + } + + callback_daemon = CeleryHooksCallbackDaemon(broker_url, result_backend) + case 'local': + callback_daemon = HooksModuleCallbackDaemon(Hooks.__module__) + case _: + log.error('Unsupported callback daemon protocol "%s"', protocol) + raise Exception('Unsupported callback daemon protocol.') + + extras['hooks_config'] = hooks_config + extras['hooks_protocol'] = protocol + extras['time'] = time.time() + + # register txn_id + extras['txn_id'] = txn_id + log.debug('Prepared a callback daemon: %s', callback_daemon.__class__.__name__) + return callback_daemon, extras diff --git a/rhodecode/lib/hooks_base.py b/rhodecode/lib/hooks_base.py index 795987cc..c44d5368 100644 --- a/rhodecode/lib/hooks_base.py +++ b/rhodecode/lib/hooks_base.py @@ -30,14 +30,14 @@ from rhodecode.lib import helpers as h from rhodecode.lib import audit_logger from rhodecode.lib.utils2 import safe_str, user_agent_normalizer from rhodecode.lib.exceptions import ( - HTTPLockedRC, HTTPBranchProtected, UserCreationError, ClientNotSupportedError) + HTTPLockedRepo, HTTPBranchProtected, UserCreationError, ClientNotSupported) from rhodecode.model.db import Repository, User from rhodecode.lib.statsd_client import StatsdClient log = logging.getLogger(__name__) -class HookResponse(object): +class HookResponse: def __init__(self, status, output): self.status = status self.output = output @@ -56,6 +56,8 @@ class HookResponse(object): def to_json(self): return {'status': self.status, 'output': self.output} + def __repr__(self): + return self.to_json().__repr__() def is_shadow_repo(extras): """ @@ -73,8 +75,69 @@ def check_vcs_client(extras): except ModuleNotFoundError: is_vcs_client_whitelisted = lambda *x: True backend = extras.get('scm') - if not is_vcs_client_whitelisted(extras.get('user_agent'), backend): - raise ClientNotSupportedError(f"Your {backend} client is forbidden") + user_agent = extras.get('user_agent') + if not is_vcs_client_whitelisted(user_agent, backend): + raise ClientNotSupported(f"Your {backend} client (version={user_agent}) is forbidden by security rules") + + +def check_locked_repo(extras, check_same_user=True): + user = User.get_by_username(extras.username) + output = '' + if extras.locked_by[0] and (not check_same_user or user.user_id != extras.locked_by[0]): + + locked_by = User.get(extras.locked_by[0]).username + reason = extras.locked_by[2] + # this exception is interpreted in git/hg middlewares and based + # on that proper return code is server to client + _http_ret = HTTPLockedRepo(_locked_by_explanation(extras.repository, locked_by, reason)) + if str(_http_ret.code).startswith('2'): + # 2xx Codes don't raise exceptions + output = _http_ret.title + else: + raise _http_ret + + return output + + +def check_branch_protected(extras): + if extras.commit_ids and extras.check_branch_perms: + user = User.get_by_username(extras.username) + auth_user = user.AuthUser() + repo = Repository.get_by_repo_name(extras.repository) + if not repo: + raise ValueError(f'Repo for {extras.repository} not found') + affected_branches = [] + if repo.repo_type == 'hg': + for entry in extras.commit_ids: + if entry['type'] == 'branch': + is_forced = bool(entry['multiple_heads']) + affected_branches.append([entry['name'], is_forced]) + elif repo.repo_type == 'git': + for entry in extras.commit_ids: + if entry['type'] == 'heads': + is_forced = bool(entry['pruned_sha']) + affected_branches.append([entry['name'], is_forced]) + + for branch_name, is_forced in affected_branches: + + rule, branch_perm = auth_user.get_rule_and_branch_permission(extras.repository, branch_name) + if not branch_perm: + # no branch permission found for this branch, just keep checking + continue + + if branch_perm == 'branch.push_force': + continue + elif branch_perm == 'branch.push' and is_forced is False: + continue + elif branch_perm == 'branch.push' and is_forced is True: + halt_message = f'Branch `{branch_name}` changes rejected by rule {rule}. ' \ + f'FORCE PUSH FORBIDDEN.' + else: + halt_message = f'Branch `{branch_name}` changes rejected by rule {rule}.' + + if halt_message: + _http_ret = HTTPBranchProtected(halt_message) + raise _http_ret def _get_scm_size(alias, root_path): @@ -109,116 +172,30 @@ def repo_size(extras): repo = Repository.get_by_repo_name(extras.repository) vcs_part = f'.{repo.repo_type}' size_vcs, size_root, size_total = _get_scm_size(vcs_part, repo.repo_full_path) - msg = (f'RhodeCode: `{repo.repo_name}` size summary {vcs_part}:{size_vcs} repo:{size_root} total:{size_total}\n') + msg = f'RhodeCode: `{repo.repo_name}` size summary {vcs_part}:{size_vcs} repo:{size_root} total:{size_total}\n' return HookResponse(0, msg) -def pre_push(extras): - """ - Hook executed before pushing code. - - It bans pushing when the repository is locked. - """ - - check_vcs_client(extras) - user = User.get_by_username(extras.username) - output = '' - if extras.locked_by[0] and user.user_id != int(extras.locked_by[0]): - locked_by = User.get(extras.locked_by[0]).username - reason = extras.locked_by[2] - # this exception is interpreted in git/hg middlewares and based - # on that proper return code is server to client - _http_ret = HTTPLockedRC( - _locked_by_explanation(extras.repository, locked_by, reason)) - if str(_http_ret.code).startswith('2'): - # 2xx Codes don't raise exceptions - output = _http_ret.title - else: - raise _http_ret - - hook_response = '' - if not is_shadow_repo(extras): - - if extras.commit_ids and extras.check_branch_perms: - auth_user = user.AuthUser() - repo = Repository.get_by_repo_name(extras.repository) - if not repo: - raise ValueError(f'Repo for {extras.repository} not found') - affected_branches = [] - if repo.repo_type == 'hg': - for entry in extras.commit_ids: - if entry['type'] == 'branch': - is_forced = bool(entry['multiple_heads']) - affected_branches.append([entry['name'], is_forced]) - elif repo.repo_type == 'git': - for entry in extras.commit_ids: - if entry['type'] == 'heads': - is_forced = bool(entry['pruned_sha']) - affected_branches.append([entry['name'], is_forced]) - - for branch_name, is_forced in affected_branches: - - rule, branch_perm = auth_user.get_rule_and_branch_permission( - extras.repository, branch_name) - if not branch_perm: - # no branch permission found for this branch, just keep checking - continue - - if branch_perm == 'branch.push_force': - continue - elif branch_perm == 'branch.push' and is_forced is False: - continue - elif branch_perm == 'branch.push' and is_forced is True: - halt_message = f'Branch `{branch_name}` changes rejected by rule {rule}. ' \ - f'FORCE PUSH FORBIDDEN.' - else: - halt_message = f'Branch `{branch_name}` changes rejected by rule {rule}.' - - if halt_message: - _http_ret = HTTPBranchProtected(halt_message) - raise _http_ret - - # Propagate to external components. This is done after checking the - # lock, for consistent behavior. - hook_response = pre_push_extension( - repo_store_path=Repository.base_path(), **extras) - events.trigger(events.RepoPrePushEvent( - repo_name=extras.repository, extras=extras)) - - return HookResponse(0, output) + hook_response - - def pre_pull(extras): """ Hook executed before pulling the code. It bans pulling when the repository is locked. + It bans pulling when incorrect client is used. """ - - check_vcs_client(extras) output = '' - if extras.locked_by[0]: - locked_by = User.get(extras.locked_by[0]).username - reason = extras.locked_by[2] - # this exception is interpreted in git/hg middlewares and based - # on that proper return code is server to client - _http_ret = HTTPLockedRC( - _locked_by_explanation(extras.repository, locked_by, reason)) - if str(_http_ret.code).startswith('2'): - # 2xx Codes don't raise exceptions - output = _http_ret.title - else: - raise _http_ret + check_vcs_client(extras) + + # locking repo can, but not have to stop the operation it can also just produce output + output += check_locked_repo(extras, check_same_user=False) # Propagate to external components. This is done after checking the # lock, for consistent behavior. hook_response = '' if not is_shadow_repo(extras): extras.hook_type = extras.hook_type or 'pre_pull' - hook_response = pre_pull_extension( - repo_store_path=Repository.base_path(), **extras) - events.trigger(events.RepoPrePullEvent( - repo_name=extras.repository, extras=extras)) + hook_response = pre_pull_extension(repo_store_path=Repository.base_path(), **extras) + events.trigger(events.RepoPrePullEvent(repo_name=extras.repository, extras=extras)) return HookResponse(0, output) + hook_response @@ -239,6 +216,7 @@ def post_pull(extras): statsd.incr('rhodecode_pull_total', tags=[ f'user-agent:{user_agent_normalizer(extras.user_agent)}', ]) + output = '' # make lock is a tri state False, True, None. We only make lock on True if extras.make_lock is True and not is_shadow_repo(extras): @@ -246,18 +224,9 @@ def post_pull(extras): Repository.lock(Repository.get_by_repo_name(extras.repository), user.user_id, lock_reason=Repository.LOCK_PULL) - msg = 'Made lock on repo `{}`'.format(extras.repository) + msg = f'Made lock on repo `{extras.repository}`' output += msg - if extras.locked_by[0]: - locked_by = User.get(extras.locked_by[0]).username - reason = extras.locked_by[2] - _http_ret = HTTPLockedRC( - _locked_by_explanation(extras.repository, locked_by, reason)) - if str(_http_ret.code).startswith('2'): - # 2xx Codes don't raise exceptions - output += _http_ret.title - # Propagate to external components. hook_response = '' if not is_shadow_repo(extras): @@ -270,6 +239,33 @@ def post_pull(extras): return HookResponse(0, output) + hook_response +def pre_push(extras): + """ + Hook executed before pushing code. + + It bans pushing when the repository is locked. + It banks pushing when incorrect client is used. + It also checks for Branch protection + """ + output = '' + check_vcs_client(extras) + + # locking repo can, but not have to stop the operation it can also just produce output + output += check_locked_repo(extras) + + hook_response = '' + if not is_shadow_repo(extras): + + check_branch_protected(extras) + + # Propagate to external components. This is done after checking the + # lock, for consistent behavior. + hook_response = pre_push_extension(repo_store_path=Repository.base_path(), **extras) + events.trigger(events.RepoPrePushEvent(repo_name=extras.repository, extras=extras)) + + return HookResponse(0, output) + hook_response + + def post_push(extras): """Hook executed after user pushes to the repository.""" commit_ids = extras.commit_ids @@ -292,22 +288,13 @@ def post_push(extras): # Propagate to external components. output = '' + # make lock is a tri state False, True, None. We only release lock on False if extras.make_lock is False and not is_shadow_repo(extras): Repository.unlock(Repository.get_by_repo_name(extras.repository)) msg = f'Released lock on repo `{extras.repository}`\n' output += msg - if extras.locked_by[0]: - locked_by = User.get(extras.locked_by[0]).username - reason = extras.locked_by[2] - _http_ret = HTTPLockedRC( - _locked_by_explanation(extras.repository, locked_by, reason)) - # TODO: johbo: if not? - if str(_http_ret.code).startswith('2'): - # 2xx Codes don't raise exceptions - output += _http_ret.title - if extras.new_refs: tmpl = '{}/{}/pull-request/new?{{ref_type}}={{ref_name}}'.format( safe_str(extras.server_url), safe_str(extras.repository)) @@ -322,11 +309,8 @@ def post_push(extras): hook_response = '' if not is_shadow_repo(extras): - hook_response = post_push_extension( - repo_store_path=Repository.base_path(), - **extras) - events.trigger(events.RepoPushEvent( - repo_name=extras.repository, pushed_commit_ids=commit_ids, extras=extras)) + hook_response = post_push_extension(repo_store_path=Repository.base_path(), **extras) + events.trigger(events.RepoPushEvent(repo_name=extras.repository, pushed_commit_ids=commit_ids, extras=extras)) output += 'RhodeCode: push completed\n' return HookResponse(0, output) + hook_response @@ -380,12 +364,20 @@ class ExtensionCallback(object): # with older rcextensions that require api_key present if self._hook_name in ['CREATE_USER_HOOK', 'DELETE_USER_HOOK']: kwargs_to_pass['api_key'] = '_DEPRECATED_' - return callback(**kwargs_to_pass) + result = callback(**kwargs_to_pass) + log.debug('got rcextensions result: %s', result) + return result def is_active(self): return hasattr(rhodecode.EXTENSIONS, self._hook_name) def _get_callback(self): + if rhodecode.is_test: + log.debug('In test mode, reloading rcextensions...') + # NOTE: for test re-load rcextensions always so we can dynamically change them for testing purposes + from rhodecode.lib.utils import load_rcextensions + load_rcextensions(root_path=os.path.dirname(rhodecode.CONFIG['__file__'])) + return getattr(rhodecode.EXTENSIONS, self._hook_name, None) return getattr(rhodecode.EXTENSIONS, self._hook_name, None) diff --git a/rhodecode/lib/middleware/simplegit.py b/rhodecode/lib/middleware/simplegit.py index f709a646..9580f827 100644 --- a/rhodecode/lib/middleware/simplegit.py +++ b/rhodecode/lib/middleware/simplegit.py @@ -40,16 +40,6 @@ GIT_PROTO_PAT = re.compile( GIT_LFS_PROTO_PAT = re.compile(r'^/(.+)/(info/lfs/(.+))') -def default_lfs_store(): - """ - Default lfs store location, it's consistent with Mercurials large file - store which is in .cache/largefiles - """ - from rhodecode.lib.vcs.backends.git import lfs_store - user_home = os.path.expanduser("~") - return lfs_store(user_home) - - class SimpleGit(simplevcs.SimpleVCS): SCM = 'git' @@ -151,6 +141,6 @@ class SimpleGit(simplevcs.SimpleVCS): extras['git_lfs_enabled'] = utils2.str2bool( config.get('vcs_git_lfs', 'enabled')) - extras['git_lfs_store_path'] = custom_store or default_lfs_store() + extras['git_lfs_store_path'] = custom_store extras['git_lfs_http_scheme'] = scheme return extras diff --git a/rhodecode/lib/middleware/simplevcs.py b/rhodecode/lib/middleware/simplevcs.py index b3f7c230..bd6d8c16 100644 --- a/rhodecode/lib/middleware/simplevcs.py +++ b/rhodecode/lib/middleware/simplevcs.py @@ -1,5 +1,3 @@ - - # Copyright (C) 2014-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -32,8 +30,7 @@ from functools import wraps import time from paste.httpheaders import REMOTE_USER, AUTH_TYPE -from pyramid.httpexceptions import ( - HTTPNotFound, HTTPForbidden, HTTPNotAcceptable, HTTPInternalServerError) +from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPNotAcceptable, HTTPInternalServerError from zope.cachedescriptors.property import Lazy as LazyProperty import rhodecode @@ -41,10 +38,9 @@ from rhodecode.authentication.base import authenticate, VCS_TYPE, loadplugin from rhodecode.lib import rc_cache from rhodecode.lib.svn_txn_utils import store_txn_id_data from rhodecode.lib.auth import AuthUser, HasPermissionAnyMiddleware -from rhodecode.lib.base import ( - BasicAuth, get_ip_addr, get_user_agent, vcs_operation_context) -from rhodecode.lib.exceptions import (UserCreationError, NotAllowedToCreateUserError) -from rhodecode.lib.hook_daemon.base import prepare_callback_daemon +from rhodecode.lib.base import BasicAuth, get_ip_addr, get_user_agent, vcs_operation_context +from rhodecode.lib.exceptions import UserCreationError, NotAllowedToCreateUserError +from rhodecode.lib.hook_daemon.utils import prepare_callback_daemon from rhodecode.lib.middleware import appenlight from rhodecode.lib.middleware.utils import scm_app_http from rhodecode.lib.str_utils import safe_bytes, safe_int @@ -78,17 +74,18 @@ def initialize_generator(factory): try: init = next(gen) except StopIteration: - raise ValueError('Generator must yield at least one element.') + raise ValueError("Generator must yield at least one element.") if init != "__init__": raise ValueError('First yielded element must be "__init__".') return gen + return wrapper class SimpleVCS(object): """Common functionality for SCM HTTP handlers.""" - SCM = 'unknown' + SCM = "unknown" acl_repo_name = None url_repo_name = None @@ -100,11 +97,11 @@ class SimpleVCS(object): # we use this regex which will match only on URLs pointing to shadow # repositories. shadow_repo_re = re.compile( - '(?P(?:{slug_pat}/)*)' # repo groups - '(?P{slug_pat})/' # target repo - 'pull-request/(?P\\d+)/' # pull request - 'repository$' # shadow repo - .format(slug_pat=SLUG_RE.pattern)) + "(?P(?:{slug_pat}/)*)" # repo groups + "(?P{slug_pat})/" # target repo + "pull-request/(?P\\d+)/" # pull request + "repository$".format(slug_pat=SLUG_RE.pattern) # shadow repo + ) def __init__(self, config, registry): self.registry = registry @@ -113,15 +110,14 @@ class SimpleVCS(object): self.repo_vcs_config = base.Config() rc_settings = SettingsModel().get_all_settings(cache=True, from_request=False) - realm = rc_settings.get('rhodecode_realm') or 'RhodeCode AUTH' + realm = rc_settings.get("rhodecode_realm") or "RhodeCode AUTH" # authenticate this VCS request using authfunc - auth_ret_code_detection = \ - str2bool(self.config.get('auth_ret_code_detection', False)) + auth_ret_code_detection = str2bool(self.config.get("auth_ret_code_detection", False)) self.authenticate = BasicAuth( - '', authenticate, registry, config.get('auth_ret_code'), - auth_ret_code_detection, rc_realm=realm) - self.ip_addr = '0.0.0.0' + "", authenticate, registry, config.get("auth_ret_code"), auth_ret_code_detection, rc_realm=realm + ) + self.ip_addr = "0.0.0.0" @LazyProperty def global_vcs_config(self): @@ -132,10 +128,10 @@ class SimpleVCS(object): @property def base_path(self): - settings_path = self.config.get('repo_store.path') + settings_path = self.config.get("repo_store.path") if not settings_path: - raise ValueError('FATAL: repo_store.path is empty') + raise ValueError("FATAL: repo_store.path is empty") return settings_path def set_repo_names(self, environ): @@ -164,17 +160,16 @@ class SimpleVCS(object): match_dict = match.groupdict() # Build acl repo name from regex match. - acl_repo_name = safe_str('{groups}{target}'.format( - groups=match_dict['groups'] or '', - target=match_dict['target'])) + acl_repo_name = safe_str( + "{groups}{target}".format(groups=match_dict["groups"] or "", target=match_dict["target"]) + ) # Retrieve pull request instance by ID from regex match. - pull_request = PullRequest.get(match_dict['pr_id']) + pull_request = PullRequest.get(match_dict["pr_id"]) # Only proceed if we got a pull request and if acl repo name from # URL equals the target repo name of the pull request. if pull_request and (acl_repo_name == pull_request.target_repo.repo_name): - # Get file system path to shadow repository. workspace_id = PullRequestModel()._workspace_id(pull_request) vcs_repo_name = pull_request.target_repo.get_shadow_repository_path(workspace_id) @@ -184,21 +179,23 @@ class SimpleVCS(object): self.acl_repo_name = acl_repo_name self.is_shadow_repo = True - log.debug('Setting all VCS repository names: %s', { - 'acl_repo_name': self.acl_repo_name, - 'url_repo_name': self.url_repo_name, - 'vcs_repo_name': self.vcs_repo_name, - }) + log.debug( + "Setting all VCS repository names: %s", + { + "acl_repo_name": self.acl_repo_name, + "url_repo_name": self.url_repo_name, + "vcs_repo_name": self.vcs_repo_name, + }, + ) @property def scm_app(self): - custom_implementation = self.config['vcs.scm_app_implementation'] - if custom_implementation == 'http': - log.debug('Using HTTP implementation of scm app.') + custom_implementation = self.config["vcs.scm_app_implementation"] + if custom_implementation == "http": + log.debug("Using HTTP implementation of scm app.") scm_app_impl = scm_app_http else: - log.debug('Using custom implementation of scm_app: "{}"'.format( - custom_implementation)) + log.debug('Using custom implementation of scm_app: "{}"'.format(custom_implementation)) scm_app_impl = importlib.import_module(custom_implementation) return scm_app_impl @@ -208,17 +205,18 @@ class SimpleVCS(object): with a repository_name for support of _ non changeable urls """ - data = repo_name.split('/') + data = repo_name.split("/") if len(data) >= 2: from rhodecode.model.repo import RepoModel + by_id_match = RepoModel().get_repo_by_id(repo_name) if by_id_match: data[1] = by_id_match.repo_name # Because PEP-3333-WSGI uses bytes-tunneled-in-latin-1 as PATH_INFO # and we use this data - maybe_new_path = '/'.join(data) - return safe_bytes(maybe_new_path).decode('latin1') + maybe_new_path = "/".join(data) + return safe_bytes(maybe_new_path).decode("latin1") def _invalidate_cache(self, repo_name): """ @@ -231,21 +229,18 @@ class SimpleVCS(object): def is_valid_and_existing_repo(self, repo_name, base_path, scm_type): db_repo = Repository.get_by_repo_name(repo_name) if not db_repo: - log.debug('Repository `%s` not found inside the database.', - repo_name) + log.debug("Repository `%s` not found inside the database.", repo_name) return False if db_repo.repo_type != scm_type: log.warning( - 'Repository `%s` have incorrect scm_type, expected %s got %s', - repo_name, db_repo.repo_type, scm_type) + "Repository `%s` have incorrect scm_type, expected %s got %s", repo_name, db_repo.repo_type, scm_type + ) return False config = db_repo._config - config.set('extensions', 'largefiles', '') - return is_valid_repo( - repo_name, base_path, - explicit_scm=scm_type, expect_scm=scm_type, config=config) + config.set("extensions", "largefiles", "") + return is_valid_repo(repo_name, base_path, explicit_scm=scm_type, expect_scm=scm_type, config=config) def valid_and_active_user(self, user): """ @@ -267,8 +262,9 @@ class SimpleVCS(object): def is_shadow_repo_dir(self): return os.path.isdir(self.vcs_repo_name) - def _check_permission(self, action, user, auth_user, repo_name, ip_addr=None, - plugin_id='', plugin_cache_active=False, cache_ttl=0): + def _check_permission( + self, action, user, auth_user, repo_name, ip_addr=None, plugin_id="", plugin_cache_active=False, cache_ttl=0 + ): """ Checks permissions using action (push/pull) user and repository name. If plugin_cache and ttl is set it will use the plugin which @@ -280,71 +276,67 @@ class SimpleVCS(object): :param repo_name: repository name """ - log.debug('AUTH_CACHE_TTL for permissions `%s` active: %s (TTL: %s)', - plugin_id, plugin_cache_active, cache_ttl) + log.debug("AUTH_CACHE_TTL for permissions `%s` active: %s (TTL: %s)", plugin_id, plugin_cache_active, cache_ttl) user_id = user.user_id - cache_namespace_uid = f'cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{user_id}' - region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid) + cache_namespace_uid = f"cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{user_id}" + region = rc_cache.get_or_create_region("cache_perms", cache_namespace_uid) - @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, - expiration_time=cache_ttl, - condition=plugin_cache_active) - def compute_perm_vcs( - cache_name, plugin_id, action, user_id, repo_name, ip_addr): - - log.debug('auth: calculating permission access now for vcs operation: %s', action) + @region.conditional_cache_on_arguments( + namespace=cache_namespace_uid, expiration_time=cache_ttl, condition=plugin_cache_active + ) + def compute_perm_vcs(cache_name, plugin_id, action, user_id, repo_name, ip_addr): + log.debug("auth: calculating permission access now for vcs operation: %s", action) # check IP inherit = user.inherit_default_permissions - ip_allowed = AuthUser.check_ip_allowed( - user_id, ip_addr, inherit_from_default=inherit) + ip_allowed = AuthUser.check_ip_allowed(user_id, ip_addr, inherit_from_default=inherit) if ip_allowed: - log.info('Access for IP:%s allowed', ip_addr) + log.info("Access for IP:%s allowed", ip_addr) else: return False - if action == 'push': - perms = ('repository.write', 'repository.admin') + if action == "push": + perms = ("repository.write", "repository.admin") if not HasPermissionAnyMiddleware(*perms)(auth_user, repo_name): return False else: # any other action need at least read permission - perms = ( - 'repository.read', 'repository.write', 'repository.admin') + perms = ("repository.read", "repository.write", "repository.admin") if not HasPermissionAnyMiddleware(*perms)(auth_user, repo_name): return False return True start = time.time() - log.debug('Running plugin `%s` permissions check', plugin_id) + log.debug("Running plugin `%s` permissions check", plugin_id) # for environ based auth, password can be empty, but then the validation is # on the server that fills in the env data needed for authentication - perm_result = compute_perm_vcs( - 'vcs_permissions', plugin_id, action, user.user_id, repo_name, ip_addr) + perm_result = compute_perm_vcs("vcs_permissions", plugin_id, action, user.user_id, repo_name, ip_addr) auth_time = time.time() - start - log.debug('Permissions for plugin `%s` completed in %.4fs, ' - 'expiration time of fetched cache %.1fs.', - plugin_id, auth_time, cache_ttl) + log.debug( + "Permissions for plugin `%s` completed in %.4fs, " "expiration time of fetched cache %.1fs.", + plugin_id, + auth_time, + cache_ttl, + ) return perm_result def _get_http_scheme(self, environ): try: - return environ['wsgi.url_scheme'] + return environ["wsgi.url_scheme"] except Exception: - log.exception('Failed to read http scheme') - return 'http' + log.exception("Failed to read http scheme") + return "http" def _get_default_cache_ttl(self): # take AUTH_CACHE_TTL from the `rhodecode` auth plugin - plugin = loadplugin('egg:rhodecode-enterprise-ce#rhodecode') + plugin = loadplugin("egg:rhodecode-enterprise-ce#rhodecode") plugin_settings = plugin.get_settings() - plugin_cache_active, cache_ttl = plugin.get_ttl_cache( - plugin_settings) or (False, 0) + plugin_cache_active, cache_ttl = plugin.get_ttl_cache(plugin_settings) or (False, 0) return plugin_cache_active, cache_ttl def __call__(self, environ, start_response): @@ -359,17 +351,17 @@ class SimpleVCS(object): def _handle_request(self, environ, start_response): if not self.url_repo_name: - log.warning('Repository name is empty: %s', self.url_repo_name) + log.warning("Repository name is empty: %s", self.url_repo_name) # failed to get repo name, we fail now return HTTPNotFound()(environ, start_response) - log.debug('Extracted repo name is %s', self.url_repo_name) + log.debug("Extracted repo name is %s", self.url_repo_name) ip_addr = get_ip_addr(environ) user_agent = get_user_agent(environ) username = None # skip passing error to error controller - environ['pylons.status_code_redirect'] = True + environ["pylons.status_code_redirect"] = True # ====================================================================== # GET ACTION PULL or PUSH @@ -380,17 +372,15 @@ class SimpleVCS(object): # Check if this is a request to a shadow repository of a pull request. # In this case only pull action is allowed. # ====================================================================== - if self.is_shadow_repo and action != 'pull': - reason = 'Only pull action is allowed for shadow repositories.' - log.debug('User not allowed to proceed, %s', reason) + if self.is_shadow_repo and action != "pull": + reason = "Only pull action is allowed for shadow repositories." + log.debug("User not allowed to proceed, %s", reason) return HTTPNotAcceptable(reason)(environ, start_response) # Check if the shadow repo actually exists, in case someone refers # to it, and it has been deleted because of successful merge. if self.is_shadow_repo and not self.is_shadow_repo_dir: - log.debug( - 'Shadow repo detected, and shadow repo dir `%s` is missing', - self.is_shadow_repo_dir) + log.debug("Shadow repo detected, and shadow repo dir `%s` is missing", self.is_shadow_repo_dir) return HTTPNotFound()(environ, start_response) # ====================================================================== @@ -398,7 +388,7 @@ class SimpleVCS(object): # ====================================================================== detect_force_push = False check_branch_perms = False - if action in ['pull', 'push']: + if action in ["pull", "push"]: user_obj = anonymous_user = User.get_default_user() auth_user = user_obj.AuthUser() username = anonymous_user.username @@ -406,8 +396,12 @@ class SimpleVCS(object): plugin_cache_active, cache_ttl = self._get_default_cache_ttl() # ONLY check permissions if the user is activated anonymous_perm = self._check_permission( - action, anonymous_user, auth_user, self.acl_repo_name, ip_addr, - plugin_id='anonymous_access', + action, + anonymous_user, + auth_user, + self.acl_repo_name, + ip_addr, + plugin_id="anonymous_access", plugin_cache_active=plugin_cache_active, cache_ttl=cache_ttl, ) @@ -416,12 +410,13 @@ class SimpleVCS(object): if not anonymous_user.active or not anonymous_perm: if not anonymous_user.active: - log.debug('Anonymous access is disabled, running ' - 'authentication') + log.debug("Anonymous access is disabled, running " "authentication") if not anonymous_perm: - log.debug('Not enough credentials to access repo: `%s` ' - 'repository as anonymous user', self.acl_repo_name) + log.debug( + "Not enough credentials to access repo: `%s` " "repository as anonymous user", + self.acl_repo_name, + ) username = None # ============================================================== @@ -430,19 +425,18 @@ class SimpleVCS(object): # ============================================================== # try to auth based on environ, container auth methods - log.debug('Running PRE-AUTH for container|headers based authentication') + log.debug("Running PRE-AUTH for container|headers based authentication") # headers auth, by just reading special headers and bypass the auth with user/passwd pre_auth = authenticate( - '', '', environ, VCS_TYPE, registry=self.registry, - acl_repo_name=self.acl_repo_name) + "", "", environ, VCS_TYPE, registry=self.registry, acl_repo_name=self.acl_repo_name + ) - if pre_auth and pre_auth.get('username'): - username = pre_auth['username'] - log.debug('PRE-AUTH got `%s` as username', username) + if pre_auth and pre_auth.get("username"): + username = pre_auth["username"] + log.debug("PRE-AUTH got `%s` as username", username) if pre_auth: - log.debug('PRE-AUTH successful from %s', - pre_auth.get('auth_data', {}).get('_plugin')) + log.debug("PRE-AUTH successful from %s", pre_auth.get("auth_data", {}).get("_plugin")) # If not authenticated by the container, running basic auth # before inject the calling repo_name for special scope checks @@ -463,16 +457,16 @@ class SimpleVCS(object): return HTTPNotAcceptable(reason)(environ, start_response) if isinstance(auth_result, dict): - AUTH_TYPE.update(environ, 'basic') - REMOTE_USER.update(environ, auth_result['username']) - username = auth_result['username'] - plugin = auth_result.get('auth_data', {}).get('_plugin') - log.info( - 'MAIN-AUTH successful for user `%s` from %s plugin', - username, plugin) + AUTH_TYPE.update(environ, "basic") + REMOTE_USER.update(environ, auth_result["username"]) + username = auth_result["username"] + plugin = auth_result.get("auth_data", {}).get("_plugin") + log.info("MAIN-AUTH successful for user `%s` from %s plugin", username, plugin) - plugin_cache_active, cache_ttl = auth_result.get( - 'auth_data', {}).get('_ttl_cache') or (False, 0) + plugin_cache_active, cache_ttl = auth_result.get("auth_data", {}).get("_ttl_cache") or ( + False, + 0, + ) else: return auth_result.wsgi_application(environ, start_response) @@ -488,21 +482,24 @@ class SimpleVCS(object): # check user attributes for password change flag user_obj = user auth_user = user_obj.AuthUser() - if user_obj and user_obj.username != User.DEFAULT_USER and \ - user_obj.user_data.get('force_password_change'): - reason = 'password change required' - log.debug('User not allowed to authenticate, %s', reason) + if ( + user_obj + and user_obj.username != User.DEFAULT_USER + and user_obj.user_data.get("force_password_change") + ): + reason = "password change required" + log.debug("User not allowed to authenticate, %s", reason) return HTTPNotAcceptable(reason)(environ, start_response) # check permissions for this repository perm = self._check_permission( - action, user, auth_user, self.acl_repo_name, ip_addr, - plugin, plugin_cache_active, cache_ttl) + action, user, auth_user, self.acl_repo_name, ip_addr, plugin, plugin_cache_active, cache_ttl + ) if not perm: return HTTPForbidden()(environ, start_response) - environ['rc_auth_user_id'] = str(user_id) + environ["rc_auth_user_id"] = str(user_id) - if action == 'push': + if action == "push": perms = auth_user.get_branch_permissions(self.acl_repo_name) if perms: check_branch_perms = True @@ -510,41 +507,48 @@ class SimpleVCS(object): # extras are injected into UI object and later available # in hooks executed by RhodeCode - check_locking = _should_check_locking(environ.get('QUERY_STRING')) + check_locking = _should_check_locking(environ.get("QUERY_STRING")) extras = vcs_operation_context( - environ, repo_name=self.acl_repo_name, username=username, - action=action, scm=self.SCM, check_locking=check_locking, - is_shadow_repo=self.is_shadow_repo, check_branch_perms=check_branch_perms, - detect_force_push=detect_force_push + environ, + repo_name=self.acl_repo_name, + username=username, + action=action, + scm=self.SCM, + check_locking=check_locking, + is_shadow_repo=self.is_shadow_repo, + check_branch_perms=check_branch_perms, + detect_force_push=detect_force_push, ) # ====================================================================== # REQUEST HANDLING # ====================================================================== - repo_path = os.path.join( - safe_str(self.base_path), safe_str(self.vcs_repo_name)) - log.debug('Repository path is %s', repo_path) + repo_path = os.path.join(safe_str(self.base_path), safe_str(self.vcs_repo_name)) + log.debug("Repository path is %s", repo_path) fix_PATH() log.info( '%s action on %s repo "%s" by "%s" from %s %s', - action, self.SCM, safe_str(self.url_repo_name), - safe_str(username), ip_addr, user_agent) + action, + self.SCM, + safe_str(self.url_repo_name), + safe_str(username), + ip_addr, + user_agent, + ) - return self._generate_vcs_response( - environ, start_response, repo_path, extras, action) + return self._generate_vcs_response(environ, start_response, repo_path, extras, action) def _get_txn_id(self, environ): - - for k in ['RAW_URI', 'HTTP_DESTINATION']: + for k in ["RAW_URI", "HTTP_DESTINATION"]: url = environ.get(k) if not url: continue # regex to search for svn-txn-id - pattern = r'/!svn/txr/([^/]+)/' + pattern = r"/!svn/txr/([^/]+)/" # Search for the pattern in the URL match = re.search(pattern, url) @@ -555,8 +559,7 @@ class SimpleVCS(object): return txn_id @initialize_generator - def _generate_vcs_response( - self, environ, start_response, repo_path, extras, action): + def _generate_vcs_response(self, environ, start_response, repo_path, extras, action): """ Returns a generator for the response content. @@ -565,24 +568,20 @@ class SimpleVCS(object): also handles the locking exceptions which will be triggered when the first chunk is produced by the underlying WSGI application. """ - svn_txn_id = '' - if action == 'push': + svn_txn_id = "" + if action == "push": svn_txn_id = self._get_txn_id(environ) - callback_daemon, extras = self._prepare_callback_daemon( - extras, environ, action, txn_id=svn_txn_id) + callback_daemon, extras = self._prepare_callback_daemon(extras, environ, action, txn_id=svn_txn_id) if svn_txn_id: - - port = safe_int(extras['hooks_uri'].split(':')[-1]) txn_id_data = extras.copy() - txn_id_data.update({'port': port}) - txn_id_data.update({'req_method': environ['REQUEST_METHOD']}) + txn_id_data.update({"req_method": environ["REQUEST_METHOD"]}) full_repo_path = repo_path store_txn_id_data(full_repo_path, svn_txn_id, txn_id_data) - log.debug('HOOKS extras is %s', extras) + log.debug("HOOKS extras is %s", extras) http_scheme = self._get_http_scheme(environ) @@ -609,7 +608,7 @@ class SimpleVCS(object): try: # invalidate cache on push - if action == 'push': + if action == "push": self._invalidate_cache(self.url_repo_name) finally: meta.Session.remove() @@ -632,12 +631,12 @@ class SimpleVCS(object): """Return the WSGI app that will finally handle the request.""" raise NotImplementedError() - def _create_config(self, extras, repo_name, scheme='http'): + def _create_config(self, extras, repo_name, scheme="http"): """Create a safe config representation.""" raise NotImplementedError() def _should_use_callback_daemon(self, extras, environ, action): - if extras.get('is_shadow_repo'): + if extras.get("is_shadow_repo"): # we don't want to execute hooks, and callback daemon for shadow repos return False return True @@ -647,11 +646,9 @@ class SimpleVCS(object): if not self._should_use_callback_daemon(extras, environ, action): # disable callback daemon for actions that don't require it - protocol = 'local' + protocol = "local" - return prepare_callback_daemon( - extras, protocol=protocol, - host=vcs_settings.HOOKS_HOST, txn_id=txn_id) + return prepare_callback_daemon(extras, protocol=protocol, txn_id=txn_id) def _should_check_locking(query_string): @@ -659,4 +656,4 @@ def _should_check_locking(query_string): # server see all operation on commit; bookmarks, phases and # obsolescence marker in different transaction, we don't want to check # locking on those - return query_string not in ['cmd=listkeys'] + return query_string not in ["cmd=listkeys"] diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py index 11331c06..ea5ada46 100644 --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -21,6 +21,7 @@ Utilities library for RhodeCode """ import datetime +import importlib import decorator import logging @@ -42,8 +43,9 @@ from webhelpers2.text import collapse, strip_tags, convert_accented_entities, co from mako import exceptions +import rhodecode from rhodecode import ConfigGet -from rhodecode.lib.exceptions import HTTPBranchProtected, HTTPLockedRC +from rhodecode.lib.exceptions import HTTPBranchProtected, HTTPLockedRepo, ClientNotSupported from rhodecode.lib.hash_utils import sha256_safe, md5, sha1 from rhodecode.lib.type_utils import AttributeDict from rhodecode.lib.str_utils import safe_bytes, safe_str @@ -86,6 +88,7 @@ def adopt_for_celery(func): @wraps(func) def wrapper(extras): extras = AttributeDict(extras) + try: # HooksResponse implements to_json method which must be used there. return func(extras).to_json() @@ -100,7 +103,18 @@ def adopt_for_celery(func): 'exception_args': error_args, 'exception_traceback': '', } - except HTTPLockedRC as error: + except ClientNotSupported as error: + # Those special cases don't need error reporting. It's a case of + # locked repo or protected branch + error_args = error.args + return { + 'status': error.code, + 'output': error.explanation, + 'exception': type(error).__name__, + 'exception_args': error_args, + 'exception_traceback': '', + } + except HTTPLockedRepo as error: # Those special cases don't need error reporting. It's a case of # locked repo or protected branch error_args = error.args @@ -117,7 +131,7 @@ def adopt_for_celery(func): 'output': '', 'exception': type(e).__name__, 'exception_args': e.args, - 'exception_traceback': '', + 'exception_traceback': traceback.format_exc(), } return wrapper @@ -411,6 +425,10 @@ def prepare_config_data(clear_session=True, repo=None): ('web', 'push_ssl', 'false'), ] for setting in ui_settings: + # skip certain deprecated keys that might be still in DB + if f"{setting.section}_{setting.key}" in ['extensions_hgsubversion']: + continue + # Todo: remove this section once transition to *.ini files will be completed if setting.section in ('largefiles', 'vcs_git_lfs'): if setting.key != 'enabled': @@ -686,22 +704,41 @@ def repo2db_mapper(initial_repo_list, remove_obsolete=False, force_hooks_rebuild return added, removed +def deep_reload_package(package_name): + """ + Deeply reload a package by removing it and its submodules from sys.modules, + then re-importing it. + """ + # Remove the package and its submodules from sys.modules + to_reload = [name for name in sys.modules if name == package_name or name.startswith(package_name + ".")] + for module_name in to_reload: + del sys.modules[module_name] + log.debug(f"Removed module from cache: {module_name}") + + # Re-import the package + package = importlib.import_module(package_name) + log.debug(f"Re-imported package: {package_name}") + + return package def load_rcextensions(root_path): import rhodecode from rhodecode.config import conf path = os.path.join(root_path) - sys.path.append(path) + deep_reload = path in sys.path + sys.path.insert(0, path) try: - rcextensions = __import__('rcextensions') + rcextensions = __import__('rcextensions', fromlist=['']) except ImportError: if os.path.isdir(os.path.join(path, 'rcextensions')): log.warning('Unable to load rcextensions from %s', path) rcextensions = None if rcextensions: + if deep_reload: + rcextensions = deep_reload_package('rcextensions') log.info('Loaded rcextensions from %s...', rcextensions) rhodecode.EXTENSIONS = rcextensions @@ -741,6 +778,7 @@ def create_test_index(repo_location, config): except ImportError: raise ImportError('Failed to import rc_testdata, ' 'please make sure this package is installed from requirements_test.txt') + rc_testdata.extract_search_index( 'vcs_search_index', os.path.dirname(config['search.location'])) @@ -785,22 +823,15 @@ def create_test_repositories(test_path, config): Creates test repositories in the temporary directory. Repositories are extracted from archives within the rc_testdata package. """ - import rc_testdata + try: + import rc_testdata + except ImportError: + raise ImportError('Failed to import rc_testdata, ' + 'please make sure this package is installed from requirements_test.txt') + from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO - log.debug('making test vcs repositories') - - idx_path = config['search.location'] - data_path = config['cache_dir'] - - # clean index and data - if idx_path and os.path.exists(idx_path): - log.debug('remove %s', idx_path) - shutil.rmtree(idx_path) - - if data_path and os.path.exists(data_path): - log.debug('remove %s', data_path) - shutil.rmtree(data_path) + log.debug('making test vcs repositories at %s', test_path) rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO)) rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO)) diff --git a/rhodecode/lib/vcs/__init__.py b/rhodecode/lib/vcs/__init__.py index da7de79b..bce7c095 100644 --- a/rhodecode/lib/vcs/__init__.py +++ b/rhodecode/lib/vcs/__init__.py @@ -140,7 +140,7 @@ class CurlSession(object): try: curl.perform() except pycurl.error as exc: - log.error('Failed to call endpoint url: {} using pycurl'.format(url)) + log.error('Failed to call endpoint url: %s using pycurl', url) raise status_code = curl.getinfo(pycurl.HTTP_CODE) diff --git a/rhodecode/lib/vcs/backends/git/__init__.py b/rhodecode/lib/vcs/backends/git/__init__.py index a2632c07..e08cd427 100644 --- a/rhodecode/lib/vcs/backends/git/__init__.py +++ b/rhodecode/lib/vcs/backends/git/__init__.py @@ -45,10 +45,3 @@ def discover_git_version(raise_on_exc=False): if raise_on_exc: raise return '' - - -def lfs_store(base_location): - """ - Return a lfs store relative to base_location - """ - return os.path.join(base_location, '.cache', 'lfs_store') diff --git a/rhodecode/lib/vcs/backends/hg/__init__.py b/rhodecode/lib/vcs/backends/hg/__init__.py index 60ebd19b..cefbe54a 100644 --- a/rhodecode/lib/vcs/backends/hg/__init__.py +++ b/rhodecode/lib/vcs/backends/hg/__init__.py @@ -45,10 +45,3 @@ def discover_hg_version(raise_on_exc=False): if raise_on_exc: raise return '' - - -def largefiles_store(base_location): - """ - Return a largefile store relative to base_location - """ - return os.path.join(base_location, '.cache', 'largefiles') diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py index b331b167..498a0eb6 100644 --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -216,7 +216,7 @@ class RemoteRepo(object): self._cache_region, self._cache_namespace = \ remote_maker.init_cache_region(cache_repo_id) - with_wire = with_wire or {} + with_wire = with_wire or {"cache": False} repo_state_uid = with_wire.get('repo_state_uid') or 'state' diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py index 075c7efd..8b44a156 100644 --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -373,6 +373,7 @@ class CommentsModel(BaseModel): Session().add(comment) Session().flush() + kwargs = { 'user': user, 'renderer_type': renderer, @@ -387,8 +388,7 @@ class CommentsModel(BaseModel): } if commit_obj: - recipients = ChangesetComment.get_users( - revision=commit_obj.raw_id) + recipients = ChangesetComment.get_users(revision=commit_obj.raw_id) # add commit author if it's in RhodeCode system cs_author = User.get_from_cs_author(commit_obj.author) if not cs_author: @@ -397,16 +397,13 @@ class CommentsModel(BaseModel): recipients += [cs_author] commit_comment_url = self.get_url(comment, request=request) - commit_comment_reply_url = self.get_url( - comment, request=request, - anchor=f'comment-{comment.comment_id}/?/ReplyToComment') + commit_comment_reply_url = self.get_url(comment, request=request, anchor=f'comment-{comment.comment_id}/?/ReplyToComment') target_repo_url = h.link_to( repo.repo_name, h.route_url('repo_summary', repo_name=repo.repo_name)) - commit_url = h.route_url('repo_commit', repo_name=repo.repo_name, - commit_id=commit_id) + commit_url = h.route_url('repo_commit', repo_name=repo.repo_name, commit_id=commit_id) # commit specifics kwargs.update({ @@ -489,7 +486,6 @@ class CommentsModel(BaseModel): if not is_draft: comment_data = comment.get_api_data() - self._log_audit_action( action, {'data': comment_data}, auth_user, comment) diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py index 0848a826..e99fad38 100644 --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -38,7 +38,7 @@ from rhodecode.translation import lazy_ugettext from rhodecode.lib import helpers as h, hooks_utils, diffs from rhodecode.lib import audit_logger from collections import OrderedDict -from rhodecode.lib.hook_daemon.base import prepare_callback_daemon +from rhodecode.lib.hook_daemon.utils import prepare_callback_daemon from rhodecode.lib.ext_json import sjson as json from rhodecode.lib.markup_renderer import ( DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer) @@ -980,9 +980,7 @@ class PullRequestModel(BaseModel): target_ref = self._refresh_reference( pull_request.target_ref_parts, target_vcs) - callback_daemon, extras = prepare_callback_daemon( - extras, protocol=vcs_settings.HOOKS_PROTOCOL, - host=vcs_settings.HOOKS_HOST) + callback_daemon, extras = prepare_callback_daemon(extras, protocol=vcs_settings.HOOKS_PROTOCOL) with callback_daemon: # TODO: johbo: Implement a clean way to run a config_override diff --git a/rhodecode/model/settings.py b/rhodecode/model/settings.py index 275cd868..23433839 100644 --- a/rhodecode/model/settings.py +++ b/rhodecode/model/settings.py @@ -862,27 +862,3 @@ class VcsSettingsModel(object): raise ValueError( f'The given data does not contain {data_key} key') return data_keys - - def create_largeobjects_dirs_if_needed(self, repo_store_path): - """ - This is subscribed to the `pyramid.events.ApplicationCreated` event. It - does a repository scan if enabled in the settings. - """ - - from rhodecode.lib.vcs.backends.hg import largefiles_store - from rhodecode.lib.vcs.backends.git import lfs_store - - paths = [ - largefiles_store(repo_store_path), - lfs_store(repo_store_path)] - - for path in paths: - if os.path.isdir(path): - continue - if os.path.isfile(path): - continue - # not a file nor dir, we try to create it - try: - os.makedirs(path) - except Exception: - log.warning('Failed to create largefiles dir:%s', path) diff --git a/rhodecode/tests/__init__.py b/rhodecode/tests/__init__.py index 3d8d082d..f2f2a9a2 100644 --- a/rhodecode/tests/__init__.py +++ b/rhodecode/tests/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -38,7 +37,7 @@ from rhodecode.lib.hash_utils import sha1_safe log = logging.getLogger(__name__) __all__ = [ - 'get_new_dir', 'TestController', + 'get_new_dir', 'TestController', 'console_printer', 'clear_cache_regions', 'assert_session_flash', 'login_user', 'no_newline_id_generator', 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'SVN_REPO', @@ -244,3 +243,11 @@ def no_newline_id_generator(test_name): return test_name or 'test-with-empty-name' +def console_printer(*msg): + print_func = print + try: + from rich import print as print_func + except ImportError: + pass + + print_func(*msg) diff --git a/rhodecode/tests/auth_external_test.py b/rhodecode/tests/auth_external_test.py index 07010731..ab4ff946 100644 --- a/rhodecode/tests/auth_external_test.py +++ b/rhodecode/tests/auth_external_test.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -90,7 +89,7 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin): 'firstname': firstname, 'lastname': lastname, 'groups': [], - 'email': '%s@rhodecode.com' % username, + 'email': f'{username}@rhodecode.com', 'admin': admin, 'active': active, "active_from_extern": None, diff --git a/rhodecode/tests/config/test_routing_links.py b/rhodecode/tests/config/test_routing_links.py index a8b7612a..bb9ada63 100644 --- a/rhodecode/tests/config/test_routing_links.py +++ b/rhodecode/tests/config/test_routing_links.py @@ -20,14 +20,14 @@ import pytest import requests from rhodecode.config import routing_links - +from rhodecode.tests import console_printer def check_connection(): try: response = requests.get('https://rhodecode.com') return response.status_code == 200 except Exception as e: - print(e) + console_printer(e) return False diff --git a/rhodecode/tests/conftest.py b/rhodecode/tests/conftest.py index e6cdc829..da5ca2af 100644 --- a/rhodecode/tests/conftest.py +++ b/rhodecode/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -16,23 +16,10 @@ # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ -""" -py.test config for test suite for making push/pull operations. - -.. important:: - - You must have git >= 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. -""" - -import pytest +import pytest # noqa import logging - -from rhodecode.authentication import AuthenticationPluginRegistry -from rhodecode.model.db import Permission, User -from rhodecode.model.meta import Session -from rhodecode.model.settings import SettingsModel -from rhodecode.model.user import UserModel +import collections +import rhodecode log = logging.getLogger(__name__) @@ -40,99 +27,3 @@ log = logging.getLogger(__name__) # Docker image running httpbin... HTTPBIN_DOMAIN = 'http://httpbin' HTTPBIN_POST = HTTPBIN_DOMAIN + '/post' - - -@pytest.fixture() -def enable_auth_plugins(request, baseapp, csrf_token): - """ - Return a factory object that when called, allows to control which - authentication plugins are enabled. - """ - - class AuthPluginManager(object): - - def cleanup(self): - self._enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode']) - - def enable(self, plugins_list, override=None): - return self._enable_plugins(plugins_list, override) - - def _enable_plugins(self, plugins_list, override=None): - override = override or {} - params = { - 'auth_plugins': ','.join(plugins_list), - } - - # helper translate some names to others, to fix settings code - name_map = { - 'token': 'authtoken' - } - log.debug('enable_auth_plugins: enabling following auth-plugins: %s', plugins_list) - - for module in plugins_list: - plugin_name = module.partition('#')[-1] - if plugin_name in name_map: - plugin_name = name_map[plugin_name] - enabled_plugin = f'auth_{plugin_name}_enabled' - cache_ttl = f'auth_{plugin_name}_cache_ttl' - - # default params that are needed for each plugin, - # `enabled` and `cache_ttl` - params.update({ - enabled_plugin: True, - cache_ttl: 0 - }) - if override.get: - params.update(override.get(module, {})) - - validated_params = params - - for k, v in validated_params.items(): - setting = SettingsModel().create_or_update_setting(k, v) - Session().add(setting) - Session().commit() - - AuthenticationPluginRegistry.invalidate_auth_plugins_cache(hard=True) - - enabled_plugins = SettingsModel().get_auth_plugins() - assert plugins_list == enabled_plugins - - enabler = AuthPluginManager() - request.addfinalizer(enabler.cleanup) - - return enabler - - -@pytest.fixture() -def test_user_factory(request, baseapp): - - def user_factory(username='test_user', password='qweqwe', first_name='John', last_name='Testing', **kwargs): - usr = UserModel().create_or_update( - username=username, - password=password, - email=f'{username}@rhodecode.org', - firstname=first_name, lastname=last_name) - Session().commit() - - for k, v in kwargs.items(): - setattr(usr, k, v) - Session().add(usr) - - new_usr = User.get_by_username(username) - new_usr_id = new_usr.user_id - assert new_usr == usr - - @request.addfinalizer - def cleanup(): - if User.get(new_usr_id) is None: - return - - perm = Permission.query().all() - for p in perm: - UserModel().revoke_perm(usr, p) - - UserModel().delete(new_usr_id) - Session().commit() - return usr - - return user_factory diff --git a/rhodecode/tests/conftest_common.py b/rhodecode/tests/conftest_common.py index a773c0fb..20d044a5 100644 --- a/rhodecode/tests/conftest_common.py +++ b/rhodecode/tests/conftest_common.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -98,16 +98,16 @@ def pytest_addoption(parser): 'pyramid_config', "Set up a Pyramid environment with the specified config file.") + parser.addini('rhodecode_config', 'rhodecode config ini for tests') + parser.addini('celery_config', 'celery config ini for tests') + parser.addini('vcsserver_config', 'vcsserver config ini for tests') + vcsgroup = parser.getgroup('vcs') + vcsgroup.addoption( '--without-vcsserver', dest='with_vcsserver', action='store_false', help="Do not start the VCSServer in a background process.") - vcsgroup.addoption( - '--with-vcsserver-http', dest='vcsserver_config_http', - help="Start the HTTP VCSServer with the specified config file.") - vcsgroup.addoption( - '--vcsserver-protocol', dest='vcsserver_protocol', - help="Start the VCSServer with HTTP protocol support.") + vcsgroup.addoption( '--vcsserver-config-override', action='store', type=_parse_json, default=None, dest='vcsserver_config_override', help=( @@ -122,12 +122,6 @@ def pytest_addoption(parser): "Allows to set the port of the vcsserver. Useful when testing " "against an already running server and random ports cause " "trouble.")) - parser.addini( - 'vcsserver_config_http', - "Start the HTTP VCSServer with the specified config file.") - parser.addini( - 'vcsserver_protocol', - "Start the VCSServer with HTTP protocol support.") @pytest.hookimpl(tryfirst=True, hookwrapper=True) diff --git a/rhodecode/tests/database/__init__.py b/rhodecode/tests/database/__init__.py new file mode 100644 index 00000000..c4d86aeb --- /dev/null +++ b/rhodecode/tests/database/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2010-2023 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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/ diff --git a/rhodecode/tests/database/conftest.py b/rhodecode/tests/database/conftest.py index 0bd2c0b3..528b1dbb 100644 --- a/rhodecode/tests/database/conftest.py +++ b/rhodecode/tests/database/conftest.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -17,7 +16,7 @@ # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ -from subprocess import Popen, PIPE +import subprocess import os import sys import tempfile @@ -26,87 +25,71 @@ import pytest from sqlalchemy.engine import url from rhodecode.lib.str_utils import safe_str, safe_bytes -from rhodecode.tests.fixture import TestINI +from rhodecode.tests.fixtures.rc_fixture import TestINI def _get_dbs_from_metafunc(metafunc): - dbs_mark = metafunc.definition.get_closest_marker('dbs') + dbs_mark = metafunc.definition.get_closest_marker("dbs") if dbs_mark: # Supported backends by this test function, created from pytest.mark.dbs backends = dbs_mark.args else: - backends = metafunc.config.getoption('--dbs') + backends = metafunc.config.getoption("--dbs") return backends def pytest_generate_tests(metafunc): # Support test generation based on --dbs parameter - if 'db_backend' in metafunc.fixturenames: - requested_backends = set(metafunc.config.getoption('--dbs')) + if "db_backend" in metafunc.fixturenames: + requested_backends = set(metafunc.config.getoption("--dbs")) backends = _get_dbs_from_metafunc(metafunc) backends = requested_backends.intersection(backends) # TODO: johbo: Disabling a backend did not work out with # parametrization, find better way to achieve this. if not backends: metafunc.function._skip = True - metafunc.parametrize('db_backend_name', backends) + metafunc.parametrize("db_backend_name", backends) def pytest_collection_modifyitems(session, config, items): - remaining = [ - i for i in items if not getattr(i.obj, '_skip', False)] + remaining = [i for i in items if not getattr(i.obj, "_skip", False)] items[:] = remaining @pytest.fixture() -def db_backend( - request, db_backend_name, ini_config, tmpdir_factory): +def db_backend(request, db_backend_name, ini_config, tmpdir_factory): basetemp = tmpdir_factory.getbasetemp().strpath klass = _get_backend(db_backend_name) - option_name = '--{}-connection-string'.format(db_backend_name) + option_name = "--{}-connection-string".format(db_backend_name) connection_string = request.config.getoption(option_name) or None - return klass( - config_file=ini_config, basetemp=basetemp, - connection_string=connection_string) + return klass(config_file=ini_config, basetemp=basetemp, connection_string=connection_string) def _get_backend(backend_type): - return { - 'sqlite': SQLiteDBBackend, - 'postgres': PostgresDBBackend, - 'mysql': MySQLDBBackend, - '': EmptyDBBackend - }[backend_type] + return {"sqlite": SQLiteDBBackend, "postgres": PostgresDBBackend, "mysql": MySQLDBBackend, "": EmptyDBBackend}[ + backend_type + ] class DBBackend(object): _store = os.path.dirname(os.path.abspath(__file__)) _type = None - _base_ini_config = [{'app:main': {'vcs.start_server': 'false', - 'startup.import_repos': 'false'}}] - _db_url = [{'app:main': {'sqlalchemy.db1.url': ''}}] - _base_db_name = 'rhodecode_test_db_backend' - std_env = {'RC_TEST': '0'} - - def __init__( - self, config_file, db_name=None, basetemp=None, - connection_string=None): - - from rhodecode.lib.vcs.backends.hg import largefiles_store - from rhodecode.lib.vcs.backends.git import lfs_store + _base_ini_config = [{"app:main": {"vcs.start_server": "false", "startup.import_repos": "false"}}] + _db_url = [{"app:main": {"sqlalchemy.db1.url": ""}}] + _base_db_name = "rhodecode_test_db_backend" + std_env = {"RC_TEST": "0"} + def __init__(self, config_file, db_name=None, basetemp=None, connection_string=None): self.fixture_store = os.path.join(self._store, self._type) self.db_name = db_name or self._base_db_name self._base_ini_file = config_file - self.stderr = '' - self.stdout = '' + self.stderr = "" + self.stdout = "" self._basetemp = basetemp or tempfile.gettempdir() - self._repos_location = os.path.join(self._basetemp, 'rc_test_repos') - self._repos_hg_largefiles_store = largefiles_store(self._basetemp) - self._repos_git_lfs_store = lfs_store(self._basetemp) + self._repos_location = os.path.join(self._basetemp, "rc_test_repos") self.connection_string = connection_string @property @@ -118,8 +101,7 @@ class DBBackend(object): if not new_connection_string: new_connection_string = self.get_default_connection_string() else: - new_connection_string = new_connection_string.format( - db_name=self.db_name) + new_connection_string = new_connection_string.format(db_name=self.db_name) url_parts = url.make_url(new_connection_string) self._connection_string = new_connection_string self.user = url_parts.username @@ -127,73 +109,67 @@ class DBBackend(object): self.host = url_parts.host def get_default_connection_string(self): - raise NotImplementedError('default connection_string is required.') + raise NotImplementedError("default connection_string is required.") def execute(self, cmd, env=None, *args): """ Runs command on the system with given ``args``. """ - command = cmd + ' ' + ' '.join(args) - sys.stdout.write(f'CMD: {command}') + command = cmd + " " + " ".join(args) + sys.stdout.write(f"CMD: {command}") # Tell Python to use UTF-8 encoding out stdout _env = os.environ.copy() - _env['PYTHONIOENCODING'] = 'UTF-8' + _env["PYTHONIOENCODING"] = "UTF-8" _env.update(self.std_env) if env: _env.update(env) - self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env) + self.p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=_env) self.stdout, self.stderr = self.p.communicate() stdout_str = safe_str(self.stdout) - sys.stdout.write(f'COMMAND:{command}\n') + sys.stdout.write(f"COMMAND:{command}\n") sys.stdout.write(stdout_str) return self.stdout, self.stderr def assert_returncode_success(self): from rich import print as pprint + if not self.p.returncode == 0: pprint(safe_str(self.stderr)) - raise AssertionError(f'non 0 retcode:{self.p.returncode}') + raise AssertionError(f"non 0 retcode:{self.p.returncode}") def assert_correct_output(self, stdout, version): - assert b'UPGRADE FOR STEP %b COMPLETED' % safe_bytes(version) in stdout + assert b"UPGRADE FOR STEP %b COMPLETED" % safe_bytes(version) in stdout def setup_rhodecode_db(self, ini_params=None, env=None): if not ini_params: ini_params = self._base_ini_config ini_params.extend(self._db_url) - with TestINI(self._base_ini_file, ini_params, - self._type, destroy=True) as _ini_file: - + with TestINI(self._base_ini_file, ini_params, self._type, destroy=True) as _ini_file: if not os.path.isdir(self._repos_location): os.makedirs(self._repos_location) - if not os.path.isdir(self._repos_hg_largefiles_store): - os.makedirs(self._repos_hg_largefiles_store) - if not os.path.isdir(self._repos_git_lfs_store): - os.makedirs(self._repos_git_lfs_store) return self.execute( "rc-setup-app {0} --user=marcink " "--email=marcin@rhodeocode.com --password={1} " - "--repos={2} --force-yes".format( - _ini_file, 'qweqwe', self._repos_location), env=env) + "--repos={2} --force-yes".format(_ini_file, "qweqwe", self._repos_location), + env=env, + ) def upgrade_database(self, ini_params=None): if not ini_params: ini_params = self._base_ini_config ini_params.extend(self._db_url) - test_ini = TestINI( - self._base_ini_file, ini_params, self._type, destroy=True) + test_ini = TestINI(self._base_ini_file, ini_params, self._type, destroy=True) with test_ini as ini_file: if not os.path.isdir(self._repos_location): os.makedirs(self._repos_location) - return self.execute( - "rc-upgrade-db {0} --force-yes".format(ini_file)) + return self.execute("rc-upgrade-db {0} --force-yes".format(ini_file)) def setup_db(self): raise NotImplementedError @@ -206,7 +182,7 @@ class DBBackend(object): class EmptyDBBackend(DBBackend): - _type = '' + _type = "" def setup_db(self): pass @@ -222,21 +198,20 @@ class EmptyDBBackend(DBBackend): class SQLiteDBBackend(DBBackend): - _type = 'sqlite' + _type = "sqlite" def get_default_connection_string(self): - return 'sqlite:///{}/{}.sqlite'.format(self._basetemp, self.db_name) + return "sqlite:///{}/{}.sqlite".format(self._basetemp, self.db_name) def setup_db(self): # dump schema for tests # cp -v $TEST_DB_NAME - self._db_url = [{'app:main': { - 'sqlalchemy.db1.url': self.connection_string}}] + self._db_url = [{"app:main": {"sqlalchemy.db1.url": self.connection_string}}] def import_dump(self, dumpname): dump = os.path.join(self.fixture_store, dumpname) - target = os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self)) - return self.execute(f'cp -v {dump} {target}') + target = os.path.join(self._basetemp, "{0.db_name}.sqlite".format(self)) + return self.execute(f"cp -v {dump} {target}") def teardown_db(self): target_db = os.path.join(self._basetemp, self.db_name) @@ -244,39 +219,39 @@ class SQLiteDBBackend(DBBackend): class MySQLDBBackend(DBBackend): - _type = 'mysql' + _type = "mysql" def get_default_connection_string(self): - return 'mysql://root:qweqwe@127.0.0.1/{}'.format(self.db_name) + return "mysql://root:qweqwe@127.0.0.1/{}".format(self.db_name) def setup_db(self): # dump schema for tests # mysqldump -uroot -pqweqwe $TEST_DB_NAME - self._db_url = [{'app:main': { - 'sqlalchemy.db1.url': self.connection_string}}] - return self.execute("mysql -v -u{} -p{} -e 'create database '{}';'".format( - self.user, self.password, self.db_name)) + self._db_url = [{"app:main": {"sqlalchemy.db1.url": self.connection_string}}] + return self.execute( + "mysql -v -u{} -p{} -e 'create database '{}';'".format(self.user, self.password, self.db_name) + ) def import_dump(self, dumpname): dump = os.path.join(self.fixture_store, dumpname) - return self.execute("mysql -u{} -p{} {} < {}".format( - self.user, self.password, self.db_name, dump)) + return self.execute("mysql -u{} -p{} {} < {}".format(self.user, self.password, self.db_name, dump)) def teardown_db(self): - return self.execute("mysql -v -u{} -p{} -e 'drop database '{}';'".format( - self.user, self.password, self.db_name)) + return self.execute( + "mysql -v -u{} -p{} -e 'drop database '{}';'".format(self.user, self.password, self.db_name) + ) class PostgresDBBackend(DBBackend): - _type = 'postgres' + _type = "postgres" def get_default_connection_string(self): - return 'postgresql://postgres:qweqwe@localhost/{}'.format(self.db_name) + return "postgresql://postgres:qweqwe@localhost/{}".format(self.db_name) def setup_db(self): # dump schema for tests # pg_dump -U postgres -h localhost $TEST_DB_NAME - self._db_url = [{'app:main': {'sqlalchemy.db1.url': self.connection_string}}] + self._db_url = [{"app:main": {"sqlalchemy.db1.url": self.connection_string}}] cmd = f"PGPASSWORD={self.password} psql -U {self.user} -h localhost -c 'create database '{self.db_name}';'" return self.execute(cmd) diff --git a/rhodecode/tests/database/test_creation.py b/rhodecode/tests/database/test_creation.py index 23e13086..25ef7a98 100755 --- a/rhodecode/tests/database/test_creation.py +++ b/rhodecode/tests/database/test_creation.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify diff --git a/rhodecode/tests/database/test_migration.py b/rhodecode/tests/database/test_migration.py index f0d90748..9a199bdf 100644 --- a/rhodecode/tests/database/test_migration.py +++ b/rhodecode/tests/database/test_migration.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -21,33 +20,42 @@ import pytest @pytest.mark.dbs("postgres") -@pytest.mark.parametrize("dumpname", [ - '1.4.4.sql', - '1.5.0.sql', - '1.6.0.sql', - '1.6.0_no_repo_name_index.sql', -]) +@pytest.mark.parametrize( + "dumpname", + [ + "1.4.4.sql", + "1.5.0.sql", + "1.6.0.sql", + "1.6.0_no_repo_name_index.sql", + ], +) def test_migrate_postgres_db(db_backend, dumpname): _run_migration_test(db_backend, dumpname) @pytest.mark.dbs("sqlite") -@pytest.mark.parametrize("dumpname", [ - 'rhodecode.1.4.4.sqlite', - 'rhodecode.1.4.4_with_groups.sqlite', - 'rhodecode.1.4.4_with_ldap_active.sqlite', -]) +@pytest.mark.parametrize( + "dumpname", + [ + "rhodecode.1.4.4.sqlite", + "rhodecode.1.4.4_with_groups.sqlite", + "rhodecode.1.4.4_with_ldap_active.sqlite", + ], +) def test_migrate_sqlite_db(db_backend, dumpname): _run_migration_test(db_backend, dumpname) @pytest.mark.dbs("mysql") -@pytest.mark.parametrize("dumpname", [ - '1.4.4.sql', - '1.5.0.sql', - '1.6.0.sql', - '1.6.0_no_repo_name_index.sql', -]) +@pytest.mark.parametrize( + "dumpname", + [ + "1.4.4.sql", + "1.5.0.sql", + "1.6.0.sql", + "1.6.0_no_repo_name_index.sql", + ], +) def test_migrate_mysql_db(db_backend, dumpname): _run_migration_test(db_backend, dumpname) @@ -60,5 +68,5 @@ def _run_migration_test(db_backend, dumpname): db_backend.import_dump(dumpname) stdout, stderr = db_backend.upgrade_database() - db_backend.assert_correct_output(stdout+stderr, version='16') + db_backend.assert_correct_output(stdout + stderr, version="16") db_backend.assert_returncode_success() diff --git a/rhodecode/tests/fixture_mods/fixture_pyramid.py b/rhodecode/tests/fixture_mods/fixture_pyramid.py deleted file mode 100644 index e90d7d5b..00000000 --- a/rhodecode/tests/fixture_mods/fixture_pyramid.py +++ /dev/null @@ -1,226 +0,0 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3 -# (only), as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# 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 pytest - -from rhodecode.lib.config_utils import get_app_config -from rhodecode.tests.fixture import TestINI -from rhodecode.tests import TESTS_TMP_PATH -from rhodecode.tests.server_utils import RcVCSServer - - -@pytest.fixture(scope='session') -def vcsserver(request, vcsserver_port, vcsserver_factory): - """ - Session scope VCSServer. - - Tests which need the VCSServer have to rely on this fixture in order - to ensure it will be running. - - For specific needs, the fixture vcsserver_factory can be used. It allows to - adjust the configuration file for the test run. - - Command line args: - - --without-vcsserver: Allows to switch this fixture off. You have to - manually start the server. - - --vcsserver-port: Will expect the VCSServer to listen on this port. - """ - - if not request.config.getoption('with_vcsserver'): - return None - - return vcsserver_factory( - request, vcsserver_port=vcsserver_port) - - -@pytest.fixture(scope='session') -def vcsserver_factory(tmpdir_factory): - """ - Use this if you need a running vcsserver with a special configuration. - """ - - def factory(request, overrides=(), vcsserver_port=None, - log_file=None, workers='3'): - - if vcsserver_port is None: - vcsserver_port = get_available_port() - - overrides = list(overrides) - overrides.append({'server:main': {'port': vcsserver_port}}) - - option_name = 'vcsserver_config_http' - override_option_name = 'vcsserver_config_override' - config_file = get_config( - request.config, option_name=option_name, - override_option_name=override_option_name, overrides=overrides, - basetemp=tmpdir_factory.getbasetemp().strpath, - prefix='test_vcs_') - - server = RcVCSServer(config_file, log_file, workers) - server.start() - - @request.addfinalizer - def cleanup(): - server.shutdown() - - server.wait_until_ready() - return server - - return factory - - -def _use_log_level(config): - level = config.getoption('test_loglevel') or 'critical' - return level.upper() - - -@pytest.fixture(scope='session') -def ini_config(request, tmpdir_factory, rcserver_port, vcsserver_port): - option_name = 'pyramid_config' - log_level = _use_log_level(request.config) - - overrides = [ - {'server:main': {'port': rcserver_port}}, - {'app:main': { - 'cache_dir': '%(here)s/rc-tests/rc_data', - 'vcs.server': f'localhost:{vcsserver_port}', - # johbo: We will always start the VCSServer on our own based on the - # fixtures of the test cases. For the test run it must always be - # off in the INI file. - 'vcs.start_server': 'false', - - 'vcs.server.protocol': 'http', - 'vcs.scm_app_implementation': 'http', - 'vcs.svn.proxy.enabled': 'true', - 'vcs.hooks.protocol.v2': 'celery', - 'vcs.hooks.host': '*', - 'repo_store.path': TESTS_TMP_PATH, - 'app.service_api.token': 'service_secret_token', - }}, - - {'handler_console': { - 'class': 'StreamHandler', - 'args': '(sys.stderr,)', - 'level': log_level, - }}, - - ] - - filename = get_config( - request.config, option_name=option_name, - override_option_name='{}_override'.format(option_name), - overrides=overrides, - basetemp=tmpdir_factory.getbasetemp().strpath, - prefix='test_rce_') - return filename - - -@pytest.fixture(scope='session') -def ini_settings(ini_config): - ini_path = ini_config - return get_app_config(ini_path) - - -def get_available_port(min_port=40000, max_port=55555): - from rhodecode.lib.utils2 import get_available_port as _get_port - return _get_port(min_port, max_port) - - -@pytest.fixture(scope='session') -def rcserver_port(request): - port = get_available_port() - print(f'Using rhodecode port {port}') - return port - - -@pytest.fixture(scope='session') -def vcsserver_port(request): - port = request.config.getoption('--vcsserver-port') - if port is None: - port = get_available_port() - print(f'Using vcsserver port {port}') - return port - - -@pytest.fixture(scope='session') -def available_port_factory() -> get_available_port: - """ - Returns a callable which returns free port numbers. - """ - return get_available_port - - -@pytest.fixture() -def available_port(available_port_factory): - """ - Gives you one free port for the current test. - - Uses "available_port_factory" to retrieve the port. - """ - return available_port_factory() - - -@pytest.fixture(scope='session') -def testini_factory(tmpdir_factory, ini_config): - """ - Factory to create an INI file based on TestINI. - - It will make sure to place the INI file in the correct directory. - """ - basetemp = tmpdir_factory.getbasetemp().strpath - return TestIniFactory(basetemp, ini_config) - - -class TestIniFactory(object): - - def __init__(self, basetemp, template_ini): - self._basetemp = basetemp - self._template_ini = template_ini - - def __call__(self, ini_params, new_file_prefix='test'): - ini_file = TestINI( - self._template_ini, ini_params=ini_params, - new_file_prefix=new_file_prefix, dir=self._basetemp) - result = ini_file.create() - return result - - -def get_config( - config, option_name, override_option_name, overrides=None, - basetemp=None, prefix='test'): - """ - Find a configuration file and apply overrides for the given `prefix`. - """ - config_file = ( - config.getoption(option_name) or config.getini(option_name)) - if not config_file: - pytest.exit( - "Configuration error, could not extract {}.".format(option_name)) - - overrides = overrides or [] - config_override = config.getoption(override_option_name) - if config_override: - overrides.append(config_override) - temp_ini_file = TestINI( - config_file, ini_params=overrides, new_file_prefix=prefix, - dir=basetemp) - - return temp_ini_file.create() diff --git a/rhodecode/tests/fixture_mods/__init__.py b/rhodecode/tests/fixtures/__init__.py similarity index 100% rename from rhodecode/tests/fixture_mods/__init__.py rename to rhodecode/tests/fixtures/__init__.py diff --git a/rhodecode/tests/fixtures/diff_with_diff_data.diff b/rhodecode/tests/fixtures/diff_fixtures/diff_with_diff_data.diff similarity index 100% rename from rhodecode/tests/fixtures/diff_with_diff_data.diff rename to rhodecode/tests/fixtures/diff_fixtures/diff_with_diff_data.diff diff --git a/rhodecode/tests/fixtures/git_diff_binary_and_normal.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_and_normal.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_binary_and_normal.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_and_normal.diff diff --git a/rhodecode/tests/fixtures/git_diff_binary_special_files.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_special_files.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_binary_special_files.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_special_files.diff diff --git a/rhodecode/tests/fixtures/git_diff_binary_special_files_2.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_special_files_2.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_binary_special_files_2.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_special_files_2.diff diff --git a/rhodecode/tests/fixtures/git_diff_chmod.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_chmod.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_chmod.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_chmod.diff diff --git a/rhodecode/tests/fixtures/git_diff_js_chars.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_js_chars.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_js_chars.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_js_chars.diff diff --git a/rhodecode/tests/fixtures/git_diff_mod_single_binary_file.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_mod_single_binary_file.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_mod_single_binary_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_mod_single_binary_file.diff diff --git a/rhodecode/tests/fixtures/git_diff_rename_file.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_rename_file.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_rename_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_rename_file.diff diff --git a/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff b/rhodecode/tests/fixtures/diff_fixtures/git_diff_rename_file_with_spaces.diff similarity index 100% rename from rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff rename to rhodecode/tests/fixtures/diff_fixtures/git_diff_rename_file_with_spaces.diff diff --git a/rhodecode/tests/fixtures/git_node_history_response.json b/rhodecode/tests/fixtures/diff_fixtures/git_node_history_response.json similarity index 100% rename from rhodecode/tests/fixtures/git_node_history_response.json rename to rhodecode/tests/fixtures/diff_fixtures/git_node_history_response.json diff --git a/rhodecode/tests/fixtures/hg_diff_add_single_binary_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_add_single_binary_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_add_single_binary_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_add_single_binary_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_binary_and_normal.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_binary_and_normal.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_binary_and_normal.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_binary_and_normal.diff diff --git a/rhodecode/tests/fixtures/hg_diff_chmod.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_chmod.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_chmod.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_chmod.diff diff --git a/rhodecode/tests/fixtures/hg_diff_chmod_and_mod_single_binary_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_chmod_and_mod_single_binary_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_chmod_and_mod_single_binary_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_chmod_and_mod_single_binary_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_copy_and_chmod_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_and_chmod_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_copy_and_chmod_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_and_chmod_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_copy_and_modify_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_and_modify_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_copy_and_modify_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_and_modify_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_copy_chmod_and_edit_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_chmod_and_edit_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_copy_chmod_and_edit_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_chmod_and_edit_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_copy_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_copy_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_file_with_spaces.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_file_with_spaces.diff diff --git a/rhodecode/tests/fixtures/hg_diff_del_single_binary_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_del_single_binary_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_del_single_binary_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_del_single_binary_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_double_file_change_double_newline.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_double_file_change_double_newline.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_double_file_change_double_newline.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_double_file_change_double_newline.diff diff --git a/rhodecode/tests/fixtures/hg_diff_double_file_change_newline.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_double_file_change_newline.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_double_file_change_newline.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_double_file_change_newline.diff diff --git a/rhodecode/tests/fixtures/hg_diff_four_file_change_newline.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_four_file_change_newline.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_four_file_change_newline.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_four_file_change_newline.diff diff --git a/rhodecode/tests/fixtures/hg_diff_mixed_filename_encodings.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_mixed_filename_encodings.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_mixed_filename_encodings.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mixed_filename_encodings.diff diff --git a/rhodecode/tests/fixtures/hg_diff_mod_file_and_rename.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_file_and_rename.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_mod_file_and_rename.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_file_and_rename.diff diff --git a/rhodecode/tests/fixtures/hg_diff_mod_single_binary_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_single_binary_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_mod_single_binary_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_single_binary_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_mod_single_file_and_rename_and_chmod.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_single_file_and_rename_and_chmod.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_mod_single_file_and_rename_and_chmod.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_single_file_and_rename_and_chmod.diff diff --git a/rhodecode/tests/fixtures/hg_diff_no_newline.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_no_newline.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_no_newline.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_no_newline.diff diff --git a/rhodecode/tests/fixtures/hg_diff_rename_and_chmod_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_and_chmod_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_rename_and_chmod_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_and_chmod_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_rename_file.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_file.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_rename_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_file.diff diff --git a/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_file_with_spaces.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_file_with_spaces.diff diff --git a/rhodecode/tests/fixtures/hg_diff_single_file_change_newline.diff b/rhodecode/tests/fixtures/diff_fixtures/hg_diff_single_file_change_newline.diff similarity index 100% rename from rhodecode/tests/fixtures/hg_diff_single_file_change_newline.diff rename to rhodecode/tests/fixtures/diff_fixtures/hg_diff_single_file_change_newline.diff diff --git a/rhodecode/tests/fixtures/hg_node_history_response.json b/rhodecode/tests/fixtures/diff_fixtures/hg_node_history_response.json similarity index 100% rename from rhodecode/tests/fixtures/hg_node_history_response.json rename to rhodecode/tests/fixtures/diff_fixtures/hg_node_history_response.json diff --git a/rhodecode/tests/fixtures/journal_dump.csv b/rhodecode/tests/fixtures/diff_fixtures/journal_dump.csv similarity index 100% rename from rhodecode/tests/fixtures/journal_dump.csv rename to rhodecode/tests/fixtures/diff_fixtures/journal_dump.csv diff --git a/rhodecode/tests/fixtures/large_diff.diff b/rhodecode/tests/fixtures/diff_fixtures/large_diff.diff similarity index 100% rename from rhodecode/tests/fixtures/large_diff.diff rename to rhodecode/tests/fixtures/diff_fixtures/large_diff.diff diff --git a/rhodecode/tests/fixtures/svn_diff_binary_add_file.diff b/rhodecode/tests/fixtures/diff_fixtures/svn_diff_binary_add_file.diff similarity index 100% rename from rhodecode/tests/fixtures/svn_diff_binary_add_file.diff rename to rhodecode/tests/fixtures/diff_fixtures/svn_diff_binary_add_file.diff diff --git a/rhodecode/tests/fixtures/svn_diff_multiple_changes.diff b/rhodecode/tests/fixtures/diff_fixtures/svn_diff_multiple_changes.diff similarity index 100% rename from rhodecode/tests/fixtures/svn_diff_multiple_changes.diff rename to rhodecode/tests/fixtures/diff_fixtures/svn_diff_multiple_changes.diff diff --git a/rhodecode/tests/fixtures/svn_node_history_branches.json b/rhodecode/tests/fixtures/diff_fixtures/svn_node_history_branches.json similarity index 100% rename from rhodecode/tests/fixtures/svn_node_history_branches.json rename to rhodecode/tests/fixtures/diff_fixtures/svn_node_history_branches.json diff --git a/rhodecode/tests/fixtures/svn_node_history_response.json b/rhodecode/tests/fixtures/diff_fixtures/svn_node_history_response.json similarity index 100% rename from rhodecode/tests/fixtures/svn_node_history_response.json rename to rhodecode/tests/fixtures/diff_fixtures/svn_node_history_response.json diff --git a/rhodecode/tests/fixtures/fixture_pyramid.py b/rhodecode/tests/fixtures/fixture_pyramid.py new file mode 100644 index 00000000..26935325 --- /dev/null +++ b/rhodecode/tests/fixtures/fixture_pyramid.py @@ -0,0 +1,308 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 pytest + +from rhodecode.lib.config_utils import get_app_config +from rhodecode.tests.fixtures.rc_fixture import TestINI +from rhodecode.tests import TESTS_TMP_PATH +from rhodecode.tests.server_utils import RcVCSServer +from rhodecode.tests.server_utils import RcWebServer +from rhodecode.tests.server_utils import CeleryServer + + +@pytest.fixture(scope="session") +def vcsserver_factory(): + """ + Use this if you need a running vcsserver with a special configuration. + """ + + def factory(request, store_dir, overrides=(), config_file=None, port=None, log_file=None, workers="3", env=None, info_prefix=""): + env = env or {"RC_NO_TEST_ENV": "1"} + vcsserver_port = port + if port is None: + vcsserver_port = get_available_port() + + overrides = list(overrides) + overrides.append({"server:main": {"port": vcsserver_port}}) + + if getattr(request, 'param', None): + config_overrides = [request.param] + overrides.extend(config_overrides) + + option_name = "vcsserver_config" + override_option_name = None + if not config_file: + config_file = get_config( + request.config, + option_name=option_name, + override_option_name=override_option_name, + overrides=overrides, + basetemp=store_dir, + prefix=f"{info_prefix}test_vcsserver_ini_", + ) + server = RcVCSServer(config_file, log_file, workers, env=env, info_prefix=info_prefix) + server.start() + + @request.addfinalizer + def cleanup(): + server.shutdown() + + server.wait_until_ready() + return server + + return factory + + +@pytest.fixture(scope="session") +def rhodecode_factory(): + def factory(request, store_dir, overrides=(), config_file=None, port=None, log_file=None, workers="3", env=None, info_prefix=""): + env = env or {"RC_NO_TEST_ENV": "1"} + rhodecode_port = port + if port is None: + rhodecode_port = get_available_port() + + overrides = list(overrides) + overrides.append({"server:main": {"port": rhodecode_port}}) + overrides.append({"app:main": {"use_celery": "true"}}) + overrides.append({"app:main": {"celery.task_always_eager": "false"}}) + + if getattr(request, 'param', None): + config_overrides = [request.param] + overrides.extend(config_overrides) + + + option_name = "rhodecode_config" + override_option_name = None + if not config_file: + config_file = get_config( + request.config, + option_name=option_name, + override_option_name=override_option_name, + overrides=overrides, + basetemp=store_dir, + prefix=f"{info_prefix}test_rhodecode_ini", + ) + + server = RcWebServer(config_file, log_file, workers, env, info_prefix=info_prefix) + server.start() + + @request.addfinalizer + def cleanup(): + server.shutdown() + + server.wait_until_ready() + return server + + return factory + + +@pytest.fixture(scope="session") +def celery_factory(): + def factory(request, store_dir, overrides=(), config_file=None, port=None, log_file=None, workers="3", env=None, info_prefix=""): + env = env or {"RC_NO_TEST_ENV": "1"} + rhodecode_port = port + + overrides = list(overrides) + overrides.append({"app:main": {"use_celery": "true"}}) + overrides.append({"app:main": {"celery.task_always_eager": "false"}}) + config_overrides = None + + if getattr(request, 'param', None): + config_overrides = [request.param] + overrides.extend(config_overrides) + + option_name = "celery_config" + override_option_name = None + + if not config_file: + config_file = get_config( + request.config, + option_name=option_name, + override_option_name=override_option_name, + overrides=overrides, + basetemp=store_dir, + prefix=f"{info_prefix}test_celery_ini_", + ) + + server = CeleryServer(config_file, log_file, workers, env, info_prefix=info_prefix) + server.start() + + @request.addfinalizer + def cleanup(): + server.shutdown() + + server.wait_until_ready() + return server + + return factory + + +def _use_log_level(config): + level = config.getoption("test_loglevel") or "critical" + return level.upper() + + +def _ini_config_factory(request, base_dir, rcserver_port, vcsserver_port): + option_name = "pyramid_config" + log_level = _use_log_level(request.config) + + overrides = [ + {"server:main": {"port": rcserver_port}}, + { + "app:main": { + #'cache_dir': '%(here)s/rc-tests/rc_data', + "vcs.server": f"localhost:{vcsserver_port}", + # johbo: We will always start the VCSServer on our own based on the + # fixtures of the test cases. For the test run it must always be + # off in the INI file. + "vcs.start_server": "false", + "vcs.server.protocol": "http", + "vcs.scm_app_implementation": "http", + "vcs.svn.proxy.enabled": "true", + "vcs.hooks.protocol.v2": "celery", + "vcs.hooks.host": "*", + "repo_store.path": TESTS_TMP_PATH, + "app.service_api.token": "service_secret_token", + } + }, + { + "handler_console": { + "class": "StreamHandler", + "args": "(sys.stderr,)", + "level": log_level, + } + }, + ] + + filename = get_config( + request.config, + option_name=option_name, + override_option_name=f"{option_name}_override", + overrides=overrides, + basetemp=base_dir, + prefix="test_rce_", + ) + return filename + + +@pytest.fixture(scope="session") +def ini_config(request, tmpdir_factory, rcserver_port, vcsserver_port): + base_dir = tmpdir_factory.getbasetemp().strpath + return _ini_config_factory(request, base_dir, rcserver_port, vcsserver_port) + + +@pytest.fixture(scope="session") +def ini_config_factory(request, tmpdir_factory, rcserver_port, vcsserver_port): + def _factory(ini_config_basedir, overrides=()): + return _ini_config_factory(request, ini_config_basedir, rcserver_port, vcsserver_port) + + return _factory + + +@pytest.fixture(scope="session") +def ini_settings(ini_config): + ini_path = ini_config + return get_app_config(ini_path) + + +def get_available_port(min_port=40000, max_port=55555): + from rhodecode.lib.utils2 import get_available_port as _get_port + + return _get_port(min_port, max_port) + + +@pytest.fixture(scope="session") +def rcserver_port(request): + port = get_available_port() + return port + + +@pytest.fixture(scope="session") +def vcsserver_port(request): + port = request.config.getoption("--vcsserver-port") + if port is None: + port = get_available_port() + return port + + +@pytest.fixture(scope="session") +def available_port_factory() -> get_available_port: + """ + Returns a callable which returns free port numbers. + """ + return get_available_port + + +@pytest.fixture() +def available_port(available_port_factory): + """ + Gives you one free port for the current test. + + Uses "available_port_factory" to retrieve the port. + """ + return available_port_factory() + + +@pytest.fixture(scope="session") +def testini_factory(tmpdir_factory, ini_config): + """ + Factory to create an INI file based on TestINI. + + It will make sure to place the INI file in the correct directory. + """ + basetemp = tmpdir_factory.getbasetemp().strpath + return TestIniFactory(basetemp, ini_config) + + +class TestIniFactory(object): + def __init__(self, ini_store_dir, template_ini): + self._ini_store_dir = ini_store_dir + self._template_ini = template_ini + + def __call__(self, ini_params, new_file_prefix="test"): + ini_file = TestINI( + self._template_ini, ini_params=ini_params, new_file_prefix=new_file_prefix, dir=self._ini_store_dir + ) + result = ini_file.create() + return result + + +def get_config(config, option_name, override_option_name, overrides=None, basetemp=None, prefix="test"): + """ + Find a configuration file and apply overrides for the given `prefix`. + """ + try: + config_file = config.getoption(option_name) + except ValueError: + config_file = None + + if not config_file: + config_file = config.getini(option_name) + + if not config_file: + pytest.exit(f"Configuration error, could not extract {option_name}.") + + overrides = overrides or [] + if override_option_name: + config_override = config.getoption(override_option_name) + if config_override: + overrides.append(config_override) + temp_ini_file = TestINI(config_file, ini_params=overrides, new_file_prefix=prefix, dir=basetemp) + + return temp_ini_file.create() diff --git a/rhodecode/tests/fixture_mods/fixture_utils.py b/rhodecode/tests/fixtures/fixture_utils.py similarity index 69% rename from rhodecode/tests/fixture_mods/fixture_utils.py rename to rhodecode/tests/fixtures/fixture_utils.py index 9b95ceeb..5caedd3f 100644 --- a/rhodecode/tests/fixture_mods/fixture_utils.py +++ b/rhodecode/tests/fixtures/fixture_utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -30,6 +29,7 @@ import uuid import dateutil.tz import logging import functools +import textwrap import mock import pyramid.testing @@ -43,8 +43,17 @@ import rhodecode.lib from rhodecode.model.changeset_status import ChangesetStatusModel from rhodecode.model.comment import CommentsModel from rhodecode.model.db import ( - PullRequest, PullRequestReviewers, Repository, RhodeCodeSetting, ChangesetStatus, - RepoGroup, UserGroup, RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi) + PullRequest, + PullRequestReviewers, + Repository, + RhodeCodeSetting, + ChangesetStatus, + RepoGroup, + UserGroup, + RepoRhodeCodeUi, + RepoRhodeCodeSetting, + RhodeCodeUi, +) from rhodecode.model.meta import Session from rhodecode.model.pull_request import PullRequestModel from rhodecode.model.repo import RepoModel @@ -60,12 +69,20 @@ from rhodecode.lib.str_utils import safe_bytes from rhodecode.lib.hash_utils import sha1_safe from rhodecode.lib.vcs.backends import get_backend from rhodecode.lib.vcs.nodes import FileNode +from rhodecode.lib.base import bootstrap_config from rhodecode.tests import ( - login_user_session, get_new_dir, utils, TESTS_TMP_PATH, - TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR2_LOGIN, - TEST_USER_REGULAR_PASS) -from rhodecode.tests.utils import CustomTestApp, set_anonymous_access -from rhodecode.tests.fixture import Fixture + login_user_session, + get_new_dir, + utils, + TESTS_TMP_PATH, + TEST_USER_ADMIN_LOGIN, + TEST_USER_REGULAR_LOGIN, + TEST_USER_REGULAR2_LOGIN, + TEST_USER_REGULAR_PASS, + console_printer, +) +from rhodecode.tests.utils import set_anonymous_access +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.config import utils as config_utils log = logging.getLogger(__name__) @@ -76,36 +93,7 @@ def cmp(a, b): return (a > b) - (a < b) -@pytest.fixture(scope='session', autouse=True) -def activate_example_rcextensions(request): - """ - Patch in an example rcextensions module which verifies passed in kwargs. - """ - from rhodecode.config import rcextensions - - old_extensions = rhodecode.EXTENSIONS - rhodecode.EXTENSIONS = rcextensions - rhodecode.EXTENSIONS.calls = collections.defaultdict(list) - - @request.addfinalizer - def cleanup(): - rhodecode.EXTENSIONS = old_extensions - - -@pytest.fixture() -def capture_rcextensions(): - """ - Returns the recorded calls to entry points in rcextensions. - """ - calls = rhodecode.EXTENSIONS.calls - calls.clear() - # Note: At this moment, it is still the empty dict, but that will - # be filled during the test run and since it is a reference this - # is enough to make it work. - return calls - - -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def http_environ_session(): """ Allow to use "http_environ" in session scope. @@ -117,7 +105,31 @@ def plain_http_host_stub(): """ Value of HTTP_HOST in the test run. """ - return 'example.com:80' + return "example.com:80" + + +def plain_config_stub(request, request_stub): + """ + Set up pyramid.testing and return the Configurator. + """ + + config = bootstrap_config(request=request_stub) + + @request.addfinalizer + def cleanup(): + pyramid.testing.tearDown() + + return config + + +def plain_request_stub(): + """ + Stub request object. + """ + from rhodecode.lib.base import bootstrap_request + + _request = bootstrap_request(scheme="https") + return _request @pytest.fixture() @@ -132,7 +144,7 @@ def plain_http_host_only_stub(): """ Value of HTTP_HOST in the test run. """ - return plain_http_host_stub().split(':')[0] + return plain_http_host_stub().split(":")[0] @pytest.fixture() @@ -147,33 +159,21 @@ def plain_http_environ(): """ HTTP extra environ keys. - User by the test application and as well for setting up the pylons + Used by the test application and as well for setting up the pylons environment. In the case of the fixture "app" it should be possible to override this for a specific test case. """ return { - 'SERVER_NAME': plain_http_host_only_stub(), - 'SERVER_PORT': plain_http_host_stub().split(':')[1], - 'HTTP_HOST': plain_http_host_stub(), - 'HTTP_USER_AGENT': 'rc-test-agent', - 'REQUEST_METHOD': 'GET' + "SERVER_NAME": plain_http_host_only_stub(), + "SERVER_PORT": plain_http_host_stub().split(":")[1], + "HTTP_HOST": plain_http_host_stub(), + "HTTP_USER_AGENT": "rc-test-agent", + "REQUEST_METHOD": "GET", } -@pytest.fixture() -def http_environ(): - """ - HTTP extra environ keys. - - User by the test application and as well for setting up the pylons - environment. In the case of the fixture "app" it should be possible - to override this for a specific test case. - """ - return plain_http_environ() - - -@pytest.fixture(scope='session') -def baseapp(ini_config, vcsserver, http_environ_session): +@pytest.fixture(scope="session") +def baseapp(request, ini_config, http_environ_session, available_port_factory, vcsserver_factory, celery_factory): from rhodecode.lib.config_utils import get_app_config from rhodecode.config.middleware import make_pyramid_app @@ -181,22 +181,41 @@ def baseapp(ini_config, vcsserver, http_environ_session): pyramid.paster.setup_logging(ini_config) settings = get_app_config(ini_config) - app = make_pyramid_app({'__file__': ini_config}, **settings) + store_dir = os.path.dirname(ini_config) - return app + # start vcsserver + _vcsserver_port = available_port_factory() + vcsserver_instance = vcsserver_factory( + request, + store_dir=store_dir, + port=_vcsserver_port, + info_prefix="base-app-" + ) + + settings["vcs.server"] = vcsserver_instance.bind_addr + + # we skip setting store_dir for baseapp, it's internally set via testing rhodecode.ini + # settings['repo_store.path'] = str(store_dir) + console_printer(f' :warning: [green]pytest-setup[/green] Starting base pyramid-app: {ini_config}') + pyramid_baseapp = make_pyramid_app({"__file__": ini_config}, **settings) + + # start celery + celery_factory( + request, + store_dir=store_dir, + port=None, + info_prefix="base-app-", + overrides=( + {'handler_console': {'level': 'DEBUG'}}, + {'app:main': {'vcs.server': vcsserver_instance.bind_addr}}, + {'app:main': {'repo_store.path': store_dir}} + ) + ) + + return pyramid_baseapp -@pytest.fixture(scope='function') -def app(request, config_stub, baseapp, http_environ): - app = CustomTestApp( - baseapp, - extra_environ=http_environ) - if request.cls: - request.cls.app = app - return app - - -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def app_settings(baseapp, ini_config): """ Settings dictionary used to create the app. @@ -207,19 +226,19 @@ def app_settings(baseapp, ini_config): return baseapp.config.get_settings() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def db_connection(ini_settings): # Initialize the database connection. config_utils.initialize_database(ini_settings) -LoginData = collections.namedtuple('LoginData', ('csrf_token', 'user')) +LoginData = collections.namedtuple("LoginData", ("csrf_token", "user")) def _autologin_user(app, *args): session = login_user_session(app, *args) csrf_token = rhodecode.lib.auth.get_csrf_token(session) - return LoginData(csrf_token, session['rhodecode_user']) + return LoginData(csrf_token, session["rhodecode_user"]) @pytest.fixture() @@ -235,18 +254,17 @@ def autologin_regular_user(app): """ Utility fixture which makes sure that the regular user is logged in """ - return _autologin_user( - app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) + return _autologin_user(app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def csrf_token(request, autologin_user): return autologin_user.csrf_token -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def xhr_header(request): - return {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'} + return {"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"} @pytest.fixture() @@ -257,18 +275,18 @@ def real_crypto_backend(monkeypatch): During the test run the crypto backend is replaced with a faster implementation based on the MD5 algorithm. """ - monkeypatch.setattr(rhodecode, 'is_test', False) + monkeypatch.setattr(rhodecode, "is_test", False) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def index_location(request, baseapp): - index_location = baseapp.config.get_settings()['search.location'] + index_location = baseapp.config.get_settings()["search.location"] if request.cls: request.cls.index_location = index_location return index_location -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def tests_tmp_path(request): """ Create temporary directory to be used during the test session. @@ -276,7 +294,8 @@ def tests_tmp_path(request): if not os.path.exists(TESTS_TMP_PATH): os.makedirs(TESTS_TMP_PATH) - if not request.config.getoption('--keep-tmp-path'): + if not request.config.getoption("--keep-tmp-path"): + @request.addfinalizer def remove_tmp_path(): shutil.rmtree(TESTS_TMP_PATH) @@ -291,7 +310,7 @@ def test_repo_group(request): usage automatically """ fixture = Fixture() - repogroupid = 'test_repo_group_%s' % str(time.time()).replace('.', '') + repogroupid = "test_repo_group_%s" % str(time.time()).replace(".", "") repo_group = fixture.create_repo_group(repogroupid) def _cleanup(): @@ -308,7 +327,7 @@ def test_user_group(request): usage automatically """ fixture = Fixture() - usergroupid = 'test_user_group_%s' % str(time.time()).replace('.', '') + usergroupid = "test_user_group_%s" % str(time.time()).replace(".", "") user_group = fixture.create_user_group(usergroupid) def _cleanup(): @@ -318,7 +337,7 @@ def test_user_group(request): return user_group -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def test_repo(request): container = TestRepoContainer() request.addfinalizer(container._cleanup) @@ -340,9 +359,9 @@ class TestRepoContainer(object): """ dump_extractors = { - 'git': utils.extract_git_repo_from_dump, - 'hg': utils.extract_hg_repo_from_dump, - 'svn': utils.extract_svn_repo_from_dump, + "git": utils.extract_git_repo_from_dump, + "hg": utils.extract_hg_repo_from_dump, + "svn": utils.extract_svn_repo_from_dump, } def __init__(self): @@ -358,7 +377,7 @@ class TestRepoContainer(object): return Repository.get(self._repos[key]) def _create_repo(self, dump_name, backend_alias, config): - repo_name = f'{backend_alias}-{dump_name}' + repo_name = f"{backend_alias}-{dump_name}" backend = get_backend(backend_alias) dump_extractor = self.dump_extractors[backend_alias] repo_path = dump_extractor(dump_name, repo_name) @@ -375,19 +394,17 @@ class TestRepoContainer(object): self._fixture.destroy_repo(repo_name) -def backend_base(request, backend_alias, baseapp, test_repo): - if backend_alias not in request.config.getoption('--backends'): - pytest.skip("Backend %s not selected." % (backend_alias, )) +def backend_base(request, backend_alias, test_repo): + if backend_alias not in request.config.getoption("--backends"): + pytest.skip(f"Backend {backend_alias} not selected.") utils.check_xfail_backends(request.node, backend_alias) utils.check_skip_backends(request.node, backend_alias) - repo_name = 'vcs_test_%s' % (backend_alias, ) + repo_name = "vcs_test_%s" % (backend_alias,) backend = Backend( - alias=backend_alias, - repo_name=repo_name, - test_name=request.node.name, - test_repo_container=test_repo) + alias=backend_alias, repo_name=repo_name, test_name=request.node.name, test_repo_container=test_repo + ) request.addfinalizer(backend.cleanup) return backend @@ -404,22 +421,22 @@ def backend(request, backend_alias, baseapp, test_repo): for specific backends. This is intended as a utility for incremental development of a new backend implementation. """ - return backend_base(request, backend_alias, baseapp, test_repo) + return backend_base(request, backend_alias, test_repo) @pytest.fixture() def backend_git(request, baseapp, test_repo): - return backend_base(request, 'git', baseapp, test_repo) + return backend_base(request, "git", test_repo) @pytest.fixture() def backend_hg(request, baseapp, test_repo): - return backend_base(request, 'hg', baseapp, test_repo) + return backend_base(request, "hg", test_repo) @pytest.fixture() def backend_svn(request, baseapp, test_repo): - return backend_base(request, 'svn', baseapp, test_repo) + return backend_base(request, "svn", test_repo) @pytest.fixture() @@ -467,9 +484,9 @@ class Backend(object): session. """ - invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+') + invalid_repo_name = re.compile(r"[^0-9a-zA-Z]+") _master_repo = None - _master_repo_path = '' + _master_repo_path = "" _commit_ids = {} def __init__(self, alias, repo_name, test_name, test_repo_container): @@ -500,6 +517,7 @@ class Backend(object): last repo which has been created with `create_repo`. """ from rhodecode.model.db import Repository + return Repository.get_by_repo_name(self.repo_name) @property @@ -517,9 +535,7 @@ class Backend(object): which can serve as the base to create a new commit on top of it. """ vcsrepo = self.repo.scm_instance() - head_id = ( - vcsrepo.DEFAULT_BRANCH_NAME or - vcsrepo.commit_ids[-1]) + head_id = vcsrepo.DEFAULT_BRANCH_NAME or vcsrepo.commit_ids[-1] return head_id @property @@ -543,9 +559,7 @@ class Backend(object): return self._commit_ids - def create_repo( - self, commits=None, number_of_commits=0, heads=None, - name_suffix='', bare=False, **kwargs): + def create_repo(self, commits=None, number_of_commits=0, heads=None, name_suffix="", bare=False, **kwargs): """ Create a repository and record it for later cleanup. @@ -559,13 +573,10 @@ class Backend(object): :param bare: set a repo as bare (no checkout) """ self.repo_name = self._next_repo_name() + name_suffix - repo = self._fixture.create_repo( - self.repo_name, repo_type=self.alias, bare=bare, **kwargs) + repo = self._fixture.create_repo(self.repo_name, repo_type=self.alias, bare=bare, **kwargs) self._cleanup_repos.append(repo.repo_name) - commits = commits or [ - {'message': f'Commit {x} of {self.repo_name}'} - for x in range(number_of_commits)] + commits = commits or [{"message": f"Commit {x} of {self.repo_name}"} for x in range(number_of_commits)] vcs_repo = repo.scm_instance() vcs_repo.count() self._add_commits_to_repo(vcs_repo, commits) @@ -579,7 +590,7 @@ class Backend(object): Make sure that repo contains all commits mentioned in `heads` """ vcsrepo = repo.scm_instance() - vcsrepo.config.clear_section('hooks') + vcsrepo.config.clear_section("hooks") commit_ids = [self._commit_ids[h] for h in heads] if do_fetch: vcsrepo.fetch(self._master_repo_path, commit_ids=commit_ids) @@ -592,21 +603,22 @@ class Backend(object): self._cleanup_repos.append(self.repo_name) return repo - def new_repo_name(self, suffix=''): + def new_repo_name(self, suffix=""): self.repo_name = self._next_repo_name() + suffix self._cleanup_repos.append(self.repo_name) return self.repo_name def _next_repo_name(self): - return "%s_%s" % ( - self.invalid_repo_name.sub('_', self._test_name), len(self._cleanup_repos)) + return "%s_%s" % (self.invalid_repo_name.sub("_", self._test_name), len(self._cleanup_repos)) - def ensure_file(self, filename, content=b'Test content\n'): + def ensure_file(self, filename, content=b"Test content\n"): assert self._cleanup_repos, "Avoid writing into vcs_test repos" commits = [ - {'added': [ - FileNode(filename, content=content), - ]}, + { + "added": [ + FileNode(filename, content=content), + ] + }, ] self._add_commits_to_repo(self.repo.scm_instance(), commits) @@ -627,11 +639,11 @@ class Backend(object): self._commit_ids = commit_ids # Creating refs for Git to allow fetching them from remote repository - if self.alias == 'git': + if self.alias == "git": refs = {} for message in self._commit_ids: - cleanup_message = message.replace(' ', '') - ref_name = f'refs/test-refs/{cleanup_message}' + cleanup_message = message.replace(" ", "") + ref_name = f"refs/test-refs/{cleanup_message}" refs[ref_name] = self._commit_ids[message] self._create_refs(repo, refs) @@ -645,7 +657,7 @@ class VcsBackend(object): Represents the test configuration for one supported vcs backend. """ - invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+') + invalid_repo_name = re.compile(r"[^0-9a-zA-Z]+") def __init__(self, alias, repo_path, test_name, test_repo_container): self.alias = alias @@ -658,7 +670,7 @@ class VcsBackend(object): return self._test_repo_container(key, self.alias).scm_instance() def __repr__(self): - return f'{self.__class__.__name__}(alias={self.alias}, repo={self._repo_path})' + return f"{self.__class__.__name__}(alias={self.alias}, repo={self._repo_path})" @property def repo(self): @@ -676,8 +688,7 @@ class VcsBackend(object): """ return get_backend(self.alias) - def create_repo(self, commits=None, number_of_commits=0, _clone_repo=None, - bare=False): + def create_repo(self, commits=None, number_of_commits=0, _clone_repo=None, bare=False): repo_name = self._next_repo_name() self._repo_path = get_new_dir(repo_name) repo_class = get_backend(self.alias) @@ -687,9 +698,7 @@ class VcsBackend(object): repo = repo_class(self._repo_path, create=True, src_url=src_url, bare=bare) self._cleanup_repos.append(repo) - commits = commits or [ - {'message': 'Commit %s of %s' % (x, repo_name)} - for x in range(number_of_commits)] + commits = commits or [{"message": "Commit %s of %s" % (x, repo_name)} for x in range(number_of_commits)] _add_commits_to_repo(repo, commits) return repo @@ -706,38 +715,30 @@ class VcsBackend(object): return self._repo_path def _next_repo_name(self): + return "{}_{}".format(self.invalid_repo_name.sub("_", self._test_name), len(self._cleanup_repos)) - return "{}_{}".format( - self.invalid_repo_name.sub('_', self._test_name), - len(self._cleanup_repos) - ) - - def add_file(self, repo, filename, content='Test content\n'): + def add_file(self, repo, filename, content="Test content\n"): imc = repo.in_memory_commit imc.add(FileNode(safe_bytes(filename), content=safe_bytes(content))) - imc.commit( - message='Automatic commit from vcsbackend fixture', - author='Automatic ') + imc.commit(message="Automatic commit from vcsbackend fixture", author="Automatic ") - def ensure_file(self, filename, content='Test content\n'): + def ensure_file(self, filename, content="Test content\n"): assert self._cleanup_repos, "Avoid writing into vcs_test repos" self.add_file(self.repo, filename, content) def vcsbackend_base(request, backend_alias, tests_tmp_path, baseapp, test_repo) -> VcsBackend: - if backend_alias not in request.config.getoption('--backends'): - pytest.skip("Backend %s not selected." % (backend_alias, )) + if backend_alias not in request.config.getoption("--backends"): + pytest.skip("Backend %s not selected." % (backend_alias,)) utils.check_xfail_backends(request.node, backend_alias) utils.check_skip_backends(request.node, backend_alias) - repo_name = f'vcs_test_{backend_alias}' + repo_name = f"vcs_test_{backend_alias}" repo_path = os.path.join(tests_tmp_path, repo_name) backend = VcsBackend( - alias=backend_alias, - repo_path=repo_path, - test_name=request.node.name, - test_repo_container=test_repo) + alias=backend_alias, repo_path=repo_path, test_name=request.node.name, test_repo_container=test_repo + ) request.addfinalizer(backend.cleanup) return backend @@ -758,17 +759,17 @@ def vcsbackend(request, backend_alias, tests_tmp_path, baseapp, test_repo): @pytest.fixture() def vcsbackend_git(request, tests_tmp_path, baseapp, test_repo): - return vcsbackend_base(request, 'git', tests_tmp_path, baseapp, test_repo) + return vcsbackend_base(request, "git", tests_tmp_path, baseapp, test_repo) @pytest.fixture() def vcsbackend_hg(request, tests_tmp_path, baseapp, test_repo): - return vcsbackend_base(request, 'hg', tests_tmp_path, baseapp, test_repo) + return vcsbackend_base(request, "hg", tests_tmp_path, baseapp, test_repo) @pytest.fixture() def vcsbackend_svn(request, tests_tmp_path, baseapp, test_repo): - return vcsbackend_base(request, 'svn', tests_tmp_path, baseapp, test_repo) + return vcsbackend_base(request, "svn", tests_tmp_path, baseapp, test_repo) @pytest.fixture() @@ -789,29 +790,28 @@ def _add_commits_to_repo(vcs_repo, commits): imc = vcs_repo.in_memory_commit for idx, commit in enumerate(commits): - message = str(commit.get('message', f'Commit {idx}')) + message = str(commit.get("message", f"Commit {idx}")) - for node in commit.get('added', []): + for node in commit.get("added", []): imc.add(FileNode(safe_bytes(node.path), content=node.content)) - for node in commit.get('changed', []): + for node in commit.get("changed", []): imc.change(FileNode(safe_bytes(node.path), content=node.content)) - for node in commit.get('removed', []): + for node in commit.get("removed", []): imc.remove(FileNode(safe_bytes(node.path))) - parents = [ - vcs_repo.get_commit(commit_id=commit_ids[p]) - for p in commit.get('parents', [])] + parents = [vcs_repo.get_commit(commit_id=commit_ids[p]) for p in commit.get("parents", [])] - operations = ('added', 'changed', 'removed') + operations = ("added", "changed", "removed") if not any((commit.get(o) for o in operations)): - imc.add(FileNode(b'file_%b' % safe_bytes(str(idx)), content=safe_bytes(message))) + imc.add(FileNode(b"file_%b" % safe_bytes(str(idx)), content=safe_bytes(message))) commit = imc.commit( message=message, - author=str(commit.get('author', 'Automatic ')), - date=commit.get('date'), - branch=commit.get('branch'), - parents=parents) + author=str(commit.get("author", "Automatic ")), + date=commit.get("date"), + branch=commit.get("branch"), + parents=parents, + ) commit_ids[commit.message] = commit.raw_id @@ -842,14 +842,14 @@ class RepoServer(object): self._cleanup_servers = [] def serve(self, vcsrepo): - if vcsrepo.alias != 'svn': + if vcsrepo.alias != "svn": raise TypeError("Backend %s not supported" % vcsrepo.alias) proc = subprocess.Popen( - ['svnserve', '-d', '--foreground', '--listen-host', 'localhost', - '--root', vcsrepo.path]) + ["svnserve", "-d", "--foreground", "--listen-host", "localhost", "--root", vcsrepo.path] + ) self._cleanup_servers.append(proc) - self.url = 'svn://localhost' + self.url = "svn://localhost" def cleanup(self): for proc in self._cleanup_servers: @@ -874,7 +874,6 @@ def pr_util(backend, request, config_stub): class PRTestUtility(object): - pull_request = None pull_request_id = None mergeable_patcher = None @@ -886,48 +885,55 @@ class PRTestUtility(object): self.backend = backend def create_pull_request( - self, commits=None, target_head=None, source_head=None, - revisions=None, approved=False, author=None, mergeable=False, - enable_notifications=True, name_suffix='', reviewers=None, observers=None, - title="Test", description="Description"): + self, + commits=None, + target_head=None, + source_head=None, + revisions=None, + approved=False, + author=None, + mergeable=False, + enable_notifications=True, + name_suffix="", + reviewers=None, + observers=None, + title="Test", + description="Description", + ): self.set_mergeable(mergeable) if not enable_notifications: # mock notification side effect - self.notification_patcher = mock.patch( - 'rhodecode.model.notification.NotificationModel.create') + self.notification_patcher = mock.patch("rhodecode.model.notification.NotificationModel.create") self.notification_patcher.start() if not self.pull_request: if not commits: commits = [ - {'message': 'c1'}, - {'message': 'c2'}, - {'message': 'c3'}, + {"message": "c1"}, + {"message": "c2"}, + {"message": "c3"}, ] - target_head = 'c1' - source_head = 'c2' - revisions = ['c2'] + target_head = "c1" + source_head = "c2" + revisions = ["c2"] self.commit_ids = self.backend.create_master_repo(commits) - self.target_repository = self.backend.create_repo( - heads=[target_head], name_suffix=name_suffix) - self.source_repository = self.backend.create_repo( - heads=[source_head], name_suffix=name_suffix) - self.author = author or UserModel().get_by_username( - TEST_USER_ADMIN_LOGIN) + self.target_repository = self.backend.create_repo(heads=[target_head], name_suffix=name_suffix) + self.source_repository = self.backend.create_repo(heads=[source_head], name_suffix=name_suffix) + self.author = author or UserModel().get_by_username(TEST_USER_ADMIN_LOGIN) model = PullRequestModel() self.create_parameters = { - 'created_by': self.author, - 'source_repo': self.source_repository.repo_name, - 'source_ref': self._default_branch_reference(source_head), - 'target_repo': self.target_repository.repo_name, - 'target_ref': self._default_branch_reference(target_head), - 'revisions': [self.commit_ids[r] for r in revisions], - 'reviewers': reviewers or self._get_reviewers(), - 'observers': observers or self._get_observers(), - 'title': title, - 'description': description, + "created_by": self.author, + "source_repo": self.source_repository.repo_name, + "source_ref": self._default_branch_reference(source_head), + "target_repo": self.target_repository.repo_name, + "target_ref": self._default_branch_reference(target_head), + "revisions": [self.commit_ids[r] for r in revisions], + "reviewers": reviewers or self._get_reviewers(), + "observers": observers or self._get_observers(), + "title": title, + "description": description, } self.pull_request = model.create(**self.create_parameters) assert model.get_versions(self.pull_request) == [] @@ -943,9 +949,7 @@ class PRTestUtility(object): return self.pull_request def approve(self): - self.create_status_votes( - ChangesetStatus.STATUS_APPROVED, - *self.pull_request.reviewers) + self.create_status_votes(ChangesetStatus.STATUS_APPROVED, *self.pull_request.reviewers) def close(self): PullRequestModel().close_pull_request(self.pull_request, self.author) @@ -953,28 +957,26 @@ class PRTestUtility(object): def _default_branch_reference(self, commit_message, branch: str = None) -> str: default_branch = branch or self.backend.default_branch_name message = self.commit_ids[commit_message] - reference = f'branch:{default_branch}:{message}' + reference = f"branch:{default_branch}:{message}" return reference def _get_reviewers(self): role = PullRequestReviewers.ROLE_REVIEWER return [ - (TEST_USER_REGULAR_LOGIN, ['default1'], False, role, []), - (TEST_USER_REGULAR2_LOGIN, ['default2'], False, role, []), + (TEST_USER_REGULAR_LOGIN, ["default1"], False, role, []), + (TEST_USER_REGULAR2_LOGIN, ["default2"], False, role, []), ] def _get_observers(self): - return [ - - ] + return [] def update_source_repository(self, head=None, do_fetch=False): - heads = [head or 'c3'] + heads = [head or "c3"] self.backend.pull_heads(self.source_repository, heads=heads, do_fetch=do_fetch) def update_target_repository(self, head=None, do_fetch=False): - heads = [head or 'c3'] + heads = [head or "c3"] self.backend.pull_heads(self.target_repository, heads=heads, do_fetch=do_fetch) def set_pr_target_ref(self, ref_type: str = "branch", ref_name: str = "branch", ref_commit_id: str = "") -> str: @@ -1004,7 +1006,7 @@ class PRTestUtility(object): # TODO: johbo: Git and Mercurial have an inconsistent vcs api here, # remove the if once that's sorted out. if self.backend.alias == "git": - kwargs = {'branch_name': self.backend.default_branch_name} + kwargs = {"branch_name": self.backend.default_branch_name} else: kwargs = {} source_vcs.strip(removed_commit_id, **kwargs) @@ -1015,10 +1017,8 @@ class PRTestUtility(object): def create_comment(self, linked_to=None): comment = CommentsModel().create( - text="Test comment", - repo=self.target_repository.repo_name, - user=self.author, - pull_request=self.pull_request) + text="Test comment", repo=self.target_repository.repo_name, user=self.author, pull_request=self.pull_request + ) assert comment.pull_request_version_id is None if linked_to: @@ -1026,15 +1026,15 @@ class PRTestUtility(object): return comment - def create_inline_comment( - self, linked_to=None, line_no='n1', file_path='file_1'): + def create_inline_comment(self, linked_to=None, line_no="n1", file_path="file_1"): comment = CommentsModel().create( text="Test comment", repo=self.target_repository.repo_name, user=self.author, line_no=line_no, f_path=file_path, - pull_request=self.pull_request) + pull_request=self.pull_request, + ) assert comment.pull_request_version_id is None if linked_to: @@ -1044,25 +1044,20 @@ class PRTestUtility(object): def create_version_of_pull_request(self): pull_request = self.create_pull_request() - version = PullRequestModel()._create_version_from_snapshot( - pull_request) + version = PullRequestModel()._create_version_from_snapshot(pull_request) return version def create_status_votes(self, status, *reviewers): for reviewer in reviewers: ChangesetStatusModel().set_status( - repo=self.pull_request.target_repo, - status=status, - user=reviewer.user_id, - pull_request=self.pull_request) + repo=self.pull_request.target_repo, status=status, user=reviewer.user_id, pull_request=self.pull_request + ) def set_mergeable(self, value): if not self.mergeable_patcher: - self.mergeable_patcher = mock.patch.object( - VcsSettingsModel, 'get_general_settings') + self.mergeable_patcher = mock.patch.object(VcsSettingsModel, "get_general_settings") self.mergeable_mock = self.mergeable_patcher.start() - self.mergeable_mock.return_value = { - 'rhodecode_pr_merge_enabled': value} + self.mergeable_mock.return_value = {"rhodecode_pr_merge_enabled": value} def cleanup(self): # In case the source repository is already cleaned up, the pull @@ -1109,7 +1104,6 @@ def user_util(request, db_connection): # TODO: johbo: Split this up into utilities per domain or something similar class UserUtility(object): - def __init__(self, test_name="test"): self._test_name = self._sanitize_name(test_name) self.fixture = Fixture() @@ -1126,37 +1120,29 @@ class UserUtility(object): self.user_permissions = [] def _sanitize_name(self, name): - for char in ['[', ']']: - name = name.replace(char, '_') + for char in ["[", "]"]: + name = name.replace(char, "_") return name - def create_repo_group( - self, owner=TEST_USER_ADMIN_LOGIN, auto_cleanup=True): - group_name = "{prefix}_repogroup_{count}".format( - prefix=self._test_name, - count=len(self.repo_group_ids)) - repo_group = self.fixture.create_repo_group( - group_name, cur_user=owner) + def create_repo_group(self, owner=TEST_USER_ADMIN_LOGIN, auto_cleanup=True): + group_name = "{prefix}_repogroup_{count}".format(prefix=self._test_name, count=len(self.repo_group_ids)) + repo_group = self.fixture.create_repo_group(group_name, cur_user=owner) if auto_cleanup: self.repo_group_ids.append(repo_group.group_id) return repo_group - def create_repo(self, owner=TEST_USER_ADMIN_LOGIN, parent=None, - auto_cleanup=True, repo_type='hg', bare=False): - repo_name = "{prefix}_repository_{count}".format( - prefix=self._test_name, - count=len(self.repos_ids)) + def create_repo(self, owner=TEST_USER_ADMIN_LOGIN, parent=None, auto_cleanup=True, repo_type="hg", bare=False): + repo_name = "{prefix}_repository_{count}".format(prefix=self._test_name, count=len(self.repos_ids)) repository = self.fixture.create_repo( - repo_name, cur_user=owner, repo_group=parent, repo_type=repo_type, bare=bare) + repo_name, cur_user=owner, repo_group=parent, repo_type=repo_type, bare=bare + ) if auto_cleanup: self.repos_ids.append(repository.repo_id) return repository def create_user(self, auto_cleanup=True, **kwargs): - user_name = "{prefix}_user_{count}".format( - prefix=self._test_name, - count=len(self.user_ids)) + user_name = "{prefix}_user_{count}".format(prefix=self._test_name, count=len(self.user_ids)) user = self.fixture.create_user(user_name, **kwargs) if auto_cleanup: self.user_ids.append(user.user_id) @@ -1171,13 +1157,9 @@ class UserUtility(object): user_group = self.create_user_group(members=[user]) return user, user_group - def create_user_group(self, owner=TEST_USER_ADMIN_LOGIN, members=None, - auto_cleanup=True, **kwargs): - group_name = "{prefix}_usergroup_{count}".format( - prefix=self._test_name, - count=len(self.user_group_ids)) - user_group = self.fixture.create_user_group( - group_name, cur_user=owner, **kwargs) + def create_user_group(self, owner=TEST_USER_ADMIN_LOGIN, members=None, auto_cleanup=True, **kwargs): + group_name = "{prefix}_usergroup_{count}".format(prefix=self._test_name, count=len(self.user_group_ids)) + user_group = self.fixture.create_user_group(group_name, cur_user=owner, **kwargs) if auto_cleanup: self.user_group_ids.append(user_group.users_group_id) @@ -1190,52 +1172,34 @@ class UserUtility(object): self.inherit_default_user_permissions(user_name, False) self.user_permissions.append((user_name, permission_name)) - def grant_user_permission_to_repo_group( - self, repo_group, user, permission_name): - permission = RepoGroupModel().grant_user_permission( - repo_group, user, permission_name) - self.user_repo_group_permission_ids.append( - (repo_group.group_id, user.user_id)) + def grant_user_permission_to_repo_group(self, repo_group, user, permission_name): + permission = RepoGroupModel().grant_user_permission(repo_group, user, permission_name) + self.user_repo_group_permission_ids.append((repo_group.group_id, user.user_id)) return permission - def grant_user_group_permission_to_repo_group( - self, repo_group, user_group, permission_name): - permission = RepoGroupModel().grant_user_group_permission( - repo_group, user_group, permission_name) - self.user_group_repo_group_permission_ids.append( - (repo_group.group_id, user_group.users_group_id)) + def grant_user_group_permission_to_repo_group(self, repo_group, user_group, permission_name): + permission = RepoGroupModel().grant_user_group_permission(repo_group, user_group, permission_name) + self.user_group_repo_group_permission_ids.append((repo_group.group_id, user_group.users_group_id)) return permission - def grant_user_permission_to_repo( - self, repo, user, permission_name): - permission = RepoModel().grant_user_permission( - repo, user, permission_name) - self.user_repo_permission_ids.append( - (repo.repo_id, user.user_id)) + def grant_user_permission_to_repo(self, repo, user, permission_name): + permission = RepoModel().grant_user_permission(repo, user, permission_name) + self.user_repo_permission_ids.append((repo.repo_id, user.user_id)) return permission - def grant_user_group_permission_to_repo( - self, repo, user_group, permission_name): - permission = RepoModel().grant_user_group_permission( - repo, user_group, permission_name) - self.user_group_repo_permission_ids.append( - (repo.repo_id, user_group.users_group_id)) + def grant_user_group_permission_to_repo(self, repo, user_group, permission_name): + permission = RepoModel().grant_user_group_permission(repo, user_group, permission_name) + self.user_group_repo_permission_ids.append((repo.repo_id, user_group.users_group_id)) return permission - def grant_user_permission_to_user_group( - self, target_user_group, user, permission_name): - permission = UserGroupModel().grant_user_permission( - target_user_group, user, permission_name) - self.user_user_group_permission_ids.append( - (target_user_group.users_group_id, user.user_id)) + def grant_user_permission_to_user_group(self, target_user_group, user, permission_name): + permission = UserGroupModel().grant_user_permission(target_user_group, user, permission_name) + self.user_user_group_permission_ids.append((target_user_group.users_group_id, user.user_id)) return permission - def grant_user_group_permission_to_user_group( - self, target_user_group, user_group, permission_name): - permission = UserGroupModel().grant_user_group_permission( - target_user_group, user_group, permission_name) - self.user_group_user_group_permission_ids.append( - (target_user_group.users_group_id, user_group.users_group_id)) + def grant_user_group_permission_to_user_group(self, target_user_group, user_group, permission_name): + permission = UserGroupModel().grant_user_group_permission(target_user_group, user_group, permission_name) + self.user_group_user_group_permission_ids.append((target_user_group.users_group_id, user_group.users_group_id)) return permission def revoke_user_permission(self, user_name, permission_name): @@ -1285,14 +1249,11 @@ class UserUtility(object): """ first_group = RepoGroup.get(first_group_id) second_group = RepoGroup.get(second_group_id) - first_group_parts = ( - len(first_group.group_name.split('/')) if first_group else 0) - second_group_parts = ( - len(second_group.group_name.split('/')) if second_group else 0) + first_group_parts = len(first_group.group_name.split("/")) if first_group else 0 + second_group_parts = len(second_group.group_name.split("/")) if second_group else 0 return cmp(second_group_parts, first_group_parts) - sorted_repo_group_ids = sorted( - self.repo_group_ids, key=functools.cmp_to_key(_repo_group_compare)) + sorted_repo_group_ids = sorted(self.repo_group_ids, key=functools.cmp_to_key(_repo_group_compare)) for repo_group_id in sorted_repo_group_ids: self.fixture.destroy_repo_group(repo_group_id) @@ -1308,16 +1269,11 @@ class UserUtility(object): """ first_group = UserGroup.get(first_group_id) second_group = UserGroup.get(second_group_id) - first_group_parts = ( - len(first_group.users_group_name.split('/')) - if first_group else 0) - second_group_parts = ( - len(second_group.users_group_name.split('/')) - if second_group else 0) + first_group_parts = len(first_group.users_group_name.split("/")) if first_group else 0 + second_group_parts = len(second_group.users_group_name.split("/")) if second_group else 0 return cmp(second_group_parts, first_group_parts) - sorted_user_group_ids = sorted( - self.user_group_ids, key=functools.cmp_to_key(_user_group_compare)) + sorted_user_group_ids = sorted(self.user_group_ids, key=functools.cmp_to_key(_user_group_compare)) for user_group_id in sorted_user_group_ids: self.fixture.destroy_user_group(user_group_id) @@ -1326,22 +1282,19 @@ class UserUtility(object): self.fixture.destroy_user(user_id) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def testrun(): return { - 'uuid': uuid.uuid4(), - 'start': datetime.datetime.utcnow().isoformat(), - 'timestamp': int(time.time()), + "uuid": uuid.uuid4(), + "start": datetime.datetime.utcnow().isoformat(), + "timestamp": int(time.time()), } class AppenlightClient(object): + url_template = "{url}?protocol_version=0.5" - url_template = '{url}?protocol_version=0.5' - - def __init__( - self, url, api_key, add_server=True, add_timestamp=True, - namespace=None, request=None, testrun=None): + def __init__(self, url, api_key, add_server=True, add_timestamp=True, namespace=None, request=None, testrun=None): self.url = self.url_template.format(url=url) self.api_key = api_key self.add_server = add_server @@ -1362,40 +1315,41 @@ class AppenlightClient(object): def collect(self, data): if self.add_server: - data.setdefault('server', self.server) + data.setdefault("server", self.server) if self.add_timestamp: - data.setdefault('date', datetime.datetime.utcnow().isoformat()) + data.setdefault("date", datetime.datetime.utcnow().isoformat()) if self.namespace: - data.setdefault('namespace', self.namespace) + data.setdefault("namespace", self.namespace) if self.request: - data.setdefault('request', self.request) + data.setdefault("request", self.request) self.stats.append(data) def send_stats(self): tags = [ - ('testrun', self.request), - ('testrun.start', self.testrun['start']), - ('testrun.timestamp', self.testrun['timestamp']), - ('test', self.namespace), + ("testrun", self.request), + ("testrun.start", self.testrun["start"]), + ("testrun.timestamp", self.testrun["timestamp"]), + ("test", self.namespace), ] for key, value in self.tags_before.items(): - tags.append((key + '.before', value)) + tags.append((key + ".before", value)) try: delta = self.tags_after[key] - value - tags.append((key + '.delta', delta)) + tags.append((key + ".delta", delta)) except Exception: pass for key, value in self.tags_after.items(): - tags.append((key + '.after', value)) - self.collect({ - 'message': "Collected tags", - 'tags': tags, - }) + tags.append((key + ".after", value)) + self.collect( + { + "message": "Collected tags", + "tags": tags, + } + ) response = requests.post( self.url, - headers={ - 'X-appenlight-api-key': self.api_key}, + headers={"X-appenlight-api-key": self.api_key}, json=self.stats, ) @@ -1403,7 +1357,7 @@ class AppenlightClient(object): pprint.pprint(self.stats) print(response.headers) print(response.text) - raise Exception('Sending to appenlight failed') + raise Exception("Sending to appenlight failed") @pytest.fixture() @@ -1454,9 +1408,8 @@ class SettingsUtility(object): self.repo_rhodecode_ui_ids = [] self.repo_rhodecode_setting_ids = [] - def create_repo_rhodecode_ui( - self, repo, section, value, key=None, active=True, cleanup=True): - key = key or sha1_safe(f'{section}{value}{repo.repo_id}') + def create_repo_rhodecode_ui(self, repo, section, value, key=None, active=True, cleanup=True): + key = key or sha1_safe(f"{section}{value}{repo.repo_id}") setting = RepoRhodeCodeUi() setting.repository_id = repo.repo_id @@ -1471,9 +1424,8 @@ class SettingsUtility(object): self.repo_rhodecode_ui_ids.append(setting.ui_id) return setting - def create_rhodecode_ui( - self, section, value, key=None, active=True, cleanup=True): - key = key or sha1_safe(f'{section}{value}') + def create_rhodecode_ui(self, section, value, key=None, active=True, cleanup=True): + key = key or sha1_safe(f"{section}{value}") setting = RhodeCodeUi() setting.ui_section = section @@ -1487,10 +1439,8 @@ class SettingsUtility(object): self.rhodecode_ui_ids.append(setting.ui_id) return setting - def create_repo_rhodecode_setting( - self, repo, name, value, type_, cleanup=True): - setting = RepoRhodeCodeSetting( - repo.repo_id, key=name, val=value, type=type_) + def create_repo_rhodecode_setting(self, repo, name, value, type_, cleanup=True): + setting = RepoRhodeCodeSetting(repo.repo_id, key=name, val=value, type=type_) Session().add(setting) Session().commit() @@ -1530,13 +1480,12 @@ class SettingsUtility(object): @pytest.fixture() def no_notifications(request): - notification_patcher = mock.patch( - 'rhodecode.model.notification.NotificationModel.create') + notification_patcher = mock.patch("rhodecode.model.notification.NotificationModel.create") notification_patcher.start() request.addfinalizer(notification_patcher.stop) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def repeat(request): """ The number of repetitions is based on this fixture. @@ -1544,7 +1493,7 @@ def repeat(request): Slower calls may divide it by 10 or 100. It is chosen in a way so that the tests are not too slow in our default test suite. """ - return request.config.getoption('--repeat') + return request.config.getoption("--repeat") @pytest.fixture() @@ -1561,43 +1510,18 @@ def context_stub(): return context -@pytest.fixture() -def request_stub(): - """ - Stub request object. - """ - from rhodecode.lib.base import bootstrap_request - request = bootstrap_request(scheme='https') - return request - - -@pytest.fixture() -def config_stub(request, request_stub): - """ - Set up pyramid.testing and return the Configurator. - """ - from rhodecode.lib.base import bootstrap_config - config = bootstrap_config(request=request_stub) - - @request.addfinalizer - def cleanup(): - pyramid.testing.tearDown() - - return config - - @pytest.fixture() def StubIntegrationType(): class _StubIntegrationType(IntegrationTypeBase): - """ Test integration type class """ + """Test integration type class""" - key = 'test' - display_name = 'Test integration type' - description = 'A test integration type for testing' + key = "test" + display_name = "Test integration type" + description = "A test integration type for testing" @classmethod def icon(cls): - return 'test_icon_html_image' + return "test_icon_html_image" def __init__(self, settings): super(_StubIntegrationType, self).__init__(settings) @@ -1611,14 +1535,14 @@ def StubIntegrationType(): test_string_field = colander.SchemaNode( colander.String(), missing=colander.required, - title='test string field', + title="test string field", ) test_int_field = colander.SchemaNode( colander.Int(), - title='some integer setting', + title="some integer setting", ) - return SettingsSchema() + return SettingsSchema() integration_type_registry.register_integration_type(_StubIntegrationType) return _StubIntegrationType @@ -1627,18 +1551,22 @@ def StubIntegrationType(): @pytest.fixture() def stub_integration_settings(): return { - 'test_string_field': 'some data', - 'test_int_field': 100, + "test_string_field": "some data", + "test_int_field": 100, } @pytest.fixture() -def repo_integration_stub(request, repo_stub, StubIntegrationType, - stub_integration_settings): +def repo_integration_stub(request, repo_stub, StubIntegrationType, stub_integration_settings): integration = IntegrationModel().create( - StubIntegrationType, settings=stub_integration_settings, enabled=True, - name='test repo integration', - repo=repo_stub, repo_group=None, child_repos_only=None) + StubIntegrationType, + settings=stub_integration_settings, + enabled=True, + name="test repo integration", + repo=repo_stub, + repo_group=None, + child_repos_only=None, + ) @request.addfinalizer def cleanup(): @@ -1648,12 +1576,16 @@ def repo_integration_stub(request, repo_stub, StubIntegrationType, @pytest.fixture() -def repogroup_integration_stub(request, test_repo_group, StubIntegrationType, - stub_integration_settings): +def repogroup_integration_stub(request, test_repo_group, StubIntegrationType, stub_integration_settings): integration = IntegrationModel().create( - StubIntegrationType, settings=stub_integration_settings, enabled=True, - name='test repogroup integration', - repo=None, repo_group=test_repo_group, child_repos_only=True) + StubIntegrationType, + settings=stub_integration_settings, + enabled=True, + name="test repogroup integration", + repo=None, + repo_group=test_repo_group, + child_repos_only=True, + ) @request.addfinalizer def cleanup(): @@ -1663,12 +1595,16 @@ def repogroup_integration_stub(request, test_repo_group, StubIntegrationType, @pytest.fixture() -def repogroup_recursive_integration_stub(request, test_repo_group, - StubIntegrationType, stub_integration_settings): +def repogroup_recursive_integration_stub(request, test_repo_group, StubIntegrationType, stub_integration_settings): integration = IntegrationModel().create( - StubIntegrationType, settings=stub_integration_settings, enabled=True, - name='test recursive repogroup integration', - repo=None, repo_group=test_repo_group, child_repos_only=False) + StubIntegrationType, + settings=stub_integration_settings, + enabled=True, + name="test recursive repogroup integration", + repo=None, + repo_group=test_repo_group, + child_repos_only=False, + ) @request.addfinalizer def cleanup(): @@ -1678,12 +1614,16 @@ def repogroup_recursive_integration_stub(request, test_repo_group, @pytest.fixture() -def global_integration_stub(request, StubIntegrationType, - stub_integration_settings): +def global_integration_stub(request, StubIntegrationType, stub_integration_settings): integration = IntegrationModel().create( - StubIntegrationType, settings=stub_integration_settings, enabled=True, - name='test global integration', - repo=None, repo_group=None, child_repos_only=None) + StubIntegrationType, + settings=stub_integration_settings, + enabled=True, + name="test global integration", + repo=None, + repo_group=None, + child_repos_only=None, + ) @request.addfinalizer def cleanup(): @@ -1693,12 +1633,16 @@ def global_integration_stub(request, StubIntegrationType, @pytest.fixture() -def root_repos_integration_stub(request, StubIntegrationType, - stub_integration_settings): +def root_repos_integration_stub(request, StubIntegrationType, stub_integration_settings): integration = IntegrationModel().create( - StubIntegrationType, settings=stub_integration_settings, enabled=True, - name='test global integration', - repo=None, repo_group=None, child_repos_only=True) + StubIntegrationType, + settings=stub_integration_settings, + enabled=True, + name="test global integration", + repo=None, + repo_group=None, + child_repos_only=True, + ) @request.addfinalizer def cleanup(): @@ -1710,8 +1654,8 @@ def root_repos_integration_stub(request, StubIntegrationType, @pytest.fixture() def local_dt_to_utc(): def _factory(dt): - return dt.replace(tzinfo=dateutil.tz.tzlocal()).astimezone( - dateutil.tz.tzutc()).replace(tzinfo=None) + return dt.replace(tzinfo=dateutil.tz.tzlocal()).astimezone(dateutil.tz.tzutc()).replace(tzinfo=None) + return _factory @@ -1724,7 +1668,7 @@ def disable_anonymous_user(request, baseapp): set_anonymous_access(True) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def rc_fixture(request): return Fixture() @@ -1734,9 +1678,9 @@ def repo_groups(request): fixture = Fixture() session = Session() - zombie_group = fixture.create_repo_group('zombie') - parent_group = fixture.create_repo_group('parent') - child_group = fixture.create_repo_group('parent/child') + zombie_group = fixture.create_repo_group("zombie") + parent_group = fixture.create_repo_group("parent") + child_group = fixture.create_repo_group("parent/child") groups_in_db = session.query(RepoGroup).all() assert len(groups_in_db) == 3 assert child_group.group_parent_id == parent_group.group_id @@ -1748,3 +1692,4 @@ def repo_groups(request): fixture.destroy_repo_group(parent_group) return zombie_group, parent_group, child_group + diff --git a/rhodecode/tests/fixtures/function_scoped_baseapp.py b/rhodecode/tests/fixtures/function_scoped_baseapp.py new file mode 100644 index 00000000..b768258f --- /dev/null +++ b/rhodecode/tests/fixtures/function_scoped_baseapp.py @@ -0,0 +1,52 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 pytest +from rhodecode.tests.utils import CustomTestApp +from rhodecode.tests.fixtures.fixture_utils import plain_http_environ, plain_config_stub, plain_request_stub + + +@pytest.fixture(scope='function') +def request_stub(): + return plain_request_stub() + + +@pytest.fixture(scope='function') +def config_stub(request, request_stub): + return plain_config_stub(request, request_stub) + + +@pytest.fixture(scope='function') +def http_environ(): + """ + HTTP extra environ keys. + + Used by the test application and as well for setting up the pylons + environment. In the case of the fixture "app" it should be possible + to override this for a specific test case. + """ + return plain_http_environ() + + +@pytest.fixture(scope='function') +def app(request, config_stub, http_environ, baseapp): + app = CustomTestApp(baseapp, extra_environ=http_environ) + if request.cls: + # inject app into a class that uses this fixtures + request.cls.app = app + return app diff --git a/rhodecode/tests/fixtures/module_scoped_baseapp.py b/rhodecode/tests/fixtures/module_scoped_baseapp.py new file mode 100644 index 00000000..3478e714 --- /dev/null +++ b/rhodecode/tests/fixtures/module_scoped_baseapp.py @@ -0,0 +1,49 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 pytest +from rhodecode.tests.utils import CustomTestApp +from rhodecode.tests.fixtures.fixture_utils import plain_http_environ, plain_config_stub, plain_request_stub + + +@pytest.fixture(scope='module') +def module_request_stub(): + return plain_request_stub() + + +@pytest.fixture(scope='module') +def module_config_stub(request, module_request_stub): + return plain_config_stub(request, module_request_stub) + + +@pytest.fixture(scope='module') +def module_http_environ(): + """ + HTTP extra environ keys. + + Used by the test application and as well for setting up the pylons + environment. In the case of the fixture "app" it should be possible + to override this for a specific test case. + """ + return plain_http_environ() + + +@pytest.fixture(scope='module') +def module_app(request, module_config_stub, module_http_environ, baseapp): + app = CustomTestApp(baseapp, extra_environ=module_http_environ) + return app diff --git a/rhodecode/tests/fixture.py b/rhodecode/tests/fixtures/rc_fixture.py similarity index 68% rename from rhodecode/tests/fixture.py rename to rhodecode/tests/fixtures/rc_fixture.py index d7ca57bd..2fc5692a 100644 --- a/rhodecode/tests/fixture.py +++ b/rhodecode/tests/fixtures/rc_fixture.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -37,17 +36,16 @@ from rhodecode.model.user_group import UserGroupModel from rhodecode.model.gist import GistModel from rhodecode.model.auth_token import AuthTokenModel from rhodecode.model.scm import ScmModel -from rhodecode.authentication.plugins.auth_rhodecode import \ - RhodeCodeAuthPlugin +from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin from rhodecode.tests import TEST_USER_ADMIN_LOGIN dn = os.path.dirname -FIXTURES = os.path.join(dn(dn(os.path.abspath(__file__))), 'tests', 'fixtures') +FIXTURES = os.path.join(dn(os.path.abspath(__file__)), "diff_fixtures") def error_function(*args, **kwargs): - raise Exception('Total Crash !') + raise Exception("Total Crash !") class TestINI(object): @@ -59,8 +57,7 @@ class TestINI(object): print('paster server %s' % new_test_ini) """ - def __init__(self, ini_file_path, ini_params, new_file_prefix='DEFAULT', - destroy=True, dir=None): + def __init__(self, ini_file_path, ini_params, new_file_prefix="DEFAULT", destroy=True, dir=None): self.ini_file_path = ini_file_path self.ini_params = ini_params self.new_path = None @@ -85,9 +82,8 @@ class TestINI(object): parser[section][key] = str(val) with tempfile.NamedTemporaryFile( - mode='w', - prefix=self.new_path_prefix, suffix='.ini', dir=self._dir, - delete=False) as new_ini_file: + mode="w", prefix=self.new_path_prefix, suffix=".ini", dir=self._dir, delete=False + ) as new_ini_file: parser.write(new_ini_file) self.new_path = new_ini_file.name @@ -99,7 +95,6 @@ class TestINI(object): class Fixture(object): - def anon_access(self, status): """ Context process for disabling anonymous access. use like: @@ -139,22 +134,19 @@ class Fixture(object): class context(object): def _get_plugin(self): - plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid) + plugin_id = "egg:rhodecode-enterprise-ce#{}".format(RhodeCodeAuthPlugin.uid) plugin = RhodeCodeAuthPlugin(plugin_id) return plugin def __enter__(self): - plugin = self._get_plugin() - plugin.create_or_update_setting('auth_restriction', auth_restriction) + plugin.create_or_update_setting("auth_restriction", auth_restriction) Session().commit() SettingsModel().invalidate_settings_cache(hard=True) def __exit__(self, exc_type, exc_val, exc_tb): - plugin = self._get_plugin() - plugin.create_or_update_setting( - 'auth_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_NONE) + plugin.create_or_update_setting("auth_restriction", RhodeCodeAuthPlugin.AUTH_RESTRICTION_NONE) Session().commit() SettingsModel().invalidate_settings_cache(hard=True) @@ -173,62 +165,61 @@ class Fixture(object): class context(object): def _get_plugin(self): - plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid) + plugin_id = "egg:rhodecode-enterprise-ce#{}".format(RhodeCodeAuthPlugin.uid) plugin = RhodeCodeAuthPlugin(plugin_id) return plugin def __enter__(self): plugin = self._get_plugin() - plugin.create_or_update_setting('scope_restriction', scope_restriction) + plugin.create_or_update_setting("scope_restriction", scope_restriction) Session().commit() SettingsModel().invalidate_settings_cache(hard=True) def __exit__(self, exc_type, exc_val, exc_tb): plugin = self._get_plugin() - plugin.create_or_update_setting( - 'scope_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_ALL) + plugin.create_or_update_setting("scope_restriction", RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_ALL) Session().commit() SettingsModel().invalidate_settings_cache(hard=True) return context() def _get_repo_create_params(self, **custom): - repo_type = custom.get('repo_type') or 'hg' + repo_type = custom.get("repo_type") or "hg" default_landing_ref, landing_ref_lbl = ScmModel.backend_landing_ref(repo_type) defs = { - 'repo_name': None, - 'repo_type': repo_type, - 'clone_uri': '', - 'push_uri': '', - 'repo_group': '-1', - 'repo_description': 'DESC', - 'repo_private': False, - 'repo_landing_commit_ref': default_landing_ref, - 'repo_copy_permissions': False, - 'repo_state': Repository.STATE_CREATED, + "repo_name": None, + "repo_type": repo_type, + "clone_uri": "", + "push_uri": "", + "repo_group": "-1", + "repo_description": "DESC", + "repo_private": False, + "repo_landing_commit_ref": default_landing_ref, + "repo_copy_permissions": False, + "repo_state": Repository.STATE_CREATED, } defs.update(custom) - if 'repo_name_full' not in custom: - defs.update({'repo_name_full': defs['repo_name']}) + if "repo_name_full" not in custom: + defs.update({"repo_name_full": defs["repo_name"]}) # fix the repo name if passed as repo_name_full - if defs['repo_name']: - defs['repo_name'] = defs['repo_name'].split('/')[-1] + if defs["repo_name"]: + defs["repo_name"] = defs["repo_name"].split("/")[-1] return defs def _get_group_create_params(self, **custom): defs = { - 'group_name': None, - 'group_description': 'DESC', - 'perm_updates': [], - 'perm_additions': [], - 'perm_deletions': [], - 'group_parent_id': -1, - 'enable_locking': False, - 'recursive': False, + "group_name": None, + "group_description": "DESC", + "perm_updates": [], + "perm_additions": [], + "perm_deletions": [], + "group_parent_id": -1, + "enable_locking": False, + "recursive": False, } defs.update(custom) @@ -236,16 +227,16 @@ class Fixture(object): def _get_user_create_params(self, name, **custom): defs = { - 'username': name, - 'password': 'qweqwe', - 'email': '%s+test@rhodecode.org' % name, - 'firstname': 'TestUser', - 'lastname': 'Test', - 'description': 'test description', - 'active': True, - 'admin': False, - 'extern_type': 'rhodecode', - 'extern_name': None, + "username": name, + "password": "qweqwe", + "email": "%s+test@rhodecode.org" % name, + "firstname": "TestUser", + "lastname": "Test", + "description": "test description", + "active": True, + "admin": False, + "extern_type": "rhodecode", + "extern_name": None, } defs.update(custom) @@ -253,30 +244,30 @@ class Fixture(object): def _get_user_group_create_params(self, name, **custom): defs = { - 'users_group_name': name, - 'user_group_description': 'DESC', - 'users_group_active': True, - 'user_group_data': {}, + "users_group_name": name, + "user_group_description": "DESC", + "users_group_active": True, + "user_group_data": {}, } defs.update(custom) return defs def create_repo(self, name, **kwargs): - repo_group = kwargs.get('repo_group') + repo_group = kwargs.get("repo_group") if isinstance(repo_group, RepoGroup): - kwargs['repo_group'] = repo_group.group_id + kwargs["repo_group"] = repo_group.group_id name = name.split(Repository.NAME_SEP)[-1] name = Repository.NAME_SEP.join((repo_group.group_name, name)) - if 'skip_if_exists' in kwargs: - del kwargs['skip_if_exists'] + if "skip_if_exists" in kwargs: + del kwargs["skip_if_exists"] r = Repository.get_by_repo_name(name) if r: return r form_data = self._get_repo_create_params(repo_name=name, **kwargs) - cur_user = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN) + cur_user = kwargs.get("cur_user", TEST_USER_ADMIN_LOGIN) RepoModel().create(form_data, cur_user) Session().commit() repo = Repository.get_by_repo_name(name) @@ -287,17 +278,15 @@ class Fixture(object): repo_to_fork = Repository.get_by_repo_name(repo_to_fork) form_data = self._get_repo_create_params( - repo_name=fork_name, - fork_parent_id=repo_to_fork.repo_id, - repo_type=repo_to_fork.repo_type, - **kwargs) + repo_name=fork_name, fork_parent_id=repo_to_fork.repo_id, repo_type=repo_to_fork.repo_type, **kwargs + ) # TODO: fix it !! - form_data['description'] = form_data['repo_description'] - form_data['private'] = form_data['repo_private'] - form_data['landing_rev'] = form_data['repo_landing_commit_ref'] + form_data["description"] = form_data["repo_description"] + form_data["private"] = form_data["repo_private"] + form_data["landing_rev"] = form_data["repo_landing_commit_ref"] - owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN) + owner = kwargs.get("cur_user", TEST_USER_ADMIN_LOGIN) RepoModel().create_fork(form_data, cur_user=owner) Session().commit() r = Repository.get_by_repo_name(fork_name) @@ -305,7 +294,7 @@ class Fixture(object): return r def destroy_repo(self, repo_name, **kwargs): - RepoModel().delete(repo_name, pull_requests='delete', artifacts='delete', **kwargs) + RepoModel().delete(repo_name, pull_requests="delete", artifacts="delete", **kwargs) Session().commit() def destroy_repo_on_filesystem(self, repo_name): @@ -314,17 +303,16 @@ class Fixture(object): shutil.rmtree(rm_path) def create_repo_group(self, name, **kwargs): - if 'skip_if_exists' in kwargs: - del kwargs['skip_if_exists'] + if "skip_if_exists" in kwargs: + del kwargs["skip_if_exists"] gr = RepoGroup.get_by_group_name(group_name=name) if gr: return gr form_data = self._get_group_create_params(group_name=name, **kwargs) - owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN) + owner = kwargs.get("cur_user", TEST_USER_ADMIN_LOGIN) gr = RepoGroupModel().create( - group_name=form_data['group_name'], - group_description=form_data['group_name'], - owner=owner) + group_name=form_data["group_name"], group_description=form_data["group_name"], owner=owner + ) Session().commit() gr = RepoGroup.get_by_group_name(gr.group_name) return gr @@ -334,8 +322,8 @@ class Fixture(object): Session().commit() def create_user(self, name, **kwargs): - if 'skip_if_exists' in kwargs: - del kwargs['skip_if_exists'] + if "skip_if_exists" in kwargs: + del kwargs["skip_if_exists"] user = User.get_by_username(name) if user: return user @@ -343,8 +331,7 @@ class Fixture(object): user = UserModel().create(form_data) # create token for user - AuthTokenModel().create( - user=user, description=u'TEST_USER_TOKEN') + AuthTokenModel().create(user=user, description="TEST_USER_TOKEN") Session().commit() user = User.get_by_username(user.username) @@ -368,22 +355,24 @@ class Fixture(object): Session().commit() def create_user_group(self, name, **kwargs): - if 'skip_if_exists' in kwargs: - del kwargs['skip_if_exists'] + if "skip_if_exists" in kwargs: + del kwargs["skip_if_exists"] gr = UserGroup.get_by_group_name(group_name=name) if gr: return gr # map active flag to the real attribute. For API consistency of fixtures - if 'active' in kwargs: - kwargs['users_group_active'] = kwargs['active'] - del kwargs['active'] + if "active" in kwargs: + kwargs["users_group_active"] = kwargs["active"] + del kwargs["active"] form_data = self._get_user_group_create_params(name, **kwargs) - owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN) + owner = kwargs.get("cur_user", TEST_USER_ADMIN_LOGIN) user_group = UserGroupModel().create( - name=form_data['users_group_name'], - description=form_data['user_group_description'], - owner=owner, active=form_data['users_group_active'], - group_data=form_data['user_group_data']) + name=form_data["users_group_name"], + description=form_data["user_group_description"], + owner=owner, + active=form_data["users_group_active"], + group_data=form_data["user_group_data"], + ) Session().commit() user_group = UserGroup.get_by_group_name(user_group.users_group_name) return user_group @@ -394,18 +383,23 @@ class Fixture(object): def create_gist(self, **kwargs): form_data = { - 'description': 'new-gist', - 'owner': TEST_USER_ADMIN_LOGIN, - 'gist_type': GistModel.cls.GIST_PUBLIC, - 'lifetime': -1, - 'acl_level': Gist.ACL_LEVEL_PUBLIC, - 'gist_mapping': {b'filename1.txt': {'content': b'hello world'},} + "description": "new-gist", + "owner": TEST_USER_ADMIN_LOGIN, + "gist_type": GistModel.cls.GIST_PUBLIC, + "lifetime": -1, + "acl_level": Gist.ACL_LEVEL_PUBLIC, + "gist_mapping": { + b"filename1.txt": {"content": b"hello world"}, + }, } form_data.update(kwargs) gist = GistModel().create( - description=form_data['description'], owner=form_data['owner'], - gist_mapping=form_data['gist_mapping'], gist_type=form_data['gist_type'], - lifetime=form_data['lifetime'], gist_acl_level=form_data['acl_level'] + description=form_data["description"], + owner=form_data["owner"], + gist_mapping=form_data["gist_mapping"], + gist_type=form_data["gist_type"], + lifetime=form_data["lifetime"], + gist_acl_level=form_data["acl_level"], ) Session().commit() return gist @@ -420,7 +414,7 @@ class Fixture(object): Session().commit() def load_resource(self, resource_name, strip=False): - with open(os.path.join(FIXTURES, resource_name), 'rb') as f: + with open(os.path.join(FIXTURES, resource_name), "rb") as f: source = f.read() if strip: source = source.strip() diff --git a/rhodecode/tests/fixtures/rcextensions_fixtures.py b/rhodecode/tests/fixtures/rcextensions_fixtures.py new file mode 100644 index 00000000..c8879912 --- /dev/null +++ b/rhodecode/tests/fixtures/rcextensions_fixtures.py @@ -0,0 +1,157 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 os +import shutil +import logging +import textwrap + +import pytest + +import rhodecode +import rhodecode.lib + +from rhodecode.tests import console_printer + +log = logging.getLogger(__name__) + + +def store_rcextensions(destination, force=False): + from rhodecode.config import rcextensions + package_path = rcextensions.__path__[0] + + # Note: rcextensions are looked up based on the path of the ini file + rcextensions_path = os.path.join(destination, 'rcextensions') + + if force: + shutil.rmtree(rcextensions_path, ignore_errors=True) + shutil.copytree(package_path, rcextensions_path) + + +@pytest.fixture(scope="module") +def rcextensions(request, tmp_storage_location): + """ + Installs a testing rcextensions pack to ensure they work as expected. + """ + + # Note: rcextensions are looked up based on the path of the ini file + rcextensions_path = os.path.join(tmp_storage_location, 'rcextensions') + + if os.path.exists(rcextensions_path): + pytest.fail( + f"Path for rcextensions already exists, please clean up before " + f"test run this path: {rcextensions_path}") + else: + store_rcextensions(tmp_storage_location) + + +@pytest.fixture(scope='function') +def rcextensions_present(request): + + class RcExtensionsPresent: + def __init__(self, rcextensions_location): + self.rcextensions_location = rcextensions_location + + def __enter__(self): + self.store() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.cleanup() + + def store(self): + store_rcextensions(self.rcextensions_location) + + def cleanup(self): + shutil.rmtree(os.path.join(self.rcextensions_location, 'rcextensions')) + + return RcExtensionsPresent + + +@pytest.fixture(scope='function') +def rcextensions_modification(request): + """ + example usage:: + + hook_name = '_pre_push_hook' + code = ''' + raise OSError('failed') + return HookResponse(1, 'FAILED') + ''' + mods = [ + (hook_name, code), + ] + # rhodecode.ini file location, where rcextensions needs to live + rcstack_location = os.path.dirname(rcstack.config_file) + with rcextensions_modification(rcstack_location, mods): + # do some stuff + """ + + class RcextensionsModification: + def __init__(self, rcextensions_location, mods, create_if_missing=False, force_create=False): + self.force_create = force_create + self.create_if_missing = create_if_missing + self.rcextensions_location = rcextensions_location + self.mods = mods + if not isinstance(mods, list): + raise ValueError('mods must be a list of modifications') + + def __enter__(self): + if self.create_if_missing: + store_rcextensions(self.rcextensions_location, force=self.force_create) + + for hook_name, method_body in self.mods: + self.modification(hook_name, method_body) + + def __exit__(self, exc_type, exc_val, exc_tb): + self.cleanup() + + def cleanup(self): + # reset rcextensions to "bare" state from the package + store_rcextensions(self.rcextensions_location, force=True) + + def modification(self, hook_name, method_body): + import ast + + rcextensions_path = os.path.join(self.rcextensions_location, 'rcextensions') + + # Load the code from hooks.py + hooks_filename = os.path.join(rcextensions_path, 'hooks.py') + with open(hooks_filename, "r") as file: + tree = ast.parse(file.read()) + + # Define new content for the function as a string + new_code = textwrap.dedent(method_body) + + # Parse the new code to add it to the function + new_body = ast.parse(new_code).body + + # Walk through the AST to find and modify the function + for node in tree.body: + if isinstance(node, ast.FunctionDef) and node.name == hook_name: + node.body = new_body # Replace the function body with the new body + + # Compile the modified AST back to code + compile(tree, hooks_filename, "exec") + + # Write the updated code back to hooks.py + with open(hooks_filename, "w") as file: + file.write(ast.unparse(tree)) # Requires Python 3.9+ + + console_printer(f" [green]rcextensions[/green] Updated the body of '{hooks_filename}' function '{hook_name}'") + + return RcextensionsModification diff --git a/rhodecode/tests/functional/test_delegated_admin.py b/rhodecode/tests/functional/test_delegated_admin.py index 8e65348a..e48cf835 100644 --- a/rhodecode/tests/functional/test_delegated_admin.py +++ b/rhodecode/tests/functional/test_delegated_admin.py @@ -21,7 +21,7 @@ import pytest from rhodecode.tests import TestController -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.routes import route_path diff --git a/rhodecode/tests/integrations/__init__.py b/rhodecode/tests/integrations/__init__.py new file mode 100644 index 00000000..ae06165a --- /dev/null +++ b/rhodecode/tests/integrations/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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/ diff --git a/rhodecode/tests/integrations/test_integration.py b/rhodecode/tests/integrations/test_integration.py index 1e320b67..8c76dbc5 100644 --- a/rhodecode/tests/integrations/test_integration.py +++ b/rhodecode/tests/integrations/test_integration.py @@ -20,7 +20,7 @@ import time import pytest from rhodecode import events -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.model.db import Session, Integration from rhodecode.model.integration import IntegrationModel diff --git a/rhodecode/tests/lib/auth_modules/__init__.py b/rhodecode/tests/lib/auth_modules/__init__.py new file mode 100644 index 00000000..ae06165a --- /dev/null +++ b/rhodecode/tests/lib/auth_modules/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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/ diff --git a/rhodecode/tests/lib/middleware/test_simplehg.py b/rhodecode/tests/lib/middleware/test_simplehg.py index 69bbf833..608a245d 100644 --- a/rhodecode/tests/lib/middleware/test_simplehg.py +++ b/rhodecode/tests/lib/middleware/test_simplehg.py @@ -123,10 +123,14 @@ def test_get_config(user_util, baseapp, request_stub): ('web', 'allow_push', '*'), ('web', 'allow_archive', 'gz zip bz2'), ('web', 'baseurl', '/'), + + # largefiles data... ('vcs_git_lfs', 'store_location', hg_config_org.get('vcs_git_lfs', 'store_location')), + ('largefiles', 'usercache', hg_config_org.get('largefiles', 'usercache')), + ('vcs_svn_branch', '9aac1a38c3b8a0cdc4ae0f960a5f83332bc4fa5e', '/branches/*'), ('vcs_svn_branch', 'c7e6a611c87da06529fd0dd733308481d67c71a8', '/trunk'), - ('largefiles', 'usercache', hg_config_org.get('largefiles', 'usercache')), + ('hooks', 'preoutgoing.pre_pull', 'python:vcsserver.hooks.pre_pull'), ('hooks', 'prechangegroup.pre_push', 'python:vcsserver.hooks.pre_push'), ('hooks', 'outgoing.pull_logger', 'python:vcsserver.hooks.log_pull_action'), diff --git a/rhodecode/tests/lib/middleware/test_simplevcs.py b/rhodecode/tests/lib/middleware/test_simplevcs.py index c8129d24..2ed7cfde 100644 --- a/rhodecode/tests/lib/middleware/test_simplevcs.py +++ b/rhodecode/tests/lib/middleware/test_simplevcs.py @@ -22,7 +22,8 @@ import pytest from rhodecode.lib.str_utils import base64_to_str from rhodecode.lib.utils2 import AttributeDict -from rhodecode.tests.utils import CustomTestApp +from rhodecode.tests.fixtures.fixture_pyramid import ini_config +from rhodecode.tests.utils import CustomTestApp, AuthPluginManager from rhodecode.lib.caching_query import FromCache from rhodecode.lib.middleware import simplevcs @@ -34,6 +35,57 @@ from rhodecode.tests import ( HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) from rhodecode.tests.lib.middleware import mock_scm_app +from rhodecode.model.db import Permission, User +from rhodecode.model.meta import Session +from rhodecode.model.user import UserModel + + +@pytest.fixture() +def enable_auth_plugins(request, app): + """ + Return a factory object that when called, allows to control which + authentication plugins are enabled. + """ + + enabler = AuthPluginManager() + request.addfinalizer(enabler.cleanup) + + return enabler + + +@pytest.fixture() +def test_user_factory(request, baseapp): + + def user_factory(username='test_user', password='qweqwe', first_name='John', last_name='Testing', **kwargs): + usr = UserModel().create_or_update( + username=username, + password=password, + email=f'{username}@rhodecode.org', + firstname=first_name, lastname=last_name) + Session().commit() + + for k, v in kwargs.items(): + setattr(usr, k, v) + Session().add(usr) + + new_usr = User.get_by_username(username) + new_usr_id = new_usr.user_id + assert new_usr == usr + + @request.addfinalizer + def cleanup(): + if User.get(new_usr_id) is None: + return + + perm = Permission.query().all() + for p in perm: + UserModel().revoke_perm(usr, p) + + UserModel().delete(new_usr_id) + Session().commit() + return usr + + return user_factory class StubVCSController(simplevcs.SimpleVCS): @@ -107,8 +159,7 @@ def _remove_default_user_from_query_cache(): Session().expire(user) -def test_handles_exceptions_during_permissions_checks( - vcscontroller, disable_anonymous_user, enable_auth_plugins, test_user_factory): +def test_handles_exceptions_during_permissions_checks(vcscontroller, disable_anonymous_user, enable_auth_plugins, test_user_factory): test_password = 'qweqwe' test_user = test_user_factory(password=test_password, extern_type='headers', extern_name='headers') @@ -373,29 +424,30 @@ class TestShadowRepoExposure(object): controller.vcs_repo_name) -@pytest.mark.usefixtures('baseapp') class TestGenerateVcsResponse(object): - def test_ensures_that_start_response_is_called_early_enough(self): - self.call_controller_with_response_body(iter(['a', 'b'])) + def test_ensures_that_start_response_is_called_early_enough(self, baseapp): + app_ini_config = baseapp.config.registry.settings['__file__'] + self.call_controller_with_response_body(app_ini_config, iter(['a', 'b'])) assert self.start_response.called - def test_invalidates_cache_after_body_is_consumed(self): - result = self.call_controller_with_response_body(iter(['a', 'b'])) + def test_invalidates_cache_after_body_is_consumed(self, baseapp): + app_ini_config = baseapp.config.registry.settings['__file__'] + result = self.call_controller_with_response_body(app_ini_config, iter(['a', 'b'])) assert not self.was_cache_invalidated() # Consume the result list(result) assert self.was_cache_invalidated() - def test_raises_unknown_exceptions(self): - result = self.call_controller_with_response_body( - self.raise_result_iter(vcs_kind='unknown')) + def test_raises_unknown_exceptions(self, baseapp): + app_ini_config = baseapp.config.registry.settings['__file__'] + result = self.call_controller_with_response_body(app_ini_config, self.raise_result_iter(vcs_kind='unknown')) with pytest.raises(Exception): list(result) - def call_controller_with_response_body(self, response_body): + def call_controller_with_response_body(self, ini_config, response_body): + settings = { - 'base_path': 'fake_base_path', 'vcs.hooks.protocol.v2': 'celery', 'vcs.hooks.direct_calls': False, } @@ -407,7 +459,7 @@ class TestGenerateVcsResponse(object): result = controller._generate_vcs_response( environ={}, start_response=self.start_response, repo_path='fake_repo_path', - extras={}, action='push') + extras={'config': ini_config}, action='push') self.controller = controller return result diff --git a/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py b/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py index 3f9b7b75..6530618c 100644 --- a/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py +++ b/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py @@ -19,6 +19,7 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import pytest +import tempfile from rhodecode.tests.utils import CustomTestApp from rhodecode.lib.middleware.utils import scm_app_http, scm_app @@ -41,10 +42,13 @@ def vcsserver_http_echo_app(request, vcsserver_factory): """ A running VCSServer with the EchoApp activated via HTTP. """ - vcsserver = vcsserver_factory( + store_dir = tempfile.gettempdir() + + vcsserver_instance = vcsserver_factory( request=request, + store_dir=store_dir, overrides=[{'app:main': {'dev.use_echo_app': 'true'}}]) - return vcsserver + return vcsserver_instance @pytest.fixture(scope='session') diff --git a/rhodecode/tests/lib/test_auth.py b/rhodecode/tests/lib/test_auth.py index 63955c53..1b8f98bc 100644 --- a/rhodecode/tests/lib/test_auth.py +++ b/rhodecode/tests/lib/test_auth.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_diffs.py b/rhodecode/tests/lib/test_diffs.py index e543d78f..c38c7bb7 100644 --- a/rhodecode/tests/lib/test_diffs.py +++ b/rhodecode/tests/lib/test_diffs.py @@ -30,7 +30,7 @@ from rhodecode.lib.diffs import ( from rhodecode.lib.utils2 import AttributeDict from rhodecode.lib.vcs.backends.git import GitCommit -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests import no_newline_id_generator from rhodecode.lib.vcs.backends.git.repository import GitDiff from rhodecode.lib.vcs.backends.hg.repository import MercurialDiff diff --git a/rhodecode/tests/lib/test_hooks_daemon.py b/rhodecode/tests/lib/test_hooks_daemon.py index cf6b6500..1e4e079e 100644 --- a/rhodecode/tests/lib/test_hooks_daemon.py +++ b/rhodecode/tests/lib/test_hooks_daemon.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -18,305 +17,71 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import logging -import io import mock -import msgpack import pytest import tempfile -from rhodecode.lib.hook_daemon import http_hooks_deamon from rhodecode.lib.hook_daemon import celery_hooks_deamon -from rhodecode.lib.hook_daemon import hook_module +from rhodecode.lib.hook_daemon import utils as hooks_utils from rhodecode.lib.hook_daemon import base as hook_base -from rhodecode.lib.str_utils import safe_bytes -from rhodecode.tests.utils import assert_message_in_log -from rhodecode.lib.ext_json import json -test_proto = http_hooks_deamon.HooksHttpHandler.MSGPACK_HOOKS_PROTO +from rhodecode.tests.utils import assert_message_in_log class TestHooks(object): def test_hooks_can_be_used_as_a_context_processor(self): - hooks = hook_module.Hooks() + hooks = hook_base.Hooks() with hooks as return_value: pass assert hooks == return_value - -class TestHooksHttpHandler(object): - def test_read_request_parses_method_name_and_arguments(self): - data = { - 'method': 'test', - 'extras': { - 'param1': 1, - 'param2': 'a' - } - } - request = self._generate_post_request(data) - hooks_patcher = mock.patch.object( - hook_module.Hooks, data['method'], create=True, return_value=1) - - with hooks_patcher as hooks_mock: - handler = http_hooks_deamon.HooksHttpHandler - handler.DEFAULT_HOOKS_PROTO = test_proto - handler.wbufsize = 10240 - MockServer(handler, request) - - hooks_mock.assert_called_once_with(data['extras']) - - def test_hooks_serialized_result_is_returned(self): - request = self._generate_post_request({}) - rpc_method = 'test' - hook_result = { - 'first': 'one', - 'second': 2 - } - extras = {} - - # patching our _read to return test method and proto used - read_patcher = mock.patch.object( - http_hooks_deamon.HooksHttpHandler, '_read_request', - return_value=(test_proto, rpc_method, extras)) - - # patch Hooks instance to return hook_result data on 'test' call - hooks_patcher = mock.patch.object( - hook_module.Hooks, rpc_method, create=True, - return_value=hook_result) - - with read_patcher, hooks_patcher: - handler = http_hooks_deamon.HooksHttpHandler - handler.DEFAULT_HOOKS_PROTO = test_proto - handler.wbufsize = 10240 - server = MockServer(handler, request) - - expected_result = http_hooks_deamon.HooksHttpHandler.serialize_data(hook_result) - - server.request.output_stream.seek(0) - assert server.request.output_stream.readlines()[-1] == expected_result - - def test_exception_is_returned_in_response(self): - request = self._generate_post_request({}) - rpc_method = 'test' - - read_patcher = mock.patch.object( - http_hooks_deamon.HooksHttpHandler, '_read_request', - return_value=(test_proto, rpc_method, {})) - - hooks_patcher = mock.patch.object( - hook_module.Hooks, rpc_method, create=True, - side_effect=Exception('Test exception')) - - with read_patcher, hooks_patcher: - handler = http_hooks_deamon.HooksHttpHandler - handler.DEFAULT_HOOKS_PROTO = test_proto - handler.wbufsize = 10240 - server = MockServer(handler, request) - - server.request.output_stream.seek(0) - data = server.request.output_stream.readlines() - msgpack_data = b''.join(data[5:]) - org_exc = http_hooks_deamon.HooksHttpHandler.deserialize_data(msgpack_data) - expected_result = { - 'exception': 'Exception', - 'exception_traceback': org_exc['exception_traceback'], - 'exception_args': ['Test exception'] - } - assert org_exc == expected_result - - def test_log_message_writes_to_debug_log(self, caplog): - ip_port = ('0.0.0.0', 8888) - handler = http_hooks_deamon.HooksHttpHandler(MockRequest('POST /'), ip_port, mock.Mock()) - fake_date = '1/Nov/2015 00:00:00' - date_patcher = mock.patch.object( - handler, 'log_date_time_string', return_value=fake_date) - - with date_patcher, caplog.at_level(logging.DEBUG): - handler.log_message('Some message %d, %s', 123, 'string') - - expected_message = f"HOOKS: client={ip_port} - - [{fake_date}] Some message 123, string" - - assert_message_in_log( - caplog.records, expected_message, - levelno=logging.DEBUG, module='http_hooks_deamon') - - def _generate_post_request(self, data, proto=test_proto): - if proto == http_hooks_deamon.HooksHttpHandler.MSGPACK_HOOKS_PROTO: - payload = msgpack.packb(data) - else: - payload = json.dumps(data) - - return b'POST / HTTP/1.0\nContent-Length: %d\n\n%b' % ( - len(payload), payload) - - -class ThreadedHookCallbackDaemon(object): - def test_constructor_calls_prepare(self): - prepare_daemon_patcher = mock.patch.object( - http_hooks_deamon.ThreadedHookCallbackDaemon, '_prepare') - with prepare_daemon_patcher as prepare_daemon_mock: - http_hooks_deamon.ThreadedHookCallbackDaemon() - prepare_daemon_mock.assert_called_once_with() - - def test_run_is_called_on_context_start(self): - patchers = mock.patch.multiple( - http_hooks_deamon.ThreadedHookCallbackDaemon, - _run=mock.DEFAULT, _prepare=mock.DEFAULT, __exit__=mock.DEFAULT) - - with patchers as mocks: - daemon = http_hooks_deamon.ThreadedHookCallbackDaemon() - with daemon as daemon_context: - pass - mocks['_run'].assert_called_once_with() - assert daemon_context == daemon - - def test_stop_is_called_on_context_exit(self): - patchers = mock.patch.multiple( - http_hooks_deamon.ThreadedHookCallbackDaemon, - _run=mock.DEFAULT, _prepare=mock.DEFAULT, _stop=mock.DEFAULT) - - with patchers as mocks: - daemon = http_hooks_deamon.ThreadedHookCallbackDaemon() - with daemon as daemon_context: - assert mocks['_stop'].call_count == 0 - - mocks['_stop'].assert_called_once_with() - assert daemon_context == daemon - - -class TestHttpHooksCallbackDaemon(object): - def test_hooks_callback_generates_new_port(self, caplog): - with caplog.at_level(logging.DEBUG): - daemon = http_hooks_deamon.HttpHooksCallbackDaemon(host='127.0.0.1', port=8881) - assert daemon._daemon.server_address == ('127.0.0.1', 8881) - - with caplog.at_level(logging.DEBUG): - daemon = http_hooks_deamon.HttpHooksCallbackDaemon(host=None, port=None) - assert daemon._daemon.server_address[1] in range(0, 66000) - assert daemon._daemon.server_address[0] != '127.0.0.1' - - def test_prepare_inits_daemon_variable(self, tcp_server, caplog): - with self._tcp_patcher(tcp_server), caplog.at_level(logging.DEBUG): - daemon = http_hooks_deamon.HttpHooksCallbackDaemon(host='127.0.0.1', port=8881) - assert daemon._daemon == tcp_server - - _, port = tcp_server.server_address - - msg = f"HOOKS: 127.0.0.1:{port} Preparing HTTP callback daemon registering " \ - f"hook object: " - assert_message_in_log( - caplog.records, msg, levelno=logging.DEBUG, module='http_hooks_deamon') - - def test_prepare_inits_hooks_uri_and_logs_it( - self, tcp_server, caplog): - with self._tcp_patcher(tcp_server), caplog.at_level(logging.DEBUG): - daemon = http_hooks_deamon.HttpHooksCallbackDaemon(host='127.0.0.1', port=8881) - - _, port = tcp_server.server_address - expected_uri = '{}:{}'.format('127.0.0.1', port) - assert daemon.hooks_uri == expected_uri - - msg = f"HOOKS: 127.0.0.1:{port} Preparing HTTP callback daemon registering " \ - f"hook object: " - - assert_message_in_log( - caplog.records, msg, - levelno=logging.DEBUG, module='http_hooks_deamon') - - def test_run_creates_a_thread(self, tcp_server): - thread = mock.Mock() - - with self._tcp_patcher(tcp_server): - daemon = http_hooks_deamon.HttpHooksCallbackDaemon() - - with self._thread_patcher(thread) as thread_mock: - daemon._run() - - thread_mock.assert_called_once_with( - target=tcp_server.serve_forever, - kwargs={'poll_interval': daemon.POLL_INTERVAL}) - assert thread.daemon is True - thread.start.assert_called_once_with() - - def test_run_logs(self, tcp_server, caplog): - - with self._tcp_patcher(tcp_server): - daemon = http_hooks_deamon.HttpHooksCallbackDaemon() - - with self._thread_patcher(mock.Mock()), caplog.at_level(logging.DEBUG): - daemon._run() - - assert_message_in_log( - caplog.records, - 'Running thread-based loop of callback daemon in background', - levelno=logging.DEBUG, module='http_hooks_deamon') - - def test_stop_cleans_up_the_connection(self, tcp_server, caplog): - thread = mock.Mock() - - with self._tcp_patcher(tcp_server): - daemon = http_hooks_deamon.HttpHooksCallbackDaemon() - - with self._thread_patcher(thread), caplog.at_level(logging.DEBUG): - with daemon: - assert daemon._daemon == tcp_server - assert daemon._callback_thread == thread - - assert daemon._daemon is None - assert daemon._callback_thread is None - tcp_server.shutdown.assert_called_with() - thread.join.assert_called_once_with() - - assert_message_in_log( - caplog.records, 'Waiting for background thread to finish.', - levelno=logging.DEBUG, module='http_hooks_deamon') - - def _tcp_patcher(self, tcp_server): - return mock.patch.object( - http_hooks_deamon, 'TCPServer', return_value=tcp_server) - - def _thread_patcher(self, thread): - return mock.patch.object( - http_hooks_deamon.threading, 'Thread', return_value=thread) - - class TestPrepareHooksDaemon(object): @pytest.mark.parametrize('protocol', ('celery',)) - def test_returns_celery_hooks_callback_daemon_when_celery_protocol_specified( - self, protocol): + def test_returns_celery_hooks_callback_daemon_when_celery_protocol_specified(self, protocol): with tempfile.NamedTemporaryFile(mode='w') as temp_file: - temp_file.write("[app:main]\ncelery.broker_url = redis://redis/0\n" - "celery.result_backend = redis://redis/0") + temp_file.write( + "[app:main]\n" + "celery.broker_url = redis://redis/0\n" + "celery.result_backend = redis://redis/0\n" + ) temp_file.flush() expected_extras = {'config': temp_file.name} - callback, extras = hook_base.prepare_callback_daemon( - expected_extras, protocol=protocol, host='') + callback, extras = hooks_utils.prepare_callback_daemon(expected_extras, protocol=protocol) assert isinstance(callback, celery_hooks_deamon.CeleryHooksCallbackDaemon) @pytest.mark.parametrize('protocol, expected_class', ( - ('http', http_hooks_deamon.HttpHooksCallbackDaemon), + ('celery', celery_hooks_deamon.CeleryHooksCallbackDaemon), )) - def test_returns_real_hooks_callback_daemon_when_protocol_is_specified( - self, protocol, expected_class): - expected_extras = { - 'extra1': 'value1', - 'txn_id': 'txnid2', - 'hooks_protocol': protocol.lower(), - 'task_backend': '', - 'task_queue': '', - 'repo_store': '/var/opt/rhodecode_repo_store', - 'repository': 'rhodecode', - } - from rhodecode import CONFIG - CONFIG['vcs.svn.redis_conn'] = 'redis://redis:6379/0' - callback, extras = hook_base.prepare_callback_daemon( - expected_extras.copy(), protocol=protocol, host='127.0.0.1', - txn_id='txnid2') - assert isinstance(callback, expected_class) - extras.pop('hooks_uri') - expected_extras['time'] = extras['time'] - assert extras == expected_extras + def test_returns_real_hooks_callback_daemon_when_protocol_is_specified(self, protocol, expected_class): + + with tempfile.NamedTemporaryFile(mode='w') as temp_file: + temp_file.write( + "[app:main]\n" + "celery.broker_url = redis://redis:6379/0\n" + "celery.result_backend = redis://redis:6379/0\n" + ) + temp_file.flush() + + expected_extras = { + 'extra1': 'value1', + 'txn_id': 'txnid2', + 'hooks_protocol': protocol.lower(), + 'hooks_config': { + 'broker_url': 'redis://redis:6379/0', + 'result_backend': 'redis://redis:6379/0', + }, + 'repo_store': '/var/opt/rhodecode_repo_store', + 'repository': 'rhodecode', + 'config': temp_file.name + } + from rhodecode import CONFIG + CONFIG['vcs.svn.redis_conn'] = 'redis://redis:6379/0' + callback, extras = hooks_utils.prepare_callback_daemon(expected_extras.copy(), protocol=protocol,txn_id='txnid2') + assert isinstance(callback, expected_class) + expected_extras['time'] = extras['time'] + assert extras == expected_extras @pytest.mark.parametrize('protocol', ( 'invalid', @@ -330,35 +95,4 @@ class TestPrepareHooksDaemon(object): 'hooks_protocol': protocol.lower() } with pytest.raises(Exception): - callback, extras = hook_base.prepare_callback_daemon( - expected_extras.copy(), - protocol=protocol, host='127.0.0.1') - - -class MockRequest(object): - - def __init__(self, request): - self.request = request - self.input_stream = io.BytesIO(safe_bytes(self.request)) - self.output_stream = io.BytesIO() # make it un-closable for testing invesitagion - self.output_stream.close = lambda: None - - def makefile(self, mode, *args, **kwargs): - return self.output_stream if mode == 'wb' else self.input_stream - - -class MockServer(object): - - def __init__(self, handler_cls, request): - ip_port = ('0.0.0.0', 8888) - self.request = MockRequest(request) - self.server_address = ip_port - self.handler = handler_cls(self.request, ip_port, self) - - -@pytest.fixture() -def tcp_server(): - server = mock.Mock() - server.server_address = ('127.0.0.1', 8881) - server.wbufsize = 1024 - return server + callback, extras = hooks_utils.prepare_callback_daemon(expected_extras.copy(), protocol=protocol) diff --git a/rhodecode/tests/lib/test_utils.py b/rhodecode/tests/lib/test_utils.py index 41a48c40..6f68b38e 100644 --- a/rhodecode/tests/lib/test_utils.py +++ b/rhodecode/tests/lib/test_utils.py @@ -33,7 +33,7 @@ from rhodecode.model import meta from rhodecode.model.repo import RepoModel from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.settings import UiSetting, SettingsModel -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode_tools.lib.hash_utils import md5_safe from rhodecode.lib.ext_json import json @@ -403,12 +403,9 @@ class TestPrepareConfigData(object): self._assert_repo_name_passed(model_mock, repo_name) - expected_result = [ - ('section1', 'option1', 'value1'), - ('section2', 'option2', 'value2'), - ] - # We have extra config items returned, so we're ignoring two last items - assert result[:2] == expected_result + assert ('section1', 'option1', 'value1') in result + assert ('section2', 'option2', 'value2') in result + assert ('section3', 'option3', 'value3') not in result def _assert_repo_name_passed(self, model_mock, repo_name): assert model_mock.call_count == 1 diff --git a/rhodecode/tests/load/http_performance.py b/rhodecode/tests/load/http_performance.py index 505e37d4..ce64121b 100644 --- a/rhodecode/tests/load/http_performance.py +++ b/rhodecode/tests/load/http_performance.py @@ -25,7 +25,7 @@ It works by replaying a group of commits to the repo. import argparse import collections -import ConfigParser +import configparser import functools import itertools import os @@ -294,7 +294,7 @@ class HgMixin(object): def add_remote(self, repo, remote_url, remote_name='upstream'): self.remove_remote(repo, remote_name) os.chdir(repo) - hgrc = ConfigParser.RawConfigParser() + hgrc = configparser.RawConfigParser() hgrc.read('.hg/hgrc') hgrc.set('paths', remote_name, remote_url) with open('.hg/hgrc', 'w') as f: @@ -303,7 +303,7 @@ class HgMixin(object): @keep_cwd def remove_remote(self, repo, remote_name='upstream'): os.chdir(repo) - hgrc = ConfigParser.RawConfigParser() + hgrc = configparser.RawConfigParser() hgrc.read('.hg/hgrc') hgrc.remove_option('paths', remote_name) with open('.hg/hgrc', 'w') as f: diff --git a/rhodecode/tests/load/profile-mem.py b/rhodecode/tests/load/profile-mem.py index 087fc644..96778bd1 100644 --- a/rhodecode/tests/load/profile-mem.py +++ b/rhodecode/tests/load/profile-mem.py @@ -59,16 +59,6 @@ def parse_options(): parser.add_argument( '--interval', '-i', type=float, default=5, help="Interval in secods.") - parser.add_argument( - '--appenlight', '--ae', action='store_true') - parser.add_argument( - '--appenlight-url', '--ae-url', - default='https://ae.rhodecode.com/api/logs', - help='URL of the Appenlight API endpoint, defaults to "%(default)s".') - parser.add_argument( - '--appenlight-api-key', '--ae-key', - help='API key to use when sending data to appenlight. This has to be ' - 'set if Appenlight is enabled.') return parser.parse_args() diff --git a/rhodecode/tests/load/profile.py b/rhodecode/tests/load/profile.py index f45567af..5a6a1f5f 100644 --- a/rhodecode/tests/load/profile.py +++ b/rhodecode/tests/load/profile.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify diff --git a/rhodecode/tests/load/vcs_performance.py b/rhodecode/tests/load/vcs_performance.py index e5ab98a7..f4ca7294 100644 --- a/rhodecode/tests/load/vcs_performance.py +++ b/rhodecode/tests/load/vcs_performance.py @@ -1,5 +1,3 @@ - - # Copyright (C) 2016-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify diff --git a/rhodecode/tests/models/common.py b/rhodecode/tests/models/common.py index f41c4aab..0c98304e 100644 --- a/rhodecode/tests/models/common.py +++ b/rhodecode/tests/models/common.py @@ -22,7 +22,7 @@ from rhodecode.model.meta import Session from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.repo import RepoModel from rhodecode.model.user import UserModel -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture fixture = Fixture() diff --git a/rhodecode/tests/models/test_notifications.py b/rhodecode/tests/models/test_notifications.py index 3b5b8715..fee49a4f 100644 --- a/rhodecode/tests/models/test_notifications.py +++ b/rhodecode/tests/models/test_notifications.py @@ -19,7 +19,7 @@ import pytest -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.model.db import User, Notification, UserNotification from rhodecode.model.meta import Session diff --git a/rhodecode/tests/models/test_permissions.py b/rhodecode/tests/models/test_permissions.py index 2efb6475..8722a774 100644 --- a/rhodecode/tests/models/test_permissions.py +++ b/rhodecode/tests/models/test_permissions.py @@ -29,7 +29,7 @@ from rhodecode.model.repo import RepoModel from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.user import UserModel from rhodecode.model.user_group import UserGroupModel -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture fixture = Fixture() diff --git a/rhodecode/tests/models/test_pullrequest.py b/rhodecode/tests/models/test_pullrequest.py index 0df90b8c..9a09c098 100644 --- a/rhodecode/tests/models/test_pullrequest.py +++ b/rhodecode/tests/models/test_pullrequest.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,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 os import mock import pytest @@ -23,8 +23,7 @@ import textwrap import rhodecode from rhodecode.lib.vcs.backends import get_backend -from rhodecode.lib.vcs.backends.base import ( - MergeResponse, MergeFailureReason, Reference) +from rhodecode.lib.vcs.backends.base import MergeResponse, MergeFailureReason, Reference from rhodecode.lib.vcs.exceptions import RepositoryError from rhodecode.lib.vcs.nodes import FileNode from rhodecode.model.comment import CommentsModel @@ -39,54 +38,42 @@ pytestmark = [ ] -@pytest.mark.usefixtures('config_stub') +@pytest.mark.usefixtures("config_stub") class TestPullRequestModel(object): - @pytest.fixture() def pull_request(self, request, backend, pr_util): """ A pull request combined with multiples patches. """ BackendClass = get_backend(backend.alias) - merge_resp = MergeResponse( - False, False, None, MergeFailureReason.UNKNOWN, - metadata={'exception': 'MockError'}) - self.merge_patcher = mock.patch.object( - BackendClass, 'merge', return_value=merge_resp) - self.workspace_remove_patcher = mock.patch.object( - BackendClass, 'cleanup_merge_workspace') + merge_resp = MergeResponse(False, False, None, MergeFailureReason.UNKNOWN, metadata={"exception": "MockError"}) + self.merge_patcher = mock.patch.object(BackendClass, "merge", return_value=merge_resp) + self.workspace_remove_patcher = mock.patch.object(BackendClass, "cleanup_merge_workspace") self.workspace_remove_mock = self.workspace_remove_patcher.start() self.merge_mock = self.merge_patcher.start() - self.comment_patcher = mock.patch( - 'rhodecode.model.changeset_status.ChangesetStatusModel.set_status') + self.comment_patcher = mock.patch("rhodecode.model.changeset_status.ChangesetStatusModel.set_status") self.comment_patcher.start() - self.notification_patcher = mock.patch( - 'rhodecode.model.notification.NotificationModel.create') + self.notification_patcher = mock.patch("rhodecode.model.notification.NotificationModel.create") self.notification_patcher.start() - self.helper_patcher = mock.patch( - 'rhodecode.lib.helpers.route_path') + self.helper_patcher = mock.patch("rhodecode.lib.helpers.route_path") self.helper_patcher.start() - self.hook_patcher = mock.patch.object(PullRequestModel, - 'trigger_pull_request_hook') + self.hook_patcher = mock.patch.object(PullRequestModel, "trigger_pull_request_hook") self.hook_mock = self.hook_patcher.start() - self.invalidation_patcher = mock.patch( - 'rhodecode.model.pull_request.ScmModel.mark_for_invalidation') + self.invalidation_patcher = mock.patch("rhodecode.model.pull_request.ScmModel.mark_for_invalidation") self.invalidation_mock = self.invalidation_patcher.start() - self.pull_request = pr_util.create_pull_request( - mergeable=True, name_suffix=u'ąć') + self.pull_request = pr_util.create_pull_request(mergeable=True, name_suffix="ąć") self.source_commit = self.pull_request.source_ref_parts.commit_id self.target_commit = self.pull_request.target_ref_parts.commit_id - self.workspace_id = 'pr-%s' % self.pull_request.pull_request_id + self.workspace_id = f"pr-{self.pull_request.pull_request_id}" self.repo_id = self.pull_request.target_repo.repo_id @request.addfinalizer def cleanup_pull_request(): - calls = [mock.call( - self.pull_request, self.pull_request.author, 'create')] + calls = [mock.call(self.pull_request, self.pull_request.author, "create")] self.hook_mock.assert_has_calls(calls) self.workspace_remove_patcher.stop() @@ -114,29 +101,30 @@ class TestPullRequestModel(object): assert len(prs) == 1 def test_count_awaiting_review(self, pull_request): - pr_count = PullRequestModel().count_awaiting_review( - pull_request.target_repo) + pr_count = PullRequestModel().count_awaiting_review(pull_request.target_repo) assert pr_count == 1 def test_get_awaiting_my_review(self, pull_request): PullRequestModel().update_reviewers( - pull_request, [(pull_request.author, ['author'], False, 'reviewer', [])], - pull_request.author) + pull_request, [(pull_request.author, ["author"], False, "reviewer", [])], pull_request.author + ) Session().commit() prs = PullRequestModel().get_awaiting_my_review( - pull_request.target_repo.repo_name, user_id=pull_request.author.user_id) + pull_request.target_repo.repo_name, user_id=pull_request.author.user_id + ) assert isinstance(prs, list) assert len(prs) == 1 def test_count_awaiting_my_review(self, pull_request): PullRequestModel().update_reviewers( - pull_request, [(pull_request.author, ['author'], False, 'reviewer', [])], - pull_request.author) + pull_request, [(pull_request.author, ["author"], False, "reviewer", [])], pull_request.author + ) Session().commit() pr_count = PullRequestModel().count_awaiting_my_review( - pull_request.target_repo.repo_name, user_id=pull_request.author.user_id) + pull_request.target_repo.repo_name, user_id=pull_request.author.user_id + ) assert pr_count == 1 def test_delete_calls_cleanup_merge(self, pull_request): @@ -144,24 +132,19 @@ class TestPullRequestModel(object): PullRequestModel().delete(pull_request, pull_request.author) Session().commit() - self.workspace_remove_mock.assert_called_once_with( - repo_id, self.workspace_id) + self.workspace_remove_mock.assert_called_once_with(repo_id, self.workspace_id) def test_close_calls_cleanup_and_hook(self, pull_request): - PullRequestModel().close_pull_request( - pull_request, pull_request.author) + PullRequestModel().close_pull_request(pull_request, pull_request.author) Session().commit() repo_id = pull_request.target_repo.repo_id - self.workspace_remove_mock.assert_called_once_with( - repo_id, self.workspace_id) - self.hook_mock.assert_called_with( - self.pull_request, self.pull_request.author, 'close') + self.workspace_remove_mock.assert_called_once_with(repo_id, self.workspace_id) + self.hook_mock.assert_called_with(self.pull_request, self.pull_request.author, "close") def test_merge_status(self, pull_request): - self.merge_mock.return_value = MergeResponse( - True, False, None, MergeFailureReason.NONE) + self.merge_mock.return_value = MergeResponse(True, False, None, MergeFailureReason.NONE) assert pull_request._last_merge_source_rev is None assert pull_request._last_merge_target_rev is None @@ -169,13 +152,17 @@ class TestPullRequestModel(object): merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is True - assert msg == 'This pull request can be automatically merged.' + assert msg == "This pull request can be automatically merged." self.merge_mock.assert_called_with( - self.repo_id, self.workspace_id, + self.repo_id, + self.workspace_id, pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), - pull_request.source_ref_parts, dry_run=True, - use_rebase=False, close_branch=False) + pull_request.source_ref_parts, + dry_run=True, + use_rebase=False, + close_branch=False, + ) assert pull_request._last_merge_source_rev == self.source_commit assert pull_request._last_merge_target_rev == self.target_commit @@ -184,13 +171,13 @@ class TestPullRequestModel(object): self.merge_mock.reset_mock() merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is True - assert msg == 'This pull request can be automatically merged.' + assert msg == "This pull request can be automatically merged." assert self.merge_mock.called is False def test_merge_status_known_failure(self, pull_request): self.merge_mock.return_value = MergeResponse( - False, False, None, MergeFailureReason.MERGE_FAILED, - metadata={'unresolved_files': 'file1'}) + False, False, None, MergeFailureReason.MERGE_FAILED, metadata={"unresolved_files": "file1"} + ) assert pull_request._last_merge_source_rev is None assert pull_request._last_merge_target_rev is None @@ -198,13 +185,17 @@ class TestPullRequestModel(object): merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == 'This pull request cannot be merged because of merge conflicts. file1' + assert msg == "This pull request cannot be merged because of merge conflicts. file1" self.merge_mock.assert_called_with( - self.repo_id, self.workspace_id, + self.repo_id, + self.workspace_id, pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), - pull_request.source_ref_parts, dry_run=True, - use_rebase=False, close_branch=False) + pull_request.source_ref_parts, + dry_run=True, + use_rebase=False, + close_branch=False, + ) assert pull_request._last_merge_source_rev == self.source_commit assert pull_request._last_merge_target_rev == self.target_commit @@ -213,13 +204,13 @@ class TestPullRequestModel(object): self.merge_mock.reset_mock() merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == 'This pull request cannot be merged because of merge conflicts. file1' + assert msg == "This pull request cannot be merged because of merge conflicts. file1" assert self.merge_mock.called is False def test_merge_status_unknown_failure(self, pull_request): self.merge_mock.return_value = MergeResponse( - False, False, None, MergeFailureReason.UNKNOWN, - metadata={'exception': 'MockError'}) + False, False, None, MergeFailureReason.UNKNOWN, metadata={"exception": "MockError"} + ) assert pull_request._last_merge_source_rev is None assert pull_request._last_merge_target_rev is None @@ -227,15 +218,17 @@ class TestPullRequestModel(object): merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == ( - 'This pull request cannot be merged because of an unhandled exception. ' - 'MockError') + assert msg == "This pull request cannot be merged because of an unhandled exception. MockError" self.merge_mock.assert_called_with( - self.repo_id, self.workspace_id, + self.repo_id, + self.workspace_id, pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), - pull_request.source_ref_parts, dry_run=True, - use_rebase=False, close_branch=False) + pull_request.source_ref_parts, + dry_run=True, + use_rebase=False, + close_branch=False, + ) assert pull_request._last_merge_source_rev is None assert pull_request._last_merge_target_rev is None @@ -244,155 +237,136 @@ class TestPullRequestModel(object): self.merge_mock.reset_mock() merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == ( - 'This pull request cannot be merged because of an unhandled exception. ' - 'MockError') + assert msg == "This pull request cannot be merged because of an unhandled exception. MockError" assert self.merge_mock.called is True def test_merge_status_when_target_is_locked(self, pull_request): - pull_request.target_repo.locked = [1, u'12345.50', 'lock_web'] + pull_request.target_repo.locked = [1, "12345.50", "lock_web"] merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == ( - 'This pull request cannot be merged because the target repository ' - 'is locked by user:1.') + assert msg == "This pull request cannot be merged because the target repository is locked by user:1." def test_merge_status_requirements_check_target(self, pull_request): - def has_largefiles(self, repo): return repo == pull_request.source_repo - patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles) + patcher = mock.patch.object(PullRequestModel, "_has_largefiles", has_largefiles) with patcher: merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == 'Target repository large files support is disabled.' + assert msg == "Target repository large files support is disabled." def test_merge_status_requirements_check_source(self, pull_request): - def has_largefiles(self, repo): return repo == pull_request.target_repo - patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles) + patcher = mock.patch.object(PullRequestModel, "_has_largefiles", has_largefiles) with patcher: merge_response, status, msg = PullRequestModel().merge_status(pull_request) assert status is False - assert msg == 'Source repository large files support is disabled.' + assert msg == "Source repository large files support is disabled." def test_merge(self, pull_request, merge_extras): user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN) - merge_ref = Reference( - 'type', 'name', '6126b7bfcc82ad2d3deaee22af926b082ce54cc6') - self.merge_mock.return_value = MergeResponse( - True, True, merge_ref, MergeFailureReason.NONE) + merge_ref = Reference("type", "name", "6126b7bfcc82ad2d3deaee22af926b082ce54cc6") + self.merge_mock.return_value = MergeResponse(True, True, merge_ref, MergeFailureReason.NONE) - merge_extras['repository'] = pull_request.target_repo.repo_name - PullRequestModel().merge_repo( - pull_request, pull_request.author, extras=merge_extras) + merge_extras["repository"] = pull_request.target_repo.repo_name + PullRequestModel().merge_repo(pull_request, pull_request.author, extras=merge_extras) Session().commit() - message = ( - u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}' - u'\n\n {pr_title}'.format( - pr_id=pull_request.pull_request_id, - source_repo=safe_str( - pull_request.source_repo.scm_instance().name), - source_ref_name=pull_request.source_ref_parts.name, - pr_title=safe_str(pull_request.title) - ) + message = "Merge pull request !{pr_id} from {source_repo} {source_ref_name}" "\n\n {pr_title}".format( + pr_id=pull_request.pull_request_id, + source_repo=safe_str(pull_request.source_repo.scm_instance().name), + source_ref_name=pull_request.source_ref_parts.name, + pr_title=safe_str(pull_request.title), ) self.merge_mock.assert_called_with( - self.repo_id, self.workspace_id, + self.repo_id, + self.workspace_id, pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, - user_name=user.short_contact, user_email=user.email, message=message, - use_rebase=False, close_branch=False + user_name=user.short_contact, + user_email=user.email, + message=message, + use_rebase=False, + close_branch=False, ) - self.invalidation_mock.assert_called_once_with( - pull_request.target_repo.repo_name) + self.invalidation_mock.assert_called_once_with(pull_request.target_repo.repo_name) - self.hook_mock.assert_called_with( - self.pull_request, self.pull_request.author, 'merge') + self.hook_mock.assert_called_with(self.pull_request, self.pull_request.author, "merge") pull_request = PullRequest.get(pull_request.pull_request_id) - assert pull_request.merge_rev == '6126b7bfcc82ad2d3deaee22af926b082ce54cc6' + assert pull_request.merge_rev == "6126b7bfcc82ad2d3deaee22af926b082ce54cc6" def test_merge_with_status_lock(self, pull_request, merge_extras): user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN) - merge_ref = Reference( - 'type', 'name', '6126b7bfcc82ad2d3deaee22af926b082ce54cc6') - self.merge_mock.return_value = MergeResponse( - True, True, merge_ref, MergeFailureReason.NONE) + merge_ref = Reference("type", "name", "6126b7bfcc82ad2d3deaee22af926b082ce54cc6") + self.merge_mock.return_value = MergeResponse(True, True, merge_ref, MergeFailureReason.NONE) - merge_extras['repository'] = pull_request.target_repo.repo_name + merge_extras["repository"] = pull_request.target_repo.repo_name with pull_request.set_state(PullRequest.STATE_UPDATING): assert pull_request.pull_request_state == PullRequest.STATE_UPDATING - PullRequestModel().merge_repo( - pull_request, pull_request.author, extras=merge_extras) + PullRequestModel().merge_repo(pull_request, pull_request.author, extras=merge_extras) Session().commit() assert pull_request.pull_request_state == PullRequest.STATE_CREATED - message = ( - u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}' - u'\n\n {pr_title}'.format( - pr_id=pull_request.pull_request_id, - source_repo=safe_str( - pull_request.source_repo.scm_instance().name), - source_ref_name=pull_request.source_ref_parts.name, - pr_title=safe_str(pull_request.title) - ) + message = "Merge pull request !{pr_id} from {source_repo} {source_ref_name}" "\n\n {pr_title}".format( + pr_id=pull_request.pull_request_id, + source_repo=safe_str(pull_request.source_repo.scm_instance().name), + source_ref_name=pull_request.source_ref_parts.name, + pr_title=safe_str(pull_request.title), ) self.merge_mock.assert_called_with( - self.repo_id, self.workspace_id, + self.repo_id, + self.workspace_id, pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, - user_name=user.short_contact, user_email=user.email, message=message, - use_rebase=False, close_branch=False + user_name=user.short_contact, + user_email=user.email, + message=message, + use_rebase=False, + close_branch=False, ) - self.invalidation_mock.assert_called_once_with( - pull_request.target_repo.repo_name) + self.invalidation_mock.assert_called_once_with(pull_request.target_repo.repo_name) - self.hook_mock.assert_called_with( - self.pull_request, self.pull_request.author, 'merge') + self.hook_mock.assert_called_with(self.pull_request, self.pull_request.author, "merge") pull_request = PullRequest.get(pull_request.pull_request_id) - assert pull_request.merge_rev == '6126b7bfcc82ad2d3deaee22af926b082ce54cc6' + assert pull_request.merge_rev == "6126b7bfcc82ad2d3deaee22af926b082ce54cc6" def test_merge_failed(self, pull_request, merge_extras): user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN) - merge_ref = Reference( - 'type', 'name', '6126b7bfcc82ad2d3deaee22af926b082ce54cc6') - self.merge_mock.return_value = MergeResponse( - False, False, merge_ref, MergeFailureReason.MERGE_FAILED) + merge_ref = Reference("type", "name", "6126b7bfcc82ad2d3deaee22af926b082ce54cc6") + self.merge_mock.return_value = MergeResponse(False, False, merge_ref, MergeFailureReason.MERGE_FAILED) - merge_extras['repository'] = pull_request.target_repo.repo_name - PullRequestModel().merge_repo( - pull_request, pull_request.author, extras=merge_extras) + merge_extras["repository"] = pull_request.target_repo.repo_name + PullRequestModel().merge_repo(pull_request, pull_request.author, extras=merge_extras) Session().commit() - message = ( - u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}' - u'\n\n {pr_title}'.format( - pr_id=pull_request.pull_request_id, - source_repo=safe_str( - pull_request.source_repo.scm_instance().name), - source_ref_name=pull_request.source_ref_parts.name, - pr_title=safe_str(pull_request.title) - ) + message = "Merge pull request !{pr_id} from {source_repo} {source_ref_name}" "\n\n {pr_title}".format( + pr_id=pull_request.pull_request_id, + source_repo=safe_str(pull_request.source_repo.scm_instance().name), + source_ref_name=pull_request.source_ref_parts.name, + pr_title=safe_str(pull_request.title), ) self.merge_mock.assert_called_with( - self.repo_id, self.workspace_id, + self.repo_id, + self.workspace_id, pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, - user_name=user.short_contact, user_email=user.email, message=message, - use_rebase=False, close_branch=False + user_name=user.short_contact, + user_email=user.email, + message=message, + use_rebase=False, + close_branch=False, ) pull_request = PullRequest.get(pull_request.pull_request_id) @@ -410,7 +384,7 @@ class TestPullRequestModel(object): assert commit_ids == pull_request.revisions # Merge revision is not in the revisions list - pull_request.merge_rev = 'f000' * 10 + pull_request.merge_rev = "f000" * 10 commit_ids = PullRequestModel()._get_commit_ids(pull_request) assert commit_ids == pull_request.revisions + [pull_request.merge_rev] @@ -419,147 +393,126 @@ class TestPullRequestModel(object): source_ref_id = pull_request.source_ref_parts.commit_id target_ref_id = pull_request.target_ref_parts.commit_id diff = PullRequestModel()._get_diff_from_pr_or_version( - source_repo, source_ref_id, target_ref_id, - hide_whitespace_changes=False, diff_context=6) - assert b'file_1' in diff.raw.tobytes() + source_repo, source_ref_id, target_ref_id, hide_whitespace_changes=False, diff_context=6 + ) + assert b"file_1" in diff.raw.tobytes() def test_generate_title_returns_unicode(self): title = PullRequestModel().generate_pullrequest_title( - source='source-dummy', - source_ref='source-ref-dummy', - target='target-dummy', + source="source-dummy", + source_ref="source-ref-dummy", + target="target-dummy", ) assert type(title) == str - @pytest.mark.parametrize('title, has_wip', [ - ('hello', False), - ('hello wip', False), - ('hello wip: xxx', False), - ('[wip] hello', True), - ('[wip] hello', True), - ('wip: hello', True), - ('wip hello', True), - - ]) + @pytest.mark.parametrize( + "title, has_wip", + [ + ("hello", False), + ("hello wip", False), + ("hello wip: xxx", False), + ("[wip] hello", True), + ("[wip] hello", True), + ("wip: hello", True), + ("wip hello", True), + ], + ) def test_wip_title_marker(self, pull_request, title, has_wip): pull_request.title = title assert pull_request.work_in_progress == has_wip -@pytest.mark.usefixtures('config_stub') +@pytest.mark.usefixtures("config_stub") class TestIntegrationMerge(object): - @pytest.mark.parametrize('extra_config', ( - {'vcs.hooks.protocol.v2': 'celery', 'vcs.hooks.direct_calls': False}, - )) - def test_merge_triggers_push_hooks( - self, pr_util, user_admin, capture_rcextensions, merge_extras, - extra_config): - pull_request = pr_util.create_pull_request( - approved=True, mergeable=True) - # TODO: johbo: Needed for sqlite, try to find an automatic way for it - merge_extras['repository'] = pull_request.target_repo.repo_name - Session().commit() - - with mock.patch.dict(rhodecode.CONFIG, extra_config, clear=False): - merge_state = PullRequestModel().merge_repo( - pull_request, user_admin, extras=merge_extras) - Session().commit() - - assert merge_state.executed - assert '_pre_push_hook' in capture_rcextensions - assert '_push_hook' in capture_rcextensions - - def test_merge_can_be_rejected_by_pre_push_hook( - self, pr_util, user_admin, capture_rcextensions, merge_extras): - pull_request = pr_util.create_pull_request( - approved=True, mergeable=True) - # TODO: johbo: Needed for sqlite, try to find an automatic way for it - merge_extras['repository'] = pull_request.target_repo.repo_name - Session().commit() - - with mock.patch('rhodecode.EXTENSIONS.PRE_PUSH_HOOK') as pre_pull: - pre_pull.side_effect = RepositoryError("Disallow push!") - merge_status = PullRequestModel().merge_repo( - pull_request, user_admin, extras=merge_extras) - Session().commit() - - assert not merge_status.executed - assert 'pre_push' not in capture_rcextensions - assert 'post_push' not in capture_rcextensions - - def test_merge_fails_if_target_is_locked( - self, pr_util, user_regular, merge_extras): - pull_request = pr_util.create_pull_request( - approved=True, mergeable=True) - locked_by = [user_regular.user_id + 1, 12345.50, 'lock_web'] + def test_merge_fails_if_target_is_locked(self, pr_util, user_regular, merge_extras): + pull_request = pr_util.create_pull_request(approved=True, mergeable=True) + locked_by = [user_regular.user_id + 1, 12345.50, "lock_web"] pull_request.target_repo.locked = locked_by # TODO: johbo: Check if this can work based on the database, currently # all data is pre-computed, that's why just updating the DB is not # enough. - merge_extras['locked_by'] = locked_by - merge_extras['repository'] = pull_request.target_repo.repo_name + merge_extras["locked_by"] = locked_by + merge_extras["repository"] = pull_request.target_repo.repo_name # TODO: johbo: Needed for sqlite, try to find an automatic way for it Session().commit() - merge_status = PullRequestModel().merge_repo( - pull_request, user_regular, extras=merge_extras) + merge_status = PullRequestModel().merge_repo(pull_request, user_regular, extras=merge_extras) Session().commit() assert not merge_status.executed -@pytest.mark.parametrize('use_outdated, inlines_count, outdated_count', [ - (False, 1, 0), - (True, 0, 1), -]) -def test_outdated_comments( - pr_util, use_outdated, inlines_count, outdated_count, config_stub): +@pytest.mark.parametrize( + "use_outdated, inlines_count, outdated_count", + [ + (False, 1, 0), + (True, 0, 1), + ], +) +def test_outdated_comments(pr_util, use_outdated, inlines_count, outdated_count, config_stub): pull_request = pr_util.create_pull_request() - pr_util.create_inline_comment(file_path='not_in_updated_diff') + pr_util.create_inline_comment(file_path="not_in_updated_diff") with outdated_comments_patcher(use_outdated) as outdated_comment_mock: pr_util.add_one_commit() - assert_inline_comments( - pull_request, visible=inlines_count, outdated=outdated_count) + assert_inline_comments(pull_request, visible=inlines_count, outdated=outdated_count) outdated_comment_mock.assert_called_with(pull_request) -@pytest.mark.parametrize('mr_type, expected_msg', [ - (MergeFailureReason.NONE, - 'This pull request can be automatically merged.'), - (MergeFailureReason.UNKNOWN, - 'This pull request cannot be merged because of an unhandled exception. CRASH'), - (MergeFailureReason.MERGE_FAILED, - 'This pull request cannot be merged because of merge conflicts. CONFLICT_FILE'), - (MergeFailureReason.PUSH_FAILED, - 'This pull request could not be merged because push to target:`some-repo@merge_commit` failed.'), - (MergeFailureReason.TARGET_IS_NOT_HEAD, - 'This pull request cannot be merged because the target `ref_name` is not a head.'), - (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 `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, - 'This pull request cannot be merged because the target reference `ref_name` is missing.'), - (MergeFailureReason.MISSING_SOURCE_REF, - 'This pull request cannot be merged because the source reference `ref_name` is missing.'), - (MergeFailureReason.SUBREPO_MERGE_FAILED, - 'This pull request cannot be merged because of conflicts related to sub repositories.'), - -]) +@pytest.mark.parametrize( + "mr_type, expected_msg", + [ + (MergeFailureReason.NONE, "This pull request can be automatically merged."), + (MergeFailureReason.UNKNOWN, "This pull request cannot be merged because of an unhandled exception. CRASH"), + ( + MergeFailureReason.MERGE_FAILED, + "This pull request cannot be merged because of merge conflicts. CONFLICT_FILE", + ), + ( + MergeFailureReason.PUSH_FAILED, + "This pull request could not be merged because push to target:`some-repo@merge_commit` failed.", + ), + ( + MergeFailureReason.TARGET_IS_NOT_HEAD, + "This pull request cannot be merged because the target `ref_name` is not a head.", + ), + ( + 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 `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, + "This pull request cannot be merged because the target reference `ref_name` is missing.", + ), + ( + MergeFailureReason.MISSING_SOURCE_REF, + "This pull request cannot be merged because the source reference `ref_name` is missing.", + ), + ( + MergeFailureReason.SUBREPO_MERGE_FAILED, + "This pull request cannot be merged because of conflicts related to sub repositories.", + ), + ], +) def test_merge_response_message(mr_type, expected_msg): - merge_ref = Reference('type', 'ref_name', '6126b7bfcc82ad2d3deaee22af926b082ce54cc6') + merge_ref = Reference("type", "ref_name", "6126b7bfcc82ad2d3deaee22af926b082ce54cc6") metadata = { - 'unresolved_files': 'CONFLICT_FILE', - 'exception': "CRASH", - 'target': 'some-repo', - 'merge_commit': 'merge_commit', - 'target_ref': merge_ref, - 'source_ref': merge_ref, - 'heads': ','.join(['a', 'b', 'c']), - 'locked_by': 'user:123' + "unresolved_files": "CONFLICT_FILE", + "exception": "CRASH", + "target": "some-repo", + "merge_commit": "merge_commit", + "target_ref": merge_ref, + "source_ref": merge_ref, + "heads": ",".join(["a", "b", "c"]), + "locked_by": "user:123", } merge_response = MergeResponse(True, True, merge_ref, mr_type, metadata=metadata) @@ -573,30 +526,28 @@ def merge_extras(request, user_regular): """ extras = { - 'ip': '127.0.0.1', - 'username': user_regular.username, - 'user_id': user_regular.user_id, - 'action': 'push', - 'repository': 'fake_target_repo_name', - 'scm': 'git', - 'config': request.config.getini('pyramid_config'), - 'repo_store': '', - 'make_lock': None, - 'locked_by': [None, None, None], - 'server_url': 'http://test.example.com:5000', - 'hooks': ['push', 'pull'], - 'is_shadow_repo': False, + "ip": "127.0.0.1", + "username": user_regular.username, + "user_id": user_regular.user_id, + "action": "push", + "repository": "fake_target_repo_name", + "scm": "git", + "config": request.config.getini("pyramid_config"), + "repo_store": "", + "make_lock": None, + "locked_by": [None, None, None], + "server_url": "http://test.example.com:5000", + "hooks": ["push", "pull"], + "is_shadow_repo": False, } return extras -@pytest.mark.usefixtures('config_stub') +@pytest.mark.usefixtures("config_stub") class TestUpdateCommentHandling(object): - - @pytest.fixture(autouse=True, scope='class') + @pytest.fixture(autouse=True, scope="class") def enable_outdated_comments(self, request, baseapp): - config_patch = mock.patch.dict( - 'rhodecode.CONFIG', {'rhodecode_use_outdated_comments': True}) + config_patch = mock.patch.dict("rhodecode.CONFIG", {"rhodecode_use_outdated_comments": True}) config_patch.start() @request.addfinalizer @@ -605,206 +556,194 @@ class TestUpdateCommentHandling(object): def test_comment_stays_unflagged_on_unchanged_diff(self, pr_util): commits = [ - {'message': 'a'}, - {'message': 'b', 'added': [FileNode(b'file_b', b'test_content\n')]}, - {'message': 'c', 'added': [FileNode(b'file_c', b'test_content\n')]}, + {"message": "a"}, + {"message": "b", "added": [FileNode(b"file_b", b"test_content\n")]}, + {"message": "c", "added": [FileNode(b"file_c", b"test_content\n")]}, ] - pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b']) - pr_util.create_inline_comment(file_path='file_b') - pr_util.add_one_commit(head='c') + pull_request = pr_util.create_pull_request(commits=commits, target_head="a", source_head="b", revisions=["b"]) + pr_util.create_inline_comment(file_path="file_b") + pr_util.add_one_commit(head="c") assert_inline_comments(pull_request, visible=1, outdated=0) def test_comment_stays_unflagged_on_change_above(self, pr_util): - original_content = b''.join((b'line %d\n' % x for x in range(1, 11))) - updated_content = b'new_line_at_top\n' + original_content + original_content = b"".join((b"line %d\n" % x for x in range(1, 11))) + updated_content = b"new_line_at_top\n" + original_content commits = [ - {'message': 'a'}, - {'message': 'b', 'added': [FileNode(b'file_b', original_content)]}, - {'message': 'c', 'changed': [FileNode(b'file_b', updated_content)]}, + {"message": "a"}, + {"message": "b", "added": [FileNode(b"file_b", original_content)]}, + {"message": "c", "changed": [FileNode(b"file_b", updated_content)]}, ] - pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b']) + pull_request = pr_util.create_pull_request(commits=commits, target_head="a", source_head="b", revisions=["b"]) with outdated_comments_patcher(): - comment = pr_util.create_inline_comment( - line_no=u'n8', file_path='file_b') - pr_util.add_one_commit(head='c') + comment = pr_util.create_inline_comment(line_no="n8", file_path="file_b") + pr_util.add_one_commit(head="c") assert_inline_comments(pull_request, visible=1, outdated=0) - assert comment.line_no == u'n9' + assert comment.line_no == "n9" def test_comment_stays_unflagged_on_change_below(self, pr_util): - original_content = b''.join([b'line %d\n' % x for x in range(10)]) - updated_content = original_content + b'new_line_at_end\n' + original_content = b"".join([b"line %d\n" % x for x in range(10)]) + updated_content = original_content + b"new_line_at_end\n" commits = [ - {'message': 'a'}, - {'message': 'b', 'added': [FileNode(b'file_b', original_content)]}, - {'message': 'c', 'changed': [FileNode(b'file_b', updated_content)]}, + {"message": "a"}, + {"message": "b", "added": [FileNode(b"file_b", original_content)]}, + {"message": "c", "changed": [FileNode(b"file_b", updated_content)]}, ] - pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b']) - pr_util.create_inline_comment(file_path='file_b') - pr_util.add_one_commit(head='c') + pull_request = pr_util.create_pull_request(commits=commits, target_head="a", source_head="b", revisions=["b"]) + pr_util.create_inline_comment(file_path="file_b") + pr_util.add_one_commit(head="c") assert_inline_comments(pull_request, visible=1, outdated=0) - @pytest.mark.parametrize('line_no', ['n4', 'o4', 'n10', 'o9']) + @pytest.mark.parametrize("line_no", ["n4", "o4", "n10", "o9"]) def test_comment_flagged_on_change_around_context(self, pr_util, line_no): - base_lines = [b'line %d\n' % x for x in range(1, 13)] + base_lines = [b"line %d\n" % x for x in range(1, 13)] change_lines = list(base_lines) - change_lines.insert(6, b'line 6a added\n') + change_lines.insert(6, b"line 6a added\n") # Changes on the last line of sight update_lines = list(change_lines) - update_lines[0] = b'line 1 changed\n' - update_lines[-1] = b'line 12 changed\n' + update_lines[0] = b"line 1 changed\n" + update_lines[-1] = b"line 12 changed\n" def file_b(lines): - return FileNode(b'file_b', b''.join(lines)) + return FileNode(b"file_b", b"".join(lines)) commits = [ - {'message': 'a', 'added': [file_b(base_lines)]}, - {'message': 'b', 'changed': [file_b(change_lines)]}, - {'message': 'c', 'changed': [file_b(update_lines)]}, + {"message": "a", "added": [file_b(base_lines)]}, + {"message": "b", "changed": [file_b(change_lines)]}, + {"message": "c", "changed": [file_b(update_lines)]}, ] - pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b']) - pr_util.create_inline_comment(line_no=line_no, file_path='file_b') + pull_request = pr_util.create_pull_request(commits=commits, target_head="a", source_head="b", revisions=["b"]) + pr_util.create_inline_comment(line_no=line_no, file_path="file_b") with outdated_comments_patcher(): - pr_util.add_one_commit(head='c') + pr_util.add_one_commit(head="c") assert_inline_comments(pull_request, visible=0, outdated=1) - @pytest.mark.parametrize("change, content", [ - ('changed', b'changed\n'), - ('removed', b''), - ], ids=['changed', b'removed']) + @pytest.mark.parametrize( + "change, content", + [ + ("changed", b"changed\n"), + ("removed", b""), + ], + ids=["changed", b"removed"], + ) def test_comment_flagged_on_change(self, pr_util, change, content): commits = [ - {'message': 'a'}, - {'message': 'b', 'added': [FileNode(b'file_b', b'test_content\n')]}, - {'message': 'c', change: [FileNode(b'file_b', content)]}, + {"message": "a"}, + {"message": "b", "added": [FileNode(b"file_b", b"test_content\n")]}, + {"message": "c", change: [FileNode(b"file_b", content)]}, ] - pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b']) - pr_util.create_inline_comment(file_path='file_b') + pull_request = pr_util.create_pull_request(commits=commits, target_head="a", source_head="b", revisions=["b"]) + pr_util.create_inline_comment(file_path="file_b") with outdated_comments_patcher(): - pr_util.add_one_commit(head='c') + pr_util.add_one_commit(head="c") assert_inline_comments(pull_request, visible=0, outdated=1) -@pytest.mark.usefixtures('config_stub') +@pytest.mark.usefixtures("config_stub") class TestUpdateChangedFiles(object): - def test_no_changes_on_unchanged_diff(self, pr_util): commits = [ - {'message': 'a'}, - {'message': 'b', - 'added': [FileNode(b'file_b', b'test_content b\n')]}, - {'message': 'c', - 'added': [FileNode(b'file_c', b'test_content c\n')]}, + {"message": "a"}, + {"message": "b", "added": [FileNode(b"file_b", b"test_content b\n")]}, + {"message": "c", "added": [FileNode(b"file_c", b"test_content c\n")]}, ] # open a PR from a to b, adding file_b pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b'], - name_suffix='per-file-review') + commits=commits, target_head="a", source_head="b", revisions=["b"], name_suffix="per-file-review" + ) # modify PR adding new file file_c - pr_util.add_one_commit(head='c') + pr_util.add_one_commit(head="c") - assert_pr_file_changes( - pull_request, - added=['file_c'], - modified=[], - removed=[]) + assert_pr_file_changes(pull_request, added=["file_c"], modified=[], removed=[]) def test_modify_and_undo_modification_diff(self, pr_util): commits = [ - {'message': 'a'}, - {'message': 'b', - 'added': [FileNode(b'file_b', b'test_content b\n')]}, - {'message': 'c', - 'changed': [FileNode(b'file_b', b'test_content b modified\n')]}, - {'message': 'd', - 'changed': [FileNode(b'file_b', b'test_content b\n')]}, + {"message": "a"}, + {"message": "b", "added": [FileNode(b"file_b", b"test_content b\n")]}, + {"message": "c", "changed": [FileNode(b"file_b", b"test_content b modified\n")]}, + {"message": "d", "changed": [FileNode(b"file_b", b"test_content b\n")]}, ] # open a PR from a to b, adding file_b pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b'], - name_suffix='per-file-review') + commits=commits, target_head="a", source_head="b", revisions=["b"], name_suffix="per-file-review" + ) # modify PR modifying file file_b - pr_util.add_one_commit(head='c') + pr_util.add_one_commit(head="c") - assert_pr_file_changes( - pull_request, - added=[], - modified=['file_b'], - removed=[]) + assert_pr_file_changes(pull_request, added=[], modified=["file_b"], removed=[]) # move the head again to d, which rollbacks change, # meaning we should indicate no changes - pr_util.add_one_commit(head='d') + pr_util.add_one_commit(head="d") - assert_pr_file_changes( - pull_request, - added=[], - modified=[], - removed=[]) + assert_pr_file_changes(pull_request, added=[], modified=[], removed=[]) def test_updated_all_files_in_pr(self, pr_util): commits = [ - {'message': 'a'}, - {'message': 'b', 'added': [ - FileNode(b'file_a', b'test_content a\n'), - FileNode(b'file_b', b'test_content b\n'), - FileNode(b'file_c', b'test_content c\n')]}, - {'message': 'c', 'changed': [ - FileNode(b'file_a', b'test_content a changed\n'), - FileNode(b'file_b', b'test_content b changed\n'), - FileNode(b'file_c', b'test_content c changed\n')]}, + {"message": "a"}, + { + "message": "b", + "added": [ + FileNode(b"file_a", b"test_content a\n"), + FileNode(b"file_b", b"test_content b\n"), + FileNode(b"file_c", b"test_content c\n"), + ], + }, + { + "message": "c", + "changed": [ + FileNode(b"file_a", b"test_content a changed\n"), + FileNode(b"file_b", b"test_content b changed\n"), + FileNode(b"file_c", b"test_content c changed\n"), + ], + }, ] # open a PR from a to b, changing 3 files pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b'], - name_suffix='per-file-review') + commits=commits, target_head="a", source_head="b", revisions=["b"], name_suffix="per-file-review" + ) - pr_util.add_one_commit(head='c') + pr_util.add_one_commit(head="c") - assert_pr_file_changes( - pull_request, - added=[], - modified=['file_a', 'file_b', 'file_c'], - removed=[]) + assert_pr_file_changes(pull_request, added=[], modified=["file_a", "file_b", "file_c"], removed=[]) def test_updated_and_removed_all_files_in_pr(self, pr_util): commits = [ - {'message': 'a'}, - {'message': 'b', 'added': [ - FileNode(b'file_a', b'test_content a\n'), - FileNode(b'file_b', b'test_content b\n'), - FileNode(b'file_c', b'test_content c\n')]}, - {'message': 'c', 'removed': [ - FileNode(b'file_a', b'test_content a changed\n'), - FileNode(b'file_b', b'test_content b changed\n'), - FileNode(b'file_c', b'test_content c changed\n')]}, + {"message": "a"}, + { + "message": "b", + "added": [ + FileNode(b"file_a", b"test_content a\n"), + FileNode(b"file_b", b"test_content b\n"), + FileNode(b"file_c", b"test_content c\n"), + ], + }, + { + "message": "c", + "removed": [ + FileNode(b"file_a", b"test_content a changed\n"), + FileNode(b"file_b", b"test_content b changed\n"), + FileNode(b"file_c", b"test_content c changed\n"), + ], + }, ] # open a PR from a to b, removing 3 files pull_request = pr_util.create_pull_request( - commits=commits, target_head='a', source_head='b', revisions=['b'], - name_suffix='per-file-review') + commits=commits, target_head="a", source_head="b", revisions=["b"], name_suffix="per-file-review" + ) - pr_util.add_one_commit(head='c') + pr_util.add_one_commit(head="c") - assert_pr_file_changes( - pull_request, - added=[], - modified=[], - removed=['file_a', 'file_b', 'file_c']) + assert_pr_file_changes(pull_request, added=[], modified=[], removed=["file_a", "file_b", "file_c"]) def test_update_writes_snapshot_into_pull_request_version(pr_util, config_stub): @@ -866,8 +805,7 @@ def test_update_adds_a_comment_to_the_pull_request_about_the_change(pr_util, con .. |under_review| replace:: *"Under Review"*""" ).format(commit_id[:12]) - pull_request_comments = sorted( - pull_request.comments, key=lambda c: c.modified_at) + pull_request_comments = sorted(pull_request.comments, key=lambda c: c.modified_at) update_comment = pull_request_comments[-1] assert update_comment.text == expected_message @@ -890,8 +828,8 @@ def test_create_version_from_snapshot_updates_attributes(pr_util, config_stub): version = PullRequestModel()._create_version_from_snapshot(pull_request) # Check attributes - assert version.title == pr_util.create_parameters['title'] - assert version.description == pr_util.create_parameters['description'] + assert version.title == pr_util.create_parameters["title"] + assert version.description == pr_util.create_parameters["description"] assert version.status == PullRequest.STATUS_CLOSED # versions get updated created_on @@ -899,11 +837,11 @@ def test_create_version_from_snapshot_updates_attributes(pr_util, config_stub): assert version.updated_on == updated_on assert version.user_id == pull_request.user_id - assert version.revisions == pr_util.create_parameters['revisions'] + assert version.revisions == pr_util.create_parameters["revisions"] assert version.source_repo == pr_util.source_repository - assert version.source_ref == pr_util.create_parameters['source_ref'] + assert version.source_ref == pr_util.create_parameters["source_ref"] assert version.target_repo == pr_util.target_repository - assert version.target_ref == pr_util.create_parameters['target_ref'] + assert version.target_ref == pr_util.create_parameters["target_ref"] assert version._last_merge_source_rev == pull_request._last_merge_source_rev assert version._last_merge_target_rev == pull_request._last_merge_target_rev assert version.last_merge_status == pull_request.last_merge_status @@ -921,15 +859,9 @@ def test_link_comments_to_version_only_updates_unlinked_comments(pr_util, config Session().commit() # Expect that only the new comment is linked to version2 - assert ( - comment_unlinked.pull_request_version_id == - version2.pull_request_version_id) - assert ( - comment_linked.pull_request_version_id == - version1.pull_request_version_id) - assert ( - comment_unlinked.pull_request_version_id != - comment_linked.pull_request_version_id) + assert comment_unlinked.pull_request_version_id == version2.pull_request_version_id + assert comment_linked.pull_request_version_id == version1.pull_request_version_id + assert comment_unlinked.pull_request_version_id != comment_linked.pull_request_version_id def test_calculate_commits(): @@ -945,35 +877,26 @@ def test_calculate_commits(): def assert_inline_comments(pull_request, visible=None, outdated=None): if visible is not None: inline_comments = CommentsModel().get_inline_comments( - pull_request.target_repo.repo_id, pull_request=pull_request) - inline_cnt = len(CommentsModel().get_inline_comments_as_list( - inline_comments)) + pull_request.target_repo.repo_id, pull_request=pull_request + ) + inline_cnt = len(CommentsModel().get_inline_comments_as_list(inline_comments)) assert inline_cnt == visible if outdated is not None: - outdated_comments = CommentsModel().get_outdated_comments( - pull_request.target_repo.repo_id, pull_request) + outdated_comments = CommentsModel().get_outdated_comments(pull_request.target_repo.repo_id, pull_request) assert len(outdated_comments) == outdated -def assert_pr_file_changes( - pull_request, added=None, modified=None, removed=None): +def assert_pr_file_changes(pull_request, added=None, modified=None, removed=None): pr_versions = PullRequestModel().get_versions(pull_request) # always use first version, ie original PR to calculate changes pull_request_version = pr_versions[0] - old_diff_data, new_diff_data = PullRequestModel()._generate_update_diffs( - pull_request, pull_request_version) - file_changes = PullRequestModel()._calculate_file_changes( - old_diff_data, new_diff_data) + old_diff_data, new_diff_data = PullRequestModel()._generate_update_diffs(pull_request, pull_request_version) + file_changes = PullRequestModel()._calculate_file_changes(old_diff_data, new_diff_data) - assert added == file_changes.added, \ - 'expected added:%s vs value:%s' % (added, file_changes.added) - assert modified == file_changes.modified, \ - 'expected modified:%s vs value:%s' % (modified, file_changes.modified) - assert removed == file_changes.removed, \ - 'expected removed:%s vs value:%s' % (removed, file_changes.removed) + assert added == file_changes.added, "expected added:%s vs value:%s" % (added, file_changes.added) + assert modified == file_changes.modified, "expected modified:%s vs value:%s" % (modified, file_changes.modified) + assert removed == file_changes.removed, "expected removed:%s vs value:%s" % (removed, file_changes.removed) def outdated_comments_patcher(use_outdated=True): - return mock.patch.object( - CommentsModel, 'use_outdated_comments', - return_value=use_outdated) + return mock.patch.object(CommentsModel, "use_outdated_comments", return_value=use_outdated) diff --git a/rhodecode/tests/models/test_repo_groups.py b/rhodecode/tests/models/test_repo_groups.py index 91a88a75..dee2a806 100644 --- a/rhodecode/tests/models/test_repo_groups.py +++ b/rhodecode/tests/models/test_repo_groups.py @@ -23,7 +23,7 @@ from sqlalchemy.exc import IntegrityError import pytest from rhodecode.tests import TESTS_TMP_PATH -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.repo import RepoModel diff --git a/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py b/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py index c32f7beb..83279902 100644 --- a/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py +++ b/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py @@ -28,7 +28,7 @@ from rhodecode.model.user_group import UserGroupModel from rhodecode.tests.models.common import ( _create_project_tree, check_tree_perms, _get_perms, _check_expected_count, expected_count, _destroy_project_tree) -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture fixture = Fixture() diff --git a/rhodecode/tests/models/test_user_groups.py b/rhodecode/tests/models/test_user_groups.py index 6f27e775..c21b6a31 100644 --- a/rhodecode/tests/models/test_user_groups.py +++ b/rhodecode/tests/models/test_user_groups.py @@ -22,7 +22,7 @@ import pytest from rhodecode.model.db import User from rhodecode.tests import TEST_USER_REGULAR_LOGIN -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.model.user_group import UserGroupModel from rhodecode.model.meta import Session diff --git a/rhodecode/tests/models/test_users.py b/rhodecode/tests/models/test_users.py index 1c9184a0..c4851c75 100644 --- a/rhodecode/tests/models/test_users.py +++ b/rhodecode/tests/models/test_users.py @@ -27,7 +27,7 @@ from rhodecode.model.user import UserModel from rhodecode.model.user_group import UserGroupModel from rhodecode.model.repo import RepoModel from rhodecode.model.repo_group import RepoGroupModel -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.lib.str_utils import safe_str diff --git a/rhodecode/tests/other/test_validators.py b/rhodecode/tests/other/test_validators.py index 6be345e6..d4fd4842 100644 --- a/rhodecode/tests/other/test_validators.py +++ b/rhodecode/tests/other/test_validators.py @@ -32,11 +32,11 @@ from rhodecode.model.meta import Session from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.db import ChangesetStatus, Repository from rhodecode.model.changeset_status import ChangesetStatusModel -from rhodecode.tests.fixture import Fixture +from rhodecode.tests.fixtures.rc_fixture import Fixture fixture = Fixture() -pytestmark = pytest.mark.usefixtures('baseapp') +pytestmark = pytest.mark.usefixtures("baseapp") @pytest.fixture() diff --git a/rhodecode/tests/rhodecode.ini b/rhodecode/tests/rhodecode.ini index ab57657f..c628eba7 100644 --- a/rhodecode/tests/rhodecode.ini +++ b/rhodecode/tests/rhodecode.ini @@ -111,7 +111,7 @@ app.base_url = http://rhodecode.local app.service_api.host = http://rhodecode.local:10020 ; Secret for Service API authentication. -app.service_api.token = +app.service_api.token = secret4 ; Unique application ID. Should be a random unique string for security. app_instance_uuid = rc-production @@ -351,7 +351,7 @@ archive_cache.objectstore.retry_attempts = 10 ; and served from the cache during subsequent requests for the same archive of ; the repository. This path is important to be shared across filesystems and with ; RhodeCode and vcsserver -archive_cache.filesystem.store_dir = %(here)s/rc-tests/archive_cache +archive_cache.filesystem.store_dir = %(here)s/.rc-test-data/archive_cache ; The limit in GB sets how much data we cache before recycling last used, defaults to 10 gb archive_cache.filesystem.cache_size_gb = 2 @@ -406,7 +406,7 @@ celery.task_store_eager_result = true ; Default cache dir for caches. Putting this into a ramdisk can boost performance. ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space -cache_dir = %(here)s/rc-test-data +cache_dir = %(here)s/.rc-test-data ; ********************************************* ; `sql_cache_short` cache for heavy SQL queries @@ -435,7 +435,7 @@ rc_cache.cache_repo_longterm.max_size = 10000 rc_cache.cache_general.backend = dogpile.cache.rc.file_namespace rc_cache.cache_general.expiration_time = 43200 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set -rc_cache.cache_general.arguments.filename = %(here)s/rc-tests/cache-backend/cache_general_db +rc_cache.cache_general.arguments.filename = %(here)s/.rc-test-data/cache-backend/cache_general_db ; alternative `cache_general` redis backend with distributed lock #rc_cache.cache_general.backend = dogpile.cache.rc.redis @@ -454,6 +454,10 @@ rc_cache.cache_general.arguments.filename = %(here)s/rc-tests/cache-backend/cach ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_general.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_general.arguments.key_prefix = custom-prefix- + + ; ************************************************* ; `cache_perms` cache for permission tree, auth TTL ; for simplicity use rc.file_namespace backend, @@ -462,7 +466,7 @@ rc_cache.cache_general.arguments.filename = %(here)s/rc-tests/cache-backend/cach rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace rc_cache.cache_perms.expiration_time = 0 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set -rc_cache.cache_perms.arguments.filename = %(here)s/rc-tests/cache-backend/cache_perms_db +rc_cache.cache_perms.arguments.filename = %(here)s/.rc-test-data/cache-backend/cache_perms_db ; alternative `cache_perms` redis backend with distributed lock #rc_cache.cache_perms.backend = dogpile.cache.rc.redis @@ -481,6 +485,10 @@ rc_cache.cache_perms.arguments.filename = %(here)s/rc-tests/cache-backend/cache_ ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_perms.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_perms.arguments.key_prefix = custom-prefix- + + ; *************************************************** ; `cache_repo` cache for file tree, Readme, RSS FEEDS ; for simplicity use rc.file_namespace backend, @@ -489,7 +497,7 @@ rc_cache.cache_perms.arguments.filename = %(here)s/rc-tests/cache-backend/cache_ rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace rc_cache.cache_repo.expiration_time = 2592000 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set -rc_cache.cache_repo.arguments.filename = %(here)s/rc-tests/cache-backend/cache_repo_db +rc_cache.cache_repo.arguments.filename = %(here)s/.rc-test-data/cache-backend/cache_repo_db ; alternative `cache_repo` redis backend with distributed lock #rc_cache.cache_repo.backend = dogpile.cache.rc.redis @@ -508,6 +516,10 @@ rc_cache.cache_repo.arguments.filename = %(here)s/rc-tests/cache-backend/cache_r ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen #rc_cache.cache_repo.arguments.lock_auto_renewal = true +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_repo.arguments.key_prefix = custom-prefix- + + ; ############## ; BEAKER SESSION ; ############## @@ -516,7 +528,7 @@ rc_cache.cache_repo.arguments.filename = %(here)s/rc-tests/cache-backend/cache_r ; types are file, ext:redis, ext:database, ext:memcached ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session beaker.session.type = file -beaker.session.data_dir = %(here)s/rc-tests/data/sessions +beaker.session.data_dir = %(here)s/.rc-test-data/data/sessions ; Redis based sessions #beaker.session.type = ext:redis @@ -532,7 +544,7 @@ beaker.session.data_dir = %(here)s/rc-tests/data/sessions beaker.session.key = rhodecode beaker.session.secret = test-rc-uytcxaz -beaker.session.lock_dir = %(here)s/rc-tests/data/sessions/lock +beaker.session.lock_dir = %(here)s/.rc-test-data/data/sessions/lock ; Secure encrypted cookie. Requires AES and AES python libraries ; you must disable beaker.session.secret to use this @@ -564,7 +576,7 @@ beaker.session.secure = false ; WHOOSH Backend, doesn't require additional services to run ; it works good with few dozen repos search.module = rhodecode.lib.index.whoosh -search.location = %(here)s/rc-tests/data/index +search.location = %(here)s/.rc-test-data/data/index ; #################### ; CHANNELSTREAM CONFIG @@ -584,7 +596,7 @@ channelstream.server = channelstream:9800 ; see Nginx/Apache configuration examples in our docs channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream channelstream.secret = ENV_GENERATED -channelstream.history.location = %(here)s/rc-tests/channelstream_history +channelstream.history.location = %(here)s/.rc-test-data/channelstream_history ; Internal application path that Javascript uses to connect into. ; If you use proxy-prefix the prefix should be added before /_channelstream @@ -601,7 +613,7 @@ channelstream.proxy_path = /_channelstream ; pymysql is an alternative driver for MySQL, use in case of problems with default one #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode -sqlalchemy.db1.url = sqlite:///%(here)s/rc-tests/rhodecode_test.db?timeout=30 +sqlalchemy.db1.url = sqlite:///%(here)s/.rc-test-data/rhodecode_test.db?timeout=30 ; see sqlalchemy docs for other advanced settings ; print the sql statements to output @@ -737,7 +749,7 @@ ssh.generate_authorized_keyfile = true ; Path to the authorized_keys file where the generate entries are placed. ; It is possible to have multiple key files specified in `sshd_config` e.g. ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode -ssh.authorized_keys_file_path = %(here)s/rc-tests/authorized_keys_rhodecode +ssh.authorized_keys_file_path = %(here)s/.rc-test-data/authorized_keys_rhodecode ; Command to execute the SSH wrapper. The binary is available in the ; RhodeCode installation directory. diff --git a/rhodecode/tests/run_full_tests.sh b/rhodecode/tests/run_full_tests.sh deleted file mode 100755 index d50eefca..00000000 --- a/rhodecode/tests/run_full_tests.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -set -e - -## MIGRATIONS AND DB TESTS ## -echo "DATABASE CREATION TESTS" -rhodecode/tests/database/test_creation.sh - -echo "DATABASE MIGRATIONS TESTS" -rhodecode/tests/database/test_migration.sh - -## TEST VCS OPERATIONS ## -echo "VCS FUNCTIONAL TESTS" -rhodecode/tests/test_vcs_operations.sh - -## TOX TESTS ## -echo "TOX TESTS" -tox -r --develop diff --git a/rhodecode/tests/server_utils.py b/rhodecode/tests/server_utils.py index 4b062a47..d5aadc4b 100644 --- a/rhodecode/tests/server_utils.py +++ b/rhodecode/tests/server_utils.py @@ -24,13 +24,13 @@ import tempfile import pytest import subprocess import logging -from urllib.request import urlopen -from urllib.error import URLError +import requests import configparser from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS from rhodecode.tests.utils import is_url_reachable +from rhodecode.tests import console_printer log = logging.getLogger(__name__) @@ -49,7 +49,7 @@ def get_host_url(pyramid_config): def assert_no_running_instance(url): if is_url_reachable(url): - print(f"Hint: Usually this means another instance of server " + console_printer(f"Hint: Usually this means another instance of server " f"is running in the background at {url}.") pytest.fail(f"Port is not free at {url}, cannot start server at") @@ -58,8 +58,9 @@ class ServerBase(object): _args = [] log_file_name = 'NOT_DEFINED.log' status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping' + console_marker = " :warning: [green]pytest-setup[/green] " - def __init__(self, config_file, log_file): + def __init__(self, config_file, log_file, env): self.config_file = config_file config = configparser.ConfigParser() config.read(config_file) @@ -69,10 +70,10 @@ class ServerBase(object): self._args = [] self.log_file = log_file or os.path.join( tempfile.gettempdir(), self.log_file_name) + self.env = env self.process = None self.server_out = None - log.info("Using the {} configuration:{}".format( - self.__class__.__name__, config_file)) + log.info(f"Using the {self.__class__.__name__} configuration:{config_file}") if not os.path.isfile(config_file): raise RuntimeError(f'Failed to get config at {config_file}') @@ -110,18 +111,17 @@ class ServerBase(object): while time.time() - start < timeout: try: - urlopen(status_url) + requests.get(status_url) break - except URLError: + except requests.exceptions.ConnectionError: time.sleep(0.2) else: pytest.fail( - "Starting the {} failed or took more than {} " - "seconds. cmd: `{}`".format( - self.__class__.__name__, timeout, self.command)) + f"Starting the {self.__class__.__name__} failed or took more than {timeout} seconds." + f"cmd: `{self.command}`" + ) - log.info('Server of {} ready at url {}'.format( - self.__class__.__name__, status_url)) + log.info(f'Server of {self.__class__.__name__} ready at url {status_url}') def shutdown(self): self.process.kill() @@ -130,7 +130,7 @@ class ServerBase(object): def get_log_file_with_port(self): log_file = list(self.log_file.partition('.log')) - log_file.insert(1, get_port(self.config_file)) + log_file.insert(1, f'-{get_port(self.config_file)}') log_file = ''.join(log_file) return log_file @@ -140,11 +140,12 @@ class RcVCSServer(ServerBase): Represents a running VCSServer instance. """ - log_file_name = 'rc-vcsserver.log' + log_file_name = 'rhodecode-vcsserver.log' status_url_tmpl = 'http://{host}:{port}/status' - def __init__(self, config_file, log_file=None, workers='3'): - super(RcVCSServer, self).__init__(config_file, log_file) + def __init__(self, config_file, log_file=None, workers='3', env=None, info_prefix=''): + super(RcVCSServer, self).__init__(config_file, log_file, env) + self.info_prefix = info_prefix self._args = [ 'gunicorn', '--bind', self.bind_addr, @@ -164,9 +165,10 @@ class RcVCSServer(ServerBase): host_url = self.host_url() assert_no_running_instance(host_url) - print(f'rhodecode-vcsserver starting at: {host_url}') - print(f'rhodecode-vcsserver command: {self.command}') - print(f'rhodecode-vcsserver logfile: {self.log_file}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-vcsserver starting at: {host_url}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-vcsserver command: {self.command}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-vcsserver logfile: {self.log_file}') + console_printer() self.process = subprocess.Popen( self._args, bufsize=0, env=env, @@ -178,11 +180,12 @@ class RcWebServer(ServerBase): Represents a running RCE web server used as a test fixture. """ - log_file_name = 'rc-web.log' + log_file_name = 'rhodecode-ce.log' status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping' - def __init__(self, config_file, log_file=None, workers='2'): - super(RcWebServer, self).__init__(config_file, log_file) + def __init__(self, config_file, log_file=None, workers='2', env=None, info_prefix=''): + super(RcWebServer, self).__init__(config_file, log_file, env) + self.info_prefix = info_prefix self._args = [ 'gunicorn', '--bind', self.bind_addr, @@ -195,7 +198,8 @@ class RcWebServer(ServerBase): def start(self): env = os.environ.copy() - env['RC_NO_TMP_PATH'] = '1' + if self.env: + env.update(self.env) self.log_file = self.get_log_file_with_port() self.server_out = open(self.log_file, 'w') @@ -203,9 +207,10 @@ class RcWebServer(ServerBase): host_url = self.host_url() assert_no_running_instance(host_url) - print(f'rhodecode-web starting at: {host_url}') - print(f'rhodecode-web command: {self.command}') - print(f'rhodecode-web logfile: {self.log_file}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-ce starting at: {host_url}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-ce command: {self.command}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-ce logfile: {self.log_file}') + console_printer() self.process = subprocess.Popen( self._args, bufsize=0, env=env, @@ -229,3 +234,44 @@ class RcWebServer(ServerBase): } params.update(**kwargs) return params['user'], params['passwd'] + +class CeleryServer(ServerBase): + log_file_name = 'rhodecode-celery.log' + status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping' + + def __init__(self, config_file, log_file=None, workers='2', env=None, info_prefix=''): + super(CeleryServer, self).__init__(config_file, log_file, env) + self.info_prefix = info_prefix + self._args = \ + ['celery', + '--no-color', + '--app=rhodecode.lib.celerylib.loader', + 'worker', + '--autoscale=4,2', + '--max-tasks-per-child=30', + '--task-events', + '--loglevel=DEBUG', + '--ini=' + self.config_file] + + def start(self): + env = os.environ.copy() + env['RC_NO_TEST_ENV'] = '1' + + self.log_file = self.get_log_file_with_port() + self.server_out = open(self.log_file, 'w') + + host_url = "Celery" #self.host_url() + #assert_no_running_instance(host_url) + + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-celery starting at: {host_url}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-celery command: {self.command}') + console_printer(f'{self.console_marker}{self.info_prefix}rhodecode-celery logfile: {self.log_file}') + console_printer() + + self.process = subprocess.Popen( + self._args, bufsize=0, env=env, + stdout=self.server_out, stderr=self.server_out) + + + def wait_until_ready(self, timeout=30): + time.sleep(2) diff --git a/rhodecode/tests/test_vcs_operations.sh b/rhodecode/tests/test_vcs_operations.sh deleted file mode 100755 index 60009b23..00000000 --- a/rhodecode/tests/test_vcs_operations.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -set -e - -SQLITE_DB_PATH=/mnt/hgfs/marcink-shared/workspace-python/rhodecode -RC_LOG=/tmp/rc.log -INI_FILE=test.ini -TEST_DB_NAME=rhodecode_test - - -for databaseName in p m s; do - # set the different DBs - if [ "$databaseName" = "s" ]; then - echo "sqlite" - rhodecode-config --filename=$INI_FILE --update '[app:main]sqlalchemy.db1.url=sqlite:///'$SQLITE_DB_PATH/$TEST_DB_NAME'.sqlite' - elif [ "$databaseName" = "p" ]; then - echo "postgres" - rhodecode-config --filename=$INI_FILE --update '[app:main]sqlalchemy.db1.url=postgresql://postgres:qweqwe@localhost/'$TEST_DB_NAME'' - elif [ "$databaseName" = "m" ]; then - echo "mysql" - rhodecode-config --filename=$INI_FILE --update '[app:main]sqlalchemy.db1.url=mysql://root:qweqwe@localhost/'$TEST_DB_NAME'' - fi - - # running just VCS tests - RC_NO_TMP_PATH=1 py.test \ - rhodecode/tests/other/test_vcs_operations.py - -done diff --git a/rhodecode/tests/utils.py b/rhodecode/tests/utils.py index ec551d33..64a5d8f4 100644 --- a/rhodecode/tests/utils.py +++ b/rhodecode/tests/utils.py @@ -36,24 +36,29 @@ from webtest.app import TestResponse, TestApp import pytest -try: - import rc_testdata -except ImportError: - raise ImportError('Failed to import rc_testdata, ' - 'please make sure this package is installed from requirements_test.txt') - from rhodecode.model.db import User, Repository from rhodecode.model.meta import Session from rhodecode.model.scm import ScmModel from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository from rhodecode.lib.vcs.backends.base import EmptyCommit -from rhodecode.tests import login_user_session +from rhodecode.tests import login_user_session, console_printer +from rhodecode.authentication import AuthenticationPluginRegistry +from rhodecode.model.settings import SettingsModel log = logging.getLogger(__name__) -def print_to_func(value, print_to=sys.stderr): - print(value, file=print_to) +def console_printer_utils(msg): + console_printer(f" :white_check_mark: [green]test-utils[/green] {msg}") + + +def get_rc_testdata(): + try: + import rc_testdata + except ImportError: + raise ImportError('Failed to import rc_testdata, ' + 'please make sure this package is installed from requirements_test.txt') + return rc_testdata class CustomTestResponse(TestResponse): @@ -73,7 +78,6 @@ class CustomTestResponse(TestResponse): assert string in res """ print_body = kw.pop('print_body', False) - print_to = kw.pop('print_to', sys.stderr) if 'no' in kw: no = kw['no'] @@ -89,18 +93,18 @@ class CustomTestResponse(TestResponse): for s in strings: if s not in self: - print_to_func(f"Actual response (no {s!r}):", print_to=print_to) - print_to_func(f"body output saved as `{f}`", print_to=print_to) + console_printer_utils(f"Actual response (no {s!r}):") + console_printer_utils(f"body output saved as `{f}`") if print_body: - print_to_func(str(self), print_to=print_to) + console_printer_utils(str(self)) raise IndexError(f"Body does not contain string {s!r}, body output saved as {f}") for no_s in no: if no_s in self: - print_to_func(f"Actual response (has {no_s!r})", print_to=print_to) - print_to_func(f"body output saved as `{f}`", print_to=print_to) + console_printer_utils(f"Actual response (has {no_s!r})") + console_printer_utils(f"body output saved as `{f}`") if print_body: - print_to_func(str(self), print_to=print_to) + console_printer_utils(str(self)) raise IndexError(f"Body contains bad string {no_s!r}, body output saved as {f}") def assert_response(self): @@ -209,6 +213,7 @@ def extract_git_repo_from_dump(dump_name, repo_name): """Create git repo `repo_name` from dump `dump_name`.""" repos_path = ScmModel().repos_path target_path = os.path.join(repos_path, repo_name) + rc_testdata = get_rc_testdata() rc_testdata.extract_git_dump(dump_name, target_path) return target_path @@ -217,6 +222,7 @@ def extract_hg_repo_from_dump(dump_name, repo_name): """Create hg repo `repo_name` from dump `dump_name`.""" repos_path = ScmModel().repos_path target_path = os.path.join(repos_path, repo_name) + rc_testdata = get_rc_testdata() rc_testdata.extract_hg_dump(dump_name, target_path) return target_path @@ -245,6 +251,7 @@ def _load_svn_dump_into_repo(dump_name, repo_path): Currently the dumps are in rc_testdata. They might later on be integrated with the main repository once they stabilize more. """ + rc_testdata = get_rc_testdata() dump = rc_testdata.load_svn_dump(dump_name) load_dump = subprocess.Popen( ['svnadmin', 'load', repo_path], @@ -254,9 +261,7 @@ def _load_svn_dump_into_repo(dump_name, repo_path): if load_dump.returncode != 0: log.error("Output of load_dump command: %s", out) log.error("Error output of load_dump command: %s", err) - raise Exception( - 'Failed to load dump "%s" into repository at path "%s".' - % (dump_name, repo_path)) + raise Exception(f'Failed to load dump "{dump_name}" into repository at path "{repo_path}".') class AssertResponse(object): @@ -492,3 +497,54 @@ def permission_update_data_generator(csrf_token, default=None, grant=None, revok ('perm_del_member_type_{}'.format(obj_id), obj_type), ]) return form_data + + + +class AuthPluginManager: + + def cleanup(self): + self._enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode']) + + def enable(self, plugins_list, override=None): + return self._enable_plugins(plugins_list, override) + + @classmethod + def _enable_plugins(cls, plugins_list, override: object = None): + override = override or {} + params = { + 'auth_plugins': ','.join(plugins_list), + } + + # helper translate some names to others, to fix settings code + name_map = { + 'token': 'authtoken' + } + log.debug('enable_auth_plugins: enabling following auth-plugins: %s', plugins_list) + + for module in plugins_list: + plugin_name = module.partition('#')[-1] + if plugin_name in name_map: + plugin_name = name_map[plugin_name] + enabled_plugin = f'auth_{plugin_name}_enabled' + cache_ttl = f'auth_{plugin_name}_cache_ttl' + + # default params that are needed for each plugin, + # `enabled` and `cache_ttl` + params.update({ + enabled_plugin: True, + cache_ttl: 0 + }) + if override.get: + params.update(override.get(module, {})) + + validated_params = params + + for k, v in validated_params.items(): + setting = SettingsModel().create_or_update_setting(k, v) + Session().add(setting) + Session().commit() + + AuthenticationPluginRegistry.invalidate_auth_plugins_cache(hard=True) + + enabled_plugins = SettingsModel().get_auth_plugins() + assert plugins_list == enabled_plugins diff --git a/rhodecode/tests/vcs/__init__.py b/rhodecode/tests/vcs/__init__.py index bdc53285..c4d86aeb 100644 --- a/rhodecode/tests/vcs/__init__.py +++ b/rhodecode/tests/vcs/__init__.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify diff --git a/rhodecode/tests/vcs/conftest.py b/rhodecode/tests/vcs/conftest.py index 5d321182..7e0313f8 100644 --- a/rhodecode/tests/vcs/conftest.py +++ b/rhodecode/tests/vcs/conftest.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -32,8 +31,7 @@ from rhodecode.tests.utils import check_skip_backends, check_xfail_backends @pytest.fixture() -def vcs_repository_support( - request, backend_alias, baseapp, _vcs_repo_container): +def vcs_repository_support(request, backend_alias, baseapp, _vcs_repo_container): """ Provide a test repository for the test run. @@ -63,7 +61,7 @@ def vcs_repository_support( return backend_alias, repo -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def _vcs_repo_container(request): """ Internal fixture intended to help support class based scoping on demand. @@ -73,13 +71,12 @@ def _vcs_repo_container(request): def _create_vcs_repo_container(request): repo_container = VcsRepoContainer() - if not request.config.getoption('--keep-tmp-path'): + if not request.config.getoption("--keep-tmp-path"): request.addfinalizer(repo_container.cleanup) return repo_container class VcsRepoContainer(object): - def __init__(self): self._cleanup_paths = [] self._repos = {} @@ -98,14 +95,14 @@ class VcsRepoContainer(object): def _should_create_repo_per_test(cls): - return getattr(cls, 'recreate_repo_per_test', False) + return getattr(cls, "recreate_repo_per_test", False) def _create_empty_repository(cls, backend_alias=None): Backend = get_backend(backend_alias or cls.backend_alias) repo_path = get_new_dir(str(time.time())) repo = Backend(repo_path, create=True) - if hasattr(cls, '_get_commits'): + if hasattr(cls, "_get_commits"): commits = cls._get_commits() cls.tip = _add_commits_to_repo(repo, commits) @@ -127,7 +124,7 @@ def config(): specific content is required. """ config = Config() - config.set('section-a', 'a-1', 'value-a-1') + config.set("section-a", "a-1", "value-a-1") return config @@ -136,24 +133,24 @@ def _add_commits_to_repo(repo, commits): tip = None for commit in commits: - for node in commit.get('added', []): + for node in commit.get("added", []): if not isinstance(node, FileNode): node = FileNode(safe_bytes(node.path), content=node.content) imc.add(node) - for node in commit.get('changed', []): + for node in commit.get("changed", []): if not isinstance(node, FileNode): node = FileNode(safe_bytes(node.path), content=node.content) imc.change(node) - for node in commit.get('removed', []): + for node in commit.get("removed", []): imc.remove(FileNode(safe_bytes(node.path))) tip = imc.commit( - message=str(commit['message']), - author=str(commit['author']), - date=commit['date'], - branch=commit.get('branch') + message=str(commit["message"]), + author=str(commit["author"]), + date=commit["date"], + branch=commit.get("branch"), ) return tip @@ -183,16 +180,15 @@ def generate_repo_with_commits(vcs_repo): start_date = datetime.datetime(2010, 1, 1, 20) for x in range(num): yield { - 'message': 'Commit %d' % x, - 'author': 'Joe Doe ', - 'date': start_date + datetime.timedelta(hours=12 * x), - 'added': [ - FileNode(b'file_%d.txt' % x, content=b'Foobar %d' % x), + "message": "Commit %d" % x, + "author": "Joe Doe ", + "date": start_date + datetime.timedelta(hours=12 * x), + "added": [ + FileNode(b"file_%d.txt" % x, content=b"Foobar %d" % x), + ], + "modified": [ + FileNode(b"file_%d.txt" % x, content=b"Foobar %d modified" % (x - 1)), ], - 'modified': [ - FileNode(b'file_%d.txt' % x, - content=b'Foobar %d modified' % (x-1)), - ] } def commit_maker(num=5): @@ -231,34 +227,33 @@ class BackendTestMixin(object): created before every single test. Defaults to ``True``. """ + recreate_repo_per_test = True @classmethod def _get_commits(cls): commits = [ { - 'message': 'Initial commit', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 20), - 'added': [ - FileNode(b'foobar', content=b'Foobar'), - FileNode(b'foobar2', content=b'Foobar II'), - FileNode(b'foo/bar/baz', content=b'baz here!'), + "message": "Initial commit", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 20), + "added": [ + FileNode(b"foobar", content=b"Foobar"), + FileNode(b"foobar2", content=b"Foobar II"), + FileNode(b"foo/bar/baz", content=b"baz here!"), ], }, { - 'message': 'Changes...', - 'author': 'Jane Doe ', - 'date': datetime.datetime(2010, 1, 1, 21), - 'added': [ - FileNode(b'some/new.txt', content=b'news...'), + "message": "Changes...", + "author": "Jane Doe ", + "date": datetime.datetime(2010, 1, 1, 21), + "added": [ + FileNode(b"some/new.txt", content=b"news..."), ], - 'changed': [ - FileNode(b'foobar', b'Foobar I'), + "changed": [ + FileNode(b"foobar", b"Foobar I"), ], - 'removed': [], + "removed": [], }, ] return commits - - diff --git a/rhodecode/tests/vcs/test_archives.py b/rhodecode/tests/vcs/test_archives.py index b9be4745..a22ff43d 100644 --- a/rhodecode/tests/vcs/test_archives.py +++ b/rhodecode/tests/vcs/test_archives.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -43,121 +42,120 @@ def d_cache_config(): @pytest.mark.usefixtures("vcs_repository_support") class TestArchives(BackendTestMixin): - @classmethod def _get_commits(cls): start_date = datetime.datetime(2010, 1, 1, 20) yield { - 'message': 'Initial Commit', - 'author': 'Joe Doe ', - 'date': start_date + datetime.timedelta(hours=12), - 'added': [ - FileNode(b'executable_0o100755', b'mode_755', mode=0o100755), - FileNode(b'executable_0o100500', b'mode_500', mode=0o100500), - FileNode(b'not_executable', b'mode_644', mode=0o100644), + "message": "Initial Commit", + "author": "Joe Doe ", + "date": start_date + datetime.timedelta(hours=12), + "added": [ + FileNode(b"executable_0o100755", b"mode_755", mode=0o100755), + FileNode(b"executable_0o100500", b"mode_500", mode=0o100500), + FileNode(b"not_executable", b"mode_644", mode=0o100644), ], } for x in range(5): yield { - 'message': 'Commit %d' % x, - 'author': 'Joe Doe ', - 'date': start_date + datetime.timedelta(hours=12 * x), - 'added': [ - FileNode(b'%d/file_%d.txt' % (x, x), content=b'Foobar %d' % x), + "message": "Commit %d" % x, + "author": "Joe Doe ", + "date": start_date + datetime.timedelta(hours=12 * x), + "added": [ + FileNode(b"%d/file_%d.txt" % (x, x), content=b"Foobar %d" % x), ], } - @pytest.mark.parametrize('compressor', ['gz', 'bz2']) + @pytest.mark.parametrize("compressor", ["gz", "bz2"]) def test_archive_tar(self, compressor, tmpdir, tmp_path, d_cache_config): - - archive_node = tmp_path / 'archive-node' + archive_node = tmp_path / "archive-node" archive_node.touch() archive_lnk = self.tip.archive_repo( - str(archive_node), kind=f't{compressor}', archive_dir_name='repo', cache_config=d_cache_config) + str(archive_node), kind=f"t{compressor}", archive_dir_name="repo", cache_config=d_cache_config + ) out_dir = tmpdir - out_file = tarfile.open(str(archive_lnk), f'r|{compressor}') + out_file = tarfile.open(str(archive_lnk), f"r|{compressor}") out_file.extractall(out_dir) out_file.close() for x in range(5): - node_path = '%d/file_%d.txt' % (x, x) - with open(os.path.join(out_dir, 'repo/' + node_path), 'rb') as f: + node_path = "%d/file_%d.txt" % (x, x) + with open(os.path.join(out_dir, "repo/" + node_path), "rb") as f: file_content = f.read() assert file_content == self.tip.get_node(node_path).content shutil.rmtree(out_dir) - @pytest.mark.parametrize('compressor', ['gz', 'bz2']) + @pytest.mark.parametrize("compressor", ["gz", "bz2"]) def test_archive_tar_symlink(self, compressor): - pytest.skip('Not supported') + pytest.skip("Not supported") - @pytest.mark.parametrize('compressor', ['gz', 'bz2']) + @pytest.mark.parametrize("compressor", ["gz", "bz2"]) def test_archive_tar_file_modes(self, compressor, tmpdir, tmp_path, d_cache_config): - archive_node = tmp_path / 'archive-node' + archive_node = tmp_path / "archive-node" archive_node.touch() archive_lnk = self.tip.archive_repo( - str(archive_node), kind='t{}'.format(compressor), archive_dir_name='repo', cache_config=d_cache_config) + str(archive_node), kind="t{}".format(compressor), archive_dir_name="repo", cache_config=d_cache_config + ) out_dir = tmpdir - out_file = tarfile.open(str(archive_lnk), 'r|{}'.format(compressor)) + out_file = tarfile.open(str(archive_lnk), "r|{}".format(compressor)) out_file.extractall(out_dir) out_file.close() def dest(inp): return os.path.join(out_dir, "repo/" + inp) - assert oct(os.stat(dest('not_executable')).st_mode) == '0o100644' + assert oct(os.stat(dest("not_executable")).st_mode) == "0o100644" def test_archive_zip(self, tmp_path, d_cache_config): - archive_node = tmp_path / 'archive-node' - archive_node.touch() - - archive_lnk = self.tip.archive_repo(str(archive_node), kind='zip', - archive_dir_name='repo', cache_config=d_cache_config) - zip_file = zipfile.ZipFile(str(archive_lnk)) - - for x in range(5): - node_path = '%d/file_%d.txt' % (x, x) - data = zip_file.read(f'repo/{node_path}') - - decompressed = io.BytesIO() - decompressed.write(data) - assert decompressed.getvalue() == \ - self.tip.get_node(node_path).content - decompressed.close() - - def test_archive_zip_with_metadata(self, tmp_path, d_cache_config): - archive_node = tmp_path / 'archive-node' + archive_node = tmp_path / "archive-node" archive_node.touch() archive_lnk = self.tip.archive_repo( - str(archive_node), kind='zip', - archive_dir_name='repo', write_metadata=True, cache_config=d_cache_config) - + str(archive_node), kind="zip", archive_dir_name="repo", cache_config=d_cache_config + ) zip_file = zipfile.ZipFile(str(archive_lnk)) - metafile = zip_file.read('repo/.archival.txt') - - raw_id = ascii_bytes(self.tip.raw_id) - assert b'commit_id:%b' % raw_id in metafile for x in range(5): - node_path = '%d/file_%d.txt' % (x, x) - data = zip_file.read(f'repo/{node_path}') + node_path = "%d/file_%d.txt" % (x, x) + data = zip_file.read(f"repo/{node_path}") + decompressed = io.BytesIO() decompressed.write(data) - assert decompressed.getvalue() == \ - self.tip.get_node(node_path).content + assert decompressed.getvalue() == self.tip.get_node(node_path).content + decompressed.close() + + def test_archive_zip_with_metadata(self, tmp_path, d_cache_config): + archive_node = tmp_path / "archive-node" + archive_node.touch() + + archive_lnk = self.tip.archive_repo( + str(archive_node), kind="zip", archive_dir_name="repo", write_metadata=True, cache_config=d_cache_config + ) + + zip_file = zipfile.ZipFile(str(archive_lnk)) + metafile = zip_file.read("repo/.archival.txt") + + raw_id = ascii_bytes(self.tip.raw_id) + assert b"commit_id:%b" % raw_id in metafile + + for x in range(5): + node_path = "%d/file_%d.txt" % (x, x) + data = zip_file.read(f"repo/{node_path}") + decompressed = io.BytesIO() + decompressed.write(data) + assert decompressed.getvalue() == self.tip.get_node(node_path).content decompressed.close() def test_archive_wrong_kind(self, tmp_path, d_cache_config): - archive_node = tmp_path / 'archive-node' + archive_node = tmp_path / "archive-node" archive_node.touch() with pytest.raises(ImproperArchiveTypeError): - self.tip.archive_repo(str(archive_node), kind='wrong kind', cache_config=d_cache_config) + self.tip.archive_repo(str(archive_node), kind="wrong kind", cache_config=d_cache_config) @pytest.fixture() @@ -167,8 +165,8 @@ def base_commit(): """ commit = base.BaseCommit() commit.repository = mock.Mock() - commit.repository.name = 'fake_repo' - commit.short_id = 'fake_id' + commit.repository.name = "fake_repo" + commit.short_id = "fake_id" return commit @@ -180,19 +178,17 @@ def test_validate_archive_prefix_enforces_non_ascii_as_prefix(base_commit): def test_validate_archive_prefix_empty_prefix(base_commit): # TODO: johbo: Should raise a ValueError here. with pytest.raises(VCSError): - base_commit._validate_archive_prefix('') + base_commit._validate_archive_prefix("") def test_validate_archive_prefix_with_leading_slash(base_commit): # TODO: johbo: Should raise a ValueError here. with pytest.raises(VCSError): - base_commit._validate_archive_prefix('/any') + base_commit._validate_archive_prefix("/any") def test_validate_archive_prefix_falls_back_to_repository_name(base_commit): prefix = base_commit._validate_archive_prefix(None) - expected_prefix = base_commit._ARCHIVE_PREFIX_TEMPLATE.format( - repo_name='fake_repo', - short_id='fake_id') + expected_prefix = base_commit._ARCHIVE_PREFIX_TEMPLATE.format(repo_name="fake_repo", short_id="fake_id") assert isinstance(prefix, str) assert prefix == expected_prefix diff --git a/rhodecode/tests/vcs/test_branches.py b/rhodecode/tests/vcs/test_branches.py index 2159c6f9..2cea7691 100644 --- a/rhodecode/tests/vcs/test_branches.py +++ b/rhodecode/tests/vcs/test_branches.py @@ -64,18 +64,14 @@ class TestBranches(BackendTestMixin): def test_new_head(self): tip = self.repo.get_commit() - self.imc.add( - FileNode(b"docs/index.txt", content=b"Documentation\n") - ) + self.imc.add(FileNode(b"docs/index.txt", content=b"Documentation\n")) foobar_tip = self.imc.commit( message="New branch: foobar", author="joe ", branch="foobar", parents=[tip], ) - self.imc.change( - FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n") - ) + self.imc.change(FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n")) assert foobar_tip.branch == "foobar" newtip = self.imc.commit( message="At foobar_tip branch", @@ -96,21 +92,15 @@ class TestBranches(BackendTestMixin): @pytest.mark.backends("git", "hg") def test_branch_with_slash_in_name(self): self.imc.add(FileNode(b"extrafile", content=b"Some data\n")) - self.imc.commit( - "Branch with a slash!", author="joe ", branch="issue/123" - ) + self.imc.commit("Branch with a slash!", author="joe ", branch="issue/123") assert "issue/123" in self.repo.branches @pytest.mark.backends("git", "hg") def test_branch_with_slash_in_name_and_similar_without(self): self.imc.add(FileNode(b"extrafile", content=b"Some data\n")) - self.imc.commit( - "Branch with a slash!", author="joe ", branch="issue/123" - ) + self.imc.commit("Branch with a slash!", author="joe ", branch="issue/123") self.imc.add(FileNode(b"extrafile II", content=b"Some data\n")) - self.imc.commit( - "Branch without a slash...", author="joe ", branch="123" - ) + self.imc.commit("Branch without a slash...", author="joe ", branch="123") assert "issue/123" in self.repo.branches assert "123" in self.repo.branches diff --git a/rhodecode/tests/vcs/test_client_http.py b/rhodecode/tests/vcs/test_client_http.py index eb7fe2bc..2618dd24 100644 --- a/rhodecode/tests/vcs/test_client_http.py +++ b/rhodecode/tests/vcs/test_client_http.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -28,9 +27,7 @@ from rhodecode.lib.vcs import client_http, exceptions def is_new_connection(logger, level, message): - return ( - logger == 'requests.packages.urllib3.connectionpool' and - message.startswith('Starting new HTTP')) + return logger == "requests.packages.urllib3.connectionpool" and message.startswith("Starting new HTTP") @pytest.fixture() @@ -54,7 +51,7 @@ def stub_fail_session(): """ session = mock.Mock() post = session.post() - post.content = msgpack.packb({'error': '500'}) + post.content = msgpack.packb({"error": "500"}) post.status_code = 500 session.reset_mock() @@ -89,44 +86,37 @@ def test_uses_persistent_http_connections(caplog, vcsbackend_hg): for x in range(5): remote_call(normal=True, closed=False) - new_connections = [ - r for r in caplog.record_tuples if is_new_connection(*r)] + new_connections = [r for r in caplog.record_tuples if is_new_connection(*r)] assert len(new_connections) <= 1 def test_repo_maker_uses_session_for_classmethods(stub_session_factory): - repo_maker = client_http.RemoteVCSMaker( - 'server_and_port', 'endpoint', 'test_dummy_scm', stub_session_factory) + repo_maker = client_http.RemoteVCSMaker("server_and_port", "endpoint", "test_dummy_scm", stub_session_factory) repo_maker.example_call() - stub_session_factory().post.assert_called_with( - 'http://server_and_port/endpoint', data=mock.ANY) + stub_session_factory().post.assert_called_with("http://server_and_port/endpoint", data=mock.ANY) -def test_repo_maker_uses_session_for_instance_methods( - stub_session_factory, config): - repo_maker = client_http.RemoteVCSMaker( - 'server_and_port', 'endpoint', 'test_dummy_scm', stub_session_factory) - repo = repo_maker('stub_path', 'stub_repo_id', config) +def test_repo_maker_uses_session_for_instance_methods(stub_session_factory, config): + repo_maker = client_http.RemoteVCSMaker("server_and_port", "endpoint", "test_dummy_scm", stub_session_factory) + repo = repo_maker("stub_path", "stub_repo_id", config) repo.example_call() - stub_session_factory().post.assert_called_with( - 'http://server_and_port/endpoint', data=mock.ANY) + stub_session_factory().post.assert_called_with("http://server_and_port/endpoint", data=mock.ANY) -@mock.patch('rhodecode.lib.vcs.client_http.ThreadlocalSessionFactory') -@mock.patch('rhodecode.lib.vcs.connection') -def test_connect_passes_in_the_same_session( - connection, session_factory_class, stub_session): +@mock.patch("rhodecode.lib.vcs.client_http.ThreadlocalSessionFactory") +@mock.patch("rhodecode.lib.vcs.connection") +def test_connect_passes_in_the_same_session(connection, session_factory_class, stub_session): session_factory = session_factory_class.return_value session_factory.return_value = stub_session - vcs.connect_http('server_and_port') + vcs.connect_http("server_and_port") -def test_repo_maker_uses_session_that_throws_error( - stub_session_failing_factory, config): +def test_repo_maker_uses_session_that_throws_error(stub_session_failing_factory, config): repo_maker = client_http.RemoteVCSMaker( - 'server_and_port', 'endpoint', 'test_dummy_scm', stub_session_failing_factory) - repo = repo_maker('stub_path', 'stub_repo_id', config) + "server_and_port", "endpoint", "test_dummy_scm", stub_session_failing_factory + ) + repo = repo_maker("stub_path", "stub_repo_id", config) with pytest.raises(exceptions.HttpVCSCommunicationError): repo.example_call() diff --git a/rhodecode/tests/vcs/test_commits.py b/rhodecode/tests/vcs/test_commits.py index 81fedd0d..df57ca1f 100644 --- a/rhodecode/tests/vcs/test_commits.py +++ b/rhodecode/tests/vcs/test_commits.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -23,27 +22,31 @@ import time import pytest from rhodecode.lib.str_utils import safe_bytes -from rhodecode.lib.vcs.backends.base import ( - CollectionGenerator, FILEMODE_DEFAULT, EmptyCommit) +from rhodecode.lib.vcs.backends.base import CollectionGenerator, FILEMODE_DEFAULT, EmptyCommit from rhodecode.lib.vcs.exceptions import ( - BranchDoesNotExistError, CommitDoesNotExistError, - RepositoryError, EmptyRepositoryError) + BranchDoesNotExistError, + CommitDoesNotExistError, + RepositoryError, + EmptyRepositoryError, +) from rhodecode.lib.vcs.nodes import ( - FileNode, AddedFileNodesGenerator, - ChangedFileNodesGenerator, RemovedFileNodesGenerator) + FileNode, + AddedFileNodesGenerator, + ChangedFileNodesGenerator, + RemovedFileNodesGenerator, +) from rhodecode.tests import get_new_dir from rhodecode.tests.vcs.conftest import BackendTestMixin class TestBaseChangeset(object): - def test_is_deprecated(self): from rhodecode.lib.vcs.backends.base import BaseChangeset + pytest.deprecated_call(BaseChangeset) class TestEmptyCommit(object): - def test_branch_without_alias_returns_none(self): commit = EmptyCommit() assert commit.branch is None @@ -58,29 +61,28 @@ class TestCommitsInNonEmptyRepo(BackendTestMixin): start_date = datetime.datetime(2010, 1, 1, 20) for x in range(5): yield { - 'message': 'Commit %d' % x, - 'author': 'Joe Doe ', - 'date': start_date + datetime.timedelta(hours=12 * x), - 'added': [ - FileNode(b'file_%d.txt' % x, - content=b'Foobar %d' % x), + "message": "Commit %d" % x, + "author": "Joe Doe ", + "date": start_date + datetime.timedelta(hours=12 * x), + "added": [ + FileNode(b"file_%d.txt" % x, content=b"Foobar %d" % x), ], } def test_walk_returns_empty_list_in_case_of_file(self): - result = list(self.tip.walk('file_0.txt')) + result = list(self.tip.walk("file_0.txt")) assert result == [] @pytest.mark.backends("git", "hg") def test_new_branch(self): - self.imc.add(FileNode(b'docs/index.txt', content=b'Documentation\n')) + self.imc.add(FileNode(b"docs/index.txt", content=b"Documentation\n")) foobar_tip = self.imc.commit( - message='New branch: foobar', - author='joe ', - branch='foobar', + message="New branch: foobar", + author="joe ", + branch="foobar", ) - assert 'foobar' in self.repo.branches - assert foobar_tip.branch == 'foobar' + assert "foobar" in self.repo.branches + assert foobar_tip.branch == "foobar" # 'foobar' should be the only branch that contains the new commit branch = list(self.repo.branches.values()) assert branch[0] != branch[1] @@ -89,18 +91,14 @@ class TestCommitsInNonEmptyRepo(BackendTestMixin): def test_new_head_in_default_branch(self): tip = self.repo.get_commit() - self.imc.add( - FileNode(b"docs/index.txt", content=b"Documentation\n") - ) + self.imc.add(FileNode(b"docs/index.txt", content=b"Documentation\n")) foobar_tip = self.imc.commit( message="New branch: foobar", author="joe ", branch="foobar", parents=[tip], ) - self.imc.change( - FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n") - ) + self.imc.change(FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n")) assert foobar_tip.branch == "foobar" newtip = self.imc.commit( message="At foobar_tip branch", @@ -132,51 +130,55 @@ class TestCommitsInNonEmptyRepo(BackendTestMixin): :return: """ DEFAULT_BRANCH = self.repo.DEFAULT_BRANCH_NAME - TEST_BRANCH = 'docs' + TEST_BRANCH = "docs" org_tip = self.repo.get_commit() - self.imc.add(FileNode(b'readme.txt', content=b'Document\n')) + self.imc.add(FileNode(b"readme.txt", content=b"Document\n")) initial = self.imc.commit( - message='Initial commit', - author='joe ', + message="Initial commit", + author="joe ", parents=[org_tip], - branch=DEFAULT_BRANCH,) + branch=DEFAULT_BRANCH, + ) - self.imc.add(FileNode(b'newdoc.txt', content=b'foobar\n')) + self.imc.add(FileNode(b"newdoc.txt", content=b"foobar\n")) docs_branch_commit1 = self.imc.commit( - message='New branch: docs', - author='joe ', + message="New branch: docs", + author="joe ", parents=[initial], - branch=TEST_BRANCH,) + branch=TEST_BRANCH, + ) - self.imc.add(FileNode(b'newdoc2.txt', content=b'foobar2\n')) + self.imc.add(FileNode(b"newdoc2.txt", content=b"foobar2\n")) docs_branch_commit2 = self.imc.commit( - message='New branch: docs2', - author='joe ', + message="New branch: docs2", + author="joe ", parents=[docs_branch_commit1], - branch=TEST_BRANCH,) + branch=TEST_BRANCH, + ) - self.imc.add(FileNode(b'newfile', content=b'hello world\n')) + self.imc.add(FileNode(b"newfile", content=b"hello world\n")) self.imc.commit( - message='Back in default branch', - author='joe ', + message="Back in default branch", + author="joe ", parents=[initial], - branch=DEFAULT_BRANCH,) + branch=DEFAULT_BRANCH, + ) default_branch_commits = self.repo.get_commits(branch_name=DEFAULT_BRANCH) assert docs_branch_commit1 not in list(default_branch_commits) assert docs_branch_commit2 not in list(default_branch_commits) docs_branch_commits = self.repo.get_commits( - start_id=self.repo.commit_ids[0], end_id=self.repo.commit_ids[-1], - branch_name=TEST_BRANCH) + start_id=self.repo.commit_ids[0], end_id=self.repo.commit_ids[-1], branch_name=TEST_BRANCH + ) assert docs_branch_commit1 in list(docs_branch_commits) assert docs_branch_commit2 in list(docs_branch_commits) @pytest.mark.backends("svn") def test_get_commits_respects_branch_name_svn(self, vcsbackend_svn): - repo = vcsbackend_svn['svn-simple-layout'] - commits = repo.get_commits(branch_name='trunk') + repo = vcsbackend_svn["svn-simple-layout"] + commits = repo.get_commits(branch_name="trunk") commit_indexes = [c.idx for c in commits] assert commit_indexes == [1, 2, 3, 7, 12, 15] @@ -214,13 +216,10 @@ class TestCommits(BackendTestMixin): start_date = datetime.datetime(2010, 1, 1, 20) for x in range(5): yield { - 'message': 'Commit %d' % x, - 'author': 'Joe Doe ', - 'date': start_date + datetime.timedelta(hours=12 * x), - 'added': [ - FileNode(b'file_%d.txt' % x, - content=b'Foobar %d' % x) - ], + "message": "Commit %d" % x, + "author": "Joe Doe ", + "date": start_date + datetime.timedelta(hours=12 * x), + "added": [FileNode(b"file_%d.txt" % x, content=b"Foobar %d" % x)], } def test_simple(self): @@ -231,11 +230,11 @@ class TestCommits(BackendTestMixin): tip = self.repo.get_commit() # json.dumps(tip) uses .__json__() method data = tip.__json__() - assert 'branch' in data - assert data['revision'] + assert "branch" in data + assert data["revision"] def test_retrieve_tip(self): - tip = self.repo.get_commit('tip') + tip = self.repo.get_commit("tip") assert tip == self.repo.get_commit() def test_invalid(self): @@ -259,34 +258,34 @@ class TestCommits(BackendTestMixin): def test_size(self): tip = self.repo.get_commit() - size = 5 * len('Foobar N') # Size of 5 files + size = 5 * len("Foobar N") # Size of 5 files assert tip.size == size def test_size_at_commit(self): tip = self.repo.get_commit() - size = 5 * len('Foobar N') # Size of 5 files + size = 5 * len("Foobar N") # Size of 5 files assert self.repo.size_at_commit(tip.raw_id) == size def test_size_at_first_commit(self): commit = self.repo[0] - size = len('Foobar N') # Size of 1 file + size = len("Foobar N") # Size of 1 file assert self.repo.size_at_commit(commit.raw_id) == size def test_author(self): tip = self.repo.get_commit() - assert_text_equal(tip.author, 'Joe Doe ') + assert_text_equal(tip.author, "Joe Doe ") def test_author_name(self): tip = self.repo.get_commit() - assert_text_equal(tip.author_name, 'Joe Doe') + assert_text_equal(tip.author_name, "Joe Doe") def test_author_email(self): tip = self.repo.get_commit() - assert_text_equal(tip.author_email, 'joe.doe@example.com') + assert_text_equal(tip.author_email, "joe.doe@example.com") def test_message(self): tip = self.repo.get_commit() - assert_text_equal(tip.message, 'Commit 4') + assert_text_equal(tip.message, "Commit 4") def test_diff(self): tip = self.repo.get_commit() @@ -296,7 +295,7 @@ class TestCommits(BackendTestMixin): def test_prev(self): tip = self.repo.get_commit() prev_commit = tip.prev() - assert prev_commit.message == 'Commit 3' + assert prev_commit.message == "Commit 3" def test_prev_raises_on_first_commit(self): commit = self.repo.get_commit(commit_idx=0) @@ -311,7 +310,7 @@ class TestCommits(BackendTestMixin): def test_next(self): commit = self.repo.get_commit(commit_idx=2) next_commit = commit.next() - assert next_commit.message == 'Commit 3' + assert next_commit.message == "Commit 3" def test_next_raises_on_tip(self): commit = self.repo.get_commit() @@ -320,36 +319,36 @@ class TestCommits(BackendTestMixin): def test_get_path_commit(self): commit = self.repo.get_commit() - commit.get_path_commit('file_4.txt') - assert commit.message == 'Commit 4' + commit.get_path_commit("file_4.txt") + assert commit.message == "Commit 4" def test_get_filenodes_generator(self): tip = self.repo.get_commit() filepaths = [node.path for node in tip.get_filenodes_generator()] - assert filepaths == ['file_%d.txt' % x for x in range(5)] + assert filepaths == ["file_%d.txt" % x for x in range(5)] def test_get_file_annotate(self): file_added_commit = self.repo.get_commit(commit_idx=3) - annotations = list(file_added_commit.get_file_annotate('file_3.txt')) + annotations = list(file_added_commit.get_file_annotate("file_3.txt")) line_no, commit_id, commit_loader, line = annotations[0] assert line_no == 1 assert commit_id == file_added_commit.raw_id assert commit_loader() == file_added_commit - assert b'Foobar 3' in line + assert b"Foobar 3" in line def test_get_file_annotate_does_not_exist(self): file_added_commit = self.repo.get_commit(commit_idx=2) # TODO: Should use a specific exception class here? with pytest.raises(Exception): - list(file_added_commit.get_file_annotate('file_3.txt')) + list(file_added_commit.get_file_annotate("file_3.txt")) def test_get_file_annotate_tip(self): tip = self.repo.get_commit() commit = self.repo.get_commit(commit_idx=3) - expected_values = list(commit.get_file_annotate('file_3.txt')) - annotations = list(tip.get_file_annotate('file_3.txt')) + expected_values = list(commit.get_file_annotate("file_3.txt")) + annotations = list(tip.get_file_annotate("file_3.txt")) # Note: Skip index 2 because the loader function is not the same for idx in (0, 1, 3): @@ -398,7 +397,7 @@ class TestCommits(BackendTestMixin): repo = self.Backend(repo_path, create=True) with pytest.raises(EmptyRepositoryError): - list(repo.get_commits(start_id='foobar')) + list(repo.get_commits(start_id="foobar")) def test_get_commits_respects_hidden(self): commits = self.repo.get_commits(show_hidden=True) @@ -424,8 +423,7 @@ class TestCommits(BackendTestMixin): def test_get_commits_respects_start_date_with_branch(self): start_date = datetime.datetime(2010, 1, 2) - commits = self.repo.get_commits( - start_date=start_date, branch_name=self.repo.DEFAULT_BRANCH_NAME) + commits = self.repo.get_commits(start_date=start_date, branch_name=self.repo.DEFAULT_BRANCH_NAME) assert isinstance(commits, CollectionGenerator) # Should be 4 commits after 2010-01-02 00:00:00 assert len(commits) == 4 @@ -435,8 +433,7 @@ class TestCommits(BackendTestMixin): def test_get_commits_respects_start_date_and_end_date(self): start_date = datetime.datetime(2010, 1, 2) end_date = datetime.datetime(2010, 1, 3) - commits = self.repo.get_commits(start_date=start_date, - end_date=end_date) + commits = self.repo.get_commits(start_date=start_date, end_date=end_date) assert isinstance(commits, CollectionGenerator) assert len(commits) == 2 for c in commits: @@ -459,23 +456,22 @@ class TestCommits(BackendTestMixin): assert list(commit_ids) == list(reversed(self.repo.commit_ids)) def test_get_commits_slice_generator(self): - commits = self.repo.get_commits( - branch_name=self.repo.DEFAULT_BRANCH_NAME) + commits = self.repo.get_commits(branch_name=self.repo.DEFAULT_BRANCH_NAME) assert isinstance(commits, CollectionGenerator) commit_slice = list(commits[1:3]) assert len(commit_slice) == 2 def test_get_commits_raise_commitdoesnotexist_for_wrong_start(self): with pytest.raises(CommitDoesNotExistError): - list(self.repo.get_commits(start_id='foobar')) + list(self.repo.get_commits(start_id="foobar")) def test_get_commits_raise_commitdoesnotexist_for_wrong_end(self): with pytest.raises(CommitDoesNotExistError): - list(self.repo.get_commits(end_id='foobar')) + list(self.repo.get_commits(end_id="foobar")) def test_get_commits_raise_branchdoesnotexist_for_wrong_branch_name(self): with pytest.raises(BranchDoesNotExistError): - list(self.repo.get_commits(branch_name='foobar')) + list(self.repo.get_commits(branch_name="foobar")) def test_get_commits_raise_repositoryerror_for_wrong_start_end(self): start_id = self.repo.commit_ids[-1] @@ -498,13 +494,16 @@ class TestCommits(BackendTestMixin): assert commit1 is not None assert commit2 is not None assert 1 != commit1 - assert 'string' != commit1 + assert "string" != commit1 -@pytest.mark.parametrize("filename, expected", [ - ("README.rst", False), - ("README", True), -]) +@pytest.mark.parametrize( + "filename, expected", + [ + ("README.rst", False), + ("README", True), + ], +) def test_commit_is_link(vcsbackend, filename, expected): commit = vcsbackend.repo.get_commit() link_status = commit.is_link(filename) @@ -519,75 +518,74 @@ class TestCommitsChanges(BackendTestMixin): def _get_commits(cls): return [ { - 'message': 'Initial', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 20), - 'added': [ - FileNode(b'foo/bar', content=b'foo'), - FileNode(safe_bytes('foo/bał'), content=b'foo'), - FileNode(b'foobar', content=b'foo'), - FileNode(b'qwe', content=b'foo'), + "message": "Initial", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 20), + "added": [ + FileNode(b"foo/bar", content=b"foo"), + FileNode(safe_bytes("foo/bał"), content=b"foo"), + FileNode(b"foobar", content=b"foo"), + FileNode(b"qwe", content=b"foo"), ], }, { - 'message': 'Massive changes', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 22), - 'added': [FileNode(b'fallout', content=b'War never changes')], - 'changed': [ - FileNode(b'foo/bar', content=b'baz'), - FileNode(b'foobar', content=b'baz'), + "message": "Massive changes", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 22), + "added": [FileNode(b"fallout", content=b"War never changes")], + "changed": [ + FileNode(b"foo/bar", content=b"baz"), + FileNode(b"foobar", content=b"baz"), ], - 'removed': [FileNode(b'qwe')], + "removed": [FileNode(b"qwe")], }, ] def test_initial_commit(self, local_dt_to_utc): commit = self.repo.get_commit(commit_idx=0) assert set(commit.added) == { - commit.get_node('foo/bar'), - commit.get_node('foo/bał'), - commit.get_node('foobar'), - commit.get_node('qwe') + commit.get_node("foo/bar"), + commit.get_node("foo/bał"), + commit.get_node("foobar"), + commit.get_node("qwe"), } assert set(commit.changed) == set() assert set(commit.removed) == set() - assert set(commit.affected_files) == {'foo/bar', 'foo/bał', 'foobar', 'qwe'} - assert commit.date == local_dt_to_utc( - datetime.datetime(2010, 1, 1, 20, 0)) + assert set(commit.affected_files) == {"foo/bar", "foo/bał", "foobar", "qwe"} + assert commit.date == local_dt_to_utc(datetime.datetime(2010, 1, 1, 20, 0)) def test_head_added(self): commit = self.repo.get_commit() assert isinstance(commit.added, AddedFileNodesGenerator) - assert set(commit.added) == {commit.get_node('fallout')} + assert set(commit.added) == {commit.get_node("fallout")} assert isinstance(commit.changed, ChangedFileNodesGenerator) - assert set(commit.changed) == {commit.get_node('foo/bar'), commit.get_node('foobar')} + assert set(commit.changed) == {commit.get_node("foo/bar"), commit.get_node("foobar")} assert isinstance(commit.removed, RemovedFileNodesGenerator) assert len(commit.removed) == 1 - assert list(commit.removed)[0].path == 'qwe' + assert list(commit.removed)[0].path == "qwe" def test_get_filemode(self): commit = self.repo.get_commit() - assert FILEMODE_DEFAULT == commit.get_file_mode('foo/bar') + assert FILEMODE_DEFAULT == commit.get_file_mode("foo/bar") def test_get_filemode_non_ascii(self): commit = self.repo.get_commit() - assert FILEMODE_DEFAULT == commit.get_file_mode('foo/bał') - assert FILEMODE_DEFAULT == commit.get_file_mode('foo/bał') + assert FILEMODE_DEFAULT == commit.get_file_mode("foo/bał") + assert FILEMODE_DEFAULT == commit.get_file_mode("foo/bał") def test_get_path_history(self): commit = self.repo.get_commit() - history = commit.get_path_history('foo/bar') + history = commit.get_path_history("foo/bar") assert len(history) == 2 def test_get_path_history_with_limit(self): commit = self.repo.get_commit() - history = commit.get_path_history('foo/bar', limit=1) + history = commit.get_path_history("foo/bar", limit=1) assert len(history) == 1 def test_get_path_history_first_commit(self): commit = self.repo[0] - history = commit.get_path_history('foo/bar') + history = commit.get_path_history("foo/bar") assert len(history) == 1 diff --git a/rhodecode/tests/vcs/test_config.py b/rhodecode/tests/vcs/test_config.py index 9aac9d64..4ecea4a2 100644 --- a/rhodecode/tests/vcs/test_config.py +++ b/rhodecode/tests/vcs/test_config.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -21,14 +20,17 @@ import pytest def test_get_existing_value(config): - value = config.get('section-a', 'a-1') - assert value == 'value-a-1' + value = config.get("section-a", "a-1") + assert value == "value-a-1" -@pytest.mark.parametrize('section, option', [ - ('section-a', 'does-not-exist'), - ('does-not-exist', 'does-not-exist'), -]) +@pytest.mark.parametrize( + "section, option", + [ + ("section-a", "does-not-exist"), + ("does-not-exist", "does-not-exist"), + ], +) def test_get_unset_value_returns_none(config, section, option): value = config.get(section, option) assert value is None @@ -41,11 +43,11 @@ def test_allows_to_create_a_copy(config): def test_changes_in_the_copy_dont_affect_the_original(config): clone = config.copy() - clone.set('section-a', 'a-2', 'value-a-2') - assert set(config.serialize()) == {('section-a', 'a-1', 'value-a-1')} + clone.set("section-a", "a-2", "value-a-2") + assert set(config.serialize()) == {("section-a", "a-1", "value-a-1")} def test_changes_in_the_original_dont_affect_the_copy(config): clone = config.copy() - config.set('section-a', 'a-2', 'value-a-2') - assert set(clone.serialize()) == {('section-a', 'a-1', 'value-a-1')} + config.set("section-a", "a-2", "value-a-2") + assert set(clone.serialize()) == {("section-a", "a-1", "value-a-1")} diff --git a/rhodecode/tests/vcs/test_diff.py b/rhodecode/tests/vcs/test_diff.py index 190b22b0..38b4a3f4 100644 --- a/rhodecode/tests/vcs/test_diff.py +++ b/rhodecode/tests/vcs/test_diff.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -25,7 +24,6 @@ from rhodecode.tests.vcs.conftest import BackendTestMixin class TestGetDiffValidation: - def test_raises_on_string_input(self, vcsbackend): repo = vcsbackend.repo with pytest.raises(TypeError): @@ -54,63 +52,58 @@ class TestGetDiffValidation: def test_supports_path1_parameter(self, vcsbackend): repo = vcsbackend.repo commit = repo[1] - repo.get_diff( - repo.EMPTY_COMMIT, commit, - path='vcs/__init__.py', path1='vcs/__init__.py') + repo.get_diff(repo.EMPTY_COMMIT, commit, path="vcs/__init__.py", path1="vcs/__init__.py") @pytest.mark.backends("git", "hg") def test_raises_value_error_if_paths_not_supported(self, vcsbackend): repo = vcsbackend.repo commit = repo[1] with pytest.raises(ValueError): - repo.get_diff( - repo.EMPTY_COMMIT, commit, - path='trunk/example.py', path1='branches/argparse/example.py') + repo.get_diff(repo.EMPTY_COMMIT, commit, path="trunk/example.py", path1="branches/argparse/example.py") @pytest.mark.usefixtures("vcs_repository_support") class TestRepositoryGetDiff(BackendTestMixin): - recreate_repo_per_test = False @classmethod def _get_commits(cls): commits = [ { - 'message': 'Initial commit', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 20), - 'added': [ - FileNode(b'foobar', content=b'foobar'), - FileNode(b'foobar2', content=b'foobar2'), + "message": "Initial commit", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 20), + "added": [ + FileNode(b"foobar", content=b"foobar"), + FileNode(b"foobar2", content=b"foobar2"), ], }, { - 'message': 'Changed foobar, added foobar3', - 'author': 'Jane Doe ', - 'date': datetime.datetime(2010, 1, 1, 21), - 'added': [ - FileNode(b'foobar3', content=b'foobar3'), + "message": "Changed foobar, added foobar3", + "author": "Jane Doe ", + "date": datetime.datetime(2010, 1, 1, 21), + "added": [ + FileNode(b"foobar3", content=b"foobar3"), ], - 'changed': [ - FileNode(b'foobar', b'FOOBAR'), + "changed": [ + FileNode(b"foobar", b"FOOBAR"), ], }, { - 'message': 'Removed foobar, changed foobar3', - 'author': 'Jane Doe ', - 'date': datetime.datetime(2010, 1, 1, 22), - 'changed': [ - FileNode(b'foobar3', content=b'FOOBAR\nFOOBAR\nFOOBAR\n'), + "message": "Removed foobar, changed foobar3", + "author": "Jane Doe ", + "date": datetime.datetime(2010, 1, 1, 22), + "changed": [ + FileNode(b"foobar3", content=b"FOOBAR\nFOOBAR\nFOOBAR\n"), ], - 'removed': [FileNode(b'foobar')], + "removed": [FileNode(b"foobar")], }, { - 'message': 'Whitespace changes', - 'author': 'Jane Doe ', - 'date': datetime.datetime(2010, 1, 1, 23), - 'changed': [ - FileNode(b'foobar3', content=b'FOOBAR \nFOOBAR\nFOOBAR\n'), + "message": "Whitespace changes", + "author": "Jane Doe ", + "date": datetime.datetime(2010, 1, 1, 23), + "changed": [ + FileNode(b"foobar3", content=b"FOOBAR \nFOOBAR\nFOOBAR\n"), ], }, ] @@ -130,28 +123,24 @@ class TestRepositoryGetDiff(BackendTestMixin): assert diff.raw.tobytes() == self.third_commit_diffs[self.repo.alias] def test_ignore_whitespace(self): - diff = self.repo.get_diff( - self.repo[2], self.repo[3], ignore_whitespace=True) - assert b'@@' not in diff.raw.tobytes() + diff = self.repo.get_diff(self.repo[2], self.repo[3], ignore_whitespace=True) + assert b"@@" not in diff.raw.tobytes() def test_only_one_file(self): - diff = self.repo.get_diff( - self.repo.EMPTY_COMMIT, self.repo[0], path='foobar') - assert b'foobar2' not in diff.raw.tobytes() + diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, self.repo[0], path="foobar") + assert b"foobar2" not in diff.raw.tobytes() def test_context_parameter(self): first_commit = self.repo.get_commit(commit_idx=0) - diff = self.repo.get_diff( - self.repo.EMPTY_COMMIT, first_commit, context=2) + diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, first_commit, context=2) assert diff.raw.tobytes() == self.first_commit_diffs[self.repo.alias] def test_context_only_one_file(self): - diff = self.repo.get_diff( - self.repo.EMPTY_COMMIT, self.repo[0], path='foobar', context=2) + diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, self.repo[0], path="foobar", context=2) assert diff.raw.tobytes() == self.first_commit_one_file[self.repo.alias] first_commit_diffs = { - 'git': br"""diff --git a/foobar b/foobar + "git": rb"""diff --git a/foobar b/foobar new file mode 100644 index 0000000..f6ea049 --- /dev/null @@ -168,7 +157,7 @@ index 0000000..e8c9d6b +foobar2 \ No newline at end of file """, - 'hg': br"""diff --git a/foobar b/foobar + "hg": rb"""diff --git a/foobar b/foobar new file mode 100644 --- /dev/null +++ b/foobar @@ -183,7 +172,7 @@ new file mode 100644 +foobar2 \ No newline at end of file """, - 'svn': b"""Index: foobar + "svn": b"""Index: foobar =================================================================== diff --git a/foobar b/foobar new file mode 10644 @@ -205,7 +194,7 @@ new file mode 10644 } second_commit_diffs = { - 'git': br"""diff --git a/foobar b/foobar + "git": rb"""diff --git a/foobar b/foobar index f6ea049..389865b 100644 --- a/foobar +++ b/foobar @@ -223,7 +212,7 @@ index 0000000..c11c37d +foobar3 \ No newline at end of file """, - 'hg': br"""diff --git a/foobar b/foobar + "hg": rb"""diff --git a/foobar b/foobar --- a/foobar +++ b/foobar @@ -1,1 +1,1 @@ @@ -239,7 +228,7 @@ new file mode 100644 +foobar3 \ No newline at end of file """, - 'svn': b"""Index: foobar + "svn": b"""Index: foobar =================================================================== diff --git a/foobar b/foobar --- a/foobar\t(revision 1) @@ -262,7 +251,7 @@ new file mode 10644 } third_commit_diffs = { - 'git': br"""diff --git a/foobar b/foobar + "git": rb"""diff --git a/foobar b/foobar deleted file mode 100644 index 389865b..0000000 --- a/foobar @@ -281,7 +270,7 @@ index c11c37d..f932447 100644 +FOOBAR +FOOBAR """, - 'hg': br"""diff --git a/foobar b/foobar + "hg": rb"""diff --git a/foobar b/foobar deleted file mode 100644 --- a/foobar +++ /dev/null @@ -298,7 +287,7 @@ diff --git a/foobar3 b/foobar3 +FOOBAR +FOOBAR """, - 'svn': b"""Index: foobar + "svn": b"""Index: foobar =================================================================== diff --git a/foobar b/foobar deleted file mode 10644 @@ -322,7 +311,7 @@ diff --git a/foobar3 b/foobar3 } first_commit_one_file = { - 'git': br"""diff --git a/foobar b/foobar + "git": rb"""diff --git a/foobar b/foobar new file mode 100644 index 0000000..f6ea049 --- /dev/null @@ -331,7 +320,7 @@ index 0000000..f6ea049 +foobar \ No newline at end of file """, - 'hg': br"""diff --git a/foobar b/foobar + "hg": rb"""diff --git a/foobar b/foobar new file mode 100644 --- /dev/null +++ b/foobar @@ -339,7 +328,7 @@ new file mode 100644 +foobar \ No newline at end of file """, - 'svn': b"""Index: foobar + "svn": b"""Index: foobar =================================================================== diff --git a/foobar b/foobar new file mode 10644 @@ -353,13 +342,11 @@ new file mode 10644 class TestSvnGetDiff(object): - - @pytest.mark.parametrize('path, path1', [ - ('trunk/example.py', 'tags/v0.2/example.py'), - ('trunk', 'tags/v0.2') - ], ids=['file', 'dir']) + @pytest.mark.parametrize( + "path, path1", [("trunk/example.py", "tags/v0.2/example.py"), ("trunk", "tags/v0.2")], ids=["file", "dir"] + ) def test_diff_to_tagged_version(self, vcsbackend_svn, path, path1): - repo = vcsbackend_svn['svn-simple-layout'] + repo = vcsbackend_svn["svn-simple-layout"] commit1 = repo[-2] commit2 = repo[-1] diff = repo.get_diff(commit1, commit2, path=path, path1=path1) @@ -386,7 +373,7 @@ diff --git a/example.py b/example.py ''' def test_diff_of_moved_directory(self, vcsbackend_svn): - repo = vcsbackend_svn['svn-move-directory'] + repo = vcsbackend_svn["svn-move-directory"] diff = repo.get_diff(repo[0], repo[1]) # TODO: johbo: Think about supporting svn directory nodes # a little bit better, source is here like a file @@ -408,7 +395,6 @@ new file mode 10644 @pytest.mark.usefixtures("vcs_repository_support") class TestGetDiffBinary(BackendTestMixin): - recreate_repo_per_test = False # Note: "Fake" PNG files, has the correct magic as prefix @@ -419,26 +405,29 @@ class TestGetDiffBinary(BackendTestMixin): def _get_commits(): commits = [ { - 'message': 'Add binary file image.png', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 20), - 'added': [ - FileNode(b'image.png', content=TestGetDiffBinary.BINARY), - ]}, + "message": "Add binary file image.png", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 20), + "added": [ + FileNode(b"image.png", content=TestGetDiffBinary.BINARY), + ], + }, { - 'message': 'Modify image.png', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 21), - 'changed': [ - FileNode(b'image.png', content=TestGetDiffBinary.BINARY2), - ]}, + "message": "Modify image.png", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 21), + "changed": [ + FileNode(b"image.png", content=TestGetDiffBinary.BINARY2), + ], + }, { - 'message': 'Remove image.png', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 21), - 'removed': [ - FileNode(b'image.png'), - ]}, + "message": "Remove image.png", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 21), + "removed": [ + FileNode(b"image.png"), + ], + }, ] return commits @@ -446,7 +435,7 @@ class TestGetDiffBinary(BackendTestMixin): diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, self.repo[0]) expected = { - 'git': b"""diff --git a/image.png b/image.png + "git": b"""diff --git a/image.png b/image.png new file mode 100644 index 0000000000000000000000000000000000000000..28380fd4a25c58be1b68b523ba2a314f4459ee9c GIT binary patch @@ -457,7 +446,7 @@ literal 0 Hc$@', - 'date': datetime.datetime(2010, 1, 1, 20), - 'added': nodes, + "message": "Initial commit", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 20), + "added": nodes, }, ] return commits diff --git a/rhodecode/tests/vcs/test_getitem.py b/rhodecode/tests/vcs/test_getitem.py index 883646ce..d95c7bd8 100644 --- a/rhodecode/tests/vcs/test_getitem.py +++ b/rhodecode/tests/vcs/test_getitem.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -28,28 +27,30 @@ from rhodecode.tests.vcs.conftest import BackendTestMixin @pytest.mark.usefixtures("vcs_repository_support") class TestGetitem(BackendTestMixin): - @classmethod def _get_commits(cls): start_date = datetime.datetime(2010, 1, 1, 20) for x in range(5): yield { - 'message': 'Commit %d' % x, - 'author': 'Joe Doe ', - 'date': start_date + datetime.timedelta(hours=12 * x), - 'added': [ - FileNode(b'file_%d.txt' % x, content='Foobar %d' % x), + "message": "Commit %d" % x, + "author": "Joe Doe ", + "date": start_date + datetime.timedelta(hours=12 * x), + "added": [ + FileNode(b"file_%d.txt" % x, content="Foobar %d" % x), ], } def test_last_item_is_tip(self): assert self.repo[-1] == self.repo.get_commit() - @pytest.mark.parametrize("offset, message", [ - (-1, 'Commit 4'), - (-2, 'Commit 3'), - (-5, 'Commit 0'), - ]) + @pytest.mark.parametrize( + "offset, message", + [ + (-1, "Commit 4"), + (-2, "Commit 3"), + (-5, "Commit 0"), + ], + ) def test_negative_offset_fetches_correct_commit(self, offset, message): assert self.repo[offset].message == message diff --git a/rhodecode/tests/vcs/test_getslice.py b/rhodecode/tests/vcs/test_getslice.py index add11b3f..225aaf83 100644 --- a/rhodecode/tests/vcs/test_getslice.py +++ b/rhodecode/tests/vcs/test_getslice.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -25,17 +24,16 @@ from rhodecode.tests.vcs.conftest import BackendTestMixin @pytest.mark.usefixtures("vcs_repository_support") class TestGetslice(BackendTestMixin): - @classmethod def _get_commits(cls): start_date = datetime.datetime(2010, 1, 1, 20) for x in range(5): yield { - 'message': 'Commit %d' % x, - 'author': 'Joe Doe ', - 'date': start_date + datetime.timedelta(hours=12 * x), - 'added': [ - FileNode(b'file_%d.txt' % x, content='Foobar %d' % x), + "message": "Commit %d" % x, + "author": "Joe Doe ", + "date": start_date + datetime.timedelta(hours=12 * x), + "added": [ + FileNode(b"file_%d.txt" % x, content="Foobar %d" % x), ], } @@ -43,34 +41,24 @@ class TestGetslice(BackendTestMixin): assert list(self.repo[-1:])[0] == self.repo.get_commit() def test__getslice__respects_start_index(self): - assert list(self.repo[2:]) == \ - [self.repo.get_commit(commit_id) - for commit_id in self.repo.commit_ids[2:]] + assert list(self.repo[2:]) == [self.repo.get_commit(commit_id) for commit_id in self.repo.commit_ids[2:]] def test__getslice__respects_negative_start_index(self): - assert list(self.repo[-2:]) == \ - [self.repo.get_commit(commit_id) - for commit_id in self.repo.commit_ids[-2:]] + assert list(self.repo[-2:]) == [self.repo.get_commit(commit_id) for commit_id in self.repo.commit_ids[-2:]] def test__getslice__respects_end_index(self): - assert list(self.repo[:2]) == \ - [self.repo.get_commit(commit_id) - for commit_id in self.repo.commit_ids[:2]] + assert list(self.repo[:2]) == [self.repo.get_commit(commit_id) for commit_id in self.repo.commit_ids[:2]] def test__getslice__respects_negative_end_index(self): - assert list(self.repo[:-2]) == \ - [self.repo.get_commit(commit_id) - for commit_id in self.repo.commit_ids[:-2]] + assert list(self.repo[:-2]) == [self.repo.get_commit(commit_id) for commit_id in self.repo.commit_ids[:-2]] def test__getslice__start_grater_than_end(self): assert list(self.repo[10:0]) == [] def test__getslice__negative_iteration(self): - assert list(self.repo[::-1]) == \ - [self.repo.get_commit(commit_id) - for commit_id in self.repo.commit_ids[::-1]] + assert list(self.repo[::-1]) == [self.repo.get_commit(commit_id) for commit_id in self.repo.commit_ids[::-1]] def test__getslice__iterate_even(self): - assert list(self.repo[0:10:2]) == \ - [self.repo.get_commit(commit_id) - for commit_id in self.repo.commit_ids[0:10:2]] + assert list(self.repo[0:10:2]) == [ + self.repo.get_commit(commit_id) for commit_id in self.repo.commit_ids[0:10:2] + ] diff --git a/rhodecode/tests/vcs/test_git.py b/rhodecode/tests/vcs/test_git.py index 3bdd6c41..48e3c7fc 100644 --- a/rhodecode/tests/vcs/test_git.py +++ b/rhodecode/tests/vcs/test_git.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -27,12 +26,9 @@ import pytest from rhodecode.lib.utils import make_db_config from rhodecode.lib.vcs.backends.base import Reference -from rhodecode.lib.vcs.backends.git import ( - GitRepository, GitCommit, discover_git_version) -from rhodecode.lib.vcs.exceptions import ( - RepositoryError, VCSError, NodeDoesNotExistError) -from rhodecode.lib.vcs.nodes import ( - NodeKind, FileNode, DirNode, NodeState, SubModuleNode) +from rhodecode.lib.vcs.backends.git import GitRepository, GitCommit, discover_git_version +from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError +from rhodecode.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState, SubModuleNode from rhodecode.tests import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir from rhodecode.tests.vcs.conftest import BackendTestMixin @@ -40,7 +36,7 @@ from rhodecode.tests.vcs.conftest import BackendTestMixin pytestmark = pytest.mark.backends("git") -DIFF_FROM_REMOTE = br"""diff --git a/foobar b/foobar +DIFF_FROM_REMOTE = rb"""diff --git a/foobar b/foobar new file mode 100644 index 0000000..f6ea049 --- /dev/null @@ -64,7 +60,6 @@ def callable_get_diff(*args, **kwargs): class TestGitRepository(object): - @pytest.fixture(autouse=True) def prepare(self, request, baseapp): self.repo = GitRepository(TEST_GIT_REPO, bare=True) @@ -74,9 +69,8 @@ class TestGitRepository(object): """ Return a non bare clone of the base repo. """ - clone_path = str(tmpdir.join('clone-repo')) - repo_clone = GitRepository( - clone_path, create=True, src_url=self.repo.path, bare=False) + clone_path = str(tmpdir.join("clone-repo")) + repo_clone = GitRepository(clone_path, create=True, src_url=self.repo.path, bare=False) return repo_clone @@ -84,20 +78,18 @@ class TestGitRepository(object): """ Return a non bare empty repo. """ - clone_path = str(tmpdir.join('empty-repo')) + clone_path = str(tmpdir.join("empty-repo")) return GitRepository(clone_path, create=True, bare=bare) def test_wrong_repo_path(self): - wrong_repo_path = '/tmp/errorrepo_git' + wrong_repo_path = "/tmp/errorrepo_git" with pytest.raises(RepositoryError): GitRepository(wrong_repo_path) def test_repo_clone(self, tmp_path_factory): repo = GitRepository(TEST_GIT_REPO) - clone_path = '{}_{}'.format(tmp_path_factory.mktemp('_'), TEST_GIT_REPO_CLONE) - repo_clone = GitRepository( - clone_path, - src_url=TEST_GIT_REPO, create=True, do_workspace_checkout=True) + clone_path = f"{tmp_path_factory.mktemp('_')}_{TEST_GIT_REPO_CLONE}" + repo_clone = GitRepository(clone_path, src_url=TEST_GIT_REPO, create=True, do_workspace_checkout=True) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # Checking hashes of commits should be enough @@ -107,48 +99,42 @@ class TestGitRepository(object): def test_repo_clone_without_create(self): with pytest.raises(RepositoryError): - GitRepository( - TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO) + GitRepository(TEST_GIT_REPO_CLONE + "_wo_create", src_url=TEST_GIT_REPO) def test_repo_clone_with_update(self, tmp_path_factory): repo = GitRepository(TEST_GIT_REPO) - clone_path = '{}_{}_update'.format(tmp_path_factory.mktemp('_'), TEST_GIT_REPO_CLONE) + clone_path = "{}_{}_update".format(tmp_path_factory.mktemp("_"), TEST_GIT_REPO_CLONE) - repo_clone = GitRepository( - clone_path, - create=True, src_url=TEST_GIT_REPO, do_workspace_checkout=True) + repo_clone = GitRepository(clone_path, create=True, src_url=TEST_GIT_REPO, do_workspace_checkout=True) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # check if current workdir was updated - fpath = os.path.join(clone_path, 'MANIFEST.in') + fpath = os.path.join(clone_path, "MANIFEST.in") assert os.path.isfile(fpath) def test_repo_clone_without_update(self, tmp_path_factory): repo = GitRepository(TEST_GIT_REPO) - clone_path = '{}_{}_without_update'.format(tmp_path_factory.mktemp('_'), TEST_GIT_REPO_CLONE) - repo_clone = GitRepository( - clone_path, - create=True, src_url=TEST_GIT_REPO, do_workspace_checkout=False) + clone_path = "{}_{}_without_update".format(tmp_path_factory.mktemp("_"), TEST_GIT_REPO_CLONE) + repo_clone = GitRepository(clone_path, create=True, src_url=TEST_GIT_REPO, do_workspace_checkout=False) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # check if current workdir was *NOT* updated - fpath = os.path.join(clone_path, 'MANIFEST.in') + fpath = os.path.join(clone_path, "MANIFEST.in") # Make sure it's not bare repo assert not repo_clone.bare assert not os.path.isfile(fpath) def test_repo_clone_into_bare_repo(self, tmp_path_factory): repo = GitRepository(TEST_GIT_REPO) - clone_path = '{}_{}_bare.git'.format(tmp_path_factory.mktemp('_'), TEST_GIT_REPO_CLONE) - repo_clone = GitRepository( - clone_path, create=True, src_url=repo.path, bare=True) + clone_path = "{}_{}_bare.git".format(tmp_path_factory.mktemp("_"), TEST_GIT_REPO_CLONE) + repo_clone = GitRepository(clone_path, create=True, src_url=repo.path, bare=True) assert repo_clone.bare def test_create_repo_is_not_bare_by_default(self): - repo = GitRepository(get_new_dir('not-bare-by-default'), create=True) + repo = GitRepository(get_new_dir("not-bare-by-default"), create=True) assert not repo.bare def test_create_bare_repo(self): - repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True) + repo = GitRepository(get_new_dir("bare-repo"), create=True, bare=True) assert repo.bare def test_update_server_info(self): @@ -167,37 +153,38 @@ class TestGitRepository(object): def test_commit_ids(self): # there are 112 commits (by now) # so we can assume they would be available from now on - subset = {'c1214f7e79e02fc37156ff215cd71275450cffc3', - '38b5fe81f109cb111f549bfe9bb6b267e10bc557', - 'fa6600f6848800641328adbf7811fd2372c02ab2', - '102607b09cdd60e2793929c4f90478be29f85a17', - '49d3fd156b6f7db46313fac355dca1a0b94a0017', - '2d1028c054665b962fa3d307adfc923ddd528038', - 'd7e0d30fbcae12c90680eb095a4f5f02505ce501', - 'ff7ca51e58c505fec0dd2491de52c622bb7a806b', - 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f', - '8430a588b43b5d6da365400117c89400326e7992', - 'd955cd312c17b02143c04fa1099a352b04368118', - 'f67b87e5c629c2ee0ba58f85197e423ff28d735b', - 'add63e382e4aabc9e1afdc4bdc24506c269b7618', - 'f298fe1189f1b69779a4423f40b48edf92a703fc', - 'bd9b619eb41994cac43d67cf4ccc8399c1125808', - '6e125e7c890379446e98980d8ed60fba87d0f6d1', - 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd', - '0b05e4ed56c802098dfc813cbe779b2f49e92500', - '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e', - '45223f8f114c64bf4d6f853e3c35a369a6305520', - 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e', - 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68', - '27d48942240f5b91dfda77accd2caac94708cc7d', - '622f0eb0bafd619d2560c26f80f09e3b0b0d78af', - 'e686b958768ee96af8029fe19c6050b1a8dd3b2b'} + subset = { + "c1214f7e79e02fc37156ff215cd71275450cffc3", + "38b5fe81f109cb111f549bfe9bb6b267e10bc557", + "fa6600f6848800641328adbf7811fd2372c02ab2", + "102607b09cdd60e2793929c4f90478be29f85a17", + "49d3fd156b6f7db46313fac355dca1a0b94a0017", + "2d1028c054665b962fa3d307adfc923ddd528038", + "d7e0d30fbcae12c90680eb095a4f5f02505ce501", + "ff7ca51e58c505fec0dd2491de52c622bb7a806b", + "dd80b0f6cf5052f17cc738c2951c4f2070200d7f", + "8430a588b43b5d6da365400117c89400326e7992", + "d955cd312c17b02143c04fa1099a352b04368118", + "f67b87e5c629c2ee0ba58f85197e423ff28d735b", + "add63e382e4aabc9e1afdc4bdc24506c269b7618", + "f298fe1189f1b69779a4423f40b48edf92a703fc", + "bd9b619eb41994cac43d67cf4ccc8399c1125808", + "6e125e7c890379446e98980d8ed60fba87d0f6d1", + "d4a54db9f745dfeba6933bf5b1e79e15d0af20bd", + "0b05e4ed56c802098dfc813cbe779b2f49e92500", + "191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", + "45223f8f114c64bf4d6f853e3c35a369a6305520", + "ca1eb7957a54bce53b12d1a51b13452f95bc7c7e", + "f5ea29fc42ef67a2a5a7aecff10e1566699acd68", + "27d48942240f5b91dfda77accd2caac94708cc7d", + "622f0eb0bafd619d2560c26f80f09e3b0b0d78af", + "e686b958768ee96af8029fe19c6050b1a8dd3b2b", + } assert subset.issubset(set(self.repo.commit_ids)) def test_slicing(self): # 4 1 5 10 95 - for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5), - (10, 20, 10), (5, 100, 95)]: + for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5), (10, 20, 10), (5, 100, 95)]: commit_ids = list(self.repo[sfrom:sto]) assert len(commit_ids) == size assert commit_ids[0] == self.repo.get_commit(commit_idx=sfrom) @@ -214,8 +201,8 @@ class TestGitRepository(object): def test_tags(self): # TODO: Need more tests here - assert 'v0.1.1' in self.repo.tags - assert 'v0.1.2' in self.repo.tags + assert "v0.1.1" in self.repo.tags + assert "v0.1.2" in self.repo.tags for __, commit_id in self.repo.tags.items(): assert isinstance(self.repo.get_commit(commit_id), GitCommit) @@ -229,37 +216,34 @@ class TestGitRepository(object): init_commit = self.repo.get_commit(commit_id) init_author = init_commit.author - assert init_commit.message == 'initial import\n' - assert init_author == 'Marcin Kuzminski ' + assert init_commit.message == "initial import\n" + assert init_author == "Marcin Kuzminski " assert init_author == init_commit.committer - for path in ('vcs/__init__.py', - 'vcs/backends/BaseRepository.py', - 'vcs/backends/__init__.py'): + for path in ("vcs/__init__.py", "vcs/backends/BaseRepository.py", "vcs/backends/__init__.py"): assert isinstance(init_commit.get_node(path), FileNode) - for path in ('', 'vcs', 'vcs/backends'): + for path in ("", "vcs", "vcs/backends"): assert isinstance(init_commit.get_node(path), DirNode) with pytest.raises(NodeDoesNotExistError): - init_commit.get_node(path='foobar') + init_commit.get_node(path="foobar") - node = init_commit.get_node('vcs/') - assert hasattr(node, 'kind') + node = init_commit.get_node("vcs/") + assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node('vcs') - assert hasattr(node, 'kind') + node = init_commit.get_node("vcs") + assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node('vcs/__init__.py') - assert hasattr(node, 'kind') + node = init_commit.get_node("vcs/__init__.py") + assert hasattr(node, "kind") assert node.kind == NodeKind.FILE def test_not_existing_commit(self): with pytest.raises(RepositoryError): - self.repo.get_commit('f' * 40) + self.repo.get_commit("f" * 40) def test_commit10(self): - commit10 = self.repo.get_commit(self.repo.commit_ids[9]) README = """=== VCS @@ -273,7 +257,7 @@ Introduction TODO: To be written... """ - node = commit10.get_node('README.rst') + node = commit10.get_node("README.rst") assert node.kind == NodeKind.FILE assert node.str_content == README @@ -283,39 +267,39 @@ TODO: To be written... def test_checkout_with_create(self, tmpdir): repo_clone = self.get_clone_repo(tmpdir) - new_branch = 'new_branch' - assert repo_clone._current_branch() == 'master' - assert set(repo_clone.branches) == {'master'} + new_branch = "new_branch" + assert repo_clone._current_branch() == "master" + assert set(repo_clone.branches) == {"master"} repo_clone._checkout(new_branch, create=True) # Branches is a lazy property so we need to recrete the Repo object. repo_clone = GitRepository(repo_clone.path) - assert set(repo_clone.branches) == {'master', new_branch} + assert set(repo_clone.branches) == {"master", new_branch} assert repo_clone._current_branch() == new_branch def test_checkout(self, tmpdir): repo_clone = self.get_clone_repo(tmpdir) - repo_clone._checkout('new_branch', create=True) - repo_clone._checkout('master') + repo_clone._checkout("new_branch", create=True) + repo_clone._checkout("master") - assert repo_clone._current_branch() == 'master' + assert repo_clone._current_branch() == "master" def test_checkout_same_branch(self, tmpdir): repo_clone = self.get_clone_repo(tmpdir) - repo_clone._checkout('master') - assert repo_clone._current_branch() == 'master' + repo_clone._checkout("master") + assert repo_clone._current_branch() == "master" def test_checkout_branch_already_exists(self, tmpdir): repo_clone = self.get_clone_repo(tmpdir) with pytest.raises(RepositoryError): - repo_clone._checkout('master', create=True) + repo_clone._checkout("master", create=True) def test_checkout_bare_repo(self): with pytest.raises(RepositoryError): - self.repo._checkout('master') + self.repo._checkout("master") def test_current_branch_bare_repo(self): with pytest.raises(RepositoryError): @@ -326,8 +310,8 @@ TODO: To be written... assert repo._current_branch() is None def test_local_clone(self, tmp_path_factory): - clone_path = str(tmp_path_factory.mktemp('test-local-clone')) - self.repo._local_clone(clone_path, 'master') + clone_path = str(tmp_path_factory.mktemp("test-local-clone")) + self.repo._local_clone(clone_path, "master") repo_clone = GitRepository(clone_path) assert self.repo.commit_ids == repo_clone.commit_ids @@ -338,23 +322,23 @@ TODO: To be written... # Create a new branch in source repo new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) - source_repo._checkout('new_branch', create=True) + source_repo._checkout("new_branch", create=True) - clone_path = str(tmpdir.join('git-clone-path-1')) - source_repo._local_clone(clone_path, 'new_branch') + clone_path = str(tmpdir.join("git-clone-path-1")) + source_repo._local_clone(clone_path, "new_branch") repo_clone = GitRepository(clone_path) - assert source_repo.commit_ids[:-3 + 1] == repo_clone.commit_ids + assert source_repo.commit_ids[: -3 + 1] == repo_clone.commit_ids - clone_path = str(tmpdir.join('git-clone-path-2')) - source_repo._local_clone(clone_path, 'master') + clone_path = str(tmpdir.join("git-clone-path-2")) + source_repo._local_clone(clone_path, "master") repo_clone = GitRepository(clone_path) assert source_repo.commit_ids == repo_clone.commit_ids def test_local_clone_fails_if_target_exists(self): with pytest.raises(RepositoryError): - self.repo._local_clone(self.repo.path, 'master') + self.repo._local_clone(self.repo.path, "master") def test_local_fetch(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) @@ -364,30 +348,30 @@ TODO: To be written... master_commit = source_repo.commit_ids[-1] new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) - source_repo._checkout('new_branch', create=True) + source_repo._checkout("new_branch", create=True) - target_repo._local_fetch(source_repo.path, 'new_branch') + target_repo._local_fetch(source_repo.path, "new_branch") assert target_repo._last_fetch_heads() == [new_branch_commit] - target_repo._local_fetch(source_repo.path, 'master') + target_repo._local_fetch(source_repo.path, "master") assert target_repo._last_fetch_heads() == [master_commit] def test_local_fetch_from_bare_repo(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) - target_repo._local_fetch(self.repo.path, 'master') + target_repo._local_fetch(self.repo.path, "master") master_commit = self.repo.commit_ids[-1] assert target_repo._last_fetch_heads() == [master_commit] def test_local_fetch_from_same_repo(self): with pytest.raises(ValueError): - self.repo._local_fetch(self.repo.path, 'master') + self.repo._local_fetch(self.repo.path, "master") def test_local_fetch_branch_does_not_exist(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) with pytest.raises(RepositoryError): - target_repo._local_fetch(self.repo.path, 'new_branch') + target_repo._local_fetch(self.repo.path, "new_branch") def test_local_pull(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) @@ -397,19 +381,19 @@ TODO: To be written... master_commit = source_repo.commit_ids[-1] new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) - source_repo._checkout('new_branch', create=True) + source_repo._checkout("new_branch", create=True) - target_repo._local_pull(source_repo.path, 'new_branch') + target_repo._local_pull(source_repo.path, "new_branch") target_repo = GitRepository(target_repo.path) assert target_repo.head == new_branch_commit - target_repo._local_pull(source_repo.path, 'master') + target_repo._local_pull(source_repo.path, "master") target_repo = GitRepository(target_repo.path) assert target_repo.head == master_commit def test_local_pull_in_bare_repo(self): with pytest.raises(RepositoryError): - self.repo._local_pull(self.repo.path, 'master') + self.repo._local_pull(self.repo.path, "master") def test_local_merge(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) @@ -419,159 +403,144 @@ TODO: To be written... master_commit = source_repo.commit_ids[-1] new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) - source_repo._checkout('new_branch', create=True) + source_repo._checkout("new_branch", create=True) # This is required as one cannot do a -ff-only merge in an empty repo. - target_repo._local_pull(source_repo.path, 'new_branch') + target_repo._local_pull(source_repo.path, "new_branch") - target_repo._local_fetch(source_repo.path, 'master') - merge_message = 'Merge message\n\nDescription:...' - user_name = 'Albert Einstein' - user_email = 'albert@einstein.com' - target_repo._local_merge(merge_message, user_name, user_email, - target_repo._last_fetch_heads()) + target_repo._local_fetch(source_repo.path, "master") + merge_message = "Merge message\n\nDescription:..." + user_name = "Albert Einstein" + user_email = "albert@einstein.com" + target_repo._local_merge(merge_message, user_name, user_email, target_repo._last_fetch_heads()) target_repo = GitRepository(target_repo.path) assert target_repo.commit_ids[-2] == master_commit last_commit = target_repo.get_commit(target_repo.head) assert last_commit.message.strip() == merge_message - assert last_commit.author == '%s <%s>' % (user_name, user_email) + assert last_commit.author == "%s <%s>" % (user_name, user_email) - assert not os.path.exists( - os.path.join(target_repo.path, '.git', 'MERGE_HEAD')) + assert not os.path.exists(os.path.join(target_repo.path, ".git", "MERGE_HEAD")) def test_local_merge_raises_exception_on_conflict(self, vcsbackend_git): target_repo = vcsbackend_git.create_repo(number_of_commits=1) - vcsbackend_git.ensure_file(b'README', b'I will conflict with you!!!') + vcsbackend_git.ensure_file(b"README", b"I will conflict with you!!!") - target_repo._local_fetch(self.repo.path, 'master') + target_repo._local_fetch(self.repo.path, "master") with pytest.raises(RepositoryError): - target_repo._local_merge( - 'merge_message', 'user name', 'user@name.com', - target_repo._last_fetch_heads()) + target_repo._local_merge("merge_message", "user name", "user@name.com", target_repo._last_fetch_heads()) # Check we are not left in an intermediate merge state - assert not os.path.exists( - os.path.join(target_repo.path, '.git', 'MERGE_HEAD')) + assert not os.path.exists(os.path.join(target_repo.path, ".git", "MERGE_HEAD")) def test_local_merge_into_empty_repo(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) # This is required as one cannot do a -ff-only merge in an empty repo. - target_repo._local_fetch(self.repo.path, 'master') + target_repo._local_fetch(self.repo.path, "master") with pytest.raises(RepositoryError): - target_repo._local_merge( - 'merge_message', 'user name', 'user@name.com', - target_repo._last_fetch_heads()) + target_repo._local_merge("merge_message", "user name", "user@name.com", target_repo._last_fetch_heads()) def test_local_merge_in_bare_repo(self): with pytest.raises(RepositoryError): - self.repo._local_merge( - 'merge_message', 'user name', 'user@name.com', None) + self.repo._local_merge("merge_message", "user name", "user@name.com", None) def test_local_push_non_bare(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) - pushed_branch = 'pushed_branch' - self.repo._local_push('master', target_repo.path, pushed_branch) + pushed_branch = "pushed_branch" + self.repo._local_push("master", target_repo.path, pushed_branch) # Fix the HEAD of the target repo, or otherwise GitRepository won't # report any branches. - with open(os.path.join(target_repo.path, '.git', 'HEAD'), 'w') as f: - f.write('ref: refs/heads/%s' % pushed_branch) + with open(os.path.join(target_repo.path, ".git", "HEAD"), "w") as f: + f.write("ref: refs/heads/%s" % pushed_branch) target_repo = GitRepository(target_repo.path) - assert (target_repo.branches[pushed_branch] == - self.repo.branches['master']) + assert target_repo.branches[pushed_branch] == self.repo.branches["master"] def test_local_push_bare(self, tmpdir): target_repo = self.get_empty_repo(tmpdir, bare=True) - pushed_branch = 'pushed_branch' - self.repo._local_push('master', target_repo.path, pushed_branch) + pushed_branch = "pushed_branch" + self.repo._local_push("master", target_repo.path, pushed_branch) # Fix the HEAD of the target repo, or otherwise GitRepository won't # report any branches. - with open(os.path.join(target_repo.path, 'HEAD'), 'w') as f: - f.write('ref: refs/heads/%s' % pushed_branch) + with open(os.path.join(target_repo.path, "HEAD"), "w") as f: + f.write("ref: refs/heads/%s" % pushed_branch) target_repo = GitRepository(target_repo.path) - assert (target_repo.branches[pushed_branch] == - self.repo.branches['master']) + assert target_repo.branches[pushed_branch] == self.repo.branches["master"] def test_local_push_non_bare_target_branch_is_checked_out(self, tmpdir): target_repo = self.get_clone_repo(tmpdir) - pushed_branch = 'pushed_branch' + pushed_branch = "pushed_branch" # Create a new branch in source repo new_branch_commit = target_repo.commit_ids[-3] target_repo._checkout(new_branch_commit) target_repo._checkout(pushed_branch, create=True) - self.repo._local_push('master', target_repo.path, pushed_branch) + self.repo._local_push("master", target_repo.path, pushed_branch) target_repo = GitRepository(target_repo.path) - assert (target_repo.branches[pushed_branch] == - self.repo.branches['master']) + assert target_repo.branches[pushed_branch] == self.repo.branches["master"] def test_local_push_raises_exception_on_conflict(self, vcsbackend_git): target_repo = vcsbackend_git.create_repo(number_of_commits=1) with pytest.raises(RepositoryError): - self.repo._local_push('master', target_repo.path, 'master') + self.repo._local_push("master", target_repo.path, "master") def test_hooks_can_be_enabled_via_env_variable_for_local_push(self, tmpdir): target_repo = self.get_empty_repo(tmpdir, bare=True) - with mock.patch.object(self.repo, 'run_git_command') as run_mock: - self.repo._local_push( - 'master', target_repo.path, 'master', enable_hooks=True) - env = run_mock.call_args[1]['extra_env'] - assert 'RC_SKIP_HOOKS' not in env + with mock.patch.object(self.repo, "run_git_command") as run_mock: + self.repo._local_push("master", target_repo.path, "master", enable_hooks=True) + env = run_mock.call_args[1]["extra_env"] + assert "RC_SKIP_HOOKS" not in env def _add_failing_hook(self, repo_path, hook_name, bare=False): - path_components = ( - ['hooks', hook_name] if bare else ['.git', 'hooks', hook_name]) + path_components = ["hooks", hook_name] if bare else [".git", "hooks", hook_name] hook_path = os.path.join(repo_path, *path_components) - with open(hook_path, 'w') as f: + with open(hook_path, "w") as f: script_lines = [ - '#!%s' % sys.executable, - 'import os', - 'import sys', + "#!%s" % sys.executable, + "import os", + "import sys", 'if os.environ.get("RC_SKIP_HOOKS"):', - ' sys.exit(0)', - 'sys.exit(1)', + " sys.exit(0)", + "sys.exit(1)", ] - f.write('\n'.join(script_lines)) + f.write("\n".join(script_lines)) os.chmod(hook_path, 0o755) def test_local_push_does_not_execute_hook(self, tmpdir): target_repo = self.get_empty_repo(tmpdir) - pushed_branch = 'pushed_branch' - self._add_failing_hook(target_repo.path, 'pre-receive') - self.repo._local_push('master', target_repo.path, pushed_branch) + pushed_branch = "pushed_branch" + self._add_failing_hook(target_repo.path, "pre-receive") + self.repo._local_push("master", target_repo.path, pushed_branch) # Fix the HEAD of the target repo, or otherwise GitRepository won't # report any branches. - with open(os.path.join(target_repo.path, '.git', 'HEAD'), 'w') as f: - f.write('ref: refs/heads/%s' % pushed_branch) + with open(os.path.join(target_repo.path, ".git", "HEAD"), "w") as f: + f.write("ref: refs/heads/%s" % pushed_branch) target_repo = GitRepository(target_repo.path) - assert (target_repo.branches[pushed_branch] == - self.repo.branches['master']) + assert target_repo.branches[pushed_branch] == self.repo.branches["master"] def test_local_push_executes_hook(self, tmpdir): target_repo = self.get_empty_repo(tmpdir, bare=True) - self._add_failing_hook(target_repo.path, 'pre-receive', bare=True) + self._add_failing_hook(target_repo.path, "pre-receive", bare=True) with pytest.raises(RepositoryError): - self.repo._local_push( - 'master', target_repo.path, 'master', enable_hooks=True) + self.repo._local_push("master", target_repo.path, "master", enable_hooks=True) def test_maybe_prepare_merge_workspace(self): workspace = self.repo._maybe_prepare_merge_workspace( - 2, 'pr2', Reference('branch', 'master', 'unused'), - Reference('branch', 'master', 'unused')) + 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "master", "unused") + ) assert os.path.isdir(workspace) workspace_repo = GitRepository(workspace) @@ -579,14 +548,14 @@ TODO: To be written... # Calling it a second time should also succeed workspace = self.repo._maybe_prepare_merge_workspace( - 2, 'pr2', Reference('branch', 'master', 'unused'), - Reference('branch', 'master', 'unused')) + 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "master", "unused") + ) assert os.path.isdir(workspace) def test_maybe_prepare_merge_workspace_different_refs(self): workspace = self.repo._maybe_prepare_merge_workspace( - 2, 'pr2', Reference('branch', 'master', 'unused'), - Reference('branch', 'develop', 'unused')) + 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "develop", "unused") + ) assert os.path.isdir(workspace) workspace_repo = GitRepository(workspace) @@ -594,48 +563,47 @@ TODO: To be written... # Calling it a second time should also succeed workspace = self.repo._maybe_prepare_merge_workspace( - 2, 'pr2', Reference('branch', 'master', 'unused'), - Reference('branch', 'develop', 'unused')) + 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "develop", "unused") + ) assert os.path.isdir(workspace) def test_cleanup_merge_workspace(self): workspace = self.repo._maybe_prepare_merge_workspace( - 2, 'pr3', Reference('branch', 'master', 'unused'), - Reference('branch', 'master', 'unused')) - self.repo.cleanup_merge_workspace(2, 'pr3') + 2, "pr3", Reference("branch", "master", "unused"), Reference("branch", "master", "unused") + ) + self.repo.cleanup_merge_workspace(2, "pr3") assert not os.path.exists(workspace) def test_cleanup_merge_workspace_invalid_workspace_id(self): # No assert: because in case of an inexistent workspace this function # should still succeed. - self.repo.cleanup_merge_workspace(1, 'pr4') + self.repo.cleanup_merge_workspace(1, "pr4") def test_set_refs(self): - test_ref = 'refs/test-refs/abcde' - test_commit_id = 'ecb86e1f424f2608262b130db174a7dfd25a6623' + test_ref = "refs/test-refs/abcde" + test_commit_id = "ecb86e1f424f2608262b130db174a7dfd25a6623" self.repo.set_refs(test_ref, test_commit_id) - stdout, _ = self.repo.run_git_command(['show-ref']) + stdout, _ = self.repo.run_git_command(["show-ref"]) assert test_ref in stdout assert test_commit_id in stdout def test_remove_ref(self): - test_ref = 'refs/test-refs/abcde' - test_commit_id = 'ecb86e1f424f2608262b130db174a7dfd25a6623' + test_ref = "refs/test-refs/abcde" + test_commit_id = "ecb86e1f424f2608262b130db174a7dfd25a6623" self.repo.set_refs(test_ref, test_commit_id) - stdout, _ = self.repo.run_git_command(['show-ref']) + stdout, _ = self.repo.run_git_command(["show-ref"]) assert test_ref in stdout assert test_commit_id in stdout self.repo.remove_ref(test_ref) - stdout, _ = self.repo.run_git_command(['show-ref']) + stdout, _ = self.repo.run_git_command(["show-ref"]) assert test_ref not in stdout assert test_commit_id not in stdout class TestGitCommit(object): - @pytest.fixture(autouse=True) def prepare(self): self.repo = GitRepository(TEST_GIT_REPO) @@ -643,11 +611,11 @@ class TestGitCommit(object): def test_default_commit(self): tip = self.repo.get_commit() assert tip == self.repo.get_commit(None) - assert tip == self.repo.get_commit('tip') + assert tip == self.repo.get_commit("tip") def test_root_node(self): tip = self.repo.get_commit() - assert tip.root is tip.get_node('') + assert tip.root is tip.get_node("") def test_lazy_fetch(self): """ @@ -655,7 +623,7 @@ class TestGitCommit(object): the commit. This test is somewhat hard to write as order of tests is a key here. Written by running command after command in a shell. """ - commit_id = '2a13f185e4525f9d4b59882791a2d397b90d5ddc' + commit_id = "2a13f185e4525f9d4b59882791a2d397b90d5ddc" assert commit_id in self.repo.commit_ids commit = self.repo.get_commit(commit_id) assert len(commit.nodes) == 0 @@ -665,31 +633,29 @@ class TestGitCommit(object): # accessing root.nodes updates commit.nodes assert len(commit.nodes) == 9 - docs = root.get_node('docs') + docs = root.get_node("docs") # we haven't yet accessed anything new as docs dir was already cached assert len(commit.nodes) == 9 assert len(docs.nodes) == 8 # accessing docs.nodes updates commit.nodes assert len(commit.nodes) == 17 - assert docs is commit.get_node('docs') + assert docs is commit.get_node("docs") assert docs is root.nodes[0] assert docs is root.dirs[0] - assert docs is commit.get_node('docs') + assert docs is commit.get_node("docs") def test_nodes_with_commit(self): - commit_id = '2a13f185e4525f9d4b59882791a2d397b90d5ddc' + commit_id = "2a13f185e4525f9d4b59882791a2d397b90d5ddc" commit = self.repo.get_commit(commit_id) root = commit.root - docs = root.get_node('docs') - assert docs is commit.get_node('docs') - api = docs.get_node('api') - assert api is commit.get_node('docs/api') - index = api.get_node('index.rst') - assert index is commit.get_node('docs/api/index.rst') - assert index is commit.get_node('docs')\ - .get_node('api')\ - .get_node('index.rst') + docs = root.get_node("docs") + assert docs is commit.get_node("docs") + api = docs.get_node("api") + assert api is commit.get_node("docs/api") + index = api.get_node("index.rst") + assert index is commit.get_node("docs/api/index.rst") + assert index is commit.get_node("docs").get_node("api").get_node("index.rst") def test_branch_and_tags(self): """ @@ -716,19 +682,12 @@ class TestGitCommit(object): def test_file_size(self): to_check = ( - ('c1214f7e79e02fc37156ff215cd71275450cffc3', - 'vcs/backends/BaseRepository.py', 502), - ('d7e0d30fbcae12c90680eb095a4f5f02505ce501', - 'vcs/backends/hg.py', 854), - ('6e125e7c890379446e98980d8ed60fba87d0f6d1', - 'setup.py', 1068), - - ('d955cd312c17b02143c04fa1099a352b04368118', - 'vcs/backends/base.py', 2921), - ('ca1eb7957a54bce53b12d1a51b13452f95bc7c7e', - 'vcs/backends/base.py', 3936), - ('f50f42baeed5af6518ef4b0cb2f1423f3851a941', - 'vcs/backends/base.py', 6189), + ("c1214f7e79e02fc37156ff215cd71275450cffc3", "vcs/backends/BaseRepository.py", 502), + ("d7e0d30fbcae12c90680eb095a4f5f02505ce501", "vcs/backends/hg.py", 854), + ("6e125e7c890379446e98980d8ed60fba87d0f6d1", "setup.py", 1068), + ("d955cd312c17b02143c04fa1099a352b04368118", "vcs/backends/base.py", 2921), + ("ca1eb7957a54bce53b12d1a51b13452f95bc7c7e", "vcs/backends/base.py", 3936), + ("f50f42baeed5af6518ef4b0cb2f1423f3851a941", "vcs/backends/base.py", 6189), ) for commit_id, path, size in to_check: node = self.repo.get_commit(commit_id).get_node(path) @@ -736,80 +695,77 @@ class TestGitCommit(object): assert node.size == size def test_file_history_from_commits(self): - node = self.repo[10].get_node('setup.py') + node = self.repo[10].get_node("setup.py") commit_ids = [commit.raw_id for commit in node.history] - assert ['ff7ca51e58c505fec0dd2491de52c622bb7a806b'] == commit_ids + assert ["ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == commit_ids - node = self.repo[20].get_node('setup.py') + node = self.repo[20].get_node("setup.py") node_ids = [commit.raw_id for commit in node.history] - assert ['191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e', - 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'] == node_ids + assert ["191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == node_ids # special case we check history from commit that has this particular # file changed this means we check if it's included as well - node = self.repo.get_commit('191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e') \ - .get_node('setup.py') + node = self.repo.get_commit("191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e").get_node("setup.py") node_ids = [commit.raw_id for commit in node.history] - assert ['191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e', - 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'] == node_ids + assert ["191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == node_ids def test_file_history(self): # we can only check if those commits are present in the history # as we cannot update this test every time file is changed files = { - 'setup.py': [ - '54386793436c938cff89326944d4c2702340037d', - '51d254f0ecf5df2ce50c0b115741f4cf13985dab', - '998ed409c795fec2012b1c0ca054d99888b22090', - '5e0eb4c47f56564395f76333f319d26c79e2fb09', - '0115510b70c7229dbc5dc49036b32e7d91d23acd', - '7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e', - '2a13f185e4525f9d4b59882791a2d397b90d5ddc', - '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e', - 'ff7ca51e58c505fec0dd2491de52c622bb7a806b', + "setup.py": [ + "54386793436c938cff89326944d4c2702340037d", + "51d254f0ecf5df2ce50c0b115741f4cf13985dab", + "998ed409c795fec2012b1c0ca054d99888b22090", + "5e0eb4c47f56564395f76333f319d26c79e2fb09", + "0115510b70c7229dbc5dc49036b32e7d91d23acd", + "7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e", + "2a13f185e4525f9d4b59882791a2d397b90d5ddc", + "191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", + "ff7ca51e58c505fec0dd2491de52c622bb7a806b", ], - 'vcs/nodes.py': [ - '33fa3223355104431402a888fa77a4e9956feb3e', - 'fa014c12c26d10ba682fadb78f2a11c24c8118e1', - 'e686b958768ee96af8029fe19c6050b1a8dd3b2b', - 'ab5721ca0a081f26bf43d9051e615af2cc99952f', - 'c877b68d18e792a66b7f4c529ea02c8f80801542', - '4313566d2e417cb382948f8d9d7c765330356054', - '6c2303a793671e807d1cfc70134c9ca0767d98c2', - '54386793436c938cff89326944d4c2702340037d', - '54000345d2e78b03a99d561399e8e548de3f3203', - '1c6b3677b37ea064cb4b51714d8f7498f93f4b2b', - '2d03ca750a44440fb5ea8b751176d1f36f8e8f46', - '2a08b128c206db48c2f0b8f70df060e6db0ae4f8', - '30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b', - 'ac71e9503c2ca95542839af0ce7b64011b72ea7c', - '12669288fd13adba2a9b7dd5b870cc23ffab92d2', - '5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382', - '12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5', - '5eab1222a7cd4bfcbabc218ca6d04276d4e27378', - 'f50f42baeed5af6518ef4b0cb2f1423f3851a941', - 'd7e390a45f6aa96f04f5e7f583ad4f867431aa25', - 'f15c21f97864b4f071cddfbf2750ec2e23859414', - 'e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade', - 'ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b', - '84dec09632a4458f79f50ddbbd155506c460b4f9', - '0115510b70c7229dbc5dc49036b32e7d91d23acd', - '2a13f185e4525f9d4b59882791a2d397b90d5ddc', - '3bf1c5868e570e39569d094f922d33ced2fa3b2b', - 'b8d04012574729d2c29886e53b1a43ef16dd00a1', - '6970b057cffe4aab0a792aa634c89f4bebf01441', - 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f', - 'ff7ca51e58c505fec0dd2491de52c622bb7a806b', + "vcs/nodes.py": [ + "33fa3223355104431402a888fa77a4e9956feb3e", + "fa014c12c26d10ba682fadb78f2a11c24c8118e1", + "e686b958768ee96af8029fe19c6050b1a8dd3b2b", + "ab5721ca0a081f26bf43d9051e615af2cc99952f", + "c877b68d18e792a66b7f4c529ea02c8f80801542", + "4313566d2e417cb382948f8d9d7c765330356054", + "6c2303a793671e807d1cfc70134c9ca0767d98c2", + "54386793436c938cff89326944d4c2702340037d", + "54000345d2e78b03a99d561399e8e548de3f3203", + "1c6b3677b37ea064cb4b51714d8f7498f93f4b2b", + "2d03ca750a44440fb5ea8b751176d1f36f8e8f46", + "2a08b128c206db48c2f0b8f70df060e6db0ae4f8", + "30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b", + "ac71e9503c2ca95542839af0ce7b64011b72ea7c", + "12669288fd13adba2a9b7dd5b870cc23ffab92d2", + "5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382", + "12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5", + "5eab1222a7cd4bfcbabc218ca6d04276d4e27378", + "f50f42baeed5af6518ef4b0cb2f1423f3851a941", + "d7e390a45f6aa96f04f5e7f583ad4f867431aa25", + "f15c21f97864b4f071cddfbf2750ec2e23859414", + "e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade", + "ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b", + "84dec09632a4458f79f50ddbbd155506c460b4f9", + "0115510b70c7229dbc5dc49036b32e7d91d23acd", + "2a13f185e4525f9d4b59882791a2d397b90d5ddc", + "3bf1c5868e570e39569d094f922d33ced2fa3b2b", + "b8d04012574729d2c29886e53b1a43ef16dd00a1", + "6970b057cffe4aab0a792aa634c89f4bebf01441", + "dd80b0f6cf5052f17cc738c2951c4f2070200d7f", + "ff7ca51e58c505fec0dd2491de52c622bb7a806b", ], - 'vcs/backends/git.py': [ - '4cf116ad5a457530381135e2f4c453e68a1b0105', - '9a751d84d8e9408e736329767387f41b36935153', - 'cb681fb539c3faaedbcdf5ca71ca413425c18f01', - '428f81bb652bcba8d631bce926e8834ff49bdcc6', - '180ab15aebf26f98f714d8c68715e0f05fa6e1c7', - '2b8e07312a2e89e92b90426ab97f349f4bce2a3a', - '50e08c506174d8645a4bb517dd122ac946a0f3bf', - '54000345d2e78b03a99d561399e8e548de3f3203', + "vcs/backends/git.py": [ + "4cf116ad5a457530381135e2f4c453e68a1b0105", + "9a751d84d8e9408e736329767387f41b36935153", + "cb681fb539c3faaedbcdf5ca71ca413425c18f01", + "428f81bb652bcba8d631bce926e8834ff49bdcc6", + "180ab15aebf26f98f714d8c68715e0f05fa6e1c7", + "2b8e07312a2e89e92b90426ab97f349f4bce2a3a", + "50e08c506174d8645a4bb517dd122ac946a0f3bf", + "54000345d2e78b03a99d561399e8e548de3f3203", ], } for path, commit_ids in files.items(): @@ -817,79 +773,79 @@ class TestGitCommit(object): node_ids = [commit.raw_id for commit in node.history] assert set(commit_ids).issubset(set(node_ids)), ( "We assumed that %s is subset of commit_ids for which file %s " - "has been changed, and history of that node returned: %s" - % (commit_ids, path, node_ids)) + "has been changed, and history of that node returned: %s" % (commit_ids, path, node_ids) + ) def test_file_annotate(self): files = { - 'vcs/backends/__init__.py': { - 'c1214f7e79e02fc37156ff215cd71275450cffc3': { - 'lines_no': 1, - 'commits': [ - 'c1214f7e79e02fc37156ff215cd71275450cffc3', + "vcs/backends/__init__.py": { + "c1214f7e79e02fc37156ff215cd71275450cffc3": { + "lines_no": 1, + "commits": [ + "c1214f7e79e02fc37156ff215cd71275450cffc3", ], }, - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647': { - 'lines_no': 21, - 'commits': [ - '49d3fd156b6f7db46313fac355dca1a0b94a0017', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647": { + "lines_no": 21, + "commits": [ + "49d3fd156b6f7db46313fac355dca1a0b94a0017", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", ], }, - 'e29b67bd158580fc90fc5e9111240b90e6e86064': { - 'lines_no': 32, - 'commits': [ - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '5eab1222a7cd4bfcbabc218ca6d04276d4e27378', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '54000345d2e78b03a99d561399e8e548de3f3203', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '78c3f0c23b7ee935ec276acb8b8212444c33c396', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '2a13f185e4525f9d4b59882791a2d397b90d5ddc', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '78c3f0c23b7ee935ec276acb8b8212444c33c396', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '992f38217b979d0b0987d0bae3cc26dac85d9b19', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', - '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647', + "e29b67bd158580fc90fc5e9111240b90e6e86064": { + "lines_no": 32, + "commits": [ + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "5eab1222a7cd4bfcbabc218ca6d04276d4e27378", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "54000345d2e78b03a99d561399e8e548de3f3203", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "78c3f0c23b7ee935ec276acb8b8212444c33c396", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "2a13f185e4525f9d4b59882791a2d397b90d5ddc", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "78c3f0c23b7ee935ec276acb8b8212444c33c396", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "992f38217b979d0b0987d0bae3cc26dac85d9b19", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", + "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647", ], }, }, @@ -903,37 +859,32 @@ class TestGitCommit(object): l1_2 = [x[2]().raw_id for x in commit.get_file_annotate(fname)] assert l1_1 == l1_2 l1 = l1_1 - l2 = files[fname][commit_id]['commits'] + l2 = files[fname][commit_id]["commits"] assert l1 == l2, ( "The lists of commit_ids for %s@commit_id %s" "from annotation list should match each other, " - "got \n%s \nvs \n%s " % (fname, commit_id, l1, l2)) + "got \n%s \nvs \n%s " % (fname, commit_id, l1, l2) + ) def test_files_state(self): """ Tests state of FileNodes. """ - node = self.repo\ - .get_commit('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0')\ - .get_node('vcs/utils/diffs.py') + node = self.repo.get_commit("e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0").get_node("vcs/utils/diffs.py") assert node.state, NodeState.ADDED assert node.added assert not node.changed assert not node.not_changed assert not node.removed - node = self.repo\ - .get_commit('33fa3223355104431402a888fa77a4e9956feb3e')\ - .get_node('.hgignore') + node = self.repo.get_commit("33fa3223355104431402a888fa77a4e9956feb3e").get_node(".hgignore") assert node.state, NodeState.CHANGED assert not node.added assert node.changed assert not node.not_changed assert not node.removed - node = self.repo\ - .get_commit('e29b67bd158580fc90fc5e9111240b90e6e86064')\ - .get_node('setup.py') + node = self.repo.get_commit("e29b67bd158580fc90fc5e9111240b90e6e86064").get_node("setup.py") assert node.state, NodeState.NOT_CHANGED assert not node.added assert not node.changed @@ -942,48 +893,38 @@ class TestGitCommit(object): # If node has REMOVED state then trying to fetch it would raise # CommitError exception - commit = self.repo.get_commit( - 'fa6600f6848800641328adbf7811fd2372c02ab2') - path = 'vcs/backends/BaseRepository.py' + commit = self.repo.get_commit("fa6600f6848800641328adbf7811fd2372c02ab2") + path = "vcs/backends/BaseRepository.py" with pytest.raises(NodeDoesNotExistError): commit.get_node(path) # but it would be one of ``removed`` (commit's attribute) assert path in [rf.path for rf in commit.removed] - commit = self.repo.get_commit( - '54386793436c938cff89326944d4c2702340037d') - changed = [ - 'setup.py', 'tests/test_nodes.py', 'vcs/backends/hg.py', - 'vcs/nodes.py'] + commit = self.repo.get_commit("54386793436c938cff89326944d4c2702340037d") + changed = ["setup.py", "tests/test_nodes.py", "vcs/backends/hg.py", "vcs/nodes.py"] assert set(changed) == set([f.path for f in commit.changed]) def test_unicode_branch_refs(self): unicode_branches = { - 'refs/heads/unicode': '6c0ce52b229aa978889e91b38777f800e85f330b', - u'refs/heads/uniçö∂e': 'ürl', + "refs/heads/unicode": "6c0ce52b229aa978889e91b38777f800e85f330b", + "refs/heads/uniçö∂e": "ürl", } - with mock.patch( - ("rhodecode.lib.vcs.backends.git.repository" - ".GitRepository._refs"), - unicode_branches): + with mock.patch(("rhodecode.lib.vcs.backends.git.repository" ".GitRepository._refs"), unicode_branches): branches = self.repo.branches - assert 'unicode' in branches - assert 'uniçö∂e' in branches + assert "unicode" in branches + assert "uniçö∂e" in branches def test_unicode_tag_refs(self): unicode_tags = { - 'refs/tags/unicode': '6c0ce52b229aa978889e91b38777f800e85f330b', - u'refs/tags/uniçö∂e': '6c0ce52b229aa978889e91b38777f800e85f330b', + "refs/tags/unicode": "6c0ce52b229aa978889e91b38777f800e85f330b", + "refs/tags/uniçö∂e": "6c0ce52b229aa978889e91b38777f800e85f330b", } - with mock.patch( - ("rhodecode.lib.vcs.backends.git.repository" - ".GitRepository._refs"), - unicode_tags): + with mock.patch(("rhodecode.lib.vcs.backends.git.repository" ".GitRepository._refs"), unicode_tags): tags = self.repo.tags - assert 'unicode' in tags - assert 'uniçö∂e' in tags + assert "unicode" in tags + assert "uniçö∂e" in tags def test_commit_message_is_unicode(self): for commit in self.repo: @@ -995,190 +936,186 @@ class TestGitCommit(object): def test_repo_files_content_types(self): commit = self.repo.get_commit() - for node in commit.get_node('/'): + for node in commit.get_node("/"): if node.is_file(): assert type(node.content) == bytes assert type(node.str_content) == str def test_wrong_path(self): # There is 'setup.py' in the root dir but not there: - path = 'foo/bar/setup.py' + path = "foo/bar/setup.py" tip = self.repo.get_commit() with pytest.raises(VCSError): tip.get_node(path) - @pytest.mark.parametrize("author_email, commit_id", [ - ('marcin@python-blog.com', 'c1214f7e79e02fc37156ff215cd71275450cffc3'), - ('lukasz.balcerzak@python-center.pl', - 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'), - ('none@none', '8430a588b43b5d6da365400117c89400326e7992'), - ]) + @pytest.mark.parametrize( + "author_email, commit_id", + [ + ("marcin@python-blog.com", "c1214f7e79e02fc37156ff215cd71275450cffc3"), + ("lukasz.balcerzak@python-center.pl", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"), + ("none@none", "8430a588b43b5d6da365400117c89400326e7992"), + ], + ) def test_author_email(self, author_email, commit_id): commit = self.repo.get_commit(commit_id) assert author_email == commit.author_email - @pytest.mark.parametrize("author, commit_id", [ - ('Marcin Kuzminski', 'c1214f7e79e02fc37156ff215cd71275450cffc3'), - ('Lukasz Balcerzak', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'), - ('marcink', '8430a588b43b5d6da365400117c89400326e7992'), - ]) + @pytest.mark.parametrize( + "author, commit_id", + [ + ("Marcin Kuzminski", "c1214f7e79e02fc37156ff215cd71275450cffc3"), + ("Lukasz Balcerzak", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"), + ("marcink", "8430a588b43b5d6da365400117c89400326e7992"), + ], + ) def test_author_username(self, author, commit_id): commit = self.repo.get_commit(commit_id) assert author == commit.author_name class TestLargeFileRepo(object): - def test_large_file(self, backend_git): conf = make_db_config() - repo = backend_git.create_test_repo('largefiles', conf) + git_largefiles_store = conf.get("vcs_git_lfs", "store_location") + + repo = backend_git.create_test_repo("largefiles", conf) tip = repo.scm_instance().get_commit() + node = tip.get_node("1MB.zip") + # extract stored LF node into the origin cache - lfs_store = os.path.join(repo.repo_path, repo.repo_name, 'lfs_store') + repo_lfs_store: str = os.path.join(repo.repo_path, repo.repo_name, "lfs_store") - oid = '7b331c02e313c7599d5a90212e17e6d3cb729bd2e1c9b873c302a63c95a2f9bf' - oid_path = os.path.join(lfs_store, oid) - # Todo: oid path depends on LFSOidStorage.store_suffix. Once it will be changed update below line accordingly - oid_destination = os.path.join( - conf.get('vcs_git_lfs', 'store_location'), f'objects/{oid[:2]}/{oid[2:4]}/{oid}') + oid: str = "7b331c02e313c7599d5a90212e17e6d3cb729bd2e1c9b873c302a63c95a2f9bf" + # where the OID actually is INSIDE the repo... + oid_path = os.path.join(repo_lfs_store, oid) + + # Note: oid path depends on LFSOidStore.store_suffix. Once it will be changed update below line accordingly + oid_destination = os.path.join(git_largefiles_store, f"objects/{oid[:2]}/{oid[2:4]}/{oid}") - os.makedirs(os.path.dirname(oid_destination)) shutil.copy(oid_path, oid_destination) - node = tip.get_node('1MB.zip') - lf_node = node.get_largefile_node() assert lf_node.is_largefile() is True assert lf_node.size == 1024000 - assert lf_node.name == '1MB.zip' + assert lf_node.name == "1MB.zip" @pytest.mark.usefixtures("vcs_repository_support") class TestGitSpecificWithRepo(BackendTestMixin): - @classmethod def _get_commits(cls): return [ { - 'message': 'Initial', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 20), - 'added': [ - FileNode(b'foobar/static/js/admin/base.js', content=b'base'), - FileNode(b'foobar/static/admin', content=b'admin', mode=0o120000), # this is a link - FileNode(b'foo', content=b'foo'), + "message": "Initial", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 20), + "added": [ + FileNode(b"foobar/static/js/admin/base.js", content=b"base"), + FileNode(b"foobar/static/admin", content=b"admin", mode=0o120000), # this is a link + FileNode(b"foo", content=b"foo"), ], }, { - 'message': 'Second', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 22), - 'added': [ - FileNode(b'foo2', content=b'foo2'), + "message": "Second", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 22), + "added": [ + FileNode(b"foo2", content=b"foo2"), ], }, ] def test_paths_slow_traversing(self): commit = self.repo.get_commit() - assert commit.get_node('foobar').get_node('static').get_node('js')\ - .get_node('admin').get_node('base.js').content == b'base' + assert ( + commit.get_node("foobar").get_node("static").get_node("js").get_node("admin").get_node("base.js").content + == b"base" + ) def test_paths_fast_traversing(self): commit = self.repo.get_commit() - assert commit.get_node('foobar/static/js/admin/base.js').content == b'base' + assert commit.get_node("foobar/static/js/admin/base.js").content == b"base" def test_get_diff_runs_git_command_with_hashes(self): comm1 = self.repo[0] comm2 = self.repo[1] - with mock.patch.object(self.repo, '_remote', return_value=mock.Mock()) as remote_mock: + with mock.patch.object(self.repo, "_remote", return_value=mock.Mock()) as remote_mock: remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff) self.repo.get_diff(comm1, comm2) remote_mock.diff.assert_called_once_with( - comm1.raw_id, comm2.raw_id, - file_filter=None, opt_ignorews=False, context=3) + comm1.raw_id, comm2.raw_id, file_filter=None, opt_ignorews=False, context=3 + ) def test_get_diff_runs_git_command_with_str_hashes(self): comm2 = self.repo[1] - with mock.patch.object(self.repo, '_remote', return_value=mock.Mock()) as remote_mock: + with mock.patch.object(self.repo, "_remote", return_value=mock.Mock()) as remote_mock: remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff) self.repo.get_diff(self.repo.EMPTY_COMMIT, comm2) remote_mock.diff.assert_called_once_with( - self.repo.EMPTY_COMMIT.raw_id, comm2.raw_id, - file_filter=None, opt_ignorews=False, context=3) + self.repo.EMPTY_COMMIT.raw_id, comm2.raw_id, file_filter=None, opt_ignorews=False, context=3 + ) def test_get_diff_runs_git_command_with_path_if_its_given(self): comm1 = self.repo[0] comm2 = self.repo[1] - with mock.patch.object(self.repo, '_remote', return_value=mock.Mock()) as remote_mock: + with mock.patch.object(self.repo, "_remote", return_value=mock.Mock()) as remote_mock: remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff) - self.repo.get_diff(comm1, comm2, 'foo') + self.repo.get_diff(comm1, comm2, "foo") remote_mock.diff.assert_called_once_with( - self.repo._lookup_commit(0), comm2.raw_id, - file_filter='foo', opt_ignorews=False, context=3) + self.repo._lookup_commit(0), comm2.raw_id, file_filter="foo", opt_ignorews=False, context=3 + ) @pytest.mark.usefixtures("vcs_repository_support") class TestGitRegression(BackendTestMixin): - @classmethod def _get_commits(cls): return [ { - 'message': 'Initial', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 20), - 'added': [ - FileNode(b'bot/__init__.py', content=b'base'), - FileNode(b'bot/templates/404.html', content=b'base'), - FileNode(b'bot/templates/500.html', content=b'base'), + "message": "Initial", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 20), + "added": [ + FileNode(b"bot/__init__.py", content=b"base"), + FileNode(b"bot/templates/404.html", content=b"base"), + FileNode(b"bot/templates/500.html", content=b"base"), ], }, { - 'message': 'Second', - 'author': 'Joe Doe ', - 'date': datetime.datetime(2010, 1, 1, 22), - 'added': [ - FileNode(b'bot/build/migrations/1.py', content=b'foo2'), - FileNode(b'bot/build/migrations/2.py', content=b'foo2'), - FileNode(b'bot/build/static/templates/f.html', content=b'foo2'), - FileNode(b'bot/build/static/templates/f1.html', content=b'foo2'), - FileNode(b'bot/build/templates/err.html', content=b'foo2'), - FileNode(b'bot/build/templates/err2.html', content=b'foo2'), + "message": "Second", + "author": "Joe Doe ", + "date": datetime.datetime(2010, 1, 1, 22), + "added": [ + FileNode(b"bot/build/migrations/1.py", content=b"foo2"), + FileNode(b"bot/build/migrations/2.py", content=b"foo2"), + FileNode(b"bot/build/static/templates/f.html", content=b"foo2"), + FileNode(b"bot/build/static/templates/f1.html", content=b"foo2"), + FileNode(b"bot/build/templates/err.html", content=b"foo2"), + FileNode(b"bot/build/templates/err2.html", content=b"foo2"), ], }, ] - @pytest.mark.parametrize("path, expected_paths", [ - ('bot', [ - 'bot/build', - 'bot/templates', - 'bot/__init__.py']), - ('bot/build', [ - 'bot/build/migrations', - 'bot/build/static', - 'bot/build/templates']), - ('bot/build/static', [ - 'bot/build/static/templates']), - ('bot/build/static/templates', [ - 'bot/build/static/templates/f.html', - 'bot/build/static/templates/f1.html']), - ('bot/build/templates', [ - 'bot/build/templates/err.html', - 'bot/build/templates/err2.html']), - ('bot/templates/', [ - 'bot/templates/404.html', - 'bot/templates/500.html']), - ]) + @pytest.mark.parametrize( + "path, expected_paths", + [ + ("bot", ["bot/build", "bot/templates", "bot/__init__.py"]), + ("bot/build", ["bot/build/migrations", "bot/build/static", "bot/build/templates"]), + ("bot/build/static", ["bot/build/static/templates"]), + ("bot/build/static/templates", ["bot/build/static/templates/f.html", "bot/build/static/templates/f1.html"]), + ("bot/build/templates", ["bot/build/templates/err.html", "bot/build/templates/err2.html"]), + ("bot/templates/", ["bot/templates/404.html", "bot/templates/500.html"]), + ], + ) def test_similar_paths(self, path, expected_paths): commit = self.repo.get_commit() paths = [n.path for n in commit.get_nodes(path)] @@ -1186,122 +1123,120 @@ class TestGitRegression(BackendTestMixin): class TestDiscoverGitVersion(object): - def test_returns_git_version(self, baseapp): version = discover_git_version() assert version def test_returns_empty_string_without_vcsserver(self): mock_connection = mock.Mock() - mock_connection.discover_git_version = mock.Mock( - side_effect=Exception) - with mock.patch('rhodecode.lib.vcs.connection.Git', mock_connection): + mock_connection.discover_git_version = mock.Mock(side_effect=Exception) + with mock.patch("rhodecode.lib.vcs.connection.Git", mock_connection): version = discover_git_version() - assert version == '' + assert version == "" class TestGetSubmoduleUrl(object): def test_submodules_file_found(self): - commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1) + commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1) node = mock.Mock() - with mock.patch.object( - commit, 'get_node', return_value=node) as get_node_mock: + with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock: node.str_content = ( - '[submodule "subrepo1"]\n' - '\tpath = subrepo1\n' - '\turl = https://code.rhodecode.com/dulwich\n' + '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n" ) - result = commit._get_submodule_url('subrepo1') - get_node_mock.assert_called_once_with('.gitmodules') - assert result == 'https://code.rhodecode.com/dulwich' + result = commit._get_submodule_url("subrepo1") + get_node_mock.assert_called_once_with(".gitmodules") + assert result == "https://code.rhodecode.com/dulwich" def test_complex_submodule_path(self): - commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1) + commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1) node = mock.Mock() - with mock.patch.object( - commit, 'get_node', return_value=node) as get_node_mock: + with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock: node.str_content = ( '[submodule "complex/subrepo/path"]\n' - '\tpath = complex/subrepo/path\n' - '\turl = https://code.rhodecode.com/dulwich\n' + "\tpath = complex/subrepo/path\n" + "\turl = https://code.rhodecode.com/dulwich\n" ) - result = commit._get_submodule_url('complex/subrepo/path') - get_node_mock.assert_called_once_with('.gitmodules') - assert result == 'https://code.rhodecode.com/dulwich' + result = commit._get_submodule_url("complex/subrepo/path") + get_node_mock.assert_called_once_with(".gitmodules") + assert result == "https://code.rhodecode.com/dulwich" def test_submodules_file_not_found(self): - commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1) - with mock.patch.object( - commit, 'get_node', side_effect=NodeDoesNotExistError): - result = commit._get_submodule_url('complex/subrepo/path') + commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1) + with mock.patch.object(commit, "get_node", side_effect=NodeDoesNotExistError): + result = commit._get_submodule_url("complex/subrepo/path") assert result is None def test_path_not_found(self): - commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1) + commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1) node = mock.Mock() - with mock.patch.object( - commit, 'get_node', return_value=node) as get_node_mock: + with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock: node.str_content = ( - '[submodule "subrepo1"]\n' - '\tpath = subrepo1\n' - '\turl = https://code.rhodecode.com/dulwich\n' + '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n" ) - result = commit._get_submodule_url('subrepo2') - get_node_mock.assert_called_once_with('.gitmodules') + result = commit._get_submodule_url("subrepo2") + get_node_mock.assert_called_once_with(".gitmodules") assert result is None def test_returns_cached_values(self): - commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1) + commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1) node = mock.Mock() - with mock.patch.object( - commit, 'get_node', return_value=node) as get_node_mock: + with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock: node.str_content = ( - '[submodule "subrepo1"]\n' - '\tpath = subrepo1\n' - '\turl = https://code.rhodecode.com/dulwich\n' + '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n" ) for _ in range(3): - commit._get_submodule_url('subrepo1') - get_node_mock.assert_called_once_with('.gitmodules') + commit._get_submodule_url("subrepo1") + get_node_mock.assert_called_once_with(".gitmodules") def test_get_node_returns_a_link(self): repository = mock.Mock() - repository.alias = 'git' - commit = GitCommit(repository=repository, raw_id='abcdef12', idx=1) - submodule_url = 'https://code.rhodecode.com/dulwich' - get_id_patch = mock.patch.object( - commit, '_get_tree_id_for_path', return_value=(1, 'link')) - get_submodule_patch = mock.patch.object( - commit, '_get_submodule_url', return_value=submodule_url) + repository.alias = "git" + commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1) + submodule_url = "https://code.rhodecode.com/dulwich" + get_id_patch = mock.patch.object(commit, "_get_tree_id_for_path", return_value=(1, "link")) + get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url) with get_id_patch, get_submodule_patch as submodule_mock: - node = commit.get_node('/abcde') + node = commit.get_node("/abcde") - submodule_mock.assert_called_once_with('/abcde') + submodule_mock.assert_called_once_with("/abcde") assert type(node) == SubModuleNode assert node.url == submodule_url def test_get_nodes_returns_links(self): repository = mock.MagicMock() - repository.alias = 'git' - repository._remote.tree_items.return_value = [ - ('subrepo', 'stat', 1, 'link') - ] - commit = GitCommit(repository=repository, raw_id='abcdef12', idx=1) - submodule_url = 'https://code.rhodecode.com/dulwich' - get_id_patch = mock.patch.object( - commit, '_get_tree_id_for_path', return_value=(1, 'tree')) - get_submodule_patch = mock.patch.object( - commit, '_get_submodule_url', return_value=submodule_url) + repository.alias = "git" + repository._remote.tree_items.return_value = [("subrepo", "stat", 1, "link")] + commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1) + submodule_url = "https://code.rhodecode.com/dulwich" + get_id_patch = mock.patch.object(commit, "_get_tree_id_for_path", return_value=(1, "tree")) + get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url) with get_id_patch, get_submodule_patch as submodule_mock: - nodes = commit.get_nodes('/abcde') + nodes = commit.get_nodes("/abcde") - submodule_mock.assert_called_once_with('/abcde/subrepo') + submodule_mock.assert_called_once_with("/abcde/subrepo") assert len(nodes) == 1 assert type(nodes[0]) == SubModuleNode assert nodes[0].url == submodule_url + + +class TestGetShadowInstance(object): + + @pytest.fixture() + def repo(self, vcsbackend_git): + _git_repo = vcsbackend_git.repo + + mock.patch.object(_git_repo, "config", mock.Mock()) + connection_mock = mock.Mock(unsafe=True, name="connection.Hg") + + mock.patch("rhodecode.lib.vcs.connection.Git", connection_mock) + return _git_repo + + def test_getting_shadow_instance_copies_config(self, repo): + shadow = repo.get_shadow_instance(repo.path) + assert shadow.config.serialize() == repo.config.serialize() diff --git a/rhodecode/tests/vcs/test_hg.py b/rhodecode/tests/vcs/test_hg.py index 20c90207..cf7ce267 100644 --- a/rhodecode/tests/vcs/test_hg.py +++ b/rhodecode/tests/vcs/test_hg.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -25,11 +24,9 @@ import pytest from rhodecode.lib.str_utils import safe_bytes from rhodecode.lib.utils import make_db_config from rhodecode.lib.vcs import backends -from rhodecode.lib.vcs.backends.base import ( - Reference, MergeResponse, MergeFailureReason) +from rhodecode.lib.vcs.backends.base import Reference, MergeResponse, MergeFailureReason from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialCommit -from rhodecode.lib.vcs.exceptions import ( - RepositoryError, VCSError, NodeDoesNotExistError, CommitDoesNotExistError) +from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError, CommitDoesNotExistError from rhodecode.lib.vcs.nodes import FileNode, NodeKind, NodeState from rhodecode.tests import TEST_HG_REPO, TEST_HG_REPO_CLONE, repo_id_generator @@ -44,12 +41,13 @@ def repo_path_generator(): i = 0 while True: i += 1 - yield '%s-%d' % (TEST_HG_REPO_CLONE, i) + yield "%s-%d" % (TEST_HG_REPO_CLONE, i) + REPO_PATH_GENERATOR = repo_path_generator() -@pytest.fixture(scope='class', autouse=True) +@pytest.fixture(scope="class", autouse=True) def repo(request, baseapp): repo = MercurialRepository(TEST_HG_REPO) if request.cls: @@ -58,7 +56,6 @@ def repo(request, baseapp): class TestMercurialRepository(object): - # pylint: disable=protected-access def get_clone_repo(self): @@ -66,8 +63,7 @@ class TestMercurialRepository(object): Return a clone of the base repo. """ clone_path = next(REPO_PATH_GENERATOR) - repo_clone = MercurialRepository( - clone_path, create=True, src_url=self.repo.path) + repo_clone = MercurialRepository(clone_path, create=True, src_url=self.repo.path) return repo_clone @@ -78,40 +74,39 @@ class TestMercurialRepository(object): return MercurialRepository(next(REPO_PATH_GENERATOR), create=True) def test_wrong_repo_path(self): - wrong_repo_path = '/tmp/errorrepo_hg' + wrong_repo_path = "/tmp/errorrepo_hg" with pytest.raises(RepositoryError): MercurialRepository(wrong_repo_path) def test_unicode_path_repo(self): with pytest.raises(VCSError): - MercurialRepository('iShouldFail') + MercurialRepository("iShouldFail") def test_unicode_commit_id(self): with pytest.raises(CommitDoesNotExistError): - self.repo.get_commit('unicode-commit-id') + self.repo.get_commit("unicode-commit-id") with pytest.raises(CommitDoesNotExistError): - self.repo.get_commit('unícøde-spéçial-chäråcter-commit-id') + self.repo.get_commit("unícøde-spéçial-chäråcter-commit-id") def test_unicode_bookmark(self): - self.repo.bookmark('unicode-bookmark') - self.repo.bookmark('unícøde-spéçial-chäråcter-bookmark') + self.repo.bookmark("unicode-bookmark") + self.repo.bookmark("unícøde-spéçial-chäråcter-bookmark") def test_unicode_branch(self): with pytest.raises(KeyError): - assert self.repo.branches['unicode-branch'] + assert self.repo.branches["unicode-branch"] with pytest.raises(KeyError): - assert self.repo.branches['unícøde-spéçial-chäråcter-branch'] + assert self.repo.branches["unícøde-spéçial-chäråcter-branch"] def test_repo_clone(self): if os.path.exists(TEST_HG_REPO_CLONE): self.fail( - 'Cannot test mercurial clone repo as location %s already ' - 'exists. You should manually remove it first.' - % TEST_HG_REPO_CLONE) + "Cannot test mercurial clone repo as location %s already " + "exists. You should manually remove it first." % TEST_HG_REPO_CLONE + ) repo = MercurialRepository(TEST_HG_REPO) - repo_clone = MercurialRepository(TEST_HG_REPO_CLONE, - src_url=TEST_HG_REPO) + repo_clone = MercurialRepository(TEST_HG_REPO_CLONE, create=True, src_url=TEST_HG_REPO) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # Checking hashes of commits should be enough for commit in repo.get_commits(): @@ -121,72 +116,80 @@ class TestMercurialRepository(object): def test_repo_clone_with_update(self): repo = MercurialRepository(TEST_HG_REPO) repo_clone = MercurialRepository( - TEST_HG_REPO_CLONE + '_w_update', - src_url=TEST_HG_REPO, do_workspace_checkout=True) + TEST_HG_REPO_CLONE + "_w_update", create=True, src_url=TEST_HG_REPO, do_workspace_checkout=True + ) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # check if current workdir was updated - assert os.path.isfile( - os.path.join(TEST_HG_REPO_CLONE + '_w_update', 'MANIFEST.in')) + assert os.path.isfile(os.path.join(TEST_HG_REPO_CLONE + "_w_update", "MANIFEST.in")) def test_repo_clone_without_update(self): repo = MercurialRepository(TEST_HG_REPO) repo_clone = MercurialRepository( - TEST_HG_REPO_CLONE + '_wo_update', - src_url=TEST_HG_REPO, do_workspace_checkout=False) + TEST_HG_REPO_CLONE + "_wo_update", create=True, src_url=TEST_HG_REPO, do_workspace_checkout=False + ) assert len(repo.commit_ids) == len(repo_clone.commit_ids) - assert not os.path.isfile( - os.path.join(TEST_HG_REPO_CLONE + '_wo_update', 'MANIFEST.in')) + assert not os.path.isfile(os.path.join(TEST_HG_REPO_CLONE + "_wo_update", "MANIFEST.in")) def test_commit_ids(self): # there are 21 commits at bitbucket now # so we can assume they would be available from now on - subset = {'b986218ba1c9b0d6a259fac9b050b1724ed8e545', '3d8f361e72ab303da48d799ff1ac40d5ac37c67e', - '6cba7170863a2411822803fa77a0a264f1310b35', '56349e29c2af3ac913b28bde9a2c6154436e615b', - '2dda4e345facb0ccff1a191052dd1606dba6781d', '6fff84722075f1607a30f436523403845f84cd9e', - '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7', '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb', - 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c', 'be90031137367893f1c406e0a8683010fd115b79', - 'db8e58be770518cbb2b1cdfa69146e47cd481481', '84478366594b424af694a6c784cb991a16b87c21', - '17f8e105dddb9f339600389c6dc7175d395a535c', '20a662e756499bde3095ffc9bc0643d1def2d0eb', - '2e319b85e70a707bba0beff866d9f9de032aa4f9', '786facd2c61deb9cf91e9534735124fb8fc11842', - '94593d2128d38210a2fcd1aabff6dda0d6d9edf8', 'aa6a0de05b7612707db567078e130a6cd114a9a7', - 'eada5a770da98ab0dd7325e29d00e0714f228d09' - } + subset = { + "b986218ba1c9b0d6a259fac9b050b1724ed8e545", + "3d8f361e72ab303da48d799ff1ac40d5ac37c67e", + "6cba7170863a2411822803fa77a0a264f1310b35", + "56349e29c2af3ac913b28bde9a2c6154436e615b", + "2dda4e345facb0ccff1a191052dd1606dba6781d", + "6fff84722075f1607a30f436523403845f84cd9e", + "7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7", + "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb", + "dc5d2c0661b61928834a785d3e64a3f80d3aad9c", + "be90031137367893f1c406e0a8683010fd115b79", + "db8e58be770518cbb2b1cdfa69146e47cd481481", + "84478366594b424af694a6c784cb991a16b87c21", + "17f8e105dddb9f339600389c6dc7175d395a535c", + "20a662e756499bde3095ffc9bc0643d1def2d0eb", + "2e319b85e70a707bba0beff866d9f9de032aa4f9", + "786facd2c61deb9cf91e9534735124fb8fc11842", + "94593d2128d38210a2fcd1aabff6dda0d6d9edf8", + "aa6a0de05b7612707db567078e130a6cd114a9a7", + "eada5a770da98ab0dd7325e29d00e0714f228d09", + } assert subset.issubset(set(self.repo.commit_ids)) # check if we have the proper order of commits org = [ - 'b986218ba1c9b0d6a259fac9b050b1724ed8e545', - '3d8f361e72ab303da48d799ff1ac40d5ac37c67e', - '6cba7170863a2411822803fa77a0a264f1310b35', - '56349e29c2af3ac913b28bde9a2c6154436e615b', - '2dda4e345facb0ccff1a191052dd1606dba6781d', - '6fff84722075f1607a30f436523403845f84cd9e', - '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7', - '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb', - 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c', - 'be90031137367893f1c406e0a8683010fd115b79', - 'db8e58be770518cbb2b1cdfa69146e47cd481481', - '84478366594b424af694a6c784cb991a16b87c21', - '17f8e105dddb9f339600389c6dc7175d395a535c', - '20a662e756499bde3095ffc9bc0643d1def2d0eb', - '2e319b85e70a707bba0beff866d9f9de032aa4f9', - '786facd2c61deb9cf91e9534735124fb8fc11842', - '94593d2128d38210a2fcd1aabff6dda0d6d9edf8', - 'aa6a0de05b7612707db567078e130a6cd114a9a7', - 'eada5a770da98ab0dd7325e29d00e0714f228d09', - '2c1885c735575ca478bf9e17b0029dca68824458', - 'd9bcd465040bf869799b09ad732c04e0eea99fe9', - '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7', - '4fb8326d78e5120da2c7468dcf7098997be385da', - '62b4a097164940bd66030c4db51687f3ec035eed', - '536c1a19428381cfea92ac44985304f6a8049569', - '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4', - '9bb326a04ae5d98d437dece54be04f830cf1edd9', - 'f8940bcb890a98c4702319fbe36db75ea309b475', - 'ff5ab059786ebc7411e559a2cc309dfae3625a3b', - '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08', - 'ee87846a61c12153b51543bf860e1026c6d3dcba', + "b986218ba1c9b0d6a259fac9b050b1724ed8e545", + "3d8f361e72ab303da48d799ff1ac40d5ac37c67e", + "6cba7170863a2411822803fa77a0a264f1310b35", + "56349e29c2af3ac913b28bde9a2c6154436e615b", + "2dda4e345facb0ccff1a191052dd1606dba6781d", + "6fff84722075f1607a30f436523403845f84cd9e", + "7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7", + "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb", + "dc5d2c0661b61928834a785d3e64a3f80d3aad9c", + "be90031137367893f1c406e0a8683010fd115b79", + "db8e58be770518cbb2b1cdfa69146e47cd481481", + "84478366594b424af694a6c784cb991a16b87c21", + "17f8e105dddb9f339600389c6dc7175d395a535c", + "20a662e756499bde3095ffc9bc0643d1def2d0eb", + "2e319b85e70a707bba0beff866d9f9de032aa4f9", + "786facd2c61deb9cf91e9534735124fb8fc11842", + "94593d2128d38210a2fcd1aabff6dda0d6d9edf8", + "aa6a0de05b7612707db567078e130a6cd114a9a7", + "eada5a770da98ab0dd7325e29d00e0714f228d09", + "2c1885c735575ca478bf9e17b0029dca68824458", + "d9bcd465040bf869799b09ad732c04e0eea99fe9", + "469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7", + "4fb8326d78e5120da2c7468dcf7098997be385da", + "62b4a097164940bd66030c4db51687f3ec035eed", + "536c1a19428381cfea92ac44985304f6a8049569", + "965e8ab3c44b070cdaa5bf727ddef0ada980ecc4", + "9bb326a04ae5d98d437dece54be04f830cf1edd9", + "f8940bcb890a98c4702319fbe36db75ea309b475", + "ff5ab059786ebc7411e559a2cc309dfae3625a3b", + "6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08", + "ee87846a61c12153b51543bf860e1026c6d3dcba", ] assert org == self.repo.commit_ids[:31] @@ -197,8 +200,7 @@ class TestMercurialRepository(object): def test_slicing(self): # 4 1 5 10 95 - for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5), - (10, 20, 10), (5, 100, 95)]: + for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5), (10, 20, 10), (5, 100, 95)]: indexes = list(self.repo[sfrom:sto]) assert len(indexes) == size assert indexes[0] == self.repo.get_commit(commit_idx=sfrom) @@ -208,64 +210,64 @@ class TestMercurialRepository(object): # TODO: Need more tests here # active branches - assert 'default' in self.repo.branches - assert 'stable' in self.repo.branches + assert "default" in self.repo.branches + assert "stable" in self.repo.branches # closed - assert 'git' in self.repo._get_branches(closed=True) - assert 'web' in self.repo._get_branches(closed=True) + assert "git" in self.repo._get_branches(closed=True) + assert "web" in self.repo._get_branches(closed=True) for name, id in self.repo.branches.items(): assert isinstance(self.repo.get_commit(id), MercurialCommit) def test_tip_in_tags(self): # tip is always a tag - assert 'tip' in self.repo.tags + assert "tip" in self.repo.tags def test_tip_commit_in_tags(self): tip = self.repo.get_commit() - assert self.repo.tags['tip'] == tip.raw_id + assert self.repo.tags["tip"] == tip.raw_id def test_initial_commit(self): init_commit = self.repo.get_commit(commit_idx=0) init_author = init_commit.author - assert init_commit.message == 'initial import' - assert init_author == 'Marcin Kuzminski ' + assert init_commit.message == "initial import" + assert init_author == "Marcin Kuzminski " assert init_author == init_commit.committer - assert sorted(init_commit._file_paths) == sorted([ - 'vcs/__init__.py', - 'vcs/backends/BaseRepository.py', - 'vcs/backends/__init__.py', - ]) - assert sorted(init_commit._dir_paths) == sorted( - ['', 'vcs', 'vcs/backends']) + assert sorted(init_commit._file_paths) == sorted( + [ + "vcs/__init__.py", + "vcs/backends/BaseRepository.py", + "vcs/backends/__init__.py", + ] + ) + assert sorted(init_commit._dir_paths) == sorted(["", "vcs", "vcs/backends"]) - assert init_commit._dir_paths + init_commit._file_paths == \ - init_commit._paths + assert init_commit._dir_paths + init_commit._file_paths == init_commit._paths with pytest.raises(NodeDoesNotExistError): - init_commit.get_node(path='foobar') + init_commit.get_node(path="foobar") - node = init_commit.get_node('vcs/') - assert hasattr(node, 'kind') + node = init_commit.get_node("vcs/") + assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node('vcs') - assert hasattr(node, 'kind') + node = init_commit.get_node("vcs") + assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node('vcs/__init__.py') - assert hasattr(node, 'kind') + node = init_commit.get_node("vcs/__init__.py") + assert hasattr(node, "kind") assert node.kind == NodeKind.FILE def test_not_existing_commit(self): # rawid with pytest.raises(RepositoryError): - self.repo.get_commit('abcd' * 10) + self.repo.get_commit("abcd" * 10) # shortid with pytest.raises(RepositoryError): - self.repo.get_commit('erro' * 4) + self.repo.get_commit("erro" * 4) # numeric with pytest.raises(RepositoryError): self.repo.get_commit(commit_idx=self.repo.count() + 1) @@ -289,7 +291,7 @@ Introduction TODO: To be written... """ - node = commit10.get_node('README.rst') + node = commit10.get_node("README.rst") assert node.kind == NodeKind.FILE assert node.str_content == README @@ -308,77 +310,73 @@ TODO: To be written... repo_clone = self.get_clone_repo() branches = repo_clone.branches - repo_clone._update('default') - assert branches['default'] == repo_clone._identify() - repo_clone._update('stable') - assert branches['stable'] == repo_clone._identify() + repo_clone._update("default") + assert branches["default"] == repo_clone._identify() + repo_clone._update("stable") + assert branches["stable"] == repo_clone._identify() def test_local_pull_branch(self): target_repo = self.get_empty_repo() source_repo = self.get_clone_repo() - default = Reference( - 'branch', 'default', source_repo.branches['default']) + default = Reference("branch", "default", source_repo.branches["default"]) target_repo._local_pull(source_repo.path, default) target_repo = MercurialRepository(target_repo.path) - assert (target_repo.branches['default'] == - source_repo.branches['default']) + assert target_repo.branches["default"] == source_repo.branches["default"] - stable = Reference('branch', 'stable', source_repo.branches['stable']) + stable = Reference("branch", "stable", source_repo.branches["stable"]) target_repo._local_pull(source_repo.path, stable) target_repo = MercurialRepository(target_repo.path) - assert target_repo.branches['stable'] == source_repo.branches['stable'] + assert target_repo.branches["stable"] == source_repo.branches["stable"] def test_local_pull_bookmark(self): target_repo = self.get_empty_repo() source_repo = self.get_clone_repo() - commits = list(source_repo.get_commits(branch_name='default')) + commits = list(source_repo.get_commits(branch_name="default")) foo1_id = commits[-5].raw_id - foo1 = Reference('book', 'foo1', foo1_id) + foo1 = Reference("book", "foo1", foo1_id) source_repo._update(foo1_id) - source_repo.bookmark('foo1') + source_repo.bookmark("foo1") foo2_id = commits[-3].raw_id - foo2 = Reference('book', 'foo2', foo2_id) + foo2 = Reference("book", "foo2", foo2_id) source_repo._update(foo2_id) - source_repo.bookmark('foo2') + source_repo.bookmark("foo2") target_repo._local_pull(source_repo.path, foo1) target_repo = MercurialRepository(target_repo.path) - assert target_repo.branches['default'] == commits[-5].raw_id + assert target_repo.branches["default"] == commits[-5].raw_id target_repo._local_pull(source_repo.path, foo2) target_repo = MercurialRepository(target_repo.path) - assert target_repo.branches['default'] == commits[-3].raw_id + assert target_repo.branches["default"] == commits[-3].raw_id def test_local_pull_commit(self): target_repo = self.get_empty_repo() source_repo = self.get_clone_repo() - commits = list(source_repo.get_commits(branch_name='default')) + commits = list(source_repo.get_commits(branch_name="default")) commit_id = commits[-5].raw_id - commit = Reference('rev', commit_id, commit_id) + commit = Reference("rev", commit_id, commit_id) target_repo._local_pull(source_repo.path, commit) target_repo = MercurialRepository(target_repo.path) - assert target_repo.branches['default'] == commit_id + assert target_repo.branches["default"] == commit_id commit_id = commits[-3].raw_id - commit = Reference('rev', commit_id, commit_id) + commit = Reference("rev", commit_id, commit_id) target_repo._local_pull(source_repo.path, commit) target_repo = MercurialRepository(target_repo.path) - assert target_repo.branches['default'] == commit_id + assert target_repo.branches["default"] == commit_id def test_local_pull_from_same_repo(self): - reference = Reference('branch', 'default', None) + reference = Reference("branch", "default", None) with pytest.raises(ValueError): self.repo._local_pull(self.repo.path, reference) - def test_validate_pull_reference_raises_on_missing_reference( - self, vcsbackend_hg): + def test_validate_pull_reference_raises_on_missing_reference(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) - reference = Reference( - 'book', 'invalid_reference', 'a' * 40) + reference = Reference("book", "invalid_reference", "a" * 40) with pytest.raises(CommitDoesNotExistError): target_repo._validate_pull_reference(reference) @@ -387,51 +385,48 @@ TODO: To be written... assert set(self.repo._heads()) == set(self.repo.branches.values()) def test_ancestor(self): - commits = [ - c.raw_id for c in self.repo.get_commits(branch_name='default')] + commits = [c.raw_id for c in self.repo.get_commits(branch_name="default")] assert self.repo._ancestor(commits[-3], commits[-5]) == commits[-5] assert self.repo._ancestor(commits[-5], commits[-3]) == commits[-5] def test_local_push(self): target_repo = self.get_empty_repo() - revisions = list(self.repo.get_commits(branch_name='default')) + revisions = list(self.repo.get_commits(branch_name="default")) revision = revisions[-5].raw_id self.repo._local_push(revision, target_repo.path) target_repo = MercurialRepository(target_repo.path) - assert target_repo.branches['default'] == revision + assert target_repo.branches["default"] == revision def test_hooks_can_be_enabled_for_local_push(self): - revision = 'deadbeef' - repo_path = 'test_group/test_repo' - with mock.patch.object(self.repo, '_remote') as remote_mock: + revision = "deadbeef" + repo_path = "test_group/test_repo" + with mock.patch.object(self.repo, "_remote") as remote_mock: self.repo._local_push(revision, repo_path, enable_hooks=True) - remote_mock.push.assert_called_once_with( - [revision], repo_path, hooks=True, push_branches=False) + remote_mock.push.assert_called_once_with([revision], repo_path, hooks=True, push_branches=False) def test_local_merge(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) source_repo = vcsbackend_hg.clone_repo(target_repo) - vcsbackend_hg.add_file(target_repo, b'README_MERGE1', b'Version 1') + vcsbackend_hg.add_file(target_repo, b"README_MERGE1", b"Version 1") target_repo = MercurialRepository(target_repo.path) - target_rev = target_repo.branches['default'] - target_ref = Reference( - type='branch', name='default', commit_id=target_rev) - vcsbackend_hg.add_file(source_repo, b'README_MERGE2', b'Version 2') + target_rev = target_repo.branches["default"] + target_ref = Reference(type="branch", name="default", commit_id=target_rev) + vcsbackend_hg.add_file(source_repo, b"README_MERGE2", b"Version 2") source_repo = MercurialRepository(source_repo.path) - source_rev = source_repo.branches['default'] - source_ref = Reference( - type='branch', name='default', commit_id=source_rev) + source_rev = source_repo.branches["default"] + source_ref = Reference(type="branch", name="default", commit_id=source_rev) target_repo._local_pull(source_repo.path, source_ref) - merge_message = 'Merge message\n\nDescription:...' - user_name = 'Albert Einstein' - user_email = 'albert@einstein.com' + merge_message = "Merge message\n\nDescription:..." + user_name = "Albert Einstein" + user_email = "albert@einstein.com" merge_commit_id, needs_push = target_repo._local_merge( - target_ref, merge_message, user_name, user_email, source_ref) + target_ref, merge_message, user_name, user_email, source_ref + ) assert needs_push target_repo = MercurialRepository(target_repo.path) @@ -439,30 +434,28 @@ TODO: To be written... assert target_repo.commit_ids[-2] == source_rev last_commit = target_repo.get_commit(merge_commit_id) assert last_commit.message.strip() == merge_message - assert last_commit.author == '%s <%s>' % (user_name, user_email) + assert last_commit.author == "%s <%s>" % (user_name, user_email) - assert not os.path.exists( - os.path.join(target_repo.path, '.hg', 'merge', 'state')) + assert not os.path.exists(os.path.join(target_repo.path, ".hg", "merge", "state")) def test_local_merge_source_is_fast_forward(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) source_repo = vcsbackend_hg.clone_repo(target_repo) - target_rev = target_repo.branches['default'] - target_ref = Reference( - type='branch', name='default', commit_id=target_rev) - vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2') + target_rev = target_repo.branches["default"] + target_ref = Reference(type="branch", name="default", commit_id=target_rev) + vcsbackend_hg.add_file(source_repo, "README_MERGE2", "Version 2") source_repo = MercurialRepository(source_repo.path) - source_rev = source_repo.branches['default'] - source_ref = Reference( - type='branch', name='default', commit_id=source_rev) + source_rev = source_repo.branches["default"] + source_ref = Reference(type="branch", name="default", commit_id=source_rev) target_repo._local_pull(source_repo.path, source_ref) - merge_message = 'Merge message\n\nDescription:...' - user_name = 'Albert Einstein' - user_email = 'albert@einstein.com' + merge_message = "Merge message\n\nDescription:..." + user_name = "Albert Einstein" + user_email = "albert@einstein.com" merge_commit_id, needs_push = target_repo._local_merge( - target_ref, merge_message, user_name, user_email, source_ref) + target_ref, merge_message, user_name, user_email, source_ref + ) assert merge_commit_id == source_rev assert needs_push @@ -470,70 +463,62 @@ TODO: To be written... assert target_repo.commit_ids[-2] == target_rev assert target_repo.commit_ids[-1] == source_rev - assert not os.path.exists( - os.path.join(target_repo.path, '.hg', 'merge', 'state')) + assert not os.path.exists(os.path.join(target_repo.path, ".hg", "merge", "state")) def test_local_merge_source_is_integrated(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) - target_rev = target_repo.branches['default'] - target_ref = Reference( - type='branch', name='default', commit_id=target_rev) + target_rev = target_repo.branches["default"] + target_ref = Reference(type="branch", name="default", commit_id=target_rev) - merge_message = 'Merge message\n\nDescription:...' - user_name = 'Albert Einstein' - user_email = 'albert@einstein.com' + merge_message = "Merge message\n\nDescription:..." + user_name = "Albert Einstein" + user_email = "albert@einstein.com" merge_commit_id, needs_push = target_repo._local_merge( - target_ref, merge_message, user_name, user_email, target_ref) + target_ref, merge_message, user_name, user_email, target_ref + ) assert merge_commit_id == target_rev assert not needs_push target_repo = MercurialRepository(target_repo.path) assert target_repo.commit_ids[-1] == target_rev - assert not os.path.exists( - os.path.join(target_repo.path, '.hg', 'merge', 'state')) + assert not os.path.exists(os.path.join(target_repo.path, ".hg", "merge", "state")) def test_local_merge_raises_exception_on_conflict(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) source_repo = vcsbackend_hg.clone_repo(target_repo) - vcsbackend_hg.add_file(target_repo, 'README_MERGE', 'Version 1') + vcsbackend_hg.add_file(target_repo, "README_MERGE", "Version 1") target_repo = MercurialRepository(target_repo.path) - target_rev = target_repo.branches['default'] - target_ref = Reference( - type='branch', name='default', commit_id=target_rev) - vcsbackend_hg.add_file(source_repo, 'README_MERGE', 'Version 2') + target_rev = target_repo.branches["default"] + target_ref = Reference(type="branch", name="default", commit_id=target_rev) + vcsbackend_hg.add_file(source_repo, "README_MERGE", "Version 2") source_repo = MercurialRepository(source_repo.path) - source_rev = source_repo.branches['default'] - source_ref = Reference( - type='branch', name='default', commit_id=source_rev) + source_rev = source_repo.branches["default"] + source_ref = Reference(type="branch", name="default", commit_id=source_rev) target_repo._local_pull(source_repo.path, source_ref) with pytest.raises(RepositoryError): - target_repo._local_merge( - target_ref, 'merge_message', 'user name', 'user@name.com', - source_ref) + target_repo._local_merge(target_ref, "merge_message", "user name", "user@name.com", source_ref) # Check we are not left in an intermediate merge state - assert not os.path.exists( - os.path.join(target_repo.path, '.hg', 'merge', 'state')) + assert not os.path.exists(os.path.join(target_repo.path, ".hg", "merge", "state")) def test_local_merge_of_two_branches_of_the_same_repo(self, backend_hg): commits = [ - {'message': 'a'}, - {'message': 'b', 'branch': 'b'}, + {"message": "a"}, + {"message": "b", "branch": "b"}, ] repo = backend_hg.create_repo(commits) commit_ids = backend_hg.commit_ids - target_ref = Reference( - type='branch', name='default', commit_id=commit_ids['a']) - source_ref = Reference( - type='branch', name='b', commit_id=commit_ids['b']) - merge_message = 'Merge message\n\nDescription:...' - user_name = 'Albert Einstein' - user_email = 'albert@einstein.com' + target_ref = Reference(type="branch", name="default", commit_id=commit_ids["a"]) + source_ref = Reference(type="branch", name="b", commit_id=commit_ids["b"]) + merge_message = "Merge message\n\nDescription:..." + user_name = "Albert Einstein" + user_email = "albert@einstein.com" vcs_repo = repo.scm_instance() merge_commit_id, needs_push = vcs_repo._local_merge( - target_ref, merge_message, user_name, user_email, source_ref) + target_ref, merge_message, user_name, user_email, source_ref + ) assert merge_commit_id != source_ref.commit_id assert needs_push is True commit = vcs_repo.get_commit(merge_commit_id) @@ -541,63 +526,62 @@ TODO: To be written... assert commit.message == merge_message def test_maybe_prepare_merge_workspace(self): - workspace = self.repo._maybe_prepare_merge_workspace( - 1, 'pr2', 'unused', 'unused2') + workspace = self.repo._maybe_prepare_merge_workspace(1, "pr2", "unused", "unused2") assert os.path.isdir(workspace) workspace_repo = MercurialRepository(workspace) assert workspace_repo.branches == self.repo.branches # Calling it a second time should also succeed - workspace = self.repo._maybe_prepare_merge_workspace( - 1, 'pr2', 'unused', 'unused2') + workspace = self.repo._maybe_prepare_merge_workspace(1, "pr2", "unused", "unused2") assert os.path.isdir(workspace) def test_cleanup_merge_workspace(self): - workspace = self.repo._maybe_prepare_merge_workspace( - 1, 'pr3', 'unused', 'unused2') + workspace = self.repo._maybe_prepare_merge_workspace(1, "pr3", "unused", "unused2") assert os.path.isdir(workspace) - self.repo.cleanup_merge_workspace(1, 'pr3') + self.repo.cleanup_merge_workspace(1, "pr3") assert not os.path.exists(workspace) def test_cleanup_merge_workspace_invalid_workspace_id(self): # No assert: because in case of an inexistent workspace this function # should still succeed. - self.repo.cleanup_merge_workspace(1, 'pr4') + self.repo.cleanup_merge_workspace(1, "pr4") def test_merge_target_is_bookmark(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) source_repo = vcsbackend_hg.clone_repo(target_repo) - vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1') - vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2') + vcsbackend_hg.add_file(target_repo, "README_MERGE1", "Version 1") + vcsbackend_hg.add_file(source_repo, "README_MERGE2", "Version 2") imc = source_repo.in_memory_commit - imc.add(FileNode(b'file_x', content=source_repo.name)) - imc.commit( - message='Automatic commit from repo merge test', - author='Automatic ') + imc.add(FileNode(b"file_x", content=source_repo.name)) + imc.commit(message="Automatic commit from repo merge test", author="Automatic ") target_commit = target_repo.get_commit() source_commit = source_repo.get_commit() default_branch = target_repo.DEFAULT_BRANCH_NAME - bookmark_name = 'bookmark' + bookmark_name = "bookmark" target_repo._update(default_branch) target_repo.bookmark(bookmark_name) - target_ref = Reference('book', bookmark_name, target_commit.raw_id) - source_ref = Reference('branch', default_branch, source_commit.raw_id) - workspace_id = 'test-merge' + target_ref = Reference("book", bookmark_name, target_commit.raw_id) + source_ref = Reference("branch", default_branch, source_commit.raw_id) + workspace_id = "test-merge" repo_id = repo_id_generator(target_repo.path) merge_response = target_repo.merge( - repo_id, workspace_id, target_ref, source_repo, source_ref, - 'test user', 'test@rhodecode.com', 'merge message 1', - dry_run=False) - expected_merge_response = MergeResponse( - True, True, merge_response.merge_ref, - MergeFailureReason.NONE) + repo_id, + workspace_id, + target_ref, + source_repo, + source_ref, + "test user", + "test@rhodecode.com", + "merge message 1", + dry_run=False, + ) + expected_merge_response = MergeResponse(True, True, merge_response.merge_ref, MergeFailureReason.NONE) assert merge_response == expected_merge_response - target_repo = backends.get_backend(vcsbackend_hg.alias)( - target_repo.path) + target_repo = backends.get_backend(vcsbackend_hg.alias)(target_repo.path) target_commits = list(target_repo.get_commits()) commit_ids = [c.raw_id for c in target_commits[:-1]] assert source_ref.commit_id in commit_ids @@ -605,43 +589,43 @@ TODO: To be written... merge_commit = target_commits[-1] assert merge_commit.raw_id == merge_response.merge_ref.commit_id - assert merge_commit.message.strip() == 'merge message 1' - assert merge_commit.author == 'test user ' + assert merge_commit.message.strip() == "merge message 1" + assert merge_commit.author == "test user " # Check the bookmark was updated in the target repo - assert ( - target_repo.bookmarks[bookmark_name] == - merge_response.merge_ref.commit_id) + assert target_repo.bookmarks[bookmark_name] == merge_response.merge_ref.commit_id def test_merge_source_is_bookmark(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) source_repo = vcsbackend_hg.clone_repo(target_repo) imc = source_repo.in_memory_commit - imc.add(FileNode(b'file_x', content=source_repo.name)) - imc.commit( - message='Automatic commit from repo merge test', - author='Automatic ') + imc.add(FileNode(b"file_x", content=source_repo.name)) + imc.commit(message="Automatic commit from repo merge test", author="Automatic ") target_commit = target_repo.get_commit() source_commit = source_repo.get_commit() default_branch = target_repo.DEFAULT_BRANCH_NAME - bookmark_name = 'bookmark' - target_ref = Reference('branch', default_branch, target_commit.raw_id) + bookmark_name = "bookmark" + target_ref = Reference("branch", default_branch, target_commit.raw_id) source_repo._update(default_branch) source_repo.bookmark(bookmark_name) - source_ref = Reference('book', bookmark_name, source_commit.raw_id) - workspace_id = 'test-merge' + source_ref = Reference("book", bookmark_name, source_commit.raw_id) + workspace_id = "test-merge" repo_id = repo_id_generator(target_repo.path) merge_response = target_repo.merge( - repo_id, workspace_id, target_ref, source_repo, source_ref, - 'test user', 'test@rhodecode.com', 'merge message 1', - dry_run=False) - expected_merge_response = MergeResponse( - True, True, merge_response.merge_ref, - MergeFailureReason.NONE) + repo_id, + workspace_id, + target_ref, + source_repo, + source_ref, + "test user", + "test@rhodecode.com", + "merge message 1", + dry_run=False, + ) + expected_merge_response = MergeResponse(True, True, merge_response.merge_ref, MergeFailureReason.NONE) assert merge_response == expected_merge_response - target_repo = backends.get_backend(vcsbackend_hg.alias)( - target_repo.path) + target_repo = backends.get_backend(vcsbackend_hg.alias)(target_repo.path) target_commits = list(target_repo.get_commits()) commit_ids = [c.raw_id for c in target_commits] assert source_ref.commit_id == commit_ids[-1] @@ -650,78 +634,89 @@ TODO: To be written... def test_merge_target_has_multiple_heads(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=2) source_repo = vcsbackend_hg.clone_repo(target_repo) - vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1') - vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2') + vcsbackend_hg.add_file(target_repo, "README_MERGE1", "Version 1") + vcsbackend_hg.add_file(source_repo, "README_MERGE2", "Version 2") # add an extra head to the target repo imc = target_repo.in_memory_commit - imc.add(FileNode(b'file_x', content='foo')) + imc.add(FileNode(b"file_x", content="foo")) commits = list(target_repo.get_commits()) imc.commit( - message='Automatic commit from repo merge test', - author='Automatic ', parents=commits[0:1]) + message="Automatic commit from repo merge test", + author="Automatic ", + parents=commits[0:1], + ) target_commit = target_repo.get_commit() source_commit = source_repo.get_commit() default_branch = target_repo.DEFAULT_BRANCH_NAME target_repo._update(default_branch) - target_ref = Reference('branch', default_branch, target_commit.raw_id) - source_ref = Reference('branch', default_branch, source_commit.raw_id) - workspace_id = 'test-merge' + target_ref = Reference("branch", default_branch, target_commit.raw_id) + source_ref = Reference("branch", default_branch, source_commit.raw_id) + workspace_id = "test-merge" - assert len(target_repo._heads(branch='default')) == 2 - heads = target_repo._heads(branch='default') + assert len(target_repo._heads(branch="default")) == 2 + heads = target_repo._heads(branch="default") expected_merge_response = MergeResponse( - False, False, None, - MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS, - metadata={'heads': heads}) + False, False, None, MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS, metadata={"heads": heads} + ) repo_id = repo_id_generator(target_repo.path) merge_response = target_repo.merge( - repo_id, workspace_id, target_ref, source_repo, source_ref, - 'test user', 'test@rhodecode.com', 'merge message 1', - dry_run=False) + repo_id, + workspace_id, + target_ref, + source_repo, + source_ref, + "test user", + "test@rhodecode.com", + "merge message 1", + dry_run=False, + ) assert merge_response == expected_merge_response def test_merge_rebase_source_is_updated_bookmark(self, vcsbackend_hg): target_repo = vcsbackend_hg.create_repo(number_of_commits=1) source_repo = vcsbackend_hg.clone_repo(target_repo) - vcsbackend_hg.add_file(target_repo, b'README_MERGE1', b'Version 1') - vcsbackend_hg.add_file(source_repo, b'README_MERGE2', b'Version 2') + vcsbackend_hg.add_file(target_repo, b"README_MERGE1", b"Version 1") + vcsbackend_hg.add_file(source_repo, b"README_MERGE2", b"Version 2") imc = source_repo.in_memory_commit - imc.add(FileNode(b'file_x', content=safe_bytes(source_repo.name))) - imc.commit( - message='Automatic commit from repo merge test', - author='Automatic ') + imc.add(FileNode(b"file_x", content=safe_bytes(source_repo.name))) + imc.commit(message="Automatic commit from repo merge test", author="Automatic ") target_commit = target_repo.get_commit() source_commit = source_repo.get_commit() - vcsbackend_hg.add_file(source_repo, b'LICENSE', b'LICENSE Info') + vcsbackend_hg.add_file(source_repo, b"LICENSE", b"LICENSE Info") default_branch = target_repo.DEFAULT_BRANCH_NAME - bookmark_name = 'bookmark' + bookmark_name = "bookmark" source_repo._update(default_branch) source_repo.bookmark(bookmark_name) - target_ref = Reference('branch', default_branch, target_commit.raw_id) - source_ref = Reference('book', bookmark_name, source_commit.raw_id) + target_ref = Reference("branch", default_branch, target_commit.raw_id) + source_ref = Reference("book", bookmark_name, source_commit.raw_id) repo_id = repo_id_generator(target_repo.path) - workspace_id = 'test-merge' + workspace_id = "test-merge" merge_response = target_repo.merge( - repo_id, workspace_id, target_ref, source_repo, source_ref, - 'test user', 'test@rhodecode.com', 'merge message 1', - dry_run=False, use_rebase=True) + repo_id, + workspace_id, + target_ref, + source_repo, + source_ref, + "test user", + "test@rhodecode.com", + "merge message 1", + dry_run=False, + use_rebase=True, + ) - expected_merge_response = MergeResponse( - True, True, merge_response.merge_ref, - MergeFailureReason.NONE) + expected_merge_response = MergeResponse(True, True, merge_response.merge_ref, MergeFailureReason.NONE) assert merge_response == expected_merge_response - target_repo = backends.get_backend(vcsbackend_hg.alias)( - target_repo.path) + target_repo = backends.get_backend(vcsbackend_hg.alias)(target_repo.path) last_commit = target_repo.get_commit() assert last_commit.message == source_commit.message assert last_commit.author == source_commit.author @@ -736,27 +731,28 @@ TODO: To be written... class TestGetShadowInstance(object): @pytest.fixture() - def repo(self, vcsbackend_hg, monkeypatch): - repo = vcsbackend_hg.repo - monkeypatch.setattr(repo, 'config', mock.Mock()) - monkeypatch.setattr('rhodecode.lib.vcs.connection.Hg', mock.Mock()) - return repo + def repo(self, vcsbackend_hg): + _hg_repo = vcsbackend_hg.repo + connection_mock = mock.Mock(unsafe=True, name="connection.Hg") + mock.patch("rhodecode.lib.vcs.connection.Hg", connection_mock) + return _hg_repo - def test_passes_config(self, repo): + def test_getting_shadow_instance_copies_config(self, repo): shadow = repo.get_shadow_instance(repo.path) - assert shadow.config == repo.config.copy() + assert shadow.config.serialize() == repo.config.serialize() - def test_disables_hooks(self, repo): + def test_disables_hooks_section(self, repo): + repo.config.set('hooks', 'foo', 'val') shadow = repo.get_shadow_instance(repo.path) - shadow.config.clear_section.assert_called_once_with('hooks') + assert not shadow.config.items('hooks') def test_allows_to_keep_hooks(self, repo): + repo.config.set('hooks', 'foo', 'val') shadow = repo.get_shadow_instance(repo.path, enable_hooks=True) - assert not shadow.config.clear_section.called + assert shadow.config.items('hooks') class TestMercurialCommit(object): - def _test_equality(self, commit): idx = commit.idx assert commit == self.repo.get_commit(commit_idx=idx) @@ -768,15 +764,15 @@ class TestMercurialCommit(object): self._test_equality(commit) def test_default_commit(self): - tip = self.repo.get_commit('tip') + tip = self.repo.get_commit("tip") assert tip == self.repo.get_commit() assert tip == self.repo.get_commit(commit_id=None) assert tip == self.repo.get_commit(commit_idx=None) assert tip == list(self.repo[-1:])[0] def test_root_node(self): - tip = self.repo.get_commit('tip') - assert tip.root is tip.get_node('') + tip = self.repo.get_commit("tip") + assert tip.root is tip.get_node("") def test_lazy_fetch(self): """ @@ -792,44 +788,43 @@ class TestMercurialCommit(object): # accessing root.nodes updates commit.nodes assert len(commit.nodes) == 9 - docs = root.get_node('docs') + docs = root.get_node("docs") # we haven't yet accessed anything new as docs dir was already cached assert len(commit.nodes) == 9 assert len(docs.nodes) == 8 # accessing docs.nodes updates commit.nodes assert len(commit.nodes) == 17 - assert docs is commit.get_node('docs') + assert docs is commit.get_node("docs") assert docs is root.nodes[0] assert docs is root.dirs[0] - assert docs is commit.get_node('docs') + assert docs is commit.get_node("docs") def test_nodes_with_commit(self): commit = self.repo.get_commit(commit_idx=45) root = commit.root - docs = root.get_node('docs') - assert docs is commit.get_node('docs') - api = docs.get_node('api') - assert api is commit.get_node('docs/api') - index = api.get_node('index.rst') - assert index is commit.get_node('docs/api/index.rst') - assert index is commit.get_node( - 'docs').get_node('api').get_node('index.rst') + docs = root.get_node("docs") + assert docs is commit.get_node("docs") + api = docs.get_node("api") + assert api is commit.get_node("docs/api") + index = api.get_node("index.rst") + assert index is commit.get_node("docs/api/index.rst") + assert index is commit.get_node("docs").get_node("api").get_node("index.rst") def test_branch_and_tags(self): commit0 = self.repo.get_commit(commit_idx=0) - assert commit0.branch == 'default' + assert commit0.branch == "default" assert commit0.tags == [] commit10 = self.repo.get_commit(commit_idx=10) - assert commit10.branch == 'default' + assert commit10.branch == "default" assert commit10.tags == [] commit44 = self.repo.get_commit(commit_idx=44) - assert commit44.branch == 'web' + assert commit44.branch == "web" - tip = self.repo.get_commit('tip') - assert 'tip' in tip.tags + tip = self.repo.get_commit("tip") + assert "tip" in tip.tags def test_bookmarks(self): commit0 = self.repo.get_commit(commit_idx=0) @@ -842,46 +837,84 @@ class TestMercurialCommit(object): def test_file_size(self): to_check = ( - (10, 'setup.py', 1068), - (20, 'setup.py', 1106), - (60, 'setup.py', 1074), - - (10, 'vcs/backends/base.py', 2921), - (20, 'vcs/backends/base.py', 3936), - (60, 'vcs/backends/base.py', 6189), + (10, "setup.py", 1068), + (20, "setup.py", 1106), + (60, "setup.py", 1074), + (10, "vcs/backends/base.py", 2921), + (20, "vcs/backends/base.py", 3936), + (60, "vcs/backends/base.py", 6189), ) for idx, path, size in to_check: self._test_file_size(idx, path, size) def test_file_history_from_commits(self): - node = self.repo[10].get_node('setup.py') + node = self.repo[10].get_node("setup.py") commit_ids = [commit.raw_id for commit in node.history] - assert ['3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'] == commit_ids + assert ["3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"] == commit_ids - node = self.repo[20].get_node('setup.py') + node = self.repo[20].get_node("setup.py") node_ids = [commit.raw_id for commit in node.history] - assert ['eada5a770da98ab0dd7325e29d00e0714f228d09', - '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'] == node_ids + assert ["eada5a770da98ab0dd7325e29d00e0714f228d09", "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"] == node_ids # special case we check history from commit that has this particular # file changed this means we check if it's included as well - node = self.repo.get_commit('eada5a770da98ab0dd7325e29d00e0714f228d09')\ - .get_node('setup.py') + node = self.repo.get_commit("eada5a770da98ab0dd7325e29d00e0714f228d09").get_node("setup.py") node_ids = [commit.raw_id for commit in node.history] - assert ['eada5a770da98ab0dd7325e29d00e0714f228d09', - '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'] == node_ids + assert ["eada5a770da98ab0dd7325e29d00e0714f228d09", "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"] == node_ids def test_file_history(self): # we can only check if those commits are present in the history # as we cannot update this test every time file is changed files = { - 'setup.py': [7, 18, 45, 46, 47, 69, 77], - 'vcs/nodes.py': [ - 7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60, 61, 73, 76], - 'vcs/backends/hg.py': [ - 4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23, 26, 27, 28, 30, - 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47, 48, 49, 53, 54, - 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79, 82], + "setup.py": [7, 18, 45, 46, 47, 69, 77], + "vcs/nodes.py": [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60, 61, 73, 76], + "vcs/backends/hg.py": [ + 4, + 5, + 6, + 11, + 12, + 13, + 14, + 15, + 16, + 21, + 22, + 23, + 26, + 27, + 28, + 30, + 31, + 33, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 44, + 45, + 47, + 48, + 49, + 53, + 54, + 55, + 58, + 60, + 61, + 67, + 68, + 69, + 70, + 73, + 77, + 78, + 79, + 82, + ], } for path, indexes in files.items(): tip = self.repo.get_commit(commit_idx=indexes[-1]) @@ -889,72 +922,105 @@ class TestMercurialCommit(object): node_indexes = [commit.idx for commit in node.history] assert set(indexes).issubset(set(node_indexes)), ( "We assumed that %s is subset of commits for which file %s " - "has been changed, and history of that node returned: %s" - % (indexes, path, node_indexes)) + "has been changed, and history of that node returned: %s" % (indexes, path, node_indexes) + ) def test_file_annotate(self): files = { - 'vcs/backends/__init__.py': { + "vcs/backends/__init__.py": { 89: { - 'lines_no': 31, - 'commits': [ - 32, 32, 61, 32, 32, 37, 32, 32, 32, 44, - 37, 37, 37, 37, 45, 37, 44, 37, 37, 37, - 32, 32, 32, 32, 37, 32, 37, 37, 32, - 32, 32 - ] - }, - 20: { - 'lines_no': 1, - 'commits': [4] + "lines_no": 31, + "commits": [ + 32, + 32, + 61, + 32, + 32, + 37, + 32, + 32, + 32, + 44, + 37, + 37, + 37, + 37, + 45, + 37, + 44, + 37, + 37, + 37, + 32, + 32, + 32, + 32, + 37, + 32, + 37, + 37, + 32, + 32, + 32, + ], }, + 20: {"lines_no": 1, "commits": [4]}, 55: { - 'lines_no': 31, - 'commits': [ - 32, 32, 45, 32, 32, 37, 32, 32, 32, 44, - 37, 37, 37, 37, 45, 37, 44, 37, 37, 37, - 32, 32, 32, 32, 37, 32, 37, 37, 32, - 32, 32 - ] - } + "lines_no": 31, + "commits": [ + 32, + 32, + 45, + 32, + 32, + 37, + 32, + 32, + 32, + 44, + 37, + 37, + 37, + 37, + 45, + 37, + 44, + 37, + 37, + 37, + 32, + 32, + 32, + 32, + 37, + 32, + 37, + 37, + 32, + 32, + 32, + ], + }, }, - 'vcs/exceptions.py': { + "vcs/exceptions.py": { 89: { - 'lines_no': 18, - 'commits': [ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 17, 16, 16, 18, 18, 18 - ] + "lines_no": 18, + "commits": [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 18, 18, 18], }, 20: { - 'lines_no': 18, - 'commits': [ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 17, 16, 16, 18, 18, 18 - ] + "lines_no": 18, + "commits": [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 18, 18, 18], }, 55: { - 'lines_no': 18, - 'commits': [ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 17, 16, 16, 18, 18, 18 - ] - } + "lines_no": 18, + "commits": [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 18, 18, 18], + }, + }, + "MANIFEST.in": { + 89: {"lines_no": 5, "commits": [7, 7, 7, 71, 71]}, + 20: {"lines_no": 3, "commits": [7, 7, 7]}, + 55: {"lines_no": 3, "commits": [7, 7, 7]}, }, - 'MANIFEST.in': { - 89: { - 'lines_no': 5, - 'commits': [7, 7, 7, 71, 71] - }, - 20: { - 'lines_no': 3, - 'commits': [7, 7, 7] - }, - 55: { - 'lines_no': 3, - 'commits': [7, 7, 7] - } - } } for fname, commit_dict in files.items(): @@ -963,13 +1029,13 @@ class TestMercurialCommit(object): l1_1 = [x[1] for x in commit.get_file_annotate(fname)] l1_2 = [x[2]().raw_id for x in commit.get_file_annotate(fname)] assert l1_1 == l1_2 - l1 = l1_2 = [ - x[2]().idx for x in commit.get_file_annotate(fname)] - l2 = files[fname][idx]['commits'] + l1 = l1_2 = [x[2]().idx for x in commit.get_file_annotate(fname)] + l2 = files[fname][idx]["commits"] assert l1 == l2, ( "The lists of commit for %s@commit_id%s" "from annotation list should match each other," - "got \n%s \nvs \n%s " % (fname, idx, l1, l2)) + "got \n%s \nvs \n%s " % (fname, idx, l1, l2) + ) def test_commit_state(self): """ @@ -981,28 +1047,52 @@ class TestMercurialCommit(object): # changed: 13 # added: 20 # removed: 1 - changed = set([ - '.hgignore', 'README.rst', 'docs/conf.py', 'docs/index.rst', - 'setup.py', 'tests/test_hg.py', 'tests/test_nodes.py', - 'vcs/__init__.py', 'vcs/backends/__init__.py', - 'vcs/backends/base.py', 'vcs/backends/hg.py', 'vcs/nodes.py', - 'vcs/utils/__init__.py']) + changed = set( + [ + ".hgignore", + "README.rst", + "docs/conf.py", + "docs/index.rst", + "setup.py", + "tests/test_hg.py", + "tests/test_nodes.py", + "vcs/__init__.py", + "vcs/backends/__init__.py", + "vcs/backends/base.py", + "vcs/backends/hg.py", + "vcs/nodes.py", + "vcs/utils/__init__.py", + ] + ) - added = set([ - 'docs/api/backends/hg.rst', 'docs/api/backends/index.rst', - 'docs/api/index.rst', 'docs/api/nodes.rst', - 'docs/api/web/index.rst', 'docs/api/web/simplevcs.rst', - 'docs/installation.rst', 'docs/quickstart.rst', 'setup.cfg', - 'vcs/utils/baseui_config.py', 'vcs/utils/web.py', - 'vcs/web/__init__.py', 'vcs/web/exceptions.py', - 'vcs/web/simplevcs/__init__.py', 'vcs/web/simplevcs/exceptions.py', - 'vcs/web/simplevcs/middleware.py', 'vcs/web/simplevcs/models.py', - 'vcs/web/simplevcs/settings.py', 'vcs/web/simplevcs/utils.py', - 'vcs/web/simplevcs/views.py']) + added = set( + [ + "docs/api/backends/hg.rst", + "docs/api/backends/index.rst", + "docs/api/index.rst", + "docs/api/nodes.rst", + "docs/api/web/index.rst", + "docs/api/web/simplevcs.rst", + "docs/installation.rst", + "docs/quickstart.rst", + "setup.cfg", + "vcs/utils/baseui_config.py", + "vcs/utils/web.py", + "vcs/web/__init__.py", + "vcs/web/exceptions.py", + "vcs/web/simplevcs/__init__.py", + "vcs/web/simplevcs/exceptions.py", + "vcs/web/simplevcs/middleware.py", + "vcs/web/simplevcs/models.py", + "vcs/web/simplevcs/settings.py", + "vcs/web/simplevcs/utils.py", + "vcs/web/simplevcs/views.py", + ] + ) - removed = set(['docs/api.rst']) + removed = set(["docs/api.rst"]) - commit64 = self.repo.get_commit('46ad32a4f974') + commit64 = self.repo.get_commit("46ad32a4f974") assert set((node.path for node in commit64.added)) == added assert set((node.path for node in commit64.changed)) == changed assert set((node.path for node in commit64.removed)) == removed @@ -1012,10 +1102,9 @@ class TestMercurialCommit(object): # changed: 13 # added: 20 # removed: 1 - commit88 = self.repo.get_commit('b090f22d27d6') + commit88 = self.repo.get_commit("b090f22d27d6") assert set((node.path for node in commit88.added)) == set() - assert set((node.path for node in commit88.changed)) == \ - set(['.hgignore']) + assert set((node.path for node in commit88.changed)) == set([".hgignore"]) assert set((node.path for node in commit88.removed)) == set() # @@ -1025,24 +1114,25 @@ class TestMercurialCommit(object): # changed: 4 ['vcs/web/simplevcs/models.py', ...] # removed: 1 ['vcs/utils/web.py'] commit85 = self.repo.get_commit(commit_idx=85) - assert set((node.path for node in commit85.added)) == set([ - 'vcs/utils/diffs.py', - 'vcs/web/simplevcs/views/diffs.py']) - assert set((node.path for node in commit85.changed)) == set([ - 'vcs/web/simplevcs/models.py', - 'vcs/web/simplevcs/utils.py', - 'vcs/web/simplevcs/views/__init__.py', - 'vcs/web/simplevcs/views/repository.py', - ]) - assert set((node.path for node in commit85.removed)) == \ - set(['vcs/utils/web.py']) + assert set((node.path for node in commit85.added)) == set( + ["vcs/utils/diffs.py", "vcs/web/simplevcs/views/diffs.py"] + ) + assert set((node.path for node in commit85.changed)) == set( + [ + "vcs/web/simplevcs/models.py", + "vcs/web/simplevcs/utils.py", + "vcs/web/simplevcs/views/__init__.py", + "vcs/web/simplevcs/views/repository.py", + ] + ) + assert set((node.path for node in commit85.removed)) == set(["vcs/utils/web.py"]) def test_files_state(self): """ Tests state of FileNodes. """ commit = self.repo.get_commit(commit_idx=85) - node = commit.get_node('vcs/utils/diffs.py') + node = commit.get_node("vcs/utils/diffs.py") assert node.state, NodeState.ADDED assert node.added assert not node.changed @@ -1050,7 +1140,7 @@ class TestMercurialCommit(object): assert not node.removed commit = self.repo.get_commit(commit_idx=88) - node = commit.get_node('.hgignore') + node = commit.get_node(".hgignore") assert node.state, NodeState.CHANGED assert not node.added assert node.changed @@ -1058,7 +1148,7 @@ class TestMercurialCommit(object): assert not node.removed commit = self.repo.get_commit(commit_idx=85) - node = commit.get_node('setup.py') + node = commit.get_node("setup.py") assert node.state, NodeState.NOT_CHANGED assert not node.added assert not node.changed @@ -1068,7 +1158,7 @@ class TestMercurialCommit(object): # If node has REMOVED state then trying to fetch it would raise # CommitError exception commit = self.repo.get_commit(commit_idx=2) - path = 'vcs/backends/BaseRepository.py' + path = "vcs/backends/BaseRepository.py" with pytest.raises(NodeDoesNotExistError): commit.get_node(path) # but it would be one of ``removed`` (commit's attribute) @@ -1084,57 +1174,53 @@ class TestMercurialCommit(object): def test_repo_files_content_type(self): test_commit = self.repo.get_commit(commit_idx=100) - for node in test_commit.get_node('/'): + for node in test_commit.get_node("/"): if node.is_file(): assert type(node.content) == bytes assert type(node.str_content) == str def test_wrong_path(self): # There is 'setup.py' in the root dir but not there: - path = 'foo/bar/setup.py' + path = "foo/bar/setup.py" with pytest.raises(VCSError): self.repo.get_commit().get_node(path) def test_author_email(self): - assert 'marcin@python-blog.com' == \ - self.repo.get_commit('b986218ba1c9').author_email - assert 'lukasz.balcerzak@python-center.pl' == \ - self.repo.get_commit('3803844fdbd3').author_email - assert '' == self.repo.get_commit('84478366594b').author_email + assert "marcin@python-blog.com" == self.repo.get_commit("b986218ba1c9").author_email + assert "lukasz.balcerzak@python-center.pl" == self.repo.get_commit("3803844fdbd3").author_email + assert "" == self.repo.get_commit("84478366594b").author_email def test_author_username(self): - assert 'Marcin Kuzminski' == \ - self.repo.get_commit('b986218ba1c9').author_name - assert 'Lukasz Balcerzak' == \ - self.repo.get_commit('3803844fdbd3').author_name - assert 'marcink' == \ - self.repo.get_commit('84478366594b').author_name + assert "Marcin Kuzminski" == self.repo.get_commit("b986218ba1c9").author_name + assert "Lukasz Balcerzak" == self.repo.get_commit("3803844fdbd3").author_name + assert "marcink" == self.repo.get_commit("84478366594b").author_name class TestLargeFileRepo(object): - def test_large_file(self, backend_hg): - repo = backend_hg.create_test_repo('largefiles', make_db_config()) + conf = make_db_config() + hg_largefiles_store = conf.get("largefiles", "usercache") + repo = backend_hg.create_test_repo("largefiles", conf) tip = repo.scm_instance().get_commit() - node = tip.get_node('.hglf/thisfileislarge') + node = tip.get_node(".hglf/thisfileislarge") lf_node = node.get_largefile_node() assert lf_node.is_largefile() is True assert lf_node.size == 1024000 - assert lf_node.name == '.hglf/thisfileislarge' + assert lf_node.name == ".hglf/thisfileislarge" class TestGetBranchName(object): def test_returns_ref_name_when_type_is_branch(self): - ref = self._create_ref('branch', 'fake-name') + ref = self._create_ref("branch", "fake-name") result = self.repo._get_branch_name(ref) assert result == ref.name @pytest.mark.parametrize("type_", ("book", "tag")) def test_queries_remote_when_type_is_not_branch(self, type_): - ref = self._create_ref(type_, 'wrong-fake-name') + ref = self._create_ref(type_, "wrong-fake-name") with mock.patch.object(self.repo, "_remote") as remote_mock: remote_mock.ctx_branch.return_value = "fake-name" result = self.repo._get_branch_name(ref) @@ -1144,7 +1230,7 @@ class TestGetBranchName(object): def _create_ref(self, type_, name): ref = mock.Mock() ref.type = type_ - ref.name = 'wrong-fake-name' + ref.name = "wrong-fake-name" ref.commit_id = "deadbeef" return ref @@ -1153,8 +1239,7 @@ class TestIsTheSameBranch(object): def test_returns_true_when_branches_are_equal(self): source_ref = mock.Mock(name="source-ref") target_ref = mock.Mock(name="target-ref") - branch_name_patcher = mock.patch.object( - self.repo, "_get_branch_name", return_value="default") + branch_name_patcher = mock.patch.object(self.repo, "_get_branch_name", return_value="default") with branch_name_patcher as branch_name_mock: result = self.repo._is_the_same_branch(source_ref, target_ref) @@ -1171,8 +1256,7 @@ class TestIsTheSameBranch(object): def side_effect(ref): return ref.name - branch_name_patcher = mock.patch.object( - self.repo, "_get_branch_name", side_effect=side_effect) + branch_name_patcher = mock.patch.object(self.repo, "_get_branch_name", side_effect=side_effect) with branch_name_patcher as branch_name_mock: result = self.repo._is_the_same_branch(source_ref, target_ref) diff --git a/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py b/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py index d5868e5c..c6b5f522 100644 --- a/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py +++ b/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py @@ -1,5 +1,3 @@ - - # Copyright (C) 2016-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -35,45 +33,47 @@ class TestMercurialRemoteRepoInvalidation(object): """ # Default reference used as a dummy during tests. - default_ref = Reference('branch', 'default', None) + default_ref = Reference("branch", "default", None) # Methods of vcsserver.hg.HgRemote that are "writing" operations. writing_methods = [ - 'bookmark', - 'commit', - 'merge', - 'pull', - 'pull_cmd', - 'rebase', - 'strip', - 'tag', + "bookmark", + "commit", + "merge", + "pull", + "pull_cmd", + "rebase", + "strip", + "tag", ] - @pytest.mark.parametrize('method_name, method_args', [ - ('_local_merge', [default_ref, None, None, None, default_ref]), - ('_local_pull', ['', default_ref]), - ('bookmark', [None]), - ('pull', ['', default_ref]), - ('remove_tag', ['mytag', None]), - ('strip', [None]), - ('tag', ['newtag', None]), - ]) - def test_method_invokes_invalidate_on_remote_repo( - self, method_name, method_args, backend_hg): + @pytest.mark.parametrize( + "method_name, method_args", + [ + ("_local_merge", [default_ref, None, None, None, default_ref]), + ("_local_pull", ["", default_ref]), + ("bookmark", [None]), + ("pull", ["", default_ref]), + ("remove_tag", ["mytag", None]), + ("strip", [None]), + ("tag", ["newtag", None]), + ], + ) + def test_method_invokes_invalidate_on_remote_repo(self, method_name, method_args, backend_hg): """ Check that the listed methods are invalidating the VCSServer cache after invoking a writing method of their remote repository object. """ - tags = {'mytag': 'mytag-id'} + tags = {"mytag": "mytag-id"} def add_tag(name, raw_id, *args, **kwds): tags[name] = raw_id repo = backend_hg.repo.scm_instance() - with patch.object(repo, '_remote') as remote: + with patch.object(repo, "_remote") as remote: repo.tags = tags - remote.lookup.return_value = ('commit-id', 'commit-idx') + remote.lookup.return_value = ("commit-id", "commit-idx") remote.tags.return_value = tags remote._get_tags.return_value = tags remote.is_empty.return_value = False @@ -98,6 +98,7 @@ class TestMercurialRemoteRepoInvalidation(object): references. """ from rhodecode.model.pull_request import PullRequestModel + repo_id = pull_request.target_repo.repo_id target_vcs = pull_request.target_repo.scm_instance() target_ref = pull_request.target_ref_parts @@ -107,7 +108,8 @@ class TestMercurialRemoteRepoInvalidation(object): pr = PullRequestModel() workspace_id = pr._workspace_id(pull_request) shadow_repository_path = target_vcs._maybe_prepare_merge_workspace( - repo_id, workspace_id, target_ref, source_ref) + repo_id, workspace_id, target_ref, source_ref + ) shadow_repo = target_vcs.get_shadow_instance(shadow_repository_path, cache=True) # This will populate the cache of the mercurial repository object @@ -116,7 +118,7 @@ class TestMercurialRemoteRepoInvalidation(object): return shadow_repo, source_ref, target_ref - @pytest.mark.backends('hg') + @pytest.mark.backends("hg") def test_commit_does_not_exist_error_happens(self, pr_util, app): """ This test is somewhat special. It does not really test the system @@ -132,18 +134,18 @@ class TestMercurialRemoteRepoInvalidation(object): source_vcs = pull_request.source_repo.scm_instance() shadow_repo, source_ref, target_ref = self._prepare_shadow_repo(pull_request) - initial_cache_uid = shadow_repo._remote._wire['context'] - initial_commit_ids = shadow_repo._remote.get_all_commit_ids('visible') + initial_cache_uid = shadow_repo._remote._wire["context"] + initial_commit_ids = shadow_repo._remote.get_all_commit_ids("visible") # Pull from target and source references but without invalidation of # RemoteRepo objects and without VCSServer caching of mercurial repository objects. - with patch.object(shadow_repo._remote, 'invalidate_vcs_cache'): + with patch.object(shadow_repo._remote, "invalidate_vcs_cache"): # NOTE: Do not use patch.dict() to disable the cache because it # restores the WHOLE dict and not only the patched keys. - shadow_repo._remote._wire['cache'] = False + shadow_repo._remote._wire["cache"] = False shadow_repo._local_pull(target_vcs.path, target_ref) shadow_repo._local_pull(source_vcs.path, source_ref) - shadow_repo._remote._wire['cache'] = True + shadow_repo._remote._wire["cache"] = True # Try to lookup the target_ref in shadow repo. This should work because # test_repo_maker_uses_session_for_instance_methods @@ -153,14 +155,14 @@ class TestMercurialRemoteRepoInvalidation(object): # we ensure that call context has not changed, this is what # `invalidate_vcs_cache` does - assert initial_cache_uid == shadow_repo._remote._wire['context'] + assert initial_cache_uid == shadow_repo._remote._wire["context"] # If we try to lookup all commits. # repo commit cache doesn't get invalidated. (Due to patched # invalidation and caching above). - assert initial_commit_ids == shadow_repo._remote.get_all_commit_ids('visible') + assert initial_commit_ids == shadow_repo._remote.get_all_commit_ids("visible") - @pytest.mark.backends('hg') + @pytest.mark.backends("hg") def test_commit_does_not_exist_error_does_not_happen(self, pr_util, app): """ This test simulates a pull request merge in which the pull operations @@ -179,10 +181,10 @@ class TestMercurialRemoteRepoInvalidation(object): # of RemoteRepo objects. # NOTE: Do not use patch.dict() to disable the cache because it # restores the WHOLE dict and not only the patched keys. - shadow_repo._remote._wire['cache'] = False + shadow_repo._remote._wire["cache"] = False shadow_repo._local_pull(target_vcs.path, target_ref) shadow_repo._local_pull(source_vcs.path, source_ref) - shadow_repo._remote._wire['cache'] = True + shadow_repo._remote._wire["cache"] = True # Try to lookup the target and source references in shadow repo. This # should work because the RemoteRepo object gets invalidated during the diff --git a/rhodecode/tests/vcs/test_inmemory.py b/rhodecode/tests/vcs/test_inmemory.py index 632d0638..e28bed33 100644 --- a/rhodecode/tests/vcs/test_inmemory.py +++ b/rhodecode/tests/vcs/test_inmemory.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -20,15 +19,21 @@ """ Tests so called "in memory commits" commit API of vcs. """ + import datetime import pytest from rhodecode.lib.str_utils import safe_bytes, safe_str from rhodecode.lib.vcs.exceptions import ( - EmptyRepositoryError, NodeAlreadyAddedError, NodeAlreadyExistsError, - NodeAlreadyRemovedError, NodeAlreadyChangedError, NodeDoesNotExistError, - NodeNotChangedError) + EmptyRepositoryError, + NodeAlreadyAddedError, + NodeAlreadyExistsError, + NodeAlreadyRemovedError, + NodeAlreadyChangedError, + NodeDoesNotExistError, + NodeNotChangedError, +) from rhodecode.lib.vcs.nodes import DirNode, FileNode from rhodecode.tests.vcs.conftest import BackendTestMixin @@ -36,19 +41,21 @@ from rhodecode.tests.vcs.conftest import BackendTestMixin @pytest.fixture() def nodes(): nodes = [ - FileNode(b'foobar', content=b'Foo & bar'), - FileNode(b'foobar2', content=b'Foo & bar, doubled!'), - FileNode(b'foo bar with spaces', content=b''), - FileNode(b'foo/bar/baz', content=b'Inside'), - FileNode(b'foo/bar/file.bin', content=( - b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x03\x00\xfe' - b'\xff\t\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x01\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00' - b'\x00\x18\x00\x00\x00\x01\x00\x00\x00\xfe\xff\xff\xff\x00\x00' - b'\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff' - b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' - ) + FileNode(b"foobar", content=b"Foo & bar"), + FileNode(b"foobar2", content=b"Foo & bar, doubled!"), + FileNode(b"foo bar with spaces", content=b""), + FileNode(b"foo/bar/baz", content=b"Inside"), + FileNode( + b"foo/bar/file.bin", + content=( + b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x03\x00\xfe" + b"\xff\t\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x01\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00" + b"\x00\x18\x00\x00\x00\x01\x00\x00\x00\xfe\xff\xff\xff\x00\x00" + b"\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + ), ), ] return nodes @@ -80,14 +87,14 @@ class TestInMemoryCommit(BackendTestMixin): def test_add_on_branch_hg(self, nodes): for node in nodes: self.imc.add(node) - self.commit(branch='stable') + self.commit(branch="stable") self.assert_successful_commit(nodes) @pytest.mark.backends("git") def test_add_on_branch_git(self, nodes): for node in nodes: self.imc.add(node) - self.commit(branch='stable') + self.commit(branch="stable") self.assert_successful_commit(nodes) def test_add_in_bulk(self, nodes): @@ -98,10 +105,8 @@ class TestInMemoryCommit(BackendTestMixin): def test_add_non_ascii_files(self): nodes = [ - FileNode(safe_bytes('żółwik/zwierzątko_utf8_str'), - content=safe_bytes('ćććć')), - FileNode(safe_bytes('żółwik/zwierzątko_unicode'), - content=safe_bytes('ćććć')), + FileNode(safe_bytes("żółwik/zwierzątko_utf8_str"), content=safe_bytes("ćććć")), + FileNode(safe_bytes("żółwik/zwierzątko_unicode"), content=safe_bytes("ćććć")), ] for node in nodes: @@ -112,60 +117,56 @@ class TestInMemoryCommit(BackendTestMixin): def commit(self, branch=None): self.old_commit_count = len(self.repo.commit_ids) - self.commit_message = 'Test commit with unicode: żółwik' - self.commit_author = f'{self.__class__.__name__} ' - self.commit = self.imc.commit( - message=self.commit_message, author=self.commit_author, - branch=branch) + self.commit_message = "Test commit with unicode: żółwik" + self.commit_author = f"{self.__class__.__name__} " + self.commit = self.imc.commit(message=self.commit_message, author=self.commit_author, branch=branch) def test_add_actually_adds_all_nodes_at_second_commit_too(self): to_add = [ - FileNode(b'foo/bar/image.png', content=b'\0'), - FileNode(b'foo/README.txt', content=b'readme!'), + FileNode(b"foo/bar/image.png", content=b"\0"), + FileNode(b"foo/README.txt", content=b"readme!"), ] self.imc.add(*to_add) - commit = self.imc.commit('Initial', 'joe doe ') - assert isinstance(commit.get_node('foo'), DirNode) - assert isinstance(commit.get_node('foo/bar'), DirNode) + commit = self.imc.commit("Initial", "joe doe ") + assert isinstance(commit.get_node("foo"), DirNode) + assert isinstance(commit.get_node("foo/bar"), DirNode) self.assert_nodes_in_commit(commit, to_add) # commit some more files again to_add = [ - FileNode(b'foo/bar/foobaz/bar', content=b'foo'), - FileNode(b'foo/bar/another/bar', content=b'foo'), - FileNode(b'foo/baz.txt', content=b'foo'), - FileNode(b'foobar/foobaz/file', content=b'foo'), - FileNode(b'foobar/barbaz', content=b'foo'), + FileNode(b"foo/bar/foobaz/bar", content=b"foo"), + FileNode(b"foo/bar/another/bar", content=b"foo"), + FileNode(b"foo/baz.txt", content=b"foo"), + FileNode(b"foobar/foobaz/file", content=b"foo"), + FileNode(b"foobar/barbaz", content=b"foo"), ] self.imc.add(*to_add) - commit = self.imc.commit('Another', 'joe doe ') + commit = self.imc.commit("Another", "joe doe ") self.assert_nodes_in_commit(commit, to_add) def test_add_raise_already_added(self): - node = FileNode(b'foobar', content=b'baz') + node = FileNode(b"foobar", content=b"baz") self.imc.add(node) with pytest.raises(NodeAlreadyAddedError): self.imc.add(node) def test_check_integrity_raise_already_exist(self): - node = FileNode(b'foobar', content=b'baz') + node = FileNode(b"foobar", content=b"baz") self.imc.add(node) - self.imc.commit(message='Added foobar', - author='Some Name ') + self.imc.commit(message="Added foobar", author="Some Name ") self.imc.add(node) with pytest.raises(NodeAlreadyExistsError): - self.imc.commit(message='new message', - author='Some Name ') + self.imc.commit(message="new message", author="Some Name ") def test_change(self): - self.imc.add(FileNode(b'foo/bar/baz', content=b'foo')) - self.imc.add(FileNode(b'foo/fbar', content=b'foobar')) - tip = self.imc.commit('Initial', 'joe doe ') + self.imc.add(FileNode(b"foo/bar/baz", content=b"foo")) + self.imc.add(FileNode(b"foo/fbar", content=b"foobar")) + tip = self.imc.commit("Initial", "joe doe ") # Change node's content - node = FileNode(b'foo/bar/baz', content=b'My **changed** content') + node = FileNode(b"foo/bar/baz", content=b"My **changed** content") self.imc.change(node) - self.imc.commit('Changed %s' % node.path, 'joe doe ') + self.imc.commit("Changed %s" % node.path, "joe doe ") newtip = self.repo.get_commit() assert tip != newtip @@ -174,28 +175,22 @@ class TestInMemoryCommit(BackendTestMixin): def test_change_non_ascii(self): to_add = [ - FileNode(safe_bytes('żółwik/zwierzątko'), - content=safe_bytes('ćććć')), - FileNode(safe_bytes('żółwik/zwierzątko_uni'), - content=safe_bytes('ćććć')), + FileNode(safe_bytes("żółwik/zwierzątko"), content=safe_bytes("ćććć")), + FileNode(safe_bytes("żółwik/zwierzątko_uni"), content=safe_bytes("ćććć")), ] for node in to_add: self.imc.add(node) - tip = self.imc.commit('Initial', 'joe doe ') + tip = self.imc.commit("Initial", "joe doe ") # Change node's content - node = FileNode(safe_bytes('żółwik/zwierzątko'), - content=b'My **changed** content') + node = FileNode(safe_bytes("żółwik/zwierzątko"), content=b"My **changed** content") self.imc.change(node) - self.imc.commit('Changed %s' % safe_str(node.path), - author='joe doe ') + self.imc.commit("Changed %s" % safe_str(node.path), author="joe doe ") - node_uni = FileNode(safe_bytes('żółwik/zwierzątko_uni'), - content=b'My **changed** content') + node_uni = FileNode(safe_bytes("żółwik/zwierzątko_uni"), content=b"My **changed** content") self.imc.change(node_uni) - self.imc.commit('Changed %s' % safe_str(node_uni.path), - author='joe doe ') + self.imc.commit("Changed %s" % safe_str(node_uni.path), author="joe doe ") newtip = self.repo.get_commit() assert tip != newtip @@ -204,24 +199,24 @@ class TestInMemoryCommit(BackendTestMixin): self.assert_nodes_in_commit(newtip, (node, node_uni)) def test_change_raise_empty_repository(self): - node = FileNode(b'foobar') + node = FileNode(b"foobar") with pytest.raises(EmptyRepositoryError): self.imc.change(node) def test_check_integrity_change_raise_node_does_not_exist(self): - node = FileNode(b'foobar', content=b'baz') + node = FileNode(b"foobar", content=b"baz") self.imc.add(node) - self.imc.commit(message='Added foobar', author='Some Name ') - node = FileNode(b'not-foobar', content=b'') + self.imc.commit(message="Added foobar", author="Some Name ") + node = FileNode(b"not-foobar", content=b"") self.imc.change(node) with pytest.raises(NodeDoesNotExistError): - self.imc.commit(message='Changed not existing node', author='Some Name ') + self.imc.commit(message="Changed not existing node", author="Some Name ") def test_change_raise_node_already_changed(self): - node = FileNode(b'foobar', content=b'baz') + node = FileNode(b"foobar", content=b"baz") self.imc.add(node) - self.imc.commit(message='Added foobar', author='Some Nam ') - node = FileNode(b'foobar', content=b'more baz') + self.imc.commit(message="Added foobar", author="Some Nam ") + node = FileNode(b"foobar", content=b"more baz") self.imc.change(node) with pytest.raises(NodeAlreadyChangedError): self.imc.change(node) @@ -233,14 +228,14 @@ class TestInMemoryCommit(BackendTestMixin): self.imc.change(node) with pytest.raises(NodeNotChangedError): self.imc.commit( - message='Trying to mark node as changed without touching it', - author='Some Name ') + message="Trying to mark node as changed without touching it", author="Some Name " + ) def test_change_raise_node_already_removed(self): - node = FileNode(b'foobar', content=b'baz') + node = FileNode(b"foobar", content=b"baz") self.imc.add(node) - self.imc.commit(message='Added foobar', author='Some Name ') - self.imc.remove(FileNode(b'foobar')) + self.imc.commit(message="Added foobar", author="Some Name ") + self.imc.remove(FileNode(b"foobar")) with pytest.raises(NodeAlreadyRemovedError): self.imc.change(node) @@ -251,7 +246,7 @@ class TestInMemoryCommit(BackendTestMixin): node = nodes[0] assert node.content == tip.get_node(node.path).content self.imc.remove(node) - self.imc.commit(message=f'Removed {node.path}', author='Some Name ') + self.imc.commit(message=f"Removed {node.path}", author="Some Name ") newtip = self.repo.get_commit() assert tip != newtip @@ -260,31 +255,27 @@ class TestInMemoryCommit(BackendTestMixin): newtip.get_node(node.path) def test_remove_last_file_from_directory(self): - node = FileNode(b'omg/qwe/foo/bar', content=b'foobar') + node = FileNode(b"omg/qwe/foo/bar", content=b"foobar") self.imc.add(node) - self.imc.commit('added', author='joe doe ') + self.imc.commit("added", author="joe doe ") self.imc.remove(node) - tip = self.imc.commit('removed', 'joe doe ') + tip = self.imc.commit("removed", "joe doe ") with pytest.raises(NodeDoesNotExistError): - tip.get_node('omg/qwe/foo/bar') + tip.get_node("omg/qwe/foo/bar") def test_remove_raise_node_does_not_exist(self, nodes): self.imc.remove(nodes[0]) with pytest.raises(NodeDoesNotExistError): - self.imc.commit( - message='Trying to remove node at empty repository', - author='Some Name ') + self.imc.commit(message="Trying to remove node at empty repository", author="Some Name ") def test_check_integrity_remove_raise_node_does_not_exist(self, nodes): self.test_add(nodes) # Performs first commit - node = FileNode(b'no-such-file') + node = FileNode(b"no-such-file") self.imc.remove(node) with pytest.raises(NodeDoesNotExistError): - self.imc.commit( - message='Trying to remove not existing node', - author='Some Name ') + self.imc.commit(message="Trying to remove not existing node", author="Some Name ") def test_remove_raise_node_already_removed(self, nodes): self.test_add(nodes) # Performs first commit @@ -297,13 +288,13 @@ class TestInMemoryCommit(BackendTestMixin): def test_remove_raise_node_already_changed(self, nodes): self.test_add(nodes) # Performs first commit - node = FileNode(nodes[0].bytes_path, content=b'Bending time') + node = FileNode(nodes[0].bytes_path, content=b"Bending time") self.imc.change(node) with pytest.raises(NodeAlreadyChangedError): self.imc.remove(node) def test_reset(self): - self.imc.add(FileNode(b'foo', content=b'bar')) + self.imc.add(FileNode(b"foo", content=b"bar")) # self.imc.change(FileNode(b'baz', content='new')) # self.imc.remove(FileNode(b'qwe')) self.imc.reset() @@ -313,11 +304,11 @@ class TestInMemoryCommit(BackendTestMixin): N = 3 # number of commits to perform last = None for x in range(N): - fname = safe_bytes('file%s' % str(x).rjust(5, '0')) - content = safe_bytes('foobar\n' * x) + fname = safe_bytes("file%s" % str(x).rjust(5, "0")) + content = safe_bytes("foobar\n" * x) node = FileNode(fname, content=content) self.imc.add(node) - commit = self.imc.commit("Commit no. %s" % (x + 1), author='Vcs User ') + commit = self.imc.commit("Commit no. %s" % (x + 1), author="Vcs User ") assert last != commit last = commit @@ -329,12 +320,10 @@ class TestInMemoryCommit(BackendTestMixin): assert len(repo.commit_ids) == N def test_date_attr(self, local_dt_to_utc): - node = FileNode(b'foobar.txt', content=b'Foobared!') + node = FileNode(b"foobar.txt", content=b"Foobared!") self.imc.add(node) date = datetime.datetime(1985, 1, 30, 1, 45) - commit = self.imc.commit( - "Committed at time when I was born ;-)", - author='Test User ', date=date) + commit = self.imc.commit("Committed at time when I was born ;-)", author="Test User ", date=date) assert commit.date == local_dt_to_utc(date) diff --git a/rhodecode/tests/vcs/test_nodes.py b/rhodecode/tests/vcs/test_nodes.py index 5a3d5207..e745816e 100644 --- a/rhodecode/tests/vcs/test_nodes.py +++ b/rhodecode/tests/vcs/test_nodes.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -38,34 +37,34 @@ def binary_filenode(): b"\x10\x08\x06\x00\x00\x00\x1f??a\x00\x00\x00\x04gAMA\x00\x00\xaf?7" b"\x05\x8a?\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq?e<\x00" b"\x00\x025IDAT8?\xa5\x93?K\x94Q\x14\x87\x9f\xf7?Q\x1bs4?\x03\x9a" - b"\xa8?B\x02\x8b$\x10[U;i\x13?6h?&h[?\"\x14j?\xa2M\x7fB\x14F\x9aQ?&" - b"\x842?\x0b\x89\"\x82??!?\x9c!\x9c2l??{N\x8bW\x9dY\xb4\t/\x1c?=" - b"\x9b?}????\xa9*;9!?\x83\x91?[?\\v*?D\x04\'`EpNp\xa2X\'U?pVq\"Sw." + b'\xa8?B\x02\x8b$\x10[U;i\x13?6h?&h[?"\x14j?\xa2M\x7fB\x14F\x9aQ?&' + b'\x842?\x0b\x89"\x82??!?\x9c!\x9c2l??{N\x8bW\x9dY\xb4\t/\x1c?=' + b"\x9b?}????\xa9*;9!?\x83\x91?[?\\v*?D\x04'`EpNp\xa2X'U?pVq\"Sw." b"\x1e?\x08\x01D?jw????\xbc??7{|\x9b?\x89$\x01??W@\x15\x9c\x05q`Lt/" b"\x97?\x94\xa1d?\x18~?\x18?\x18W[%\xb0?\x83??\x14\x88\x8dB?\xa6H" - b"\tL\tl\x19>/\x01`\xac\xabx?\x9cl\nx\xb0\x98\x07\x95\x88D$\"q[" + b'\tL\tl\x19>/\x01`\xac\xabx?\x9cl\nx\xb0\x98\x07\x95\x88D$"q[' b"\x19?d\x00(o\n\xa0??\x7f\xb9\xa4?\x1bF\x1f\x8e\xac\xa8?j??eUU}?.?" - b"\x9f\x8cE??x\x94??\r\xbdtoJU5\"0N\x10U?\x00??V\t\x02\x9f\x81?U?" - b"\x00\x9eM\xae2?r\x9b7\x83\x82\x8aP3????.?&\"?\xb7ZP \x0cJ?\x80\x15T\x95\x9a\x00??S\x8c\r?\xa1" b"\x03\x07?\x96\x9b\xa7\xab=E??\xa4\xb3?\x19q??B\x91=\x8d??k?J" - b"\x0bV\"??\xf7x?\xa1\x00?\\.\x87\x87???\x02F@D\x99],??\x10#?X" + b'\x0bV"??\xf7x?\xa1\x00?\\.\x87\x87???\x02F@D\x99],??\x10#?X' b"\xb7=\xb9\x10?Z\x1by???cI??\x1ag?\x92\xbc?T?t[\x92\x81?<_\x17~" b"\x92\x88?H%?\x10Q\x02\x9f\n\x81qQ\x0bm?\x1bX?\xb1AK\xa6\x9e\xb9?u" - b"\xb2?1\xbe|/\x92M@\xa2!F?\xa9>\"\r\x92\x8e?>\x9a9Qv\x127?a" + b'\xb2?1\xbe|/\x92M@\xa2!F?\xa9>"\r\x92\x8e?>\x9a9Qv\x127?a' b"\xac?Y?8?:??]X???9\x80\xb7?u?\x0b#BZ\x8d=\x1d?p\x00\x00\x00\x00" - b"IEND\xaeB`\x82") + b"IEND\xaeB`\x82" + ) return FileNode(filename, content=data) + return node_maker class TestNodeBasics: - - @pytest.mark.parametrize("path", ['/foo', '/foo/bar']) - @pytest.mark.parametrize( - "kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"]) + @pytest.mark.parametrize("path", ["/foo", "/foo/bar"]) + @pytest.mark.parametrize("kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"]) def test_init_wrong_paths(self, path, kind): """ Cannot initialize Node objects with path with slash at the beginning. @@ -74,44 +73,46 @@ class TestNodeBasics: with pytest.raises(NodeError): Node(path, kind) - @pytest.mark.parametrize("path", ['path', 'some/path']) - @pytest.mark.parametrize( - "kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"]) + @pytest.mark.parametrize("path", ["path", "some/path"]) + @pytest.mark.parametrize("kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"]) def test_name(self, path, kind): path = safe_bytes(path) node = Node(path, kind) - assert node.name == 'path' + assert node.name == "path" def test_name_root(self): - node = Node(b'', NodeKind.DIR) - assert node.name == '' + node = Node(b"", NodeKind.DIR) + assert node.name == "" def test_root_node_cannot_be_file(self): with pytest.raises(NodeError): - Node(b'', NodeKind.FILE) + Node(b"", NodeKind.FILE) def test_kind_setter(self): - node = Node(b'', NodeKind.DIR) + node = Node(b"", NodeKind.DIR) with pytest.raises(NodeError): node.kind = NodeKind.FILE def test_compare_equal(self): - node1 = FileNode(b'test', content=b'') - node2 = FileNode(b'test', content=b'') + node1 = FileNode(b"test", content=b"") + node2 = FileNode(b"test", content=b"") assert node1 == node2 assert not node1 != node2 def test_compare_unequal(self): - node1 = FileNode(b'test', content=b'a') - node2 = FileNode(b'test', content=b'b') + node1 = FileNode(b"test", content=b"a") + node2 = FileNode(b"test", content=b"b") assert node1 != node2 assert not node1 == node2 - @pytest.mark.parametrize("node_path, expected_parent_path", [ - ('', b''), - ('some/path/', b'some/'), - ('some/longer/path/', b'some/longer/'), - ]) + @pytest.mark.parametrize( + "node_path, expected_parent_path", + [ + ("", b""), + ("some/path/", b"some/"), + ("some/longer/path/", b"some/longer/"), + ], + ) def test_parent_path_new(self, node_path, expected_parent_path): """ Tests if node's parent path are properly computed. @@ -119,11 +120,10 @@ class TestNodeBasics: node_path = safe_bytes(node_path) node = Node(node_path, NodeKind.DIR) parent_path = node.get_parent_path() - assert (parent_path.endswith(b'/') or - node.is_root() and parent_path == b'') + assert parent_path.endswith(b"/") or node.is_root() and parent_path == b"" assert parent_path == expected_parent_path - ''' + """ def _test_trailing_slash(self, path): if not path.endswith('/'): pytest.fail("Trailing slash tests needs paths to end with slash") @@ -134,22 +134,22 @@ class TestNodeBasics: def test_trailing_slash(self): for path in ('/', 'foo/', 'foo/bar/', 'foo/bar/biz/'): self._test_trailing_slash(path) - ''' + """ def test_is_file(self): - node = Node(b'any', NodeKind.FILE) + node = Node(b"any", NodeKind.FILE) assert node.is_file() - node = FileNode(b'any') + node = FileNode(b"any") assert node.is_file() with pytest.raises(AttributeError): node.nodes # noqa def test_is_dir(self): - node = Node(b'any_dir', NodeKind.DIR) + node = Node(b"any_dir", NodeKind.DIR) assert node.is_dir() - node = DirNode(b'any_dir') + node = DirNode(b"any_dir") assert node.is_dir() with pytest.raises(NodeError): @@ -157,14 +157,14 @@ class TestNodeBasics: def test_dir_node_iter(self): nodes = [ - DirNode(b'docs'), - DirNode(b'tests'), - FileNode(b'bar'), - FileNode(b'foo'), - FileNode(b'readme.txt'), - FileNode(b'setup.py'), + DirNode(b"docs"), + DirNode(b"tests"), + FileNode(b"bar"), + FileNode(b"foo"), + FileNode(b"readme.txt"), + FileNode(b"setup.py"), ] - dirnode = DirNode(b'', nodes=nodes) + dirnode = DirNode(b"", nodes=nodes) for node in dirnode: assert node == dirnode.get_node(node.path) @@ -172,15 +172,15 @@ class TestNodeBasics: """ Without link to commit nodes should raise NodeError. """ - node = FileNode(b'anything') + node = FileNode(b"anything") with pytest.raises(NodeError): node.state # noqa - node = DirNode(b'anything') + node = DirNode(b"anything") with pytest.raises(NodeError): node.state # noqa def test_file_node_stat(self): - node = FileNode(b'foobar', b'empty... almost') + node = FileNode(b"foobar", b"empty... almost") mode = node.mode # default should be 0100644 assert mode & stat.S_IRUSR assert mode & stat.S_IWUSR @@ -193,36 +193,36 @@ class TestNodeBasics: assert not mode & stat.S_IXOTH def test_file_node_is_executable(self): - node = FileNode(b'foobar', b'empty... almost', mode=0o100755) + node = FileNode(b"foobar", b"empty... almost", mode=0o100755) assert node.is_executable - node = FileNode(b'foobar', b'empty... almost', mode=0o100500) + node = FileNode(b"foobar", b"empty... almost", mode=0o100500) assert node.is_executable - node = FileNode(b'foobar', b'empty... almost', mode=0o100644) + node = FileNode(b"foobar", b"empty... almost", mode=0o100644) assert not node.is_executable def test_file_node_is_not_symlink(self): - node = FileNode(b'foobar', b'empty...') + node = FileNode(b"foobar", b"empty...") assert not node.is_link() def test_mimetype(self): - py_node = FileNode(b'test.py') - tar_node = FileNode(b'test.tar.gz') + py_node = FileNode(b"test.py") + tar_node = FileNode(b"test.tar.gz") - ext = 'CustomExtension' + ext = "CustomExtension" - my_node2 = FileNode(b'myfile2') + my_node2 = FileNode(b"myfile2") my_node2._mimetype = [ext] - my_node3 = FileNode(b'myfile3') + my_node3 = FileNode(b"myfile3") my_node3._mimetype = [ext, ext] - assert py_node.mimetype == 'text/x-python' - assert py_node.get_mimetype() == ('text/x-python', None) + assert py_node.mimetype == "text/x-python" + assert py_node.get_mimetype() == ("text/x-python", None) - assert tar_node.mimetype == 'application/x-tar' - assert tar_node.get_mimetype() == ('application/x-tar', 'gzip') + assert tar_node.mimetype == "application/x-tar" + assert tar_node.get_mimetype() == ("application/x-tar", "gzip") with pytest.raises(NodeError): my_node2.get_mimetype() @@ -232,47 +232,45 @@ class TestNodeBasics: def test_lines_counts(self): lines = [ - b'line1\n', - b'line2\n', - b'line3\n', - b'\n', - b'\n', - b'line4\n', + b"line1\n", + b"line2\n", + b"line3\n", + b"\n", + b"\n", + b"line4\n", ] - py_node = FileNode(b'test.py', b''.join(lines)) + py_node = FileNode(b"test.py", b"".join(lines)) assert (len(lines), len(lines)) == py_node.lines() assert (len(lines), len(lines) - 2) == py_node.lines(count_empty=True) def test_lines_no_newline(self): - py_node = FileNode(b'test.py', b'oneline') + py_node = FileNode(b"test.py", b"oneline") assert (1, 1) == py_node.lines() assert (1, 1) == py_node.lines(count_empty=True) class TestNodeContent(object): - def test_if_binary(self, binary_filenode): - filenode = binary_filenode(b'calendar.jpg') + filenode = binary_filenode(b"calendar.jpg") assert filenode.is_binary def test_binary_line_counts(self, binary_filenode): - tar_node = binary_filenode(b'archive.tar.gz') + tar_node = binary_filenode(b"archive.tar.gz") assert (0, 0) == tar_node.lines(count_empty=True) def test_binary_mimetype(self, binary_filenode): - tar_node = binary_filenode(b'archive.tar.gz') - assert tar_node.mimetype == 'application/x-tar' + tar_node = binary_filenode(b"archive.tar.gz") + assert tar_node.mimetype == "application/x-tar" @pytest.mark.usefixtures("vcs_repository_support") class TestNodesCommits(BackendTestMixin): - def test_node_last_commit(self, generate_repo_with_commits): repo = generate_repo_with_commits(20) last_commit = repo.get_commit() for x in range(3): - node = last_commit.get_node(f'file_{x}.txt') + node = last_commit.get_node(f"file_{x}.txt") assert node.last_commit == repo[x] diff --git a/rhodecode/tests/vcs/test_repository.py b/rhodecode/tests/vcs/test_repository.py index f1dc3a81..2ba82e49 100644 --- a/rhodecode/tests/vcs/test_repository.py +++ b/rhodecode/tests/vcs/test_repository.py @@ -114,9 +114,7 @@ class TestRepositoryBase(BackendTestMixin): assert len(self.repo.get_hook_location()) != 0 def test_last_change(self, local_dt_to_utc): - assert self.repo.last_change >= local_dt_to_utc( - datetime.datetime(2010, 1, 1, 21, 0) - ) + assert self.repo.last_change >= local_dt_to_utc(datetime.datetime(2010, 1, 1, 21, 0)) def test_last_change_in_empty_repository(self, vcsbackend, local_dt_to_utc): delta = datetime.timedelta(seconds=1) @@ -195,9 +193,7 @@ class TestRepositoryCompare: @pytest.mark.parametrize("merge", [True, False]) def test_compare_commits_of_same_repository(self, vcsbackend, merge): target_repo = vcsbackend.create_repo(number_of_commits=5) - target_repo.compare( - target_repo[1].raw_id, target_repo[3].raw_id, target_repo, merge=merge - ) + target_repo.compare(target_repo[1].raw_id, target_repo[3].raw_id, target_repo, merge=merge) @pytest.mark.xfail_backends("svn") @pytest.mark.parametrize("merge", [True, False]) @@ -209,9 +205,7 @@ class TestRepositoryCompare: vcsbackend.add_file(source_repo, b"newfile", b"somecontent") source_commit = source_repo.get_commit() - target_repo.compare( - target_repo[1].raw_id, source_repo[3].raw_id, source_repo, merge=merge - ) + target_repo.compare(target_repo[1].raw_id, source_repo[3].raw_id, source_repo, merge=merge) @pytest.mark.xfail_backends("svn") @pytest.mark.parametrize("merge", [True, False]) @@ -351,9 +345,7 @@ class TestRepositoryMerge(object): "merge message 1", dry_run=False, ) - expected_merge_response = MergeResponse( - True, True, merge_response.merge_ref, MergeFailureReason.NONE - ) + expected_merge_response = MergeResponse(True, True, merge_response.merge_ref, MergeFailureReason.NONE) assert merge_response == expected_merge_response target_repo = backends.get_backend(vcsbackend.alias)(self.target_repo.path) @@ -385,9 +377,7 @@ class TestRepositoryMerge(object): "merge message 2", dry_run=False, ) - expected_merge_response = MergeResponse( - True, True, merge_response.merge_ref, MergeFailureReason.NONE - ) + expected_merge_response = MergeResponse(True, True, merge_response.merge_ref, MergeFailureReason.NONE) assert merge_response == expected_merge_response target_repo = backends.get_backend(vcsbackend.alias)(self.target_repo.path) @@ -422,9 +412,9 @@ class TestRepositoryMerge(object): # Multiple merges may differ in their commit id. Therefore, we set the # commit id to `None` before comparing the merge responses. - merge_response.merge_ref.commit_id = 'abcdeabcde' + merge_response.merge_ref.commit_id = "abcdeabcde" - merge_response_update.merge_ref.commit_id = 'abcdeabcde' + merge_response_update.merge_ref.commit_id = "abcdeabcde" assert merge_response == merge_response_update assert merge_response.possible is True @@ -436,9 +426,7 @@ class TestRepositoryMerge(object): def test_merge_conflict(self, vcsbackend, dry_run): self.prepare_for_conflict(vcsbackend) - expected_merge_response = MergeResponse( - False, False, None, MergeFailureReason.MERGE_FAILED - ) + expected_merge_response = MergeResponse(False, False, None, MergeFailureReason.MERGE_FAILED) merge_response = self.target_repo.merge( self.repo_id, @@ -491,9 +479,7 @@ class TestRepositoryMerge(object): def test_merge_missing_source_reference(self, vcsbackend): self.prepare_for_success(vcsbackend) - source_ref = Reference( - self.source_ref.type, "not_existing", self.source_ref.commit_id - ) + source_ref = Reference(self.source_ref.type, "not_existing", self.source_ref.commit_id) expected_merge_response = MergeResponse( False, False, @@ -523,9 +509,7 @@ class TestRepositoryMerge(object): metadata={"exception": "ErrorForTest"}, ) - with mock.patch.object( - self.target_repo, "_merge_repo", side_effect=RepositoryError() - ): + with mock.patch.object(self.target_repo, "_merge_repo", side_effect=RepositoryError()): merge_response = self.target_repo.merge( self.repo_id, self.workspace_id, @@ -559,9 +543,7 @@ class TestRepositoryMerge(object): workspace_id = "test-errors-in-merge" repo_id = repo_id_generator(workspace_id) with pytest.raises(ValueError): - repo.merge( - repo_id, workspace_id, ref, self, ref, "user name", "user@email.com" - ) + repo.merge(repo_id, workspace_id, ref, self, ref, "user name", "user@email.com") @pytest.mark.usefixtures("vcs_repository_support") diff --git a/rhodecode/tests/vcs/test_svn.py b/rhodecode/tests/vcs/test_svn.py index f4510b35..620c244f 100644 --- a/rhodecode/tests/vcs/test_svn.py +++ b/rhodecode/tests/vcs/test_svn.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -47,13 +46,13 @@ def head(repo): def test_init_fails_if_path_does_not_exist(): - path = os.path.join(TEST_DIR, 'i-do-not-exist') + path = os.path.join(TEST_DIR, "i-do-not-exist") with pytest.raises(VCSError): SubversionRepository(path) def test_init_fails_if_path_is_not_a_valid_repository(tmpdir): - path = str(tmpdir.mkdir('unicode ä')) + path = str(tmpdir.mkdir("unicode ä")) with pytest.raises(VCSError): SubversionRepository(path) @@ -61,17 +60,14 @@ def test_init_fails_if_path_is_not_a_valid_repository(tmpdir): def test_repo_clone(vcsbackend, reposerver): source = vcsbackend.create_repo(number_of_commits=3) reposerver.serve(source) - repo = SubversionRepository( - vcsbackend.new_repo_path(), - create=True, - src_url=reposerver.url) + repo = SubversionRepository(vcsbackend.new_repo_path(), create=True, src_url=reposerver.url) assert source.commit_ids == repo.commit_ids assert source[0].message == repo[0].message def test_latest_commit(head): - assert head.raw_id == '393' + assert head.raw_id == "393" def test_commit_description(head): @@ -79,18 +75,22 @@ def test_commit_description(head): def test_commit_author(head): - assert head.author == 'marcin' + assert head.author == "marcin" -@pytest.mark.parametrize("filename, content, mime_type", [ - (b'test.txt', b'Text content\n', None), - (b'test.bin', b'\0 binary \0', 'application/octet-stream'), -], ids=['text', 'binary']) +@pytest.mark.parametrize( + "filename, content, mime_type", + [ + (b"test.txt", b"Text content\n", None), + (b"test.bin", b"\0 binary \0", "application/octet-stream"), + ], + ids=["text", "binary"], +) def test_sets_mime_type_correctly(vcsbackend, filename, content, mime_type): repo = vcsbackend.create_repo() vcsbackend.ensure_file(filename, content) file_properties = repo._remote.node_properties(filename, 1) - assert file_properties.get('svn:mime-type') == mime_type + assert file_properties.get("svn:mime-type") == mime_type def test_slice_access(repo): @@ -100,7 +100,7 @@ def test_slice_access(repo): end = start + page_size - 1 commits = list(repo[start:end]) - assert [commit.raw_id for commit in commits] == ['1', '2', '3', '4'] + assert [commit.raw_id for commit in commits] == ["1", "2", "3", "4"] def test_walk_changelog_page(repo): @@ -110,14 +110,14 @@ def test_walk_changelog_page(repo): end = start + page_size - 1 commits = list(repo[start:end]) - changelog = [ - 'r%s, %s, %s' % (c.raw_id, c.author, c.message) for c in commits] + changelog = ["r%s, %s, %s" % (c.raw_id, c.author, c.message) for c in commits] expexted_messages = [ - 'r1, marcin, initial import', - 'r2, marcin, hg ignore', - 'r3, marcin, Pip standards refactor', - 'r4, marcin, Base repository few new functions added'] + "r1, marcin, initial import", + "r2, marcin, hg ignore", + "r3, marcin, Pip standards refactor", + "r4, marcin, Base repository few new functions added", + ] assert changelog == expexted_messages @@ -128,68 +128,68 @@ def test_read_full_file_tree(head): def test_topnode_files_attribute(head): - topnode = head.get_node('') + topnode = head.get_node("") topnode.files - - -@pytest.mark.parametrize("filename, content, branch, mime_type", [ - ('branches/plain/test.txt', b'Text content\n', 'plain', None), - ('branches/uniçö∂e/test.bin', b'\0 binary \0', 'uniçö∂e', 'application/octet-stream'), -], ids=['text', 'binary']) +@pytest.mark.parametrize( + "filename, content, branch, mime_type", + [ + ("branches/plain/test.txt", b"Text content\n", "plain", None), + ("branches/uniçö∂e/test.bin", b"\0 binary \0", "uniçö∂e", "application/octet-stream"), + ], + ids=["text", "binary"], +) def test_unicode_refs(vcsbackend, filename, content, branch, mime_type): filename = safe_bytes(filename) repo = vcsbackend.create_repo() vcsbackend.ensure_file(filename, content) - with mock.patch(("rhodecode.lib.vcs.backends.svn.repository" - ".SubversionRepository._patterns_from_section"), - return_value=['branches/*']): - assert f'branches/{branch}' in repo.branches + with mock.patch( + ("rhodecode.lib.vcs.backends.svn.repository" ".SubversionRepository._patterns_from_section"), + return_value=["branches/*"], + ): + assert f"branches/{branch}" in repo.branches def test_compatible_version(monkeypatch, vcsbackend): - monkeypatch.setattr(settings, 'SVN_COMPATIBLE_VERSION', 'pre-1.8-compatible') + monkeypatch.setattr(settings, "SVN_COMPATIBLE_VERSION", "pre-1.8-compatible") path = vcsbackend.new_repo_path() SubversionRepository(path, create=True) - with open(f'{path}/db/format') as f: + with open(f"{path}/db/format") as f: first_line = f.readline().strip() - assert first_line == '4' + assert first_line == "4" def test_invalid_compatible_version(monkeypatch, vcsbackend): - monkeypatch.setattr(settings, 'SVN_COMPATIBLE_VERSION', 'i-am-an-invalid-setting') + monkeypatch.setattr(settings, "SVN_COMPATIBLE_VERSION", "i-am-an-invalid-setting") path = vcsbackend.new_repo_path() with pytest.raises(Exception): SubversionRepository(path, create=True) class TestSVNCommit(object): - @pytest.fixture(autouse=True) def prepare(self, repo): self.repo = repo def test_file_history_from_commits(self): - node = self.repo[10].get_node('setup.py') + node = self.repo[10].get_node("setup.py") commit_ids = [commit.raw_id for commit in node.history] - assert ['8'] == commit_ids + assert ["8"] == commit_ids - node = self.repo[20].get_node('setup.py') + node = self.repo[20].get_node("setup.py") node_ids = [commit.raw_id for commit in node.history] - assert ['18', - '8'] == node_ids + assert ["18", "8"] == node_ids # special case we check history from commit that has this particular # file changed this means we check if it's included as well - node = self.repo.get_commit('18').get_node('setup.py') + node = self.repo.get_commit("18").get_node("setup.py") node_ids = [commit.raw_id for commit in node.history] - assert ['18', - '8'] == node_ids + assert ["18", "8"] == node_ids def test_repo_files_content_type(self): test_commit = self.repo.get_commit(commit_idx=100) - for node in test_commit.get_node('/'): + for node in test_commit.get_node("/"): if node.is_file(): assert type(node.content) == bytes assert type(node.str_content) == str diff --git a/rhodecode/tests/vcs/test_tags.py b/rhodecode/tests/vcs/test_tags.py index 72972991..b5774ccb 100644 --- a/rhodecode/tests/vcs/test_tags.py +++ b/rhodecode/tests/vcs/test_tags.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -20,8 +19,7 @@ import pytest from rhodecode.tests.vcs.conftest import BackendTestMixin -from rhodecode.lib.vcs.exceptions import ( - TagAlreadyExistError, TagDoesNotExistError) +from rhodecode.lib.vcs.exceptions import TagAlreadyExistError, TagDoesNotExistError pytestmark = pytest.mark.backends("git", "hg") @@ -29,11 +27,10 @@ pytestmark = pytest.mark.backends("git", "hg") @pytest.mark.usefixtures("vcs_repository_support") class TestTags(BackendTestMixin): - def test_new_tag(self): tip = self.repo.get_commit() tagsize = len(self.repo.tags) - tag = self.repo.tag('last-commit', 'joe', tip.raw_id) + tag = self.repo.tag("last-commit", "joe", tip.raw_id) assert len(self.repo.tags) == tagsize + 1 for top, __, __ in tip.walk(): @@ -41,29 +38,29 @@ class TestTags(BackendTestMixin): def test_tag_already_exist(self): tip = self.repo.get_commit() - self.repo.tag('last-commit', 'joe', tip.raw_id) + self.repo.tag("last-commit", "joe", tip.raw_id) with pytest.raises(TagAlreadyExistError): - self.repo.tag('last-commit', 'joe', tip.raw_id) + self.repo.tag("last-commit", "joe", tip.raw_id) commit = self.repo.get_commit(commit_idx=0) with pytest.raises(TagAlreadyExistError): - self.repo.tag('last-commit', 'jane', commit.raw_id) + self.repo.tag("last-commit", "jane", commit.raw_id) def test_remove_tag(self): tip = self.repo.get_commit() - self.repo.tag('last-commit', 'joe', tip.raw_id) + self.repo.tag("last-commit", "joe", tip.raw_id) tagsize = len(self.repo.tags) - self.repo.remove_tag('last-commit', user='evil joe') + self.repo.remove_tag("last-commit", user="evil joe") assert len(self.repo.tags) == tagsize - 1 def test_remove_tag_which_does_not_exist(self): with pytest.raises(TagDoesNotExistError): - self.repo.remove_tag('last-commit', user='evil joe') + self.repo.remove_tag("last-commit", user="evil joe") def test_name_with_slash(self): - self.repo.tag('19/10/11', 'joe') - assert '19/10/11' in self.repo.tags - self.repo.tag('rel.11', 'joe') - assert 'rel.11' in self.repo.tags + self.repo.tag("19/10/11", "joe") + assert "19/10/11" in self.repo.tags + self.repo.tag("rel.11", "joe") + assert "rel.11" in self.repo.tags diff --git a/rhodecode/tests/vcs/test_utils.py b/rhodecode/tests/vcs/test_utils.py index ccf87c29..0b79766b 100644 --- a/rhodecode/tests/vcs/test_utils.py +++ b/rhodecode/tests/vcs/test_utils.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -34,23 +33,20 @@ from rhodecode.lib.vcs.utils.paths import get_dirs_for_path @pytest.mark.usefixtures("baseapp") class TestPaths(object): - def _test_get_dirs_for_path(self, path, expected): """ Tests if get_dirs_for_path returns same as expected. """ expected = sorted(expected) result = sorted(get_dirs_for_path(path)) - assert result == expected, ( - "%s != %s which was expected result for path %s" - % (result, expected, path)) + assert result == expected, "%s != %s which was expected result for path %s" % (result, expected, path) def test_get_dirs_for_path(self): - path = 'foo/bar/baz/file' + path = "foo/bar/baz/file" paths_and_results = ( - ('foo/bar/baz/file', ['foo', 'foo/bar', 'foo/bar/baz']), - ('foo/bar/', ['foo', 'foo/bar']), - ('foo/bar', ['foo']), + ("foo/bar/baz/file", ["foo", "foo/bar", "foo/bar/baz"]), + ("foo/bar/", ["foo", "foo/bar"]), + ("foo/bar", ["foo"]), ) for path, expected in paths_and_results: self._test_get_dirs_for_path(path, expected) @@ -59,18 +55,17 @@ class TestPaths(object): new = tmpdir.strpath assert get_scms_for_path(new) == [] - os.mkdir(os.path.join(new, '.tux')) + os.mkdir(os.path.join(new, ".tux")) assert get_scms_for_path(new) == [] - os.mkdir(os.path.join(new, '.git')) - assert set(get_scms_for_path(new)) == set(['git']) + os.mkdir(os.path.join(new, ".git")) + assert set(get_scms_for_path(new)) == set(["git"]) - os.mkdir(os.path.join(new, '.hg')) - assert set(get_scms_for_path(new)) == set(['git', 'hg']) + os.mkdir(os.path.join(new, ".hg")) + assert set(get_scms_for_path(new)) == set(["git", "hg"]) class TestGetScm(object): - def test_existing_repository(self, vcs_repository_support): alias, repo = vcs_repository_support assert (alias, repo.path) == get_scm(repo.path) @@ -81,114 +76,101 @@ class TestGetScm(object): def test_get_scm_error_path(self): with pytest.raises(VCSError): - get_scm('err') + get_scm("err") def test_get_two_scms_for_path(self, tmpdir): multialias_repo_path = str(tmpdir) git_default_branch = GitRepository.DEFAULT_BRANCH_NAME - subprocess.check_call(['hg', 'init', multialias_repo_path]) - subprocess.check_call(['git', '-c', f'init.defaultBranch={git_default_branch}', 'init', multialias_repo_path]) + subprocess.check_call(["hg", "init", multialias_repo_path]) + subprocess.check_call(["git", "-c", f"init.defaultBranch={git_default_branch}", "init", multialias_repo_path]) with pytest.raises(VCSError): get_scm(multialias_repo_path) def test_ignores_svn_working_copy(self, tmpdir): - tmpdir.mkdir('.svn') + tmpdir.mkdir(".svn") with pytest.raises(VCSError): get_scm(tmpdir.strpath) class TestParseDatetime(object): - def test_datetime_text(self): - assert parse_datetime('2010-04-07 21:29:41') == \ - datetime.datetime(2010, 4, 7, 21, 29, 41) + assert parse_datetime("2010-04-07 21:29:41") == datetime.datetime(2010, 4, 7, 21, 29, 41) def test_no_seconds(self): - assert parse_datetime('2010-04-07 21:29') == \ - datetime.datetime(2010, 4, 7, 21, 29) + assert parse_datetime("2010-04-07 21:29") == datetime.datetime(2010, 4, 7, 21, 29) def test_date_only(self): - assert parse_datetime('2010-04-07') == \ - datetime.datetime(2010, 4, 7) + assert parse_datetime("2010-04-07") == datetime.datetime(2010, 4, 7) def test_another_format(self): - assert parse_datetime('04/07/10 21:29:41') == \ - datetime.datetime(2010, 4, 7, 21, 29, 41) + assert parse_datetime("04/07/10 21:29:41") == datetime.datetime(2010, 4, 7, 21, 29, 41) def test_now(self): - assert parse_datetime('now') - datetime.datetime.now() < \ - datetime.timedelta(seconds=1) + assert parse_datetime("now") - datetime.datetime.now() < datetime.timedelta(seconds=1) def test_today(self): today = datetime.date.today() - assert parse_datetime('today') == \ - datetime.datetime(*today.timetuple()[:3]) + assert parse_datetime("today") == datetime.datetime(*today.timetuple()[:3]) def test_yesterday(self): yesterday = datetime.date.today() - datetime.timedelta(days=1) - assert parse_datetime('yesterday') == \ - datetime.datetime(*yesterday.timetuple()[:3]) + assert parse_datetime("yesterday") == datetime.datetime(*yesterday.timetuple()[:3]) def test_tomorrow(self): tomorrow = datetime.date.today() + datetime.timedelta(days=1) args = tomorrow.timetuple()[:3] + (23, 59, 59) - assert parse_datetime('tomorrow') == datetime.datetime(*args) + assert parse_datetime("tomorrow") == datetime.datetime(*args) def test_days(self): timestamp = datetime.datetime.today() - datetime.timedelta(days=3) args = timestamp.timetuple()[:3] + (0, 0, 0, 0) expected = datetime.datetime(*args) - assert parse_datetime('3d') == expected - assert parse_datetime('3 d') == expected - assert parse_datetime('3 day') == expected - assert parse_datetime('3 days') == expected + assert parse_datetime("3d") == expected + assert parse_datetime("3 d") == expected + assert parse_datetime("3 day") == expected + assert parse_datetime("3 days") == expected def test_weeks(self): timestamp = datetime.datetime.today() - datetime.timedelta(days=3 * 7) args = timestamp.timetuple()[:3] + (0, 0, 0, 0) expected = datetime.datetime(*args) - assert parse_datetime('3w') == expected - assert parse_datetime('3 w') == expected - assert parse_datetime('3 week') == expected - assert parse_datetime('3 weeks') == expected + assert parse_datetime("3w") == expected + assert parse_datetime("3 w") == expected + assert parse_datetime("3 week") == expected + assert parse_datetime("3 weeks") == expected def test_mixed(self): - timestamp = ( - datetime.datetime.today() - datetime.timedelta(days=2 * 7 + 3)) + timestamp = datetime.datetime.today() - datetime.timedelta(days=2 * 7 + 3) args = timestamp.timetuple()[:3] + (0, 0, 0, 0) expected = datetime.datetime(*args) - assert parse_datetime('2w3d') == expected - assert parse_datetime('2w 3d') == expected - assert parse_datetime('2w 3 days') == expected - assert parse_datetime('2 weeks 3 days') == expected + assert parse_datetime("2w3d") == expected + assert parse_datetime("2w 3d") == expected + assert parse_datetime("2w 3 days") == expected + assert parse_datetime("2 weeks 3 days") == expected -@pytest.mark.parametrize("test_str, name, email", [ - ('Marcin Kuzminski ', - 'Marcin Kuzminski', 'marcin@python-works.com'), - ('Marcin Kuzminski Spaces < marcin@python-works.com >', - 'Marcin Kuzminski Spaces', 'marcin@python-works.com'), - ('Marcin Kuzminski ', - 'Marcin Kuzminski', 'marcin.kuzminski@python-works.com'), - ('mrf RFC_SPEC ', - 'mrf RFC_SPEC', 'marcin+kuzminski@python-works.com'), - ('username ', - 'username', 'user@email.com'), - ('username ', - '', 'justemail@mail.com'), - ('justname', - 'justname', ''), - ('Mr Double Name withemail@email.com ', - 'Mr Double Name', 'withemail@email.com'), -]) +@pytest.mark.parametrize( + "test_str, name, email", + [ + ("Marcin Kuzminski ", "Marcin Kuzminski", "marcin@python-works.com"), + ("Marcin Kuzminski Spaces < marcin@python-works.com >", "Marcin Kuzminski Spaces", "marcin@python-works.com"), + ( + "Marcin Kuzminski ", + "Marcin Kuzminski", + "marcin.kuzminski@python-works.com", + ), + ("mrf RFC_SPEC ", "mrf RFC_SPEC", "marcin+kuzminski@python-works.com"), + ("username ", "username", "user@email.com"), + ("username ", "", "justemail@mail.com"), + ("justname", "justname", ""), + ("Mr Double Name withemail@email.com ", "Mr Double Name", "withemail@email.com"), + ], +) class TestAuthorExtractors(object): - def test_author_email(self, test_str, name, email): assert email == author_email(test_str) diff --git a/rhodecode/tests/vcs/test_vcs.py b/rhodecode/tests/vcs/test_vcs.py index 465c7a1a..3f9a89f8 100644 --- a/rhodecode/tests/vcs/test_vcs.py +++ b/rhodecode/tests/vcs/test_vcs.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -20,6 +19,7 @@ """ Tests for main module's methods. """ + import os import tempfile import shutil @@ -48,7 +48,7 @@ def test_alias_detect(backend): def test_wrong_alias(): - alias = 'wrong_alias' + alias = "wrong_alias" with pytest.raises(VCSError): get_backend(alias) @@ -77,12 +77,13 @@ def test_get_vcs_instance_by_path_multiple_repos(request): Test that ``get_vcs_instance_by_path`` returns None if a path is passed to a directory with multiple repositories. """ - empty_dir = tempfile.mkdtemp(prefix='pytest-empty-dir-') - os.mkdir(os.path.join(empty_dir, '.git')) - os.mkdir(os.path.join(empty_dir, '.hg')) + empty_dir = tempfile.mkdtemp(prefix="pytest-empty-dir-") + os.mkdir(os.path.join(empty_dir, ".git")) + os.mkdir(os.path.join(empty_dir, ".hg")) def fin(): shutil.rmtree(empty_dir) + request.addfinalizer(fin) repo = get_vcs_instance(empty_dir) @@ -90,39 +91,32 @@ def test_get_vcs_instance_by_path_multiple_repos(request): assert repo is None -@mock.patch('rhodecode.lib.vcs.backends.get_scm') -@mock.patch('rhodecode.lib.vcs.backends.get_backend') -def test_get_vcs_instance_by_path_args_passed( - get_backend_mock, get_scm_mock, tmpdir, vcs_repo): +@mock.patch("rhodecode.lib.vcs.backends.get_scm") +@mock.patch("rhodecode.lib.vcs.backends.get_backend") +def test_get_vcs_instance_by_path_args_passed(get_backend_mock, get_scm_mock, tmpdir, vcs_repo): """ Test that the arguments passed to ``get_vcs_instance_by_path`` are forwarded to the vcs backend class. """ backend = mock.MagicMock() get_backend_mock.return_value = backend - args = ['these-are-test-args', 0, True, None] + args = ["these-are-test-args", 0, True, None] repo = vcs_repo.path get_vcs_instance(repo, *args) backend.assert_called_with(*args, repo_path=repo) -@mock.patch('rhodecode.lib.vcs.backends.get_scm') -@mock.patch('rhodecode.lib.vcs.backends.get_backend') -def test_get_vcs_instance_by_path_kwargs_passed( - get_backend_mock, get_scm_mock, vcs_repo): +@mock.patch("rhodecode.lib.vcs.backends.get_scm") +@mock.patch("rhodecode.lib.vcs.backends.get_backend") +def test_get_vcs_instance_by_path_kwargs_passed(get_backend_mock, get_scm_mock, vcs_repo): """ Test that the keyword arguments passed to ``get_vcs_instance_by_path`` are forwarded to the vcs backend class. """ backend = mock.MagicMock() get_backend_mock.return_value = backend - kwargs = { - 'foo': 'these-are-test-args', - 'bar': 0, - 'baz': True, - 'foobar': None - } + kwargs = {"foo": "these-are-test-args", "bar": 0, "baz": True, "foobar": None} repo = vcs_repo.path get_vcs_instance(repo, **kwargs) diff --git a/rhodecode/tests/vcs/utils.py b/rhodecode/tests/vcs/utils.py index df0832f5..f3565848 100644 --- a/rhodecode/tests/vcs/utils.py +++ b/rhodecode/tests/vcs/utils.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -37,7 +36,7 @@ def run_command(cmd, args): """ Runs command on the system with given ``args``. """ - command = ' '.join((cmd, args)) + command = " ".join((cmd, args)) p = Popen(command, shell=True) status = os.waitpid(p.pid, 0)[1] return status @@ -51,12 +50,11 @@ def eprint(msg): Appends line break. """ sys.stderr.write(msg) - sys.stderr.write('\n') + sys.stderr.write("\n") # TODO: Revisit once we have CI running, if this is not helping us, remove it class SCMFetcher(object): - def __init__(self, alias, test_repo_path): """ :param clone_cmd: command which would clone remote repository; pass @@ -75,9 +73,8 @@ class SCMFetcher(object): Tries to fetch repository from remote path. """ remote = self.remote_repo - eprint( - "Fetching repository %s into %s" % (remote, self.test_repo_path)) - run_command(self.clone_cmd, '%s %s' % (remote, self.test_repo_path)) + eprint("Fetching repository %s into %s" % (remote, self.test_repo_path)) + run_command(self.clone_cmd, "%s %s" % (remote, self.test_repo_path)) def get_normalized_path(path): @@ -88,29 +85,29 @@ def get_normalized_path(path): """ if os.path.exists(path): dir, basename = os.path.split(path) - splitted_name = basename.split('.') + splitted_name = basename.split(".") if len(splitted_name) > 1: ext = splitted_name[-1] else: ext = None - name = '.'.join(splitted_name[:-1]) - matcher = re.compile(r'^.*-(\d{5})$') + name = ".".join(splitted_name[:-1]) + matcher = re.compile(r"^.*-(\d{5})$") start = 0 m = matcher.match(name) if not m: # Haven't append number yet so return first - newname = f'{name}-00000' + newname = f"{name}-00000" newpath = os.path.join(dir, newname) if ext: - newpath = '.'.join((newpath, ext)) + newpath = ".".join((newpath, ext)) return get_normalized_path(newpath) else: start = int(m.group(1)[-5:]) + 1 for x in range(start, 10000): - newname = name[:-5] + str(x).rjust(5, '0') + newname = name[:-5] + str(x).rjust(5, "0") newpath = os.path.join(dir, newname) if ext: - newpath = '.'.join((newpath, ext)) + newpath = ".".join((newpath, ext)) if not os.path.exists(newpath): return newpath raise VCSTestError("Couldn't compute new path for %s" % path) diff --git a/rhodecode/tests/vcs_operations/__init__.py b/rhodecode/tests/vcs_operations/__init__.py index b3881ecb..b77f7f30 100644 --- a/rhodecode/tests/vcs_operations/__init__.py +++ b/rhodecode/tests/vcs_operations/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -36,7 +35,6 @@ from rhodecode.lib.str_utils import safe_str from rhodecode.tests import GIT_REPO, HG_REPO, SVN_REPO DEBUG = True -RC_LOG = os.path.join(tempfile.gettempdir(), 'rc.log') REPO_GROUP = 'a_repo_group' HG_REPO_WITH_GROUP = f'{REPO_GROUP}/{HG_REPO}' GIT_REPO_WITH_GROUP = f'{REPO_GROUP}/{GIT_REPO}' diff --git a/rhodecode/tests/vcs_operations/conftest.py b/rhodecode/tests/vcs_operations/conftest.py index d70fb2cd..0cf8a4fb 100644 --- a/rhodecode/tests/vcs_operations/conftest.py +++ b/rhodecode/tests/vcs_operations/conftest.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -19,22 +18,17 @@ """ py.test config for test suite for making push/pull operations. - -.. important:: - - You must have git >= 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. """ import os -import tempfile -import textwrap + +import pyramid.paster import pytest import logging import requests from rhodecode import events -from rhodecode.lib.str_utils import safe_bytes +from rhodecode.lib.type_utils import AttributeDict from rhodecode.model.db import Integration, UserRepoToPerm, Permission, \ UserToRepoBranchPermission, User from rhodecode.model.integration import IntegrationModel @@ -42,11 +36,13 @@ from rhodecode.model.db import Repository from rhodecode.model.meta import Session from rhodecode.integrations.types.webhook import WebhookIntegrationType + from rhodecode.tests import GIT_REPO, HG_REPO, SVN_REPO from rhodecode.tests.conftest import HTTPBIN_DOMAIN, HTTPBIN_POST -from rhodecode.tests.fixture import Fixture -from rhodecode.tests.server_utils import RcWebServer - +from rhodecode.tests.fixtures.rc_fixture import Fixture +from rhodecode.tests.fixtures.fixture_utils import backend_base +from rhodecode.tests.utils import set_anonymous_access, AuthPluginManager +from rhodecode.tests import console_printer REPO_GROUP = 'a_repo_group' HG_REPO_WITH_GROUP = f'{REPO_GROUP}/{HG_REPO}' @@ -62,39 +58,42 @@ def check_httpbin_connection(): response = requests.get(HTTPBIN_DOMAIN, timeout=5) return response.status_code == 200 except Exception as e: - print(e) + console_printer(e) return False +#overrides backend_N with init_pyramid_app instead of baseapp +@pytest.fixture() +def vcs_backend_git(request, init_pyramid_app, test_repo): + return backend_base(request, 'git', test_repo) -@pytest.fixture(scope="module") -def rcextensions(request, db_connection, tmpdir_factory): - """ - Installs a testing rcextensions pack to ensure they work as expected. - """ - init_content = textwrap.dedent(""" - # Forward import the example rcextensions to make it - # active for our tests. - from rhodecode.tests.other.example_rcextensions import * - """) - # Note: rcextensions are looked up based on the path of the ini file - root_path = tmpdir_factory.getbasetemp() - rcextensions_path = root_path.join('rcextensions') - init_path = rcextensions_path.join('__init__.py') +@pytest.fixture() +def vcs_backend_hg(request, init_pyramid_app, test_repo): + return backend_base(request, 'hg', test_repo) - if rcextensions_path.check(): - pytest.fail( - "Path for rcextensions already exists, please clean up before " - "test run this path: %s" % (rcextensions_path, )) - else: - request.addfinalizer(rcextensions_path.remove) - init_path.write_binary(safe_bytes(init_content), ensure=True) + +@pytest.fixture() +def vcs_backend_svn(request, init_pyramid_app, test_repo): + return backend_base(request, 'svn', test_repo) @pytest.fixture(scope="module") -def repos(request, db_connection): +def tmp_storage_location(request, tmpdir_factory): + """ + Defines a module level storage_location, used mostly to define per-test persistent repo storage + shared across vcsserver, rhodecode and celery + """ + + dest = tmpdir_factory.mktemp('tmp_storage_location_', numbered=True) + log.info("Creating test TMP directory at %s", dest) + return dest + + +@pytest.fixture(scope="module") +def repo_group_repos(request): """Create a copy of each test repo in a repo group.""" + fixture = Fixture() repo_group = fixture.create_repo_group(REPO_GROUP) repo_group_id = repo_group.group_id @@ -116,67 +115,117 @@ def repos(request, db_connection): fixture.destroy_repo_group(repo_group_id) -@pytest.fixture(scope="module") -def rc_web_server_config_modification(): - return [] +@pytest.fixture(scope='module') +def rcstack_vcsserver_factory(vcsserver_factory): + return vcsserver_factory -@pytest.fixture(scope="module") -def rc_web_server_config_factory(testini_factory, rc_web_server_config_modification): +@pytest.fixture(scope='module') +def rcstack_celery_factory(celery_factory): + return celery_factory + + +@pytest.fixture(scope='module') +def rcstack_rhodecode_factory(rhodecode_factory): + return rhodecode_factory + + +@pytest.fixture(scope='module') +def init_pyramid_app(request, available_port_factory, ini_config_factory, rcstack_vcsserver_factory, tmp_storage_location): + from rhodecode.lib.config_utils import get_app_config + from rhodecode.config.middleware import make_pyramid_app + + store_dir = tmp_storage_location + port = available_port_factory() + rcstack_vcsserver_factory( + request, + store_dir=store_dir, + port=port, + info_prefix='init-app-' + ) + + app_ini_config = ini_config_factory(store_dir) + + pyramid.paster.setup_logging(app_ini_config) + + settings = get_app_config(app_ini_config) + settings['startup.import_repos'] = True + settings['vcs.server'] = f'localhost:{port}' + settings['repo_store.path'] = str(store_dir) + pyramid_app = make_pyramid_app({'__file__': app_ini_config}, **settings) + + return pyramid_app + + +@pytest.fixture(scope='module') +def rcstack(request, tmp_storage_location, rcextensions, available_port_factory, rcstack_vcsserver_factory, rcstack_celery_factory, rcstack_rhodecode_factory): """ - Configuration file used for the fixture `rc_web_server`. + Runs minimal rcstack, i.e vcsserver, celery, rhodecode unpacks rcextensions and repos to a shared location """ - - def factory(rcweb_port, vcsserver_port): - custom_params = [ - {'handler_console': {'level': 'DEBUG'}}, - {'server:main': {'port': rcweb_port}}, - {'app:main': {'vcs.server': 'localhost:%s' % vcsserver_port}} - ] - custom_params.extend(rc_web_server_config_modification) - return testini_factory(custom_params) - return factory - - -@pytest.fixture(scope="module") -def rc_web_server( - request, vcsserver_factory, available_port_factory, - rc_web_server_config_factory, repos, rcextensions): - """ - Run the web server as a subprocess. with its own instance of vcsserver - """ - rcweb_port: int = available_port_factory() - log.info('Using rcweb ops test port %s', rcweb_port) + rcstack_data = AttributeDict() + store_dir = tmp_storage_location vcsserver_port: int = available_port_factory() - log.info('Using vcsserver ops test port %s', vcsserver_port) + vcsserver_log = os.path.join(tmp_storage_location, 'vcsserver.log') - vcs_log = os.path.join(tempfile.gettempdir(), 'rc_op_vcs.log') - vcsserver_factory( - request, vcsserver_port=vcsserver_port, - log_file=vcs_log, + log.info('Using vcsserver test port %s and log %s', vcsserver_port, vcsserver_log) # start vcsserver + _factory = rcstack_vcsserver_factory( + request, + store_dir=store_dir, + port=vcsserver_port, + log_file=vcsserver_log, overrides=( - {'server:main': {'workers': 2}}, - {'server:main': {'graceful_timeout': 10}}, + {'handler_console': {'level': 'DEBUG'}}, + )) + rcstack_data.vcsserver_port = vcsserver_port + rcstack_data.vcsserver_log = _factory.log_file + + + celery_log = os.path.join(tmp_storage_location, 'celery.log') + + + log.info('Using celery log %s', celery_log) + # start celery + _factory = rcstack_celery_factory( + request, + store_dir=store_dir, + port=None, + log_file=celery_log, + overrides=( + {'handler_console': {'level': 'DEBUG'}}, + {'app:main': {'vcs.server': f'localhost:{vcsserver_port}'}}, + {'app:main': {'repo_store.path': store_dir}} )) - rc_log = os.path.join(tempfile.gettempdir(), 'rc_op_web.log') - rc_web_server_config = rc_web_server_config_factory( - rcweb_port=rcweb_port, - vcsserver_port=vcsserver_port) - server = RcWebServer(rc_web_server_config, log_file=rc_log) - server.start() + rcstack_data.celery_log = _factory.log_file - @request.addfinalizer - def cleanup(): - server.shutdown() + rhodecode_port: int = available_port_factory() + rhodecode_log = os.path.join(tmp_storage_location, 'rhodecode.log') - server.wait_until_ready() - return server + + log.info('Using rhodecode test port %s and log %s', rhodecode_port, rhodecode_port) + + # start rhodecode + rc = rcstack_rhodecode_factory( + request, + store_dir=store_dir, + port=rhodecode_port, + log_file=rhodecode_log, + overrides=( + {'handler_console': {'level': 'DEBUG'}}, + {'app:main': {'vcs.server': f'localhost:{vcsserver_port}'}}, + {'app:main': {'repo_store.path': store_dir}} + )) + + rcstack_data.rhodecode_port = rhodecode_port + rcstack_data.rhodecode_log = rc.log_file + + rc.rcstack_data = rcstack_data + return rc @pytest.fixture() -def disable_locking(baseapp): +def disable_locking(init_pyramid_app): r = Repository.get_by_repo_name(GIT_REPO) Repository.unlock(r) r.enable_locking = False @@ -190,6 +239,28 @@ def disable_locking(baseapp): Session().commit() +@pytest.fixture() +def disable_anonymous_user(request, init_pyramid_app, db_connection): + set_anonymous_access(False) + + @request.addfinalizer + def cleanup(): + set_anonymous_access(True) + + +@pytest.fixture(scope='module') +def enable_auth_plugins(request, init_pyramid_app): + """ + Return a factory object that when called, allows to control which + authentication plugins are enabled. + """ + + enabler = AuthPluginManager() + request.addfinalizer(enabler.cleanup) + + return enabler + + @pytest.fixture() def fs_repo_only(request, rhodecode_fixtures): def fs_repo_fabric(repo_name, repo_type): diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py index 24d022bc..76fc59ca 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -32,25 +31,27 @@ from rhodecode.tests import (GIT_REPO, HG_REPO) from rhodecode.tests.vcs_operations import Command -@pytest.fixture(scope="module") -def rc_web_server_config_modification(): - return [ - {'app:main': {'auth_ret_code': '403'}}, - #{'app:main': {'auth_ret_code_detection': 'true'}}, - ] +custom_code = [ + {'app:main': {'auth_ret_code': '403'}}, +] +@pytest.mark.parametrize('rcstack', custom_code, indirect=True) +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsAuthCode403(object): -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperationsOnCustomIniConfig(object): - - def test_clone_wrong_credentials_hg_ret_code(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_hg_ret_code(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: HTTP Error 403: Forbidden' in stderr - def test_clone_wrong_credentials_git_ret_code(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_git_ret_code(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) assert 'The requested URL returned error: 403' in stderr diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py index acd13296..3271cb7f 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py @@ -31,26 +31,27 @@ import pytest from rhodecode.tests import (GIT_REPO, HG_REPO) from rhodecode.tests.vcs_operations import Command +custom_code = [ + {'app:main': {'auth_ret_code': '404'}}, +] -@pytest.fixture(scope="module") -def rc_web_server_config_modification(): - return [ - {'app:main': {'auth_ret_code': '404'}}, - #{'app:main': {'auth_ret_code_detection': 'false'}}, - ] +@pytest.mark.parametrize('rcstack', custom_code, indirect=True) +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsOnCustomAuthCode404(object): - -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperationsOnCustomIniConfig(object): - - def test_clone_wrong_credentials_hg_ret_code(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_hg_ret_code(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: HTTP Error 404: Not Found' in stderr - def test_clone_wrong_credentials_git_ret_code(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_git_ret_code(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) assert 'not found' in stderr diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py index 9202aace..18610a4e 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py @@ -32,25 +32,27 @@ from rhodecode.tests import (GIT_REPO, HG_REPO) from rhodecode.tests.vcs_operations import Command -@pytest.fixture(scope="module") -def rc_web_server_config_modification(): - return [ - {'app:main': {'auth_ret_code': '600'}}, - #{'app:main': {'auth_ret_code_detection': 'false'}}, - ] +custom_code = [ + {'app:main': {'auth_ret_code': '600'}}, +] - -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") +@pytest.mark.parametrize('rcstack', custom_code, indirect=True) +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) class TestVCSOperationsOnCustomIniConfig(object): - def test_clone_wrong_credentials_hg_ret_code(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_hg_ret_code(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: authorization failed' in stderr - def test_clone_wrong_credentials_git_ret_code(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_git_ret_code(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) assert 'fatal: Authentication failed' in stderr diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py b/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py index b419622d..1b0b8aeb 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -20,13 +19,10 @@ """ Test suite for making push/pull operations, on specially modified INI files -.. important:: - - You must have git >= 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. """ import os +import pytest from rhodecode.lib.vcs.backends.git.repository import GitRepository from rhodecode.lib.vcs.nodes import FileNode @@ -35,31 +31,39 @@ from rhodecode.tests.vcs_operations import Command from .test_vcs_operations_git import _check_proper_clone, _check_proper_git_push -def test_git_clone_with_small_push_buffer(backend_git, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - cmd = Command('/tmp') - stdout, stderr = cmd.execute( - 'git -c http.postBuffer=1024 clone', clone_url, tmpdir.strpath) - _check_proper_clone(stdout, stderr, 'git') - cmd.assert_returncode_success() +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsOnCustomIniConfig(object): + + def test_git_clone_with_small_push_buffer(self, vcs_backend_git, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + cmd = Command(tmpdir.strpath) + stdout, stderr = cmd.execute( + 'git -c http.postBuffer=1024 clone', clone_url, tmpdir.strpath) + _check_proper_clone(stdout, stderr, 'git') + cmd.assert_returncode_success() -def test_git_push_with_small_push_buffer(backend_git, rc_web_server, tmpdir): - empty_repo = backend_git.create_repo() + def test_git_push_with_small_push_buffer(self, vcs_backend_git, rcstack, tmpdir): + empty_repo = vcs_backend_git.create_repo() - clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name) + clone_url = rcstack.repo_clone_url(empty_repo.repo_name) - cmd = Command(tmpdir.strpath) - cmd.execute('git clone', clone_url) + cmd = Command(tmpdir.strpath) + cmd.execute('git clone', clone_url) - repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name)) - repo.in_memory_commit.add(FileNode(b'readme.md', content=b'## Hello')) - repo.in_memory_commit.commit( - message='Commit on branch Master', - author='Automatic test ', - branch='master') + repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name)) + repo.in_memory_commit.add(FileNode(b'readme.md', content=b'## Hello')) + repo.in_memory_commit.commit( + message='Commit on branch Master', + author='Automatic test ', + branch='master') - repo_cmd = Command(repo.path) - stdout, stderr = repo_cmd.execute( - f'git -c http.postBuffer=1024 push --verbose {clone_url} master') - _check_proper_git_push(stdout, stderr, branch='master') + repo_cmd = Command(repo.path) + stdout, stderr = repo_cmd.execute( + f'git -c http.postBuffer=1024 push --verbose {clone_url} master') + _check_proper_git_push(stdout, stderr, branch='master') diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py new file mode 100644 index 00000000..514e3f19 --- /dev/null +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py @@ -0,0 +1,89 @@ +# Copyright (C) 2010-2023 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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/ + +""" +Test suite for making push/pull operations, on specially modified INI files +""" + +import pytest + +from rhodecode.model.meta import Session +from rhodecode.model.settings import SettingsModel + +from rhodecode.tests import GIT_REPO, HG_REPO +from rhodecode.tests.vcs_operations import Command, _add_files_and_push + + +@pytest.fixture() +def bad_client_setter_factory(request): + def _factory(client_type, client_str_val): + # set allowed clients + setting = SettingsModel().create_or_update_setting(name=f"{client_type}_allowed_clients", val=client_str_val) + Session().add(setting) + Session().commit() + + @request.addfinalizer + def cleanup(): + setting2 = SettingsModel().create_or_update_setting(name=f"{client_type}_allowed_clients", val="*") + Session().add(setting2) + Session().commit() + + return _factory + + +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsOnUsingBadClient(object): + def test_push_with_bad_client_repo_by_other_user_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("hg clone", clone_url, tmpdir.strpath) + + # set allowed clients + setting = SettingsModel().create_or_update_setting(name=f"hg_allowed_clients", val="0.0.0") + Session().add(setting) + Session().commit() + + # push fails repo is locked by other user ! + push_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = _add_files_and_push("hg", tmpdir.strpath, clone_url=push_url) + msg = "Your hg client (ver=mercurial/proto-1.0 (Mercurial 6.7.4)) is forbidden by security rules" + assert msg in stderr + + def test_push_with_bad_client_repo_by_other_user_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("git clone", clone_url, tmpdir.strpath) + + # set allowed clients + setting = SettingsModel().create_or_update_setting(name=f"git_allowed_clients", val="0.0.0") + Session().add(setting) + Session().commit() + + # push fails repo is locked by other user! + push_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = _add_files_and_push("git", tmpdir.strpath, clone_url=push_url) + + err = "Your git client (ver=git/2.45.2) is forbidden by security rules" + assert err in stderr + + @pytest.mark.xfail(reason="Lack of proper SVN support of cloning") + def test_push_with_bad_client_repo_by_other_user_svn(self, rcstack, tmpdir): + raise NotImplementedError("lacks svn support") diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py b/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py index 3b1be329..400fac8d 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py @@ -27,7 +27,12 @@ from rhodecode.tests.vcs_operations import ( Command, _check_proper_hg_push, _check_proper_git_push, _add_files_and_push) -@pytest.mark.usefixtures("disable_anonymous_user") +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) class TestVCSOperations(object): @pytest.mark.parametrize('username, password', [ @@ -41,13 +46,13 @@ class TestVCSOperations(object): 'branch.push_force', ]) def test_push_to_protected_branch_fails_with_message_hg( - self, rc_web_server, tmpdir, branch_perm, user_util, + self, rcstack, tmpdir, branch_perm, user_util, branch_permission_setter, username, password): repo = user_util.create_repo(repo_type='hg') repo_name = repo.repo_name branch_permission_setter(repo_name, username, permission=branch_perm) - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( repo.repo_name, user=username, passwd=password) Command(os.path.dirname(tmpdir.strpath)).execute( 'hg clone', clone_url, tmpdir.strpath) @@ -58,8 +63,8 @@ class TestVCSOperations(object): _check_proper_hg_push(stdout, stderr) else: msg = f"Branch `default` changes rejected by rule `*`=>{branch_perm}" - assert msg in stdout - assert "transaction abort" in stdout + assert msg in stderr + #assert "transaction abort" in stdout @pytest.mark.parametrize('username, password', [ (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS), @@ -72,13 +77,13 @@ class TestVCSOperations(object): 'branch.push_force', ]) def test_push_to_protected_branch_fails_with_message_git( - self, rc_web_server, tmpdir, branch_perm, user_util, + self, rcstack, tmpdir, branch_perm, user_util, branch_permission_setter, username, password): repo = user_util.create_repo(repo_type='git') repo_name = repo.repo_name branch_permission_setter(repo_name, username, permission=branch_perm) - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( repo.repo_name, user=username, passwd=password) Command(os.path.dirname(tmpdir.strpath)).execute( 'git clone', clone_url, tmpdir.strpath) diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_by_auth_tokens.py b/rhodecode/tests/vcs_operations/test_vcs_operations_by_auth_tokens.py index 05c7ab7c..9890825a 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_by_auth_tokens.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_by_auth_tokens.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -20,10 +19,6 @@ """ Test suite for making push/pull operations, on specially modified INI files -.. important:: - - You must have git >= 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. """ import pytest @@ -36,10 +31,15 @@ from rhodecode.tests import (GIT_REPO, HG_REPO) from rhodecode.tests.vcs_operations import (Command, _check_proper_clone) -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperations(object): +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsByAuthTokens: def test_clone_by_auth_token( - self, rc_web_server, tmpdir, user_util, enable_auth_plugins): + self, rcstack, tmpdir, user_util, enable_auth_plugins): enable_auth_plugins.enable([ 'egg:rhodecode-enterprise-ce#token', @@ -49,16 +49,16 @@ class TestVCSOperations(object): user = user_util.create_user() token = user.auth_tokens[1] - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( HG_REPO, user=user.username, passwd=token) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'hg') def test_clone_by_auth_token_expired( - self, rc_web_server, tmpdir, user_util, enable_auth_plugins): + self, rcstack, tmpdir, user_util, enable_auth_plugins): enable_auth_plugins.enable([ 'egg:rhodecode-enterprise-ce#token', 'egg:rhodecode-enterprise-ce#rhodecode' @@ -69,18 +69,18 @@ class TestVCSOperations(object): user.user_id, 'test-token', -10, AuthTokenModel.cls.ROLE_VCS) token = auth_token.api_key - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( HG_REPO, user=user.username, passwd=token) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: authorization failed' in stderr msg = 'reason: bad or inactive token.' - rc_web_server.assert_message_in_server_logs(msg) + rcstack.assert_message_in_server_logs(msg) def test_clone_by_auth_token_bad_role( - self, rc_web_server, tmpdir, user_util, enable_auth_plugins): + self, rcstack, tmpdir, user_util, enable_auth_plugins): enable_auth_plugins.enable([ 'egg:rhodecode-enterprise-ce#token', 'egg:rhodecode-enterprise-ce#rhodecode' @@ -91,15 +91,15 @@ class TestVCSOperations(object): user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_API) token = auth_token.api_key - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( HG_REPO, user=user.username, passwd=token) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: authorization failed' in stderr def test_clone_by_auth_token_user_disabled( - self, rc_web_server, tmpdir, user_util, enable_auth_plugins): + self, rcstack, tmpdir, user_util, enable_auth_plugins): enable_auth_plugins.enable([ 'egg:rhodecode-enterprise-ce#token', 'egg:rhodecode-enterprise-ce#rhodecode' @@ -111,18 +111,18 @@ class TestVCSOperations(object): Session().commit() token = user.auth_tokens[1] - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( HG_REPO, user=user.username, passwd=token) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: authorization failed' in stderr msg = 'reason: account not active.' - rc_web_server.assert_message_in_server_logs(msg) + rcstack.assert_message_in_server_logs(msg) def test_clone_by_auth_token_with_scope( - self, rc_web_server, tmpdir, user_util, enable_auth_plugins): + self, rcstack, tmpdir, user_util, enable_auth_plugins): enable_auth_plugins.enable([ 'egg:rhodecode-enterprise-ce#token', 'egg:rhodecode-enterprise-ce#rhodecode' @@ -138,15 +138,15 @@ class TestVCSOperations(object): Session().add(auth_token) Session().commit() - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( HG_REPO, user=user.username, passwd=token) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'hg') def test_clone_by_auth_token_with_wrong_scope( - self, rc_web_server, tmpdir, user_util, enable_auth_plugins): + self, rcstack, tmpdir, user_util, enable_auth_plugins): enable_auth_plugins.enable([ 'egg:rhodecode-enterprise-ce#token', 'egg:rhodecode-enterprise-ce#rhodecode' @@ -162,13 +162,13 @@ class TestVCSOperations(object): Session().add(auth_token) Session().commit() - clone_url = rc_web_server.repo_clone_url( + clone_url = rcstack.repo_clone_url( HG_REPO, user=user.username, passwd=token) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: authorization failed' in stderr msg = 'reason: bad or inactive token.' - rc_web_server.assert_message_in_server_logs(msg) + rcstack.assert_message_in_server_logs(msg) diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_clone.py b/rhodecode/tests/vcs_operations/test_vcs_operations_clone.py new file mode 100644 index 00000000..04d6d6ba --- /dev/null +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_clone.py @@ -0,0 +1,63 @@ + +# Copyright (C) 2010-2023 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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/ + +""" +Test suite for making push/pull operations, on specially modified INI files +""" + +import pytest + +from rhodecode.tests import GIT_REPO, SVN_REPO, HG_REPO + +from rhodecode.tests.vcs_operations import (Command, _check_proper_clone) +from rhodecode.tests.vcs_operations.test_vcs_operations_svn import get_cli_flags + + +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsClone: + + def test_clone_git_repo_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + cmd = Command(tmpdir.strpath) + stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath) + _check_proper_clone(stdout, stderr, 'git') + cmd.assert_returncode_success() + + def test_clone_hg_repo_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + cmd = Command(tmpdir.strpath) + stdout, stderr = cmd.execute('hg clone', clone_url, tmpdir.strpath) + _check_proper_clone(stdout, stderr, 'hg') + cmd.assert_returncode_success() + + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_clone_svn_repo_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(SVN_REPO) + username, password = rcstack.repo_clone_credentials() + flags, auth = get_cli_flags(username, password) + cmd = Command(tmpdir.strpath) + stdout, stderr = cmd.execute( + f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) + _check_proper_clone(stdout, stderr, 'svn') + cmd.assert_returncode_success() diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py index b290c406..53469d95 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -27,12 +26,17 @@ from rhodecode.tests.vcs_operations import ( _add_files, _add_files_and_push) -@pytest.mark.usefixtures("disable_anonymous_user") +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) class TestVCSOperations(object): - def test_push_force_hg(self, rc_web_server, tmpdir, user_util): + def test_push_force_hg(self, rcstack, tmpdir, user_util): repo = user_util.create_repo(repo_type='hg') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'hg clone', clone_url, tmpdir.strpath) @@ -50,9 +54,9 @@ class TestVCSOperations(object): _check_proper_hg_push(stdout, stderr) - def test_push_force_git(self, rc_web_server, tmpdir, user_util): + def test_push_force_git(self, rcstack, tmpdir, user_util): repo = user_util.create_repo(repo_type='git') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'git clone', clone_url, tmpdir.strpath) @@ -69,13 +73,12 @@ class TestVCSOperations(object): assert '(forced update)' in stderr def test_push_force_hg_blocked_by_branch_permissions( - self, rc_web_server, tmpdir, user_util, branch_permission_setter): + self, rcstack, tmpdir, user_util, branch_permission_setter): repo = user_util.create_repo(repo_type='hg') repo_name = repo.repo_name username = TEST_USER_ADMIN_LOGIN - branch_permission_setter(repo_name, username, permission='branch.push') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'hg clone', clone_url, tmpdir.strpath) @@ -88,21 +91,21 @@ class TestVCSOperations(object): 'hg checkout -r 1 && hg commit -m "starting new head"') _add_files('hg', tmpdir.strpath, clone_url=clone_url) + branch_permission_setter(repo_name, username, permission='branch.push') stdout, stderr = Command(tmpdir.strpath).execute( f'hg push --verbose -f {clone_url}') - assert "Branch `default` changes rejected by rule `*`=>branch.push" in stdout - assert "FORCE PUSH FORBIDDEN" in stdout - assert "transaction abort" in stdout + assert "Branch `default` changes rejected by rule `*`=>branch.push" in stderr + assert "FORCE PUSH FORBIDDEN" in stderr def test_push_force_git_blocked_by_branch_permissions( - self, rc_web_server, tmpdir, user_util, branch_permission_setter): + self, rcstack, tmpdir, user_util, branch_permission_setter): repo = user_util.create_repo(repo_type='git') repo_name = repo.repo_name username = TEST_USER_ADMIN_LOGIN branch_permission_setter(repo_name, username, permission='branch.push') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'git clone', clone_url, tmpdir.strpath) diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_git.py b/rhodecode/tests/vcs_operations/test_vcs_operations_git.py index d8d85cda..561da0e1 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_git.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_git.py @@ -25,8 +25,6 @@ Test suite for making push/pull operations, on specially modified INI files You must have git >= 1.8.5 for tests to work fine. With 68b939b git started to redirect things to stderr instead of stdout. """ - - import time import pytest @@ -42,41 +40,46 @@ from rhodecode.tests.vcs_operations import ( _add_files_and_push, GIT_REPO_WITH_GROUP) -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperations(object): +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsGit: - def test_clone_git_repo_by_admin(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - cmd = Command('/tmp') + def test_clone_git_repo_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + cmd = Command(tmpdir.strpath) stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'git') cmd.assert_returncode_success() - def test_clone_git_repo_by_admin_with_git_suffix(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - cmd = Command('/tmp') + def test_clone_git_repo_by_admin_with_git_suffix(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + cmd = Command(tmpdir.strpath) stdout, stderr = cmd.execute('git clone', clone_url+".git", tmpdir.strpath) _check_proper_clone(stdout, stderr, 'git') cmd.assert_returncode_success() - def test_clone_git_repo_by_id_by_admin(self, rc_web_server, tmpdir): + def test_clone_git_repo_by_id_by_admin(self, rcstack, tmpdir): repo_id = Repository.get_by_repo_name(GIT_REPO).repo_id - clone_url = rc_web_server.repo_clone_url('_%s' % repo_id) - cmd = Command('/tmp') + clone_url = rcstack.repo_clone_url('_%s' % repo_id) + cmd = Command(tmpdir.strpath) stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'git') cmd.assert_returncode_success() - def test_clone_git_repo_with_group_by_admin(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO_WITH_GROUP) - cmd = Command('/tmp') + def test_clone_git_repo_with_group_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO_WITH_GROUP) + cmd = Command(tmpdir.strpath) stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'git') cmd.assert_returncode_success() - def test_clone_git_repo_shallow_by_admin(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - cmd = Command('/tmp') + def test_clone_git_repo_shallow_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + cmd = Command(tmpdir.strpath) stdout, stderr = cmd.execute( 'git clone --depth=1', clone_url, tmpdir.strpath) @@ -84,65 +87,64 @@ class TestVCSOperations(object): assert 'Cloning into' in stderr cmd.assert_returncode_success() - - def test_clone_wrong_credentials_git(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) assert 'fatal: Authentication failed' in stderr - def test_clone_git_dir_as_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + def test_clone_git_dir_as_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr - def test_clone_non_existing_path_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url('trololo') - stdout, stderr = Command('/tmp').execute( + def test_clone_non_existing_path_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url('trololo') + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr - def test_clone_non_existing_path_git(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url('trololo') - stdout, stderr = Command('/tmp').execute('git clone', clone_url) + def test_clone_non_existing_path_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url('trololo') + stdout, stderr = Command(tmpdir.strpath).execute('git clone', clone_url) assert 'not found' in stderr - def test_clone_git_with_slashes(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url('//' + GIT_REPO) - stdout, stderr = Command('/tmp').execute('git clone', clone_url) + def test_clone_git_with_slashes(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url('//' + GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute('git clone', clone_url) assert 'not found' in stderr def test_clone_existing_path_git_not_in_database( - self, rc_web_server, tmpdir, fs_repo_only): + self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-git', repo_type='git') - clone_url = rc_web_server.repo_clone_url(db_name) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(db_name) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) assert 'not found' in stderr def test_clone_existing_path_git_not_in_database_different_scm( - self, rc_web_server, tmpdir, fs_repo_only): + self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-hg', repo_type='hg') - clone_url = rc_web_server.repo_clone_url(db_name) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(db_name) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) assert 'not found' in stderr - def test_clone_non_existing_store_path_git(self, rc_web_server, tmpdir, user_util): + def test_clone_non_existing_store_path_git(self, rcstack, tmpdir, user_util): repo = user_util.create_repo(repo_type='git') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) # Damage repo by removing it's folder RepoModel()._delete_filesystem_repo(repo) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) assert 'not found' in stderr - def test_push_new_file_git(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + def test_push_new_file_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) # commit some stuff into this repo @@ -151,37 +153,37 @@ class TestVCSOperations(object): _check_proper_git_push(stdout, stderr) - def test_push_wrong_credentials_git(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + def test_push_wrong_credentials_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) - push_url = rc_web_server.repo_clone_url( + push_url = rcstack.repo_clone_url( GIT_REPO, user='bad', passwd='name') stdout, stderr = _add_files_and_push( 'git', tmpdir.strpath, clone_url=push_url) assert 'fatal: Authentication failed' in stderr - def test_push_back_to_wrong_url_git(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + def test_push_back_to_wrong_url_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) stdout, stderr = _add_files_and_push( 'git', tmpdir.strpath, - clone_url=rc_web_server.repo_clone_url('not-existing')) + clone_url=rcstack.repo_clone_url('not-existing')) assert 'not found' in stderr - def test_ip_restriction_git(self, rc_web_server, tmpdir): + def test_ip_restriction_git(self, rcstack, tmpdir): user_model = UserModel() try: user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32') Session().commit() time.sleep(2) - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) msg = "The requested URL returned error: 403" assert msg in stderr @@ -193,7 +195,7 @@ class TestVCSOperations(object): time.sleep(2) - cmd = Command('/tmp') + cmd = Command(tmpdir.strpath) stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath) cmd.assert_returncode_success() _check_proper_clone(stdout, stderr, 'git') diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_hg.py b/rhodecode/tests/vcs_operations/test_vcs_operations_hg.py index 74d8e035..ae9a47e7 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_hg.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_hg.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 @@ -19,11 +18,6 @@ """ Test suite for making push/pull operations, on specially modified INI files - -.. important:: - - You must have git >= 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. """ @@ -42,96 +36,101 @@ from rhodecode.tests.vcs_operations import ( Command, _check_proper_clone, _add_files_and_push, HG_REPO_WITH_GROUP) -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperations(object): +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsHg(object): - def test_clone_hg_repo_by_admin(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + def test_clone_hg_repo_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'hg') - def test_clone_hg_repo_by_admin_pull_protocol(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + def test_clone_hg_repo_by_admin_pull_protocol(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone --pull', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'hg') - def test_clone_hg_repo_by_admin_pull_stream_protocol(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + def test_clone_hg_repo_by_admin_pull_stream_protocol(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone --pull --stream', clone_url, tmpdir.strpath) assert 'files to transfer,' in stdout assert 'transferred 1.' in stdout assert '114 files updated,' in stdout - def test_clone_hg_repo_by_id_by_admin(self, rc_web_server, tmpdir): + def test_clone_hg_repo_by_id_by_admin(self, rcstack, tmpdir): repo_id = Repository.get_by_repo_name(HG_REPO).repo_id - clone_url = rc_web_server.repo_clone_url('_%s' % repo_id) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url('_%s' % repo_id) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'hg') - def test_clone_hg_repo_with_group_by_admin(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO_WITH_GROUP) - stdout, stderr = Command('/tmp').execute( + def test_clone_hg_repo_with_group_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO_WITH_GROUP) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'hg') - def test_clone_wrong_credentials_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!') - stdout, stderr = Command('/tmp').execute( + def test_clone_wrong_credentials_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO, passwd='bad!') + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: authorization failed' in stderr - def test_clone_git_dir_as_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + def test_clone_git_dir_as_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr - def test_clone_non_existing_path_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url('trololo') - stdout, stderr = Command('/tmp').execute( + def test_clone_non_existing_path_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url('trololo') + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr - def test_clone_hg_with_slashes(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url('//' + HG_REPO) - stdout, stderr = Command('/tmp').execute('hg clone', clone_url, tmpdir.strpath) + def test_clone_hg_with_slashes(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url('//' + HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute('hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr def test_clone_existing_path_hg_not_in_database( - self, rc_web_server, tmpdir, fs_repo_only): + self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-hg', repo_type='hg') - clone_url = rc_web_server.repo_clone_url(db_name) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(db_name) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr def test_clone_existing_path_hg_not_in_database_different_scm( - self, rc_web_server, tmpdir, fs_repo_only): + self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-git', repo_type='git') - clone_url = rc_web_server.repo_clone_url(db_name) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(db_name) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr - def test_clone_non_existing_store_path_hg(self, rc_web_server, tmpdir, user_util): + def test_clone_non_existing_store_path_hg(self, rcstack, tmpdir, user_util): repo = user_util.create_repo() - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) # Damage repo by removing it's folder RepoModel()._delete_filesystem_repo(repo) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'HTTP Error 404: Not Found' in stderr - def test_push_new_file_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + def test_push_new_file_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) stdout, stderr = _add_files_and_push( @@ -140,7 +139,7 @@ class TestVCSOperations(object): assert 'pushing to' in stdout assert 'size summary' in stdout - def test_push_invalidates_cache(self, rc_web_server, tmpdir): + def test_push_invalidates_cache(self, rcstack, tmpdir): hg_repo = Repository.get_by_repo_name(HG_REPO) # init cache objects @@ -159,8 +158,8 @@ class TestVCSOperations(object): old_ids = [x.cache_state_uid for x in cache_keys] # clone to init cache - clone_url = rc_web_server.repo_clone_url(hg_repo.repo_name) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(hg_repo.repo_name) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) cache_keys = hg_repo.cache_keys @@ -180,37 +179,37 @@ class TestVCSOperations(object): new_ids = [x.cache_state_uid for x in cache_keys] assert new_ids != old_ids - def test_push_wrong_credentials_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + def test_push_wrong_credentials_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) - push_url = rc_web_server.repo_clone_url( + push_url = rcstack.repo_clone_url( HG_REPO, user='bad', passwd='name') stdout, stderr = _add_files_and_push( 'hg', tmpdir.strpath, clone_url=push_url) assert 'abort: authorization failed' in stderr - def test_push_back_to_wrong_url_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + def test_push_back_to_wrong_url_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) stdout, stderr = _add_files_and_push( 'hg', tmpdir.strpath, - clone_url=rc_web_server.repo_clone_url('not-existing')) + clone_url=rcstack.repo_clone_url('not-existing')) assert 'HTTP Error 404: Not Found' in stderr - def test_ip_restriction_hg(self, rc_web_server, tmpdir): + def test_ip_restriction_hg(self, rcstack, tmpdir): user_model = UserModel() try: user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32') Session().commit() time.sleep(2) - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) assert 'abort: HTTP Error 403: Forbidden' in stderr finally: @@ -221,6 +220,6 @@ class TestVCSOperations(object): time.sleep(2) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'hg') diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py b/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py index f9d918c3..c316fd2f 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -19,11 +18,6 @@ """ Test suite for making push/pull operations, on specially modified INI files - -.. important:: - - You must have git >= 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. """ import pytest @@ -37,32 +31,41 @@ from rhodecode.tests.vcs_operations.conftest import check_httpbin_connection connection_available = pytest.mark.skipif( not check_httpbin_connection(), reason="No outside internet connection available") +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", + "enable_webhook_push_integration" +) +class TestVCSOperationsOnIntegrationsTrigger(object): -@pytest.mark.usefixtures("baseapp", "enable_webhook_push_integration") -class TestVCSOperationsOnCustomIniConfig(object): + def test_push_with_webhook_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) - def test_push_with_webhook_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) + Command(tmpdir.strpath).execute('hg clone', clone_url, tmpdir.strpath) - Command('/tmp').execute('hg clone', clone_url, tmpdir.strpath) - - push_url = rc_web_server.repo_clone_url(HG_REPO) + push_url = rcstack.repo_clone_url(HG_REPO) _add_files_and_push('hg', tmpdir.strpath, clone_url=push_url) - rc_log = rc_web_server.get_rc_log() - assert 'ERROR' not in rc_log - assert "executing task TASK:<@task: rhodecode.integrations.types.webhook.post_to_webhook" in rc_log - assert "handling event repo-push with integration = 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. """ - import pytest from rhodecode.model.db import User, Repository from rhodecode.model.meta import Session from rhodecode.model.repo import RepoModel -from rhodecode.tests import ( - GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, - TEST_USER_REGULAR_PASS) -from rhodecode.tests.vcs_operations import ( - Command, _check_proper_clone, _check_proper_git_push, _add_files_and_push) +from rhodecode.tests import GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS +from rhodecode.tests.vcs_operations import Command, _check_proper_clone, _check_proper_git_push, _add_files_and_push -@pytest.fixture(scope="module") -def rc_web_server_config_modification(): - return [ - {'app:main': {'lock_ret_code': '423'}}, - ] +custom_code = [ + {'app:main': {'auth_ret_code': '423'}}, +] - -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperationsOnCustomIniConfig(object): - - def test_clone_and_create_lock_hg(self, rc_web_server, tmpdir): +@pytest.mark.parametrize('rcstack', custom_code, indirect=True) +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsOnLockingRepos(object): + def test_clone_and_create_lock_hg(self, rcstack, tmpdir): # enable locking r = Repository.get_by_repo_name(HG_REPO) r.enable_locking = True Session().add(r) Session().commit() # clone - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( - 'hg clone', clone_url, tmpdir.strpath) + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("hg clone", clone_url, tmpdir.strpath) # check if lock was made r = Repository.get_by_repo_name(HG_REPO) - assert r.locked[0] == User.get_by_username( - TEST_USER_ADMIN_LOGIN).user_id + assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id - def test_clone_and_create_lock_git(self, rc_web_server, tmpdir): + def test_clone_and_create_lock_git(self, rcstack, tmpdir): # enable locking r = Repository.get_by_repo_name(GIT_REPO) r.enable_locking = True Session().add(r) Session().commit() # clone - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( - 'git clone', clone_url, tmpdir.strpath) + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("git clone", clone_url, tmpdir.strpath) # check if lock was made r = Repository.get_by_repo_name(GIT_REPO) - assert r.locked[0] == User.get_by_username( - TEST_USER_ADMIN_LOGIN).user_id + assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id - def test_clone_after_repo_was_locked_hg(self, rc_web_server, tmpdir): + def test_clone_after_repo_was_locked_hg(self, rcstack, tmpdir): # lock repo r = Repository.get_by_repo_name(HG_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # pull fails since repo is locked - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( - 'hg clone', clone_url, tmpdir.strpath) - msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`""" - % (HG_REPO, TEST_USER_ADMIN_LOGIN)) + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("hg clone", clone_url, tmpdir.strpath) + msg = f"abort: HTTP Error 423: Repository `{HG_REPO}` locked by user `{TEST_USER_ADMIN_LOGIN}`" assert msg in stderr - def test_clone_after_repo_was_locked_git(self, rc_web_server, tmpdir): + def test_clone_after_repo_was_locked_git(self, rcstack, tmpdir): # lock repo r = Repository.get_by_repo_name(GIT_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # pull fails since repo is locked - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( - 'git clone', clone_url, tmpdir.strpath) + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("git clone", clone_url, tmpdir.strpath) - lock_msg = ( - 'remote: ERROR: Repository `vcs_test_git` locked by user ' + - '`test_admin`. Reason:`lock_auto`') + lock_msg = "remote: ERROR: Repository `vcs_test_git` locked by user `test_admin`. Reason:`lock_auto`" assert lock_msg in stderr - assert 'remote: Pre pull hook failed: aborting' in stderr - assert 'fatal: remote did not send all necessary objects' in stderr + assert "fatal: remote did not send all necessary objects" in stderr + assert "remote: Pre pull hook failed: aborting" in stderr - def test_push_on_locked_repo_by_other_user_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( - 'hg clone', clone_url, tmpdir.strpath) + def test_push_on_locked_repo_by_other_user_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("hg clone", clone_url, tmpdir.strpath) # lock repo r = Repository.get_by_repo_name(HG_REPO) # let this user actually push ! - RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, - perm='repository.write') + RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm="repository.write") Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # push fails repo is locked by other user ! - push_url = rc_web_server.repo_clone_url( - HG_REPO, - user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) - stdout, stderr = _add_files_and_push( - 'hg', tmpdir.strpath, clone_url=push_url) - msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`""" - % (HG_REPO, TEST_USER_ADMIN_LOGIN)) + push_url = rcstack.repo_clone_url(HG_REPO, user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) + stdout, stderr = _add_files_and_push("hg", tmpdir.strpath, clone_url=push_url) + msg = f"abort: HTTP Error 423: Repository `{HG_REPO}` locked by user `{TEST_USER_ADMIN_LOGIN}`" assert msg in stderr - def test_push_on_locked_repo_by_other_user_git( - self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( - 'git clone', clone_url, tmpdir.strpath) + def test_push_on_locked_repo_by_other_user_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("git clone", clone_url, tmpdir.strpath) # lock repo r = Repository.get_by_repo_name(GIT_REPO) # let this user actually push ! - RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, - perm='repository.write') + RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm="repository.write") Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # push fails repo is locked by other user! - push_url = rc_web_server.repo_clone_url( - GIT_REPO, - user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) - stdout, stderr = _add_files_and_push( - 'git', tmpdir.strpath, clone_url=push_url) + push_url = rcstack.repo_clone_url(GIT_REPO, user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) + stdout, stderr = _add_files_and_push("git", tmpdir.strpath, clone_url=push_url) - err = 'Repository `%s` locked by user `%s`' % ( - GIT_REPO, TEST_USER_ADMIN_LOGIN) + err = f"Repository `{GIT_REPO}` locked by user `{TEST_USER_ADMIN_LOGIN}`" # err = 'RPC failed; result=22, HTTP code = 423' assert err in stderr - def test_push_unlocks_repository_hg(self, rc_web_server, tmpdir): + def test_push_unlocks_repository_hg(self, rcstack, tmpdir): # enable locking r = Repository.get_by_repo_name(HG_REPO) r.enable_locking = True Session().add(r) Session().commit() - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( - 'hg clone', clone_url, tmpdir.strpath) - _check_proper_clone(stdout, stderr, 'hg') + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("hg clone", clone_url, tmpdir.strpath) + _check_proper_clone(stdout, stderr, "hg") # check for lock repo after clone r = Repository.get_by_repo_name(HG_REPO) @@ -177,16 +146,14 @@ class TestVCSOperationsOnCustomIniConfig(object): assert r.locked[0] == uid # push is ok and repo is now unlocked - stdout, stderr = _add_files_and_push( - 'hg', tmpdir.strpath, clone_url=clone_url) - assert ('remote: Released lock on repo `%s`' % HG_REPO) in stdout + stdout, stderr = _add_files_and_push("hg", tmpdir.strpath, clone_url=clone_url) + assert f"remote: Released lock on repo `{HG_REPO}`" in stdout # we need to cleanup the Session Here ! Session.remove() r = Repository.get_by_repo_name(HG_REPO) assert r.locked == [None, None, None] - def test_push_unlocks_repository_git(self, rc_web_server, tmpdir): - + def test_push_unlocks_repository_git(self, rcstack, tmpdir): # Note: Did a first debugging session. Seems that # Repository.get_locking_state is called twice. The second call # has the action "pull" and does not reset the lock. @@ -197,19 +164,16 @@ class TestVCSOperationsOnCustomIniConfig(object): Session().add(r) Session().commit() - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( - 'git clone', clone_url, tmpdir.strpath) - _check_proper_clone(stdout, stderr, 'git') + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("git clone", clone_url, tmpdir.strpath) + _check_proper_clone(stdout, stderr, "git") # check for lock repo after clone r = Repository.get_by_repo_name(GIT_REPO) - assert r.locked[0] == User.get_by_username( - TEST_USER_ADMIN_LOGIN).user_id + assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id # push is ok and repo is now unlocked - stdout, stderr = _add_files_and_push( - 'git', tmpdir.strpath, clone_url=clone_url) + stdout, stderr = _add_files_and_push("git", tmpdir.strpath, clone_url=clone_url) _check_proper_git_push(stdout, stderr) # assert ('remote: Released lock on repo `%s`' % GIT_REPO) in stdout diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py b/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py index 820c71ea..9bddd51f 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -26,102 +25,83 @@ Test suite for making push/pull operations, on specially modified INI files to redirect things to stderr instead of stdout. """ - import pytest from rhodecode.model.db import User, Repository from rhodecode.model.meta import Session from rhodecode.model.repo import RepoModel -from rhodecode.tests import ( - GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, - TEST_USER_REGULAR_PASS) +from rhodecode.tests import GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS from rhodecode.tests.vcs_operations import Command, _add_files_and_push +custom_code = [ + {'app:main': {'lock_ret_code': '400'}}, +] -@pytest.fixture(scope="module") -def rc_web_server_config_modification(): - return [ - {'app:main': {'lock_ret_code': '400'}}, - ] - - -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperationsOnCustomIniConfig(object): - - def test_clone_after_repo_was_locked_hg(self, rc_web_server, tmpdir): +@pytest.mark.parametrize('rcstack', custom_code, indirect=True) +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsOnCustomLockingCode(object): + def test_clone_after_repo_was_locked_hg(self, rcstack, tmpdir): # lock repo r = Repository.get_by_repo_name(HG_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # pull fails since repo is locked - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( - 'hg clone', clone_url, tmpdir.strpath) - msg = ("""abort: HTTP Error 400: Repository `%s` locked by user `%s`""" - % (HG_REPO, TEST_USER_ADMIN_LOGIN)) + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("hg clone", clone_url, tmpdir.strpath) + msg = f"abort: HTTP Error 400: Repository `{HG_REPO}` locked by user `{TEST_USER_ADMIN_LOGIN}`" assert msg in stderr - def test_clone_after_repo_was_locked_git(self, rc_web_server, tmpdir): + def test_clone_after_repo_was_locked_git(self, rcstack, tmpdir): # lock repo r = Repository.get_by_repo_name(GIT_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # pull fails since repo is locked - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( - 'git clone', clone_url, tmpdir.strpath) + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("git clone", clone_url, tmpdir.strpath) - lock_msg = ( - 'remote: ERROR: Repository `vcs_test_git` locked by user ' + - '`test_admin`. Reason:`lock_auto`') + lock_msg = "remote: ERROR: Repository `vcs_test_git` locked by user `test_admin`. Reason:`lock_auto`" assert lock_msg in stderr - assert 'remote: Pre pull hook failed: aborting' in stderr - assert 'fatal: remote did not send all necessary objects' in stderr + assert "remote: Pre pull hook failed: aborting" in stderr + assert "fatal: remote did not send all necessary objects" in stderr - def test_push_on_locked_repo_by_other_user_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( - 'hg clone', clone_url, tmpdir.strpath) + def test_push_on_locked_repo_by_other_user_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("hg clone", clone_url, tmpdir.strpath) # lock repo r = Repository.get_by_repo_name(HG_REPO) # let this user actually push ! - RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, - perm='repository.write') + RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm="repository.write") Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # push fails repo is locked by other user ! - push_url = rc_web_server.repo_clone_url( - HG_REPO, - user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) - stdout, stderr = _add_files_and_push( - 'hg', tmpdir.strpath, clone_url=push_url) - msg = ("""abort: HTTP Error 400: Repository `%s` locked by user `%s`""" - % (HG_REPO, TEST_USER_ADMIN_LOGIN)) + push_url = rcstack.repo_clone_url(HG_REPO, user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) + stdout, stderr = _add_files_and_push("hg", tmpdir.strpath, clone_url=push_url) + msg = f"abort: HTTP Error 400: Repository `{HG_REPO}` locked by user `{TEST_USER_ADMIN_LOGIN}`" + assert msg in stderr - def test_push_on_locked_repo_by_other_user_git( - self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( - 'git clone', clone_url, tmpdir.strpath) + def test_push_on_locked_repo_by_other_user_git(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute("git clone", clone_url, tmpdir.strpath) # lock repo r = Repository.get_by_repo_name(GIT_REPO) # let this user actually push ! - RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, - perm='repository.write') + RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm="repository.write") Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) # push fails repo is locked by other user! - push_url = rc_web_server.repo_clone_url( - GIT_REPO, - user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) - stdout, stderr = _add_files_and_push( - 'git', tmpdir.strpath, clone_url=push_url) + push_url = rcstack.repo_clone_url(GIT_REPO, user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) + stdout, stderr = _add_files_and_push("git", tmpdir.strpath, clone_url=push_url) - err = 'Repository `%s` locked by user `%s`' % ( - GIT_REPO, TEST_USER_ADMIN_LOGIN) + err = f"Repository `{GIT_REPO}` locked by user `{TEST_USER_ADMIN_LOGIN}`" assert err in stderr diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py index 1d09112c..fa469b0a 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py @@ -26,12 +26,16 @@ from rhodecode.tests.vcs_operations import ( Command, _check_proper_hg_push, _check_proper_git_push, _add_files_and_push) -@pytest.mark.usefixtures("disable_anonymous_user") -class TestVCSOperations(object): +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", +) +class TestVCSOperationsNewBranchPush(object): - def test_push_new_branch_hg(self, rc_web_server, tmpdir, user_util): + def test_push_new_branch_hg(self, rcstack, tmpdir, user_util): repo = user_util.create_repo(repo_type='hg') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'hg clone', clone_url, tmpdir.strpath) @@ -48,9 +52,9 @@ class TestVCSOperations(object): _check_proper_hg_push(stdout, stderr) - def test_push_new_branch_git(self, rc_web_server, tmpdir, user_util): + def test_push_new_branch_git(self, rcstack, tmpdir, user_util): repo = user_util.create_repo(repo_type='git') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'git clone', clone_url, tmpdir.strpath) @@ -67,13 +71,13 @@ class TestVCSOperations(object): _check_proper_git_push(stdout, stderr, branch='dev') def test_push_new_branch_hg_with_branch_permissions_no_force_push( - self, rc_web_server, tmpdir, user_util, branch_permission_setter): + self, rcstack, tmpdir, user_util, branch_permission_setter): repo = user_util.create_repo(repo_type='hg') repo_name = repo.repo_name username = TEST_USER_ADMIN_LOGIN branch_permission_setter(repo_name, username, permission='branch.push') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'hg clone', clone_url, tmpdir.strpath) @@ -91,13 +95,13 @@ class TestVCSOperations(object): _check_proper_hg_push(stdout, stderr) def test_push_new_branch_git_with_branch_permissions_no_force_push( - self, rc_web_server, tmpdir, user_util, branch_permission_setter): + self, rcstack, tmpdir, user_util, branch_permission_setter): repo = user_util.create_repo(repo_type='git') repo_name = repo.repo_name username = TEST_USER_ADMIN_LOGIN branch_permission_setter(repo_name, username, permission='branch.push') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) Command(os.path.dirname(tmpdir.strpath)).execute( 'git clone', clone_url, tmpdir.strpath) diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py new file mode 100644 index 00000000..899261b8 --- /dev/null +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py @@ -0,0 +1,81 @@ +# Copyright (C) 2010-2023 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 os +import pytest + +from rhodecode.tests.fixtures.rcextensions_fixtures import store_rcextensions +from rhodecode.tests.vcs_operations import ( + Command, _check_proper_hg_push, _check_proper_git_push, + _add_files_and_push) + + +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsWithRCExtensions(object): + + def test_push_when_rcextensions_fail_hg(self, rcstack, tmpdir, user_util, rcextensions, rcextensions_modification): + repo = user_util.create_repo(repo_type='hg') + clone_url = rcstack.repo_clone_url(repo.repo_name) + Command(os.path.dirname(tmpdir.strpath)).execute( + 'hg clone', clone_url, tmpdir.strpath) + + stdout, stderr = _add_files_and_push( + 'hg', tmpdir.strpath, clone_url=clone_url) + _check_proper_hg_push(stdout, stderr) + + mods = [ + ('_pre_push_hook', + """ + return HookResponse(1, 'HOOK_FAIL_TEST_HG') + """) + ] + rcstack_location = os.path.dirname(rcstack.config_file) + with rcextensions_modification(rcstack_location, mods): + stdout, stderr = _add_files_and_push( + 'hg', tmpdir.strpath, clone_url=clone_url) + assert 'HOOK_FAIL_TEST_HG' in stdout + + def test_push_when_rcextensions_fail_git(self, rcstack, tmpdir, user_util, rcextensions, rcextensions_modification): + repo = user_util.create_repo(repo_type='git') + clone_url = rcstack.repo_clone_url(repo.repo_name) + Command(os.path.dirname(tmpdir.strpath)).execute( + 'git clone', clone_url, tmpdir.strpath) + + stdout, stderr = _add_files_and_push( + 'git', tmpdir.strpath, clone_url=clone_url) + _check_proper_git_push(stdout, stderr) + + mods = [ + ('_pre_push_hook', + """ + return HookResponse(1, 'HOOK_FAIL_TEST_GIT') + """) + ] + + rcstack_location = os.path.dirname(rcstack.config_file) + with rcextensions_modification(rcstack_location, mods): + stdout, stderr = _add_files_and_push( + 'git', tmpdir.strpath, clone_url=clone_url) + assert 'HOOK_FAIL_TEST_GIT' in stderr + diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py index b28e9e1f..d5bff9a4 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py @@ -32,13 +32,16 @@ from rhodecode.tests.vcs_operations import ( _add_files_and_push) -@pytest.mark.usefixtures("disable_locking") +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_locking", +) class TestVCSOperationsSpecial(object): - def test_git_sets_default_branch_if_not_master( - self, backend_git, tmpdir, rc_web_server): - empty_repo = backend_git.create_repo() - clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name) + def test_git_sets_default_branch_if_not_master(self, vcs_backend_git, tmpdir, rcstack): + empty_repo = vcs_backend_git.create_repo() + clone_url = rcstack.repo_clone_url(empty_repo.repo_name) cmd = Command(tmpdir.strpath) cmd.execute('git clone', clone_url) @@ -63,25 +66,24 @@ class TestVCSOperationsSpecial(object): # Doing an explicit commit in order to get latest user logs on MySQL Session().commit() - def test_git_fetches_from_remote_repository_with_annotated_tags( - self, backend_git, rc_web_server): + def test_git_fetches_from_remote_repository_with_annotated_tags(self, vcs_backend_git, rcstack): # Note: This is a test specific to the git backend. It checks the # integration of fetching from a remote repository which contains # annotated tags. # Dulwich shows this specific behavior only when # operating against a remote repository. - source_repo = backend_git['annotated-tag'] - target_vcs_repo = backend_git.create_repo().scm_instance() - target_vcs_repo.fetch(rc_web_server.repo_clone_url(source_repo.repo_name)) + source_repo = vcs_backend_git['annotated-tag'] + target_vcs_repo = vcs_backend_git.create_repo().scm_instance() + target_vcs_repo.fetch(rcstack.repo_clone_url(source_repo.repo_name)) - def test_git_push_shows_pull_request_refs(self, backend_git, rc_web_server, tmpdir): + def test_git_push_shows_pull_request_refs(self, vcs_backend_git, rcstack, tmpdir): """ test if remote info about refs is visible """ - empty_repo = backend_git.create_repo() + empty_repo = vcs_backend_git.create_repo() - clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name) + clone_url = rcstack.repo_clone_url(empty_repo.repo_name) cmd = Command(tmpdir.strpath) cmd.execute('git clone', clone_url) @@ -97,7 +99,7 @@ class TestVCSOperationsSpecial(object): stdout, stderr = repo_cmd.execute('git push --verbose origin master') _check_proper_git_push(stdout, stderr, branch='master') - ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=master' + ref = f'{rcstack.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=master' assert f'remote: RhodeCode: open pull request link: {ref}' in stderr assert 'remote: RhodeCode: push completed' in stderr @@ -129,14 +131,14 @@ class TestVCSOperationsSpecial(object): stdout, stderr = repo_cmd.execute('git push --verbose origin feature') _check_proper_git_push(stdout, stderr, branch='feature') - ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=feature' + ref = f'{rcstack.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=feature' assert f'remote: RhodeCode: open pull request link: {ref}' in stderr assert 'remote: RhodeCode: push completed' in stderr - def test_hg_push_shows_pull_request_refs(self, backend_hg, rc_web_server, tmpdir): - empty_repo = backend_hg.create_repo() + def test_hg_push_shows_pull_request_refs(self, vcs_backend_hg, rcstack, tmpdir): + empty_repo = vcs_backend_hg.create_repo() - clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name) + clone_url = rcstack.repo_clone_url(empty_repo.repo_name) cmd = Command(tmpdir.strpath) cmd.execute('hg clone', clone_url) @@ -154,7 +156,7 @@ class TestVCSOperationsSpecial(object): stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url) _check_proper_hg_push(stdout, stderr, branch='default') - ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default' + ref = f'{rcstack.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default' assert f'remote: RhodeCode: open pull request link: {ref}' in stdout assert 'remote: RhodeCode: push completed' in stdout @@ -189,14 +191,14 @@ class TestVCSOperationsSpecial(object): stdout, stderr = repo_cmd.execute('hg push --new-branch --verbose', clone_url) _check_proper_hg_push(stdout, stderr, branch='feature') - ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=feature' + ref = f'{rcstack.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=feature' assert f'remote: RhodeCode: open pull request link: {ref}' in stdout assert 'remote: RhodeCode: push completed' in stdout - def test_hg_push_shows_pull_request_refs_book(self, backend_hg, rc_web_server, tmpdir): - empty_repo = backend_hg.create_repo() + def test_hg_push_shows_pull_request_refs_book(self, vcs_backend_hg, rcstack, tmpdir): + empty_repo = vcs_backend_hg.create_repo() - clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name) + clone_url = rcstack.repo_clone_url(empty_repo.repo_name) cmd = Command(tmpdir.strpath) cmd.execute('hg clone', clone_url) @@ -214,7 +216,7 @@ class TestVCSOperationsSpecial(object): stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url) _check_proper_hg_push(stdout, stderr, branch='default') - ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default' + ref = f'{rcstack.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default' assert f'remote: RhodeCode: open pull request link: {ref}' in stdout assert 'remote: RhodeCode: push completed' in stdout @@ -232,23 +234,23 @@ class TestVCSOperationsSpecial(object): stdout, stderr = repo_cmd.execute('hg push -B feature2 --verbose', clone_url) _check_proper_hg_push(stdout, stderr, branch='default') - ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default' + ref = f'{rcstack.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default' assert f'remote: RhodeCode: open pull request link: {ref}' in stdout - ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?bookmark=feature2' + ref = f'{rcstack.host_url()}/{empty_repo.repo_name}/pull-request/new?bookmark=feature2' assert f'remote: RhodeCode: open pull request link: {ref}' in stdout assert 'remote: RhodeCode: push completed' in stdout assert 'exporting bookmark feature2' in stdout - def test_push_is_forbidden_on_archived_repo_hg(self, backend_hg, rc_web_server, tmpdir): - empty_repo = backend_hg.create_repo() + def test_push_is_forbidden_on_archived_repo_hg(self, vcs_backend_hg, rcstack, tmpdir): + empty_repo = vcs_backend_hg.create_repo() repo_name = empty_repo.repo_name repo = Repository.get_by_repo_name(repo_name) repo.archived = True Session().commit() - clone_url = rc_web_server.repo_clone_url(repo_name) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(repo_name) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) stdout, stderr = _add_files_and_push( @@ -256,16 +258,16 @@ class TestVCSOperationsSpecial(object): assert 'abort: HTTP Error 403: Forbidden' in stderr - def test_push_is_forbidden_on_archived_repo_git(self, backend_git, rc_web_server, tmpdir): - empty_repo = backend_git.create_repo() + def test_push_is_forbidden_on_archived_repo_git(self, vcs_backend_git, rcstack, tmpdir): + empty_repo = vcs_backend_git.create_repo() repo_name = empty_repo.repo_name repo = Repository.get_by_repo_name(repo_name) repo.archived = True Session().commit() - clone_url = rc_web_server.repo_clone_url(repo_name) - stdout, stderr = Command('/tmp').execute( + clone_url = rcstack.repo_clone_url(repo_name) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) stdout, stderr = _add_files_and_push( diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py b/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py index 0b60ebfc..4dc7a98a 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py @@ -49,97 +49,110 @@ def get_cli_flags(username, password): return flags, auth -@pytest.mark.usefixtures("disable_locking", "disable_anonymous_user") -class TestVCSOperations(object): +@pytest.mark.usefixtures( + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", +) +class TestVCSOperationsSVN(object): - def test_clone_svn_repo_by_admin(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(SVN_REPO) - username, password = rc_web_server.repo_clone_credentials() + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_clone_svn_repo_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(SVN_REPO) + username, password = rcstack.repo_clone_credentials() - cmd = Command('/tmp') + cmd = Command(tmpdir.strpath) flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'svn') cmd.assert_returncode_success() - def test_clone_svn_repo_by_id_by_admin(self, rc_web_server, tmpdir): + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_clone_svn_repo_by_id_by_admin(self, rcstack, tmpdir): repo_id = Repository.get_by_repo_name(SVN_REPO).repo_id - username, password = rc_web_server.repo_clone_credentials() + username, password = rcstack.repo_clone_credentials() - clone_url = rc_web_server.repo_clone_url('_%s' % repo_id) - cmd = Command('/tmp') + clone_url = rcstack.repo_clone_url('_%s' % repo_id) + cmd = Command(tmpdir.strpath) flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'svn') cmd.assert_returncode_success() - def test_clone_svn_repo_with_group_by_admin(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(SVN_REPO_WITH_GROUP) - username, password = rc_web_server.repo_clone_credentials() + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_clone_svn_repo_with_group_by_admin(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(SVN_REPO_WITH_GROUP) + username, password = rcstack.repo_clone_credentials() flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) _check_proper_clone(stdout, stderr, 'svn') - cmd.assert_returncode_success() + rcstack.assert_returncode_success() - def test_clone_wrong_credentials_svn(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(SVN_REPO) - username, password = rc_web_server.repo_clone_credentials() + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_clone_wrong_credentials_svn(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(SVN_REPO) + username, password = rcstack.repo_clone_credentials() password = 'bad-password' flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) assert 'fatal: Authentication failed' in stderr - def test_clone_svn_with_slashes(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url('//' + SVN_REPO) + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_clone_svn_with_slashes(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url('//' + SVN_REPO) username, password = '', '' flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url) assert 'not found' in stderr + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') def test_clone_existing_path_svn_not_in_database( - self, rc_web_server, tmpdir, fs_repo_only): + self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-git', repo_type='git') - clone_url = rc_web_server.repo_clone_url(db_name) + clone_url = rcstack.repo_clone_url(db_name) username, password = '', '' flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) assert 'not found' in stderr + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') def test_clone_existing_path_svn_not_in_database_different_scm( - self, rc_web_server, tmpdir, fs_repo_only): + self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-hg', repo_type='hg') - clone_url = rc_web_server.repo_clone_url(db_name) + clone_url = rcstack.repo_clone_url(db_name) username, password = '', '' flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) assert 'not found' in stderr - def test_clone_non_existing_store_path_svn(self, rc_web_server, tmpdir, user_util): + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_clone_non_existing_store_path_svn(self, rcstack, tmpdir, user_util): repo = user_util.create_repo(repo_type='git') - clone_url = rc_web_server.repo_clone_url(repo.repo_name) + clone_url = rcstack.repo_clone_url(repo.repo_name) # Damage repo by removing it's folder RepoModel()._delete_filesystem_repo(repo) @@ -147,16 +160,17 @@ class TestVCSOperations(object): username, password = '', '' flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) assert 'not found' in stderr - def test_push_new_file_svn(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(SVN_REPO) + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_push_new_file_svn(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(SVN_REPO) username, password = '', '' flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) # commit some stuff into this repo @@ -165,37 +179,40 @@ class TestVCSOperations(object): _check_proper_svn_push(stdout, stderr) - def test_push_wrong_credentials_svn(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(SVN_REPO) + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_push_wrong_credentials_svn(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(SVN_REPO) - username, password = rc_web_server.repo_clone_credentials() + username, password = rcstack.repo_clone_credentials() flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) - push_url = rc_web_server.repo_clone_url( + push_url = rcstack.repo_clone_url( SVN_REPO, user='bad', passwd='name') stdout, stderr = _add_files_and_push( 'svn', tmpdir.strpath, clone_url=push_url, username=username, password=password) assert 'fatal: Authentication failed' in stderr - def test_push_back_to_wrong_url_svn(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(SVN_REPO) + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_push_back_to_wrong_url_svn(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(SVN_REPO) username, password = '', '' flags, auth = get_cli_flags(username, password) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) stdout, stderr = _add_files_and_push( 'svn', tmpdir.strpath, - clone_url=rc_web_server.repo_clone_url('not-existing'), username=username, password=password) + clone_url=rcstack.repo_clone_url('not-existing'), username=username, password=password) assert 'not found' in stderr - def test_ip_restriction_svn(self, rc_web_server, tmpdir): + @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') + def test_ip_restriction_svn(self, rcstack, tmpdir): user_model = UserModel() username, password = '', '' flags, auth = get_cli_flags(username, password) @@ -204,9 +221,9 @@ class TestVCSOperations(object): user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32') Session().commit() time.sleep(2) - clone_url = rc_web_server.repo_clone_url(SVN_REPO) + clone_url = rcstack.repo_clone_url(SVN_REPO) - stdout, stderr = Command('/tmp').execute( + stdout, stderr = Command(tmpdir.strpath).execute( f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) msg = "The requested URL returned error: 403" assert msg in stderr @@ -218,7 +235,7 @@ class TestVCSOperations(object): time.sleep(2) - cmd = Command('/tmp') + cmd = Command(tmpdir.strpath) stdout, stderr = cmd.execute(f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) cmd.assert_returncode_success() _check_proper_clone(stdout, stderr, 'svn') diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py index eed4aefd..ecee4f9c 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py @@ -1,4 +1,3 @@ - # Copyright (C) 2010-2023 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify @@ -19,11 +18,6 @@ """ Test suite for making push/pull operations, on specially modified INI files - -.. important:: - - You must have git >= 1.8.5 for tests to work fine. With 68b939b git started - to redirect things to stderr instead of stdout. """ import pytest @@ -37,54 +31,59 @@ connection_available = pytest.mark.skipif( not check_httpbin_connection(), reason="No outside internet connection available") + @pytest.mark.usefixtures( - "disable_locking", "disable_anonymous_user", - "enable_webhook_push_integration") -class TestVCSOperationsOnCustomIniConfig(object): + "init_pyramid_app", + "repo_group_repos", + "disable_anonymous_user", + "disable_locking", + "enable_webhook_push_integration" +) +class TestVCSOperationsTagPush(object): @connection_available - def test_push_tag_with_commit_hg(self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(HG_REPO) - stdout, stderr = Command('/tmp').execute( + def test_push_tag_with_commit_hg(self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(HG_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'hg clone', clone_url, tmpdir.strpath) - push_url = rc_web_server.repo_clone_url(HG_REPO) + push_url = rcstack.repo_clone_url(HG_REPO) _add_files_and_push( 'hg', tmpdir.strpath, clone_url=push_url, tags=[{'name': 'v1.0.0', 'commit': 'added tag v1.0.0'}]) - rc_log = rc_web_server.get_rc_log() - assert 'ERROR' not in rc_log - assert "{'name': 'v1.0.0'," in rc_log + celery_log = open(rcstack.rcstack_data.celery_log).read() + assert 'ERROR' not in celery_log + assert "{'name': 'v1.0.0'," in celery_log @connection_available def test_push_tag_with_commit_git( - self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) - push_url = rc_web_server.repo_clone_url(GIT_REPO) + push_url = rcstack.repo_clone_url(GIT_REPO) _add_files_and_push( 'git', tmpdir.strpath, clone_url=push_url, tags=[{'name': 'v1.0.0', 'commit': 'added tag v1.0.0'}]) - rc_log = rc_web_server.get_rc_log() - assert 'ERROR' not in rc_log - assert "{'name': 'v1.0.0'," in rc_log + celery_log = open(rcstack.rcstack_data.celery_log).read() + assert 'ERROR' not in celery_log + assert "{'name': 'v1.0.0'," in celery_log @connection_available def test_push_tag_with_no_commit_git( - self, rc_web_server, tmpdir): - clone_url = rc_web_server.repo_clone_url(GIT_REPO) - stdout, stderr = Command('/tmp').execute( + self, rcstack, tmpdir): + clone_url = rcstack.repo_clone_url(GIT_REPO) + stdout, stderr = Command(tmpdir.strpath).execute( 'git clone', clone_url, tmpdir.strpath) - push_url = rc_web_server.repo_clone_url(GIT_REPO) + push_url = rcstack.repo_clone_url(GIT_REPO) _add_files_and_push( 'git', tmpdir.strpath, clone_url=push_url, tags=[{'name': 'v1.0.0', 'commit': 'added tag v1.0.0'}]) - rc_log = rc_web_server.get_rc_log() - assert 'ERROR' not in rc_log - assert "{'name': 'v1.0.0'," in rc_log + celery_log = open(rcstack.rcstack_data.celery_log).read() + assert 'ERROR' not in celery_log + assert "{'name': 'v1.0.0'," in celery_log diff --git a/rhodecode/tests/vcsserver_http.ini b/rhodecode/tests/vcsserver_http.ini index 62c09696..f72a785d 100644 --- a/rhodecode/tests/vcsserver_http.ini +++ b/rhodecode/tests/vcsserver_http.ini @@ -60,7 +60,7 @@ vcs.svn.redis_conn = redis://redis:6379/0 ; Default cache dir for caches. Putting this into a ramdisk can boost performance. ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space -cache_dir = %(here)s/data +cache_dir = %(here)s/.rc-vcs-test-data ; *************************************** ; `repo_object` cache, default file based @@ -141,7 +141,7 @@ level = NOTSET handlers = console [logger_vcsserver] -level = INFO +level = DEBUG handlers = qualname = vcsserver propagate = 1 From 82f0f8d214c7e2260623995d8b89a2ecd8f9b639 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 16:39:07 +0100 Subject: [PATCH 46/99] core: updated copyright to 2024 --- conftest.py | 2 +- rhodecode/__init__.py | 2 +- rhodecode/api/__init__.py | 2 +- rhodecode/api/exc.py | 2 +- rhodecode/api/tests/__init__.py | 2 +- rhodecode/api/tests/conftest.py | 2 +- .../api/tests/test_add_user_to_user_group.py | 2 +- rhodecode/api/tests/test_api.py | 2 +- rhodecode/api/tests/test_cleanup_sessions.py | 2 +- .../api/tests/test_close_pull_request.py | 2 +- rhodecode/api/tests/test_comment_commit.py | 2 +- .../api/tests/test_comment_pull_request.py | 3 +-- rhodecode/api/tests/test_create_gist.py | 3 +-- .../api/tests/test_create_pull_request.py | 3 +-- rhodecode/api/tests/test_create_repo.py | 3 +-- rhodecode/api/tests/test_create_repo_group.py | 3 +-- rhodecode/api/tests/test_create_user.py | 3 +-- rhodecode/api/tests/test_create_user_group.py | 3 +-- rhodecode/api/tests/test_delete_gist.py | 3 +-- rhodecode/api/tests/test_delete_repo.py | 3 +-- rhodecode/api/tests/test_delete_repo_group.py | 3 +-- rhodecode/api/tests/test_delete_user.py | 3 +-- rhodecode/api/tests/test_delete_user_group.py | 3 +-- rhodecode/api/tests/test_deprecated_api.py | 3 +-- rhodecode/api/tests/test_fork_repo.py | 3 +-- rhodecode/api/tests/test_fts_search.py | 3 +-- rhodecode/api/tests/test_get_gist.py | 3 +-- rhodecode/api/tests/test_get_gists.py | 3 +-- rhodecode/api/tests/test_get_ip.py | 3 +-- rhodecode/api/tests/test_get_locks.py | 3 +-- rhodecode/api/tests/test_get_method.py | 3 +-- rhodecode/api/tests/test_get_pull_request.py | 3 +-- .../tests/test_get_pull_request_comments.py | 3 +-- rhodecode/api/tests/test_get_pull_requests.py | 3 +-- rhodecode/api/tests/test_get_repo.py | 3 +-- .../api/tests/test_get_repo_changeset.py | 3 +-- rhodecode/api/tests/test_get_repo_comments.py | 3 +-- rhodecode/api/tests/test_get_repo_group.py | 3 +-- rhodecode/api/tests/test_get_repo_groups.py | 3 +-- rhodecode/api/tests/test_get_repo_nodes.py | 3 +-- rhodecode/api/tests/test_get_repo_refs.py | 3 +-- rhodecode/api/tests/test_get_repos.py | 3 +-- rhodecode/api/tests/test_get_server_info.py | 3 +-- rhodecode/api/tests/test_get_user.py | 3 +-- rhodecode/api/tests/test_get_user_group.py | 3 +-- rhodecode/api/tests/test_get_user_groups.py | 3 +-- rhodecode/api/tests/test_get_users.py | 3 +-- .../tests/test_grant_user_group_permission.py | 3 +-- ...ant_user_group_permission_to_repo_group.py | 3 +-- ...ant_user_group_permission_to_user_group.py | 3 +-- .../api/tests/test_grant_user_permission.py | 3 +-- ...est_grant_user_permission_to_repo_group.py | 3 +-- ...est_grant_user_permission_to_user_group.py | 3 +-- rhodecode/api/tests/test_invalidate_cache.py | 3 +-- .../api/tests/test_merge_pull_request.py | 3 +-- rhodecode/api/tests/test_pull.py | 3 +-- .../api/tests/test_remove_field_from_repo.py | 3 +-- .../tests/test_remove_user_from_user_group.py | 3 +-- rhodecode/api/tests/test_repo_locking.py | 3 +-- rhodecode/api/tests/test_rescan_repos.py | 3 +-- .../test_revoke_user_group_permission.py | 3 +-- ...e_user_group_permission_from_repo_group.py | 3 +-- ...e_user_group_permission_from_user_group.py | 3 +-- .../api/tests/test_revoke_user_permission.py | 3 +-- ..._revoke_user_permission_from_repo_group.py | 3 +-- ..._revoke_user_permission_from_user_group.py | 3 +-- rhodecode/api/tests/test_service_api.py | 3 +-- rhodecode/api/tests/test_store_exception.py | 3 +-- .../api/tests/test_update_pull_request.py | 3 +-- rhodecode/api/tests/test_update_repo.py | 3 +-- rhodecode/api/tests/test_update_repo_group.py | 3 +-- rhodecode/api/tests/test_update_user.py | 3 +-- rhodecode/api/tests/test_update_user_group.py | 3 +-- rhodecode/api/tests/test_utils.py | 3 +-- rhodecode/api/tests/utils.py | 3 +-- rhodecode/api/utils.py | 2 +- rhodecode/api/views/__init__.py | 2 +- rhodecode/api/views/deprecated_api.py | 2 +- rhodecode/api/views/gist_api.py | 2 +- rhodecode/api/views/pull_request_api.py | 2 +- rhodecode/api/views/repo_api.py | 2 +- rhodecode/api/views/repo_group_api.py | 2 +- rhodecode/api/views/search_api.py | 2 +- rhodecode/api/views/server_api.py | 2 +- rhodecode/api/views/service_api.py | 2 +- rhodecode/api/views/testing_api.py | 2 +- rhodecode/api/views/user_api.py | 2 +- rhodecode/api/views/user_group_api.py | 2 +- rhodecode/apps/__init__.py | 2 +- rhodecode/apps/_base/__init__.py | 2 +- rhodecode/apps/_base/interfaces.py | 2 +- rhodecode/apps/_base/navigation.py | 3 +-- rhodecode/apps/_base/subscribers.py | 2 +- rhodecode/apps/admin/__init__.py | 2 +- .../apps/admin/tests/test_admin_audit_logs.py | 3 +-- .../admin/tests/test_admin_auth_settings.py | 3 +-- .../apps/admin/tests/test_admin_defaults.py | 3 +-- .../apps/admin/tests/test_admin_main_views.py | 3 +-- .../admin/tests/test_admin_permissions.py | 3 +-- .../apps/admin/tests/test_admin_repos.py | 3 +-- .../tests/test_admin_repository_groups.py | 3 +-- .../apps/admin/tests/test_admin_settings.py | 3 +-- .../admin/tests/test_admin_user_groups.py | 3 +-- .../apps/admin/tests/test_admin_users.py | 3 +-- .../admin/tests/test_admin_users_ssh_keys.py | 3 +-- rhodecode/apps/admin/views/__init__.py | 2 +- rhodecode/apps/admin/views/artifacts.py | 2 +- rhodecode/apps/admin/views/audit_logs.py | 2 +- rhodecode/apps/admin/views/automation.py | 2 +- rhodecode/apps/admin/views/defaults.py | 2 +- .../apps/admin/views/exception_tracker.py | 2 +- rhodecode/apps/admin/views/main_views.py | 2 +- .../apps/admin/views/open_source_licenses.py | 2 +- rhodecode/apps/admin/views/permissions.py | 2 +- .../apps/admin/views/process_management.py | 2 +- rhodecode/apps/admin/views/repo_groups.py | 2 +- rhodecode/apps/admin/views/repositories.py | 2 +- rhodecode/apps/admin/views/scheduler.py | 2 +- rhodecode/apps/admin/views/sessions.py | 2 +- rhodecode/apps/admin/views/settings.py | 2 +- rhodecode/apps/admin/views/svn_config.py | 2 +- rhodecode/apps/admin/views/system_info.py | 4 +--- rhodecode/apps/admin/views/user_groups.py | 4 +--- rhodecode/apps/admin/views/users.py | 2 +- rhodecode/apps/channelstream/__init__.py | 2 +- rhodecode/apps/channelstream/views.py | 2 +- rhodecode/apps/debug_style/__init__.py | 4 +--- rhodecode/apps/debug_style/views.py | 4 +--- rhodecode/apps/file_store/__init__.py | 2 +- .../apps/file_store/backends/__init__.py | 2 +- rhodecode/apps/file_store/backends/base.py | 2 +- .../apps/file_store/backends/filesystem.py | 2 +- .../file_store/backends/filesystem_legacy.py | 2 +- .../apps/file_store/backends/objectstore.py | 2 +- rhodecode/apps/file_store/config_keys.py | 2 +- rhodecode/apps/file_store/exceptions.py | 2 +- rhodecode/apps/file_store/extensions.py | 2 +- rhodecode/apps/file_store/tests/__init__.py | 2 +- .../tests/test_filestore_backends.py | 2 +- .../test_filestore_filesystem_backend.py | 2 +- ...t_filestore_legacy_and_v2_compatability.py | 2 +- .../tests/test_filestore_legacy_backend.py | 2 +- .../test_filestore_objectstore_backend.py | 2 +- .../apps/file_store/tests/test_upload_file.py | 2 +- rhodecode/apps/file_store/utils.py | 2 +- rhodecode/apps/file_store/views.py | 2 +- rhodecode/apps/gist/__init__.py | 2 +- rhodecode/apps/gist/tests/__init__.py | 2 +- rhodecode/apps/gist/tests/test_admin_gists.py | 2 +- rhodecode/apps/gist/views.py | 2 +- rhodecode/apps/home/__init__.py | 2 +- rhodecode/apps/home/tests/__init__.py | 2 +- .../home/tests/test_get_goto_switched_data.py | 2 +- .../home/tests/test_get_repo_list_data.py | 2 +- .../apps/home/tests/test_get_user_data.py | 2 +- .../home/tests/test_get_user_group_data.py | 21 +------------------ rhodecode/apps/home/tests/test_home.py | 2 +- rhodecode/apps/home/views.py | 2 +- rhodecode/apps/hovercards/__init__.py | 2 +- rhodecode/apps/hovercards/views.py | 2 +- rhodecode/apps/journal/__init__.py | 2 +- rhodecode/apps/journal/tests/__init__.py | 2 +- rhodecode/apps/journal/tests/test_journal.py | 3 +-- rhodecode/apps/journal/views.py | 2 +- rhodecode/apps/login/__init__.py | 2 +- rhodecode/apps/login/tests/test_login.py | 2 +- .../apps/login/tests/test_password_reset.py | 2 +- .../apps/login/tests/test_register_captcha.py | 2 +- rhodecode/apps/login/views.py | 2 +- rhodecode/apps/my_account/__init__.py | 2 +- rhodecode/apps/my_account/tests/__init__.py | 2 +- .../tests/test_my_account_auth_tokens.py | 2 +- .../my_account/tests/test_my_account_edit.py | 21 +------------------ .../tests/test_my_account_emails.py | 2 +- .../tests/test_my_account_notifications.py | 2 +- .../tests/test_my_account_password.py | 3 +-- .../tests/test_my_account_profile.py | 3 +-- .../tests/test_my_account_simple_views.py | 3 +-- .../tests/test_my_account_ssh_keys.py | 3 +-- rhodecode/apps/my_account/views/__init__.py | 2 +- rhodecode/apps/my_account/views/my_account.py | 2 +- .../views/my_account_notifications.py | 2 +- .../my_account/views/my_account_ssh_keys.py | 2 +- rhodecode/apps/ops/__init__.py | 2 +- rhodecode/apps/ops/views.py | 2 +- rhodecode/apps/repo_group/__init__.py | 2 +- rhodecode/apps/repo_group/tests/__init__.py | 2 +- .../tests/test_repo_groups_advanced.py | 2 +- .../tests/test_repo_groups_permissions.py | 2 +- .../tests/test_repo_groups_settings.py | 3 +-- rhodecode/apps/repo_group/views/__init__.py | 2 +- .../repo_group/views/repo_group_advanced.py | 2 +- .../views/repo_group_permissions.py | 2 +- .../repo_group/views/repo_group_settings.py | 2 +- rhodecode/apps/repository/__init__.py | 2 +- rhodecode/apps/repository/tests/__init__.py | 2 +- .../tests/test_pull_requests_list.py | 2 +- .../repository/tests/test_repo_bookmarks.py | 2 +- .../repository/tests/test_repo_branches.py | 2 +- .../repository/tests/test_repo_changelog.py | 2 +- .../tests/test_repo_commit_comments.py | 2 +- .../repository/tests/test_repo_commits.py | 3 +-- .../repository/tests/test_repo_compare.py | 3 +-- .../tests/test_repo_compare_local.py | 3 +-- .../tests/test_repo_compare_on_single_file.py | 3 +-- .../apps/repository/tests/test_repo_feed.py | 3 +-- .../apps/repository/tests/test_repo_files.py | 3 +-- .../apps/repository/tests/test_repo_forks.py | 3 +-- .../tests/test_repo_issue_tracker.py | 3 +-- .../tests/test_repo_maintainance.py | 3 +-- .../repository/tests/test_repo_permissions.py | 3 +-- .../tests/test_repo_pullrequests.py | 2 +- .../repository/tests/test_repo_settings.py | 3 +-- .../tests/test_repo_settings_advanced.py | 3 +-- .../repository/tests/test_repo_summary.py | 2 +- .../apps/repository/tests/test_repo_tags.py | 3 +-- .../tests/test_repo_vcs_settings.py | 3 +-- .../repository/tests/test_vcs_settings.py | 3 +-- rhodecode/apps/repository/utils.py | 2 +- rhodecode/apps/repository/views/__init__.py | 2 +- .../apps/repository/views/repo_artifacts.py | 2 +- .../apps/repository/views/repo_audit_logs.py | 2 +- .../apps/repository/views/repo_automation.py | 2 +- .../apps/repository/views/repo_bookmarks.py | 2 +- .../views/repo_branch_permissions.py | 2 +- .../apps/repository/views/repo_branches.py | 2 +- .../apps/repository/views/repo_caches.py | 2 +- .../apps/repository/views/repo_changelog.py | 2 +- .../apps/repository/views/repo_checks.py | 2 +- .../apps/repository/views/repo_commits.py | 2 +- .../apps/repository/views/repo_compare.py | 2 +- rhodecode/apps/repository/views/repo_feed.py | 2 +- rhodecode/apps/repository/views/repo_files.py | 2 +- rhodecode/apps/repository/views/repo_forks.py | 2 +- .../repository/views/repo_maintainance.py | 2 +- .../apps/repository/views/repo_permissions.py | 2 +- .../repository/views/repo_pull_requests.py | 2 +- .../repository/views/repo_review_rules.py | 2 +- .../apps/repository/views/repo_settings.py | 2 +- .../views/repo_settings_advanced.py | 2 +- .../repository/views/repo_settings_fields.py | 2 +- .../views/repo_settings_issue_trackers.py | 2 +- .../repository/views/repo_settings_remote.py | 2 +- .../repository/views/repo_settings_vcs.py | 2 +- rhodecode/apps/repository/views/repo_strip.py | 2 +- .../apps/repository/views/repo_summary.py | 2 +- rhodecode/apps/repository/views/repo_tags.py | 2 +- rhodecode/apps/search/__init__.py | 2 +- rhodecode/apps/search/tests/test_search.py | 2 +- rhodecode/apps/search/views.py | 2 +- rhodecode/apps/ssh_support/__init__.py | 2 +- rhodecode/apps/ssh_support/config_keys.py | 2 +- rhodecode/apps/ssh_support/events.py | 2 +- rhodecode/apps/ssh_support/lib/__init__.py | 2 +- .../apps/ssh_support/lib/backends/__init__.py | 2 +- .../apps/ssh_support/lib/backends/base.py | 2 +- .../apps/ssh_support/lib/backends/git.py | 2 +- rhodecode/apps/ssh_support/lib/backends/hg.py | 2 +- .../apps/ssh_support/lib/backends/svn.py | 2 +- .../apps/ssh_support/lib/ssh_wrapper_v1.py | 2 +- .../apps/ssh_support/lib/ssh_wrapper_v2.py | 2 +- rhodecode/apps/ssh_support/lib/utils.py | 2 +- rhodecode/apps/ssh_support/subscribers.py | 2 +- rhodecode/apps/ssh_support/tests/__init__.py | 2 +- rhodecode/apps/ssh_support/tests/conftest.py | 2 +- .../apps/ssh_support/tests/test_server_git.py | 2 +- .../apps/ssh_support/tests/test_server_hg.py | 2 +- .../apps/ssh_support/tests/test_server_svn.py | 2 +- .../tests/test_ssh_authorized_keys_gen.py | 2 +- .../ssh_support/tests/test_ssh_wrapper.py | 2 +- rhodecode/apps/ssh_support/utils.py | 2 +- rhodecode/apps/svn_support/__init__.py | 2 +- rhodecode/apps/svn_support/config_keys.py | 2 +- rhodecode/apps/svn_support/events.py | 2 +- rhodecode/apps/svn_support/subscribers.py | 2 +- .../tests/test_mod_dav_svn_config.py | 2 +- rhodecode/apps/svn_support/utils.py | 2 +- rhodecode/apps/user_group/__init__.py | 2 +- rhodecode/apps/user_group/tests/__init__.py | 2 +- .../apps/user_group/tests/test_user_groups.py | 2 +- .../tests/test_user_groups_permissions.py | 2 +- rhodecode/apps/user_group/views/__init__.py | 2 +- rhodecode/apps/user_group_profile/__init__.py | 2 +- .../apps/user_group_profile/tests/__init__.py | 2 +- .../tests/test_user_group.py | 2 +- rhodecode/apps/user_group_profile/views.py | 2 +- rhodecode/apps/user_profile/__init__.py | 2 +- rhodecode/apps/user_profile/tests/__init__.py | 2 +- .../apps/user_profile/tests/test_users.py | 2 +- rhodecode/apps/user_profile/views.py | 2 +- rhodecode/authentication/__init__.py | 2 +- rhodecode/authentication/base.py | 2 +- rhodecode/authentication/interface.py | 2 +- rhodecode/authentication/plugins/__init__.py | 2 +- .../authentication/plugins/auth_crowd.py | 2 +- .../authentication/plugins/auth_headers.py | 2 +- .../authentication/plugins/auth_jasig_cas.py | 2 +- rhodecode/authentication/plugins/auth_ldap.py | 2 +- rhodecode/authentication/plugins/auth_pam.py | 2 +- .../authentication/plugins/auth_rhodecode.py | 2 +- .../authentication/plugins/auth_token.py | 2 +- rhodecode/authentication/registry.py | 2 +- rhodecode/authentication/routes.py | 2 +- rhodecode/authentication/schema.py | 2 +- rhodecode/authentication/tests/conftest.py | 2 +- .../tests/functional/test_settings.py | 2 +- rhodecode/authentication/views.py | 2 +- rhodecode/config/__init__.py | 2 +- rhodecode/config/conf.py | 2 +- rhodecode/config/config_maker.py | 2 +- rhodecode/config/environment.py | 2 +- rhodecode/config/jsroutes.py | 3 +-- rhodecode/config/middleware.py | 2 +- rhodecode/config/patches.py | 2 +- rhodecode/config/rcextensions/__init__.py | 2 +- .../config/rcextensions/helpers/__init__.py | 2 +- .../rcextensions/helpers/extra_fields.py | 2 +- .../helpers/extract_post_commits.py | 2 +- .../helpers/extract_pre_commits.py | 2 +- .../rcextensions/helpers/extract_pre_files.py | 2 +- .../config/rcextensions/helpers/http_call.py | 2 +- rhodecode/config/rcextensions/hooks.py | 2 +- rhodecode/config/rcextensions/utils.py | 2 +- rhodecode/config/routing_links.py | 3 +-- rhodecode/config/settings_maker.py | 2 +- rhodecode/config/utils.py | 2 +- rhodecode/events/__init__.py | 2 +- rhodecode/events/base.py | 2 +- rhodecode/events/interfaces.py | 2 +- rhodecode/events/pullrequest.py | 2 +- rhodecode/events/repo.py | 2 +- rhodecode/events/repo_group.py | 2 +- rhodecode/events/user.py | 2 +- rhodecode/forms/__init__.py | 2 +- rhodecode/integrations/__init__.py | 2 +- rhodecode/integrations/registry.py | 2 +- rhodecode/integrations/routes.py | 2 +- rhodecode/integrations/schema.py | 2 +- rhodecode/integrations/tests/__init__.py | 2 +- .../integrations/tests/test_integrations.py | 2 +- rhodecode/integrations/types/__init__.py | 2 +- rhodecode/integrations/types/base.py | 2 +- rhodecode/integrations/types/email.py | 2 +- .../integrations/types/handlers/__init__.py | 2 +- .../integrations/types/handlers/slack.py | 2 +- .../integrations/types/handlers/webhook.py | 2 +- rhodecode/integrations/types/slack.py | 2 +- rhodecode/integrations/types/webhook.py | 2 +- rhodecode/integrations/views.py | 2 +- rhodecode/lib/__init__.py | 2 +- rhodecode/lib/_vendor/__init__.py | 2 +- rhodecode/lib/_vendor/authomatic/six.py | 4 +--- rhodecode/lib/action_parser.py | 2 +- rhodecode/lib/api_utils.py | 2 +- rhodecode/lib/audit_logger.py | 2 +- rhodecode/lib/auth.py | 2 +- rhodecode/lib/base.py | 2 +- rhodecode/lib/caching_query.py | 2 +- rhodecode/lib/celerylib/__init__.py | 2 +- rhodecode/lib/celerylib/loader.py | 2 +- rhodecode/lib/celerylib/scheduler.py | 2 +- rhodecode/lib/celerylib/utils.py | 2 +- rhodecode/lib/channelstream.py | 2 +- rhodecode/lib/codeblocks.py | 2 +- rhodecode/lib/colander_utils.py | 2 +- rhodecode/lib/colored_formatter.py | 2 +- rhodecode/lib/config_utils.py | 2 +- rhodecode/lib/datelib.py | 2 +- rhodecode/lib/db_manage.py | 2 +- rhodecode/lib/dbmigrate/__init__.py | 4 +--- rhodecode/lib/dbmigrate/schema/__init__.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_1_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_2_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_3_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_4_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_5_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_5_2.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_6_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_7_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_1_8_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_0_2.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_1_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_2_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_2_3.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py | 3 +-- rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py | 3 +-- rhodecode/lib/dbmigrate/utils.py | 3 +-- rhodecode/lib/dbmigrate/versions/__init__.py | 3 +-- rhodecode/lib/diffs.py | 2 +- rhodecode/lib/enc_utils.py | 2 +- rhodecode/lib/encrypt.py | 2 +- rhodecode/lib/exc_tracking.py | 2 +- rhodecode/lib/exceptions.py | 2 +- rhodecode/lib/ext_json_renderer.py | 2 +- rhodecode/lib/feedgenerator/__init__.py | 2 +- rhodecode/lib/graphmod.py | 2 +- rhodecode/lib/hash_utils.py | 2 +- rhodecode/lib/helpers.py | 2 +- rhodecode/lib/hook_daemon/__init__.py | 2 +- .../lib/hook_daemon/celery_hooks_deamon.py | 2 +- rhodecode/lib/hook_daemon/hook_module.py | 2 +- rhodecode/lib/hooks_base.py | 2 +- rhodecode/lib/hooks_utils.py | 2 +- rhodecode/lib/html_filters.py | 2 +- rhodecode/lib/index/__init__.py | 2 +- rhodecode/lib/index/search_utils.py | 4 +--- rhodecode/lib/index/whoosh.py | 4 +--- rhodecode/lib/index/whoosh_fallback_schema.py | 4 +--- rhodecode/lib/jsonalchemy.py | 3 +-- rhodecode/lib/logging_formatter.py | 2 +- rhodecode/lib/markdown_ext.py | 3 +-- rhodecode/lib/markup_renderer.py | 4 +--- rhodecode/lib/memory_lru_dict.py | 3 +-- rhodecode/lib/middleware/__init__.py | 4 +--- rhodecode/lib/middleware/appenlight.py | 3 +-- rhodecode/lib/middleware/csrf.py | 3 +-- rhodecode/lib/middleware/https_fixup.py | 3 +-- rhodecode/lib/middleware/request_wrapper.py | 4 +--- rhodecode/lib/middleware/simplegit.py | 3 +-- rhodecode/lib/middleware/simplehg.py | 3 +-- rhodecode/lib/middleware/simplesvn.py | 3 +-- rhodecode/lib/middleware/simplevcs.py | 2 +- rhodecode/lib/middleware/utils/__init__.py | 4 +--- rhodecode/lib/middleware/utils/scm_app.py | 4 +--- .../lib/middleware/utils/scm_app_http.py | 4 +--- .../utils/wsgi_app_caller_client.py | 4 +--- rhodecode/lib/middleware/vcs.py | 3 +-- rhodecode/lib/pagination.py | 7 ++----- rhodecode/lib/partial_renderer.py | 3 +-- rhodecode/lib/pidlock.py | 3 +-- rhodecode/lib/plugins/__init__.py | 4 +--- rhodecode/lib/plugins/utils.py | 4 +--- rhodecode/lib/pyramid_shell/__init__.py | 4 +--- rhodecode/lib/pyramid_utils.py | 4 +--- rhodecode/lib/rc_cache/__init__.py | 2 +- rhodecode/lib/rc_cache/backends.py | 2 +- rhodecode/lib/rc_cache/region_meta.py | 2 +- rhodecode/lib/rc_commands/add_artifact.py | 2 +- rhodecode/lib/rc_commands/ishell.py | 2 +- rhodecode/lib/rc_commands/migrate_artifact.py | 2 +- rhodecode/lib/rc_commands/setup_rc.py | 2 +- rhodecode/lib/rc_commands/upgrade_db.py | 2 +- rhodecode/lib/repo_maintenance.py | 2 +- rhodecode/lib/request.py | 2 +- rhodecode/lib/request_counter.py | 2 +- rhodecode/lib/str_utils.py | 2 +- rhodecode/lib/string_renderer.py | 2 +- rhodecode/lib/svn_txn_utils.py | 2 +- rhodecode/lib/system_info.py | 2 +- rhodecode/lib/type_utils.py | 2 +- rhodecode/lib/user_log_filter.py | 2 +- rhodecode/lib/user_sessions.py | 2 +- rhodecode/lib/utils.py | 2 +- rhodecode/lib/utils2.py | 2 +- rhodecode/lib/vcs/__init__.py | 2 +- rhodecode/lib/vcs/backends/__init__.py | 2 +- rhodecode/lib/vcs/backends/base.py | 2 +- rhodecode/lib/vcs/backends/git/__init__.py | 2 +- rhodecode/lib/vcs/backends/git/commit.py | 2 +- rhodecode/lib/vcs/backends/git/diff.py | 2 +- rhodecode/lib/vcs/backends/git/inmemory.py | 2 +- rhodecode/lib/vcs/backends/git/repository.py | 2 +- rhodecode/lib/vcs/backends/hg/__init__.py | 2 +- rhodecode/lib/vcs/backends/hg/commit.py | 2 +- rhodecode/lib/vcs/backends/hg/diff.py | 2 +- rhodecode/lib/vcs/backends/hg/inmemory.py | 2 +- rhodecode/lib/vcs/backends/hg/repository.py | 2 +- rhodecode/lib/vcs/backends/svn/__init__.py | 2 +- rhodecode/lib/vcs/backends/svn/commit.py | 2 +- rhodecode/lib/vcs/backends/svn/diff.py | 2 +- rhodecode/lib/vcs/backends/svn/inmemory.py | 2 +- rhodecode/lib/vcs/backends/svn/repository.py | 2 +- rhodecode/lib/vcs/client_http.py | 2 +- rhodecode/lib/vcs/conf/__init__.py | 2 +- rhodecode/lib/vcs/conf/mtypes.py | 2 +- rhodecode/lib/vcs/conf/settings.py | 2 +- rhodecode/lib/vcs/connection.py | 2 +- rhodecode/lib/vcs/exceptions.py | 2 +- rhodecode/lib/vcs/geventcurl.py | 2 +- rhodecode/lib/vcs/nodes.py | 2 +- rhodecode/lib/vcs/path.py | 2 +- rhodecode/lib/vcs/utils/__init__.py | 2 +- rhodecode/lib/vcs/utils/helpers.py | 2 +- rhodecode/lib/vcs/utils/imports.py | 2 +- rhodecode/lib/vcs/utils/paths.py | 2 +- rhodecode/lib/view_utils.py | 2 +- rhodecode/model/__init__.py | 2 +- rhodecode/model/auth_token.py | 2 +- rhodecode/model/changeset_status.py | 2 +- rhodecode/model/comment.py | 2 +- rhodecode/model/db.py | 2 +- rhodecode/model/forms.py | 2 +- rhodecode/model/gist.py | 2 +- rhodecode/model/integration.py | 2 +- rhodecode/model/meta.py | 2 +- rhodecode/model/notification.py | 2 +- rhodecode/model/permission.py | 2 +- rhodecode/model/pull_request.py | 2 +- rhodecode/model/repo.py | 2 +- rhodecode/model/repo_permission.py | 2 +- rhodecode/model/scm.py | 2 +- rhodecode/model/settings.py | 2 +- rhodecode/model/ssh_key.py | 2 +- rhodecode/model/update.py | 2 +- rhodecode/model/user.py | 2 +- rhodecode/model/user_group.py | 2 +- rhodecode/model/validation_schema/__init__.py | 2 +- .../model/validation_schema/preparers.py | 2 +- .../validation_schema/schemas/__init__.py | 2 +- .../schemas/comment_schema.py | 2 +- .../validation_schema/schemas/gist_schema.py | 2 +- .../schemas/integration_schema.py | 2 +- .../schemas/repo_group_schema.py | 2 +- .../validation_schema/schemas/repo_schema.py | 2 +- .../schemas/reviewer_schema.py | 2 +- .../schemas/search_schema.py | 2 +- .../schemas/user_group_schema.py | 2 +- .../validation_schema/schemas/user_schema.py | 2 +- rhodecode/model/validation_schema/types.py | 2 +- rhodecode/model/validation_schema/utils.py | 2 +- .../model/validation_schema/validators.py | 2 +- rhodecode/model/validation_schema/widgets.py | 2 +- rhodecode/model/validators.py | 2 +- rhodecode/subscribers.py | 2 +- rhodecode/tests/config/__init__.py | 3 +-- rhodecode/tests/config/test_environment.py | 3 +-- rhodecode/tests/config/test_routing_links.py | 3 +-- .../tests/config/test_sanitize_settings.py | 4 +--- rhodecode/tests/config/test_utils.py | 4 +--- rhodecode/tests/database/__init__.py | 2 +- rhodecode/tests/database/conftest.py | 2 +- rhodecode/tests/database/test_creation.py | 2 +- rhodecode/tests/database/test_migration.py | 2 +- rhodecode/tests/events/__init__.py | 3 +-- rhodecode/tests/events/conftest.py | 2 +- rhodecode/tests/events/test_pullrequest.py | 3 +-- rhodecode/tests/events/test_repo.py | 3 +-- rhodecode/tests/fixtures/rc_fixture.py | 2 +- rhodecode/tests/functional/__init__.py | 3 +-- .../tests/functional/test_bad_request_data.py | 2 +- .../tests/functional/test_delegated_admin.py | 4 +--- rhodecode/tests/functional/test_sessions.py | 3 +-- rhodecode/tests/integrations/conftest.py | 3 +-- .../tests/integrations/test_integration.py | 2 +- rhodecode/tests/integrations/test_slack.py | 2 +- rhodecode/tests/integrations/test_webhook.py | 3 +-- rhodecode/tests/lib/__init__.py | 3 +-- .../lib/auth_modules/test_auth_modules.py | 3 +-- rhodecode/tests/lib/middleware/__init__.py | 3 +-- .../tests/lib/middleware/mock_scm_app.py | 3 +-- .../tests/lib/middleware/test_appenlight.py | 3 +-- .../tests/lib/middleware/test_simplegit.py | 3 +-- .../tests/lib/middleware/test_simplehg.py | 3 +-- .../tests/lib/middleware/test_simplesvn.py | 3 +-- .../tests/lib/middleware/test_simplevcs.py | 3 +-- rhodecode/tests/lib/middleware/test_vcs.py | 3 +-- .../lib/middleware/test_vcs_unavailable.py | 3 +-- .../tests/lib/middleware/utils/__init__.py | 3 +-- .../lib/middleware/utils/test_scm_app_http.py | 4 +--- .../utils/test_scm_app_http_chunking.py | 4 +--- .../utils/test_wsgi_app_caller_client.py | 3 +-- rhodecode/tests/lib/test_action_parser.py | 3 +-- rhodecode/tests/lib/test_archive_caches.py | 2 +- rhodecode/tests/lib/test_audit_logger.py | 4 +--- .../tests/lib/test_auth_crypto_backend.py | 3 +-- rhodecode/tests/lib/test_base.py | 3 +-- rhodecode/tests/lib/test_caches.py | 4 +--- rhodecode/tests/lib/test_codeblocks.py | 4 +--- rhodecode/tests/lib/test_colored_formatter.py | 3 +-- rhodecode/tests/lib/test_datelib.py | 3 +-- rhodecode/tests/lib/test_db_manage.py | 3 +-- rhodecode/tests/lib/test_dbmigrate.py | 3 +-- rhodecode/tests/lib/test_diffs.py | 3 +-- rhodecode/tests/lib/test_diffs_context.py | 3 +-- rhodecode/tests/lib/test_encrypt.py | 3 +-- rhodecode/tests/lib/test_ext_json.py | 3 +-- rhodecode/tests/lib/test_graphmod.py | 3 +-- rhodecode/tests/lib/test_helpers.py | 3 +-- rhodecode/tests/lib/test_hooks_base.py | 3 +-- rhodecode/tests/lib/test_html_sanitizer.py | 3 +-- rhodecode/tests/lib/test_jsonalchemy.py | 4 +--- rhodecode/tests/lib/test_mako_emails.py | 3 +-- rhodecode/tests/lib/test_markup_renderer.py | 3 +-- rhodecode/tests/lib/test_search.py | 3 +-- rhodecode/tests/lib/test_search_utils.py | 3 +-- rhodecode/tests/lib/test_utils.py | 3 +-- rhodecode/tests/load/api.py | 3 +-- rhodecode/tests/load/http_performance.py | 3 +-- rhodecode/tests/load/profile-mem.py | 3 +-- rhodecode/tests/load/profile.py | 2 +- rhodecode/tests/load/time_urls.py | 3 +-- rhodecode/tests/load/vcs_performance.py | 2 +- rhodecode/tests/models/__init__.py | 3 +-- .../tests/models/schemas/test_gist_schema.py | 4 +--- .../models/schemas/test_integration_schema.py | 4 +--- .../models/schemas/test_repo_group_schema.py | 4 +--- .../tests/models/schemas/test_repo_schema.py | 4 +--- .../tests/models/schemas/test_schema_types.py | 4 +--- .../tests/models/schemas/test_user_schema.py | 4 +--- .../schemas/test_user_usergroup_types.py | 4 +--- rhodecode/tests/models/settings/__init__.py | 3 +-- .../models/settings/test_issue_tracker.py | 3 +-- .../tests/models/settings/test_settings.py | 3 +-- .../models/settings/test_vcs_settings.py | 3 +-- .../tests/models/test_changeset_status.py | 3 +-- .../test_changeset_status_group_voting.py | 3 +-- rhodecode/tests/models/test_comment.py | 3 +-- rhodecode/tests/models/test_db.py | 3 +-- rhodecode/tests/models/test_gist.py | 3 +-- rhodecode/tests/models/test_notifications.py | 3 +-- rhodecode/tests/models/test_permissions.py | 3 +-- rhodecode/tests/models/test_pullrequest.py | 2 +- .../tests/models/test_pullrequest_git.py | 3 +-- rhodecode/tests/models/test_repo_groups.py | 3 +-- rhodecode/tests/models/test_repo_readme.py | 3 +-- rhodecode/tests/models/test_repos.py | 3 +-- rhodecode/tests/models/test_scm.py | 3 +-- ...t_user_group_permissions_on_repo_groups.py | 3 +-- rhodecode/tests/models/test_user_groups.py | 3 +-- .../test_user_permissions_on_repo_groups.py | 3 +-- rhodecode/tests/models/test_users.py | 3 +-- rhodecode/tests/other/__init__.py | 3 +-- rhodecode/tests/other/test_base_controller.py | 3 +-- rhodecode/tests/other/test_validators.py | 3 +-- rhodecode/tests/other/test_views_utils.py | 3 +-- rhodecode/tests/routes.py | 2 +- rhodecode/tests/scripts/test_concurency.py | 3 +-- rhodecode/tests/scripts/test_crawler.py | 3 +-- rhodecode/tests/server_utils.py | 3 +-- rhodecode/tests/utils.py | 2 +- rhodecode/tests/vcs/__init__.py | 2 +- rhodecode/tests/vcs/conftest.py | 2 +- rhodecode/tests/vcs/test_archives.py | 2 +- rhodecode/tests/vcs/test_branches.py | 2 +- rhodecode/tests/vcs/test_client_http.py | 2 +- rhodecode/tests/vcs/test_commits.py | 2 +- rhodecode/tests/vcs/test_config.py | 2 +- rhodecode/tests/vcs/test_diff.py | 2 +- rhodecode/tests/vcs/test_exceptions.py | 2 +- .../tests/vcs/test_filenodes_unicode_path.py | 2 +- rhodecode/tests/vcs/test_getitem.py | 2 +- rhodecode/tests/vcs/test_getslice.py | 2 +- rhodecode/tests/vcs/test_git.py | 2 +- rhodecode/tests/vcs/test_hg.py | 2 +- .../test_hg_vcsserver_cache_invalidation.py | 2 +- rhodecode/tests/vcs/test_inmemory.py | 2 +- rhodecode/tests/vcs/test_nodes.py | 2 +- rhodecode/tests/vcs/test_repository.py | 2 +- rhodecode/tests/vcs/test_svn.py | 2 +- rhodecode/tests/vcs/test_tags.py | 2 +- rhodecode/tests/vcs/test_utils.py | 2 +- rhodecode/tests/vcs/test_vcs.py | 2 +- rhodecode/tests/vcs/utils.py | 2 +- .../test_vcs_calls_custom_auth_code_403.py | 2 +- .../test_vcs_calls_custom_auth_code_404.py | 3 +-- ...est_vcs_calls_custom_auth_code_bad_code.py | 3 +-- .../test_vcs_calls_small_post_buffer.py | 2 +- .../test_vcs_operations_bad_client.py | 2 +- .../test_vcs_operations_branch_protection.py | 3 +-- .../test_vcs_operations_clone.py | 3 +-- .../test_vcs_operations_force_push.py | 2 +- .../vcs_operations/test_vcs_operations_git.py | 3 +-- ...est_vcs_operations_integrations_trigger.py | 2 +- .../test_vcs_operations_locking.py | 2 +- ...test_vcs_operations_locking_custom_code.py | 2 +- .../test_vcs_operations_new_branch_push.py | 3 +-- .../test_vcs_operations_rcextensions_push.py | 2 +- .../test_vcs_operations_special.py | 3 +-- .../vcs_operations/test_vcs_operations_svn.py | 2 +- .../test_vcs_operations_tag_push.py | 2 +- rhodecode/translation.py | 2 +- rhodecode/tweens.py | 2 +- rhodecode/typing/__init__.py | 2 +- 701 files changed, 702 insertions(+), 1054 deletions(-) diff --git a/conftest.py b/conftest.py index 0ac472d3..445b00ea 100644 --- a/conftest.py +++ b/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py index 8754f00a..4a06569f 100644 --- a/rhodecode/__init__.py +++ b/rhodecode/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/__init__.py b/rhodecode/api/__init__.py index 60326ae0..3262051a 100644 --- a/rhodecode/api/__init__.py +++ b/rhodecode/api/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/exc.py b/rhodecode/api/exc.py index f44e5bcd..a93b0636 100644 --- a/rhodecode/api/exc.py +++ b/rhodecode/api/exc.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/__init__.py b/rhodecode/api/tests/__init__.py index c4d86aeb..ae06165a 100644 --- a/rhodecode/api/tests/__init__.py +++ b/rhodecode/api/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/conftest.py b/rhodecode/api/tests/conftest.py index ed7c8667..560afb10 100644 --- a/rhodecode/api/tests/conftest.py +++ b/rhodecode/api/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_add_user_to_user_group.py b/rhodecode/api/tests/test_add_user_to_user_group.py index 2e6c2008..a7a4c469 100644 --- a/rhodecode/api/tests/test_add_user_to_user_group.py +++ b/rhodecode/api/tests/test_add_user_to_user_group.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_api.py b/rhodecode/api/tests/test_api.py index 0b7fec97..48d01e93 100644 --- a/rhodecode/api/tests/test_api.py +++ b/rhodecode/api/tests/test_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_cleanup_sessions.py b/rhodecode/api/tests/test_cleanup_sessions.py index b4d64749..bfc09662 100644 --- a/rhodecode/api/tests/test_cleanup_sessions.py +++ b/rhodecode/api/tests/test_cleanup_sessions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_close_pull_request.py b/rhodecode/api/tests/test_close_pull_request.py index 7be5291a..c2017ce9 100644 --- a/rhodecode/api/tests/test_close_pull_request.py +++ b/rhodecode/api/tests/test_close_pull_request.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_comment_commit.py b/rhodecode/api/tests/test_comment_commit.py index 8898d320..8c247bb9 100644 --- a/rhodecode/api/tests/test_comment_commit.py +++ b/rhodecode/api/tests/test_comment_commit.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_comment_pull_request.py b/rhodecode/api/tests/test_comment_pull_request.py index 44c7b564..b0100489 100644 --- a/rhodecode/api/tests/test_comment_pull_request.py +++ b/rhodecode/api/tests/test_comment_pull_request.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_gist.py b/rhodecode/api/tests/test_create_gist.py index c440fb9e..dfffe161 100644 --- a/rhodecode/api/tests/test_create_gist.py +++ b/rhodecode/api/tests/test_create_gist.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_pull_request.py b/rhodecode/api/tests/test_create_pull_request.py index e5785578..e1ca2d3e 100644 --- a/rhodecode/api/tests/test_create_pull_request.py +++ b/rhodecode/api/tests/test_create_pull_request.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_repo.py b/rhodecode/api/tests/test_create_repo.py index 872d62e7..c19fb60d 100644 --- a/rhodecode/api/tests/test_create_repo.py +++ b/rhodecode/api/tests/test_create_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_repo_group.py b/rhodecode/api/tests/test_create_repo_group.py index a72b2180..33f7443a 100644 --- a/rhodecode/api/tests/test_create_repo_group.py +++ b/rhodecode/api/tests/test_create_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_user.py b/rhodecode/api/tests/test_create_user.py index 6b612232..cafb6e25 100644 --- a/rhodecode/api/tests/test_create_user.py +++ b/rhodecode/api/tests/test_create_user.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_create_user_group.py b/rhodecode/api/tests/test_create_user_group.py index fb101f0d..ce0c520a 100644 --- a/rhodecode/api/tests/test_create_user_group.py +++ b/rhodecode/api/tests/test_create_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_gist.py b/rhodecode/api/tests/test_delete_gist.py index a42969aa..cec1455c 100644 --- a/rhodecode/api/tests/test_delete_gist.py +++ b/rhodecode/api/tests/test_delete_gist.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_repo.py b/rhodecode/api/tests/test_delete_repo.py index f66ed54a..1934b60e 100644 --- a/rhodecode/api/tests/test_delete_repo.py +++ b/rhodecode/api/tests/test_delete_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_repo_group.py b/rhodecode/api/tests/test_delete_repo_group.py index 3d2d0d27..8034fc17 100644 --- a/rhodecode/api/tests/test_delete_repo_group.py +++ b/rhodecode/api/tests/test_delete_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_user.py b/rhodecode/api/tests/test_delete_user.py index f1024a84..c02ff4dc 100644 --- a/rhodecode/api/tests/test_delete_user.py +++ b/rhodecode/api/tests/test_delete_user.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_delete_user_group.py b/rhodecode/api/tests/test_delete_user_group.py index 4225fd63..325e98af 100644 --- a/rhodecode/api/tests/test_delete_user_group.py +++ b/rhodecode/api/tests/test_delete_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_deprecated_api.py b/rhodecode/api/tests/test_deprecated_api.py index 9119af41..4b60801c 100644 --- a/rhodecode/api/tests/test_deprecated_api.py +++ b/rhodecode/api/tests/test_deprecated_api.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_fork_repo.py b/rhodecode/api/tests/test_fork_repo.py index bb4cb554..45294e16 100644 --- a/rhodecode/api/tests/test_fork_repo.py +++ b/rhodecode/api/tests/test_fork_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_fts_search.py b/rhodecode/api/tests/test_fts_search.py index 3fe02eb4..4dd5b810 100644 --- a/rhodecode/api/tests/test_fts_search.py +++ b/rhodecode/api/tests/test_fts_search.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_gist.py b/rhodecode/api/tests/test_get_gist.py index 436ca06d..77f63a1d 100644 --- a/rhodecode/api/tests/test_get_gist.py +++ b/rhodecode/api/tests/test_get_gist.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_gists.py b/rhodecode/api/tests/test_get_gists.py index 25956993..7d17cfd3 100644 --- a/rhodecode/api/tests/test_get_gists.py +++ b/rhodecode/api/tests/test_get_gists.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_ip.py b/rhodecode/api/tests/test_get_ip.py index 0be22ae6..d071456c 100644 --- a/rhodecode/api/tests/test_get_ip.py +++ b/rhodecode/api/tests/test_get_ip.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_locks.py b/rhodecode/api/tests/test_get_locks.py index c1d2d171..944ae511 100644 --- a/rhodecode/api/tests/test_get_locks.py +++ b/rhodecode/api/tests/test_get_locks.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_method.py b/rhodecode/api/tests/test_get_method.py index 166aa972..1f294252 100644 --- a/rhodecode/api/tests/test_get_method.py +++ b/rhodecode/api/tests/test_get_method.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_pull_request.py b/rhodecode/api/tests/test_get_pull_request.py index f8a9bea0..753e1926 100644 --- a/rhodecode/api/tests/test_get_pull_request.py +++ b/rhodecode/api/tests/test_get_pull_request.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_pull_request_comments.py b/rhodecode/api/tests/test_get_pull_request_comments.py index b5166163..d9378185 100644 --- a/rhodecode/api/tests/test_get_pull_request_comments.py +++ b/rhodecode/api/tests/test_get_pull_request_comments.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_pull_requests.py b/rhodecode/api/tests/test_get_pull_requests.py index e02edbce..ab3dda82 100644 --- a/rhodecode/api/tests/test_get_pull_requests.py +++ b/rhodecode/api/tests/test_get_pull_requests.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo.py b/rhodecode/api/tests/test_get_repo.py index c27602eb..42ee1ada 100644 --- a/rhodecode/api/tests/test_get_repo.py +++ b/rhodecode/api/tests/test_get_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_changeset.py b/rhodecode/api/tests/test_get_repo_changeset.py index 35a7cdae..51cb20bb 100644 --- a/rhodecode/api/tests/test_get_repo_changeset.py +++ b/rhodecode/api/tests/test_get_repo_changeset.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_comments.py b/rhodecode/api/tests/test_get_repo_comments.py index 1d6898a6..a5d5d44e 100644 --- a/rhodecode/api/tests/test_get_repo_comments.py +++ b/rhodecode/api/tests/test_get_repo_comments.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_group.py b/rhodecode/api/tests/test_get_repo_group.py index d3b52a26..ccd8b4f6 100644 --- a/rhodecode/api/tests/test_get_repo_group.py +++ b/rhodecode/api/tests/test_get_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_groups.py b/rhodecode/api/tests/test_get_repo_groups.py index ef8a7290..b34c0bae 100644 --- a/rhodecode/api/tests/test_get_repo_groups.py +++ b/rhodecode/api/tests/test_get_repo_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_nodes.py b/rhodecode/api/tests/test_get_repo_nodes.py index 9d5e8d7f..d963fe8f 100644 --- a/rhodecode/api/tests/test_get_repo_nodes.py +++ b/rhodecode/api/tests/test_get_repo_nodes.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repo_refs.py b/rhodecode/api/tests/test_get_repo_refs.py index 9afc1eb9..21b17a32 100644 --- a/rhodecode/api/tests/test_get_repo_refs.py +++ b/rhodecode/api/tests/test_get_repo_refs.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_repos.py b/rhodecode/api/tests/test_get_repos.py index c8552912..1ee670da 100644 --- a/rhodecode/api/tests/test_get_repos.py +++ b/rhodecode/api/tests/test_get_repos.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_server_info.py b/rhodecode/api/tests/test_get_server_info.py index cc1d2166..9201b57c 100644 --- a/rhodecode/api/tests/test_get_server_info.py +++ b/rhodecode/api/tests/test_get_server_info.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_user.py b/rhodecode/api/tests/test_get_user.py index 9abe31ca..db4c0319 100644 --- a/rhodecode/api/tests/test_get_user.py +++ b/rhodecode/api/tests/test_get_user.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_user_group.py b/rhodecode/api/tests/test_get_user_group.py index ee12c2a8..72df94db 100644 --- a/rhodecode/api/tests/test_get_user_group.py +++ b/rhodecode/api/tests/test_get_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_user_groups.py b/rhodecode/api/tests/test_get_user_groups.py index 55a380f1..8f333fc8 100644 --- a/rhodecode/api/tests/test_get_user_groups.py +++ b/rhodecode/api/tests/test_get_user_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_get_users.py b/rhodecode/api/tests/test_get_users.py index 9b99891f..29965e62 100644 --- a/rhodecode/api/tests/test_get_users.py +++ b/rhodecode/api/tests/test_get_users.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_group_permission.py b/rhodecode/api/tests/test_grant_user_group_permission.py index 6fa41fc7..5bfb8a7a 100644 --- a/rhodecode/api/tests/test_grant_user_group_permission.py +++ b/rhodecode/api/tests/test_grant_user_group_permission.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py b/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py index 18999ab5..770eefbd 100644 --- a/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py +++ b/rhodecode/api/tests/test_grant_user_group_permission_to_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py b/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py index 39cd0a88..0c64acd6 100644 --- a/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py +++ b/rhodecode/api/tests/test_grant_user_group_permission_to_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_permission.py b/rhodecode/api/tests/test_grant_user_permission.py index e21a41c3..d3bae99d 100644 --- a/rhodecode/api/tests/test_grant_user_permission.py +++ b/rhodecode/api/tests/test_grant_user_permission.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py b/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py index 15ba9194..a0053326 100644 --- a/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py +++ b/rhodecode/api/tests/test_grant_user_permission_to_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_grant_user_permission_to_user_group.py b/rhodecode/api/tests/test_grant_user_permission_to_user_group.py index 998b5c87..757649f9 100644 --- a/rhodecode/api/tests/test_grant_user_permission_to_user_group.py +++ b/rhodecode/api/tests/test_grant_user_permission_to_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_invalidate_cache.py b/rhodecode/api/tests/test_invalidate_cache.py index fadbd740..0d2d0d59 100644 --- a/rhodecode/api/tests/test_invalidate_cache.py +++ b/rhodecode/api/tests/test_invalidate_cache.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_merge_pull_request.py b/rhodecode/api/tests/test_merge_pull_request.py index 70bfaf0e..ec14d3e1 100644 --- a/rhodecode/api/tests/test_merge_pull_request.py +++ b/rhodecode/api/tests/test_merge_pull_request.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_pull.py b/rhodecode/api/tests/test_pull.py index b060a1da..216cd6fa 100644 --- a/rhodecode/api/tests/test_pull.py +++ b/rhodecode/api/tests/test_pull.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_remove_field_from_repo.py b/rhodecode/api/tests/test_remove_field_from_repo.py index 404b9f8e..970330aa 100644 --- a/rhodecode/api/tests/test_remove_field_from_repo.py +++ b/rhodecode/api/tests/test_remove_field_from_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_remove_user_from_user_group.py b/rhodecode/api/tests/test_remove_user_from_user_group.py index f90b8e15..87be2df3 100644 --- a/rhodecode/api/tests/test_remove_user_from_user_group.py +++ b/rhodecode/api/tests/test_remove_user_from_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_repo_locking.py b/rhodecode/api/tests/test_repo_locking.py index 371eede5..c0077f62 100644 --- a/rhodecode/api/tests/test_repo_locking.py +++ b/rhodecode/api/tests/test_repo_locking.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_rescan_repos.py b/rhodecode/api/tests/test_rescan_repos.py index 0e614e51..c18efb4b 100644 --- a/rhodecode/api/tests/test_rescan_repos.py +++ b/rhodecode/api/tests/test_rescan_repos.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_group_permission.py b/rhodecode/api/tests/test_revoke_user_group_permission.py index a08bc9d0..d6d019f0 100644 --- a/rhodecode/api/tests/test_revoke_user_group_permission.py +++ b/rhodecode/api/tests/test_revoke_user_group_permission.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py b/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py index 8c41bcd5..9a69a6a4 100644 --- a/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py +++ b/rhodecode/api/tests/test_revoke_user_group_permission_from_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py b/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py index c25ede2b..3e767b65 100644 --- a/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py +++ b/rhodecode/api/tests/test_revoke_user_group_permission_from_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_permission.py b/rhodecode/api/tests/test_revoke_user_permission.py index 03b2bc31..49580772 100644 --- a/rhodecode/api/tests/test_revoke_user_permission.py +++ b/rhodecode/api/tests/test_revoke_user_permission.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py b/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py index 779fb7ed..3bc12bac 100644 --- a/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py +++ b/rhodecode/api/tests/test_revoke_user_permission_from_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py b/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py index d525ea90..978ea9d7 100644 --- a/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py +++ b/rhodecode/api/tests/test_revoke_user_permission_from_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_service_api.py b/rhodecode/api/tests/test_service_api.py index b5780777..b47bc735 100644 --- a/rhodecode/api/tests/test_service_api.py +++ b/rhodecode/api/tests/test_service_api.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_store_exception.py b/rhodecode/api/tests/test_store_exception.py index 7ae7e390..dfa7c035 100644 --- a/rhodecode/api/tests/test_store_exception.py +++ b/rhodecode/api/tests/test_store_exception.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_pull_request.py b/rhodecode/api/tests/test_update_pull_request.py index fad87340..dedd73c7 100644 --- a/rhodecode/api/tests/test_update_pull_request.py +++ b/rhodecode/api/tests/test_update_pull_request.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_repo.py b/rhodecode/api/tests/test_update_repo.py index 64f98f7c..713fc523 100644 --- a/rhodecode/api/tests/test_update_repo.py +++ b/rhodecode/api/tests/test_update_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_repo_group.py b/rhodecode/api/tests/test_update_repo_group.py index 17aedc23..89704a90 100644 --- a/rhodecode/api/tests/test_update_repo_group.py +++ b/rhodecode/api/tests/test_update_repo_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_user.py b/rhodecode/api/tests/test_update_user.py index 37c41bf6..c3420970 100644 --- a/rhodecode/api/tests/test_update_user.py +++ b/rhodecode/api/tests/test_update_user.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_update_user_group.py b/rhodecode/api/tests/test_update_user_group.py index 3c5217e5..1cbe92e2 100644 --- a/rhodecode/api/tests/test_update_user_group.py +++ b/rhodecode/api/tests/test_update_user_group.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/test_utils.py b/rhodecode/api/tests/test_utils.py index f3d834aa..9429ce67 100644 --- a/rhodecode/api/tests/test_utils.py +++ b/rhodecode/api/tests/test_utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/tests/utils.py b/rhodecode/api/tests/utils.py index 85537bab..30565c19 100644 --- a/rhodecode/api/tests/utils.py +++ b/rhodecode/api/tests/utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/utils.py b/rhodecode/api/utils.py index 536348ca..82a204d5 100644 --- a/rhodecode/api/utils.py +++ b/rhodecode/api/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/__init__.py b/rhodecode/api/views/__init__.py index 159436fe..677eee8c 100644 --- a/rhodecode/api/views/__init__.py +++ b/rhodecode/api/views/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2023 RhodeCode GmbH +# Copyright (C) 2015-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/deprecated_api.py b/rhodecode/api/views/deprecated_api.py index 3c0dc11a..c961ff2b 100644 --- a/rhodecode/api/views/deprecated_api.py +++ b/rhodecode/api/views/deprecated_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/gist_api.py b/rhodecode/api/views/gist_api.py index 49a993e1..33cc3f43 100644 --- a/rhodecode/api/views/gist_api.py +++ b/rhodecode/api/views/gist_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/pull_request_api.py b/rhodecode/api/views/pull_request_api.py index 490ed213..ffbc4e6f 100644 --- a/rhodecode/api/views/pull_request_api.py +++ b/rhodecode/api/views/pull_request_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/repo_api.py b/rhodecode/api/views/repo_api.py index 3c3f3c51..f4fac27e 100644 --- a/rhodecode/api/views/repo_api.py +++ b/rhodecode/api/views/repo_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/repo_group_api.py b/rhodecode/api/views/repo_group_api.py index 4cdac7a5..53ddce53 100644 --- a/rhodecode/api/views/repo_group_api.py +++ b/rhodecode/api/views/repo_group_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/search_api.py b/rhodecode/api/views/search_api.py index 5672c604..183f4c46 100644 --- a/rhodecode/api/views/search_api.py +++ b/rhodecode/api/views/search_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/server_api.py b/rhodecode/api/views/server_api.py index 1494dff1..b09d1650 100644 --- a/rhodecode/api/views/server_api.py +++ b/rhodecode/api/views/server_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/service_api.py b/rhodecode/api/views/service_api.py index e2bfa45a..f86f27a5 100644 --- a/rhodecode/api/views/service_api.py +++ b/rhodecode/api/views/service_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/testing_api.py b/rhodecode/api/views/testing_api.py index 0064d3e0..4501f43c 100644 --- a/rhodecode/api/views/testing_api.py +++ b/rhodecode/api/views/testing_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/user_api.py b/rhodecode/api/views/user_api.py index 0c252165..5954e510 100644 --- a/rhodecode/api/views/user_api.py +++ b/rhodecode/api/views/user_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/api/views/user_group_api.py b/rhodecode/api/views/user_group_api.py index a994e034..2862583f 100644 --- a/rhodecode/api/views/user_group_api.py +++ b/rhodecode/api/views/user_group_api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/__init__.py b/rhodecode/apps/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/__init__.py +++ b/rhodecode/apps/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py index cfb3157b..4ea62e78 100644 --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/_base/interfaces.py b/rhodecode/apps/_base/interfaces.py index a19c84c9..373f59fc 100644 --- a/rhodecode/apps/_base/interfaces.py +++ b/rhodecode/apps/_base/interfaces.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/_base/navigation.py b/rhodecode/apps/_base/navigation.py index 6c935602..0dcb7a75 100644 --- a/rhodecode/apps/_base/navigation.py +++ b/rhodecode/apps/_base/navigation.py @@ -1,6 +1,5 @@ -import dataclasses -# Copyright (C) 2016-2023 RhodeCode GmbH +import dataclasses# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/_base/subscribers.py b/rhodecode/apps/_base/subscribers.py index cd595cbb..f15550e6 100644 --- a/rhodecode/apps/_base/subscribers.py +++ b/rhodecode/apps/_base/subscribers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py index f5e8cc9c..4f53749b 100644 --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_audit_logs.py b/rhodecode/apps/admin/tests/test_admin_audit_logs.py index fe1475c3..2160466b 100644 --- a/rhodecode/apps/admin/tests/test_admin_audit_logs.py +++ b/rhodecode/apps/admin/tests/test_admin_audit_logs.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_auth_settings.py b/rhodecode/apps/admin/tests/test_admin_auth_settings.py index 6ec3c1aa..49d97828 100644 --- a/rhodecode/apps/admin/tests/test_admin_auth_settings.py +++ b/rhodecode/apps/admin/tests/test_admin_auth_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_defaults.py b/rhodecode/apps/admin/tests/test_admin_defaults.py index 60939d61..a51ba6c9 100644 --- a/rhodecode/apps/admin/tests/test_admin_defaults.py +++ b/rhodecode/apps/admin/tests/test_admin_defaults.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_main_views.py b/rhodecode/apps/admin/tests/test_admin_main_views.py index 1520cc51..aa48394c 100644 --- a/rhodecode/apps/admin/tests/test_admin_main_views.py +++ b/rhodecode/apps/admin/tests/test_admin_main_views.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_permissions.py b/rhodecode/apps/admin/tests/test_admin_permissions.py index e99cc95b..6545ba71 100644 --- a/rhodecode/apps/admin/tests/test_admin_permissions.py +++ b/rhodecode/apps/admin/tests/test_admin_permissions.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_repos.py b/rhodecode/apps/admin/tests/test_admin_repos.py index ab09b219..e9e23286 100644 --- a/rhodecode/apps/admin/tests/test_admin_repos.py +++ b/rhodecode/apps/admin/tests/test_admin_repos.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_repository_groups.py b/rhodecode/apps/admin/tests/test_admin_repository_groups.py index 9d168f39..2dd22ccf 100644 --- a/rhodecode/apps/admin/tests/test_admin_repository_groups.py +++ b/rhodecode/apps/admin/tests/test_admin_repository_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_settings.py b/rhodecode/apps/admin/tests/test_admin_settings.py index 11d43d89..982f5453 100644 --- a/rhodecode/apps/admin/tests/test_admin_settings.py +++ b/rhodecode/apps/admin/tests/test_admin_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_user_groups.py b/rhodecode/apps/admin/tests/test_admin_user_groups.py index 61d37d10..73ab6017 100644 --- a/rhodecode/apps/admin/tests/test_admin_user_groups.py +++ b/rhodecode/apps/admin/tests/test_admin_user_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_users.py b/rhodecode/apps/admin/tests/test_admin_users.py index 095d84a0..9e333b31 100644 --- a/rhodecode/apps/admin/tests/test_admin_users.py +++ b/rhodecode/apps/admin/tests/test_admin_users.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py b/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py index 33518b74..5ec3f4f7 100644 --- a/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py +++ b/rhodecode/apps/admin/tests/test_admin_users_ssh_keys.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/__init__.py b/rhodecode/apps/admin/views/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/admin/views/__init__.py +++ b/rhodecode/apps/admin/views/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/artifacts.py b/rhodecode/apps/admin/views/artifacts.py index eb749b07..f6765eae 100644 --- a/rhodecode/apps/admin/views/artifacts.py +++ b/rhodecode/apps/admin/views/artifacts.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/audit_logs.py b/rhodecode/apps/admin/views/audit_logs.py index 0032c901..b999c0af 100644 --- a/rhodecode/apps/admin/views/audit_logs.py +++ b/rhodecode/apps/admin/views/audit_logs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/automation.py b/rhodecode/apps/admin/views/automation.py index f59309ad..1e6e0aff 100644 --- a/rhodecode/apps/admin/views/automation.py +++ b/rhodecode/apps/admin/views/automation.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/defaults.py b/rhodecode/apps/admin/views/defaults.py index 2fc6d736..c878cbad 100644 --- a/rhodecode/apps/admin/views/defaults.py +++ b/rhodecode/apps/admin/views/defaults.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/exception_tracker.py b/rhodecode/apps/admin/views/exception_tracker.py index bb96d50b..1854b414 100644 --- a/rhodecode/apps/admin/views/exception_tracker.py +++ b/rhodecode/apps/admin/views/exception_tracker.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2023 RhodeCode GmbH +# Copyright (C) 2018-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/main_views.py b/rhodecode/apps/admin/views/main_views.py index e21b3e8b..5fdee1c3 100644 --- a/rhodecode/apps/admin/views/main_views.py +++ b/rhodecode/apps/admin/views/main_views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/open_source_licenses.py b/rhodecode/apps/admin/views/open_source_licenses.py index 75daaecc..528a451a 100644 --- a/rhodecode/apps/admin/views/open_source_licenses.py +++ b/rhodecode/apps/admin/views/open_source_licenses.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/permissions.py b/rhodecode/apps/admin/views/permissions.py index 14631216..35049bb8 100644 --- a/rhodecode/apps/admin/views/permissions.py +++ b/rhodecode/apps/admin/views/permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/process_management.py b/rhodecode/apps/admin/views/process_management.py index 0d5553b1..4e2a019c 100644 --- a/rhodecode/apps/admin/views/process_management.py +++ b/rhodecode/apps/admin/views/process_management.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/repo_groups.py b/rhodecode/apps/admin/views/repo_groups.py index ce8332b7..e06486b7 100644 --- a/rhodecode/apps/admin/views/repo_groups.py +++ b/rhodecode/apps/admin/views/repo_groups.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/repositories.py b/rhodecode/apps/admin/views/repositories.py index 58ede009..ed49c945 100644 --- a/rhodecode/apps/admin/views/repositories.py +++ b/rhodecode/apps/admin/views/repositories.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/scheduler.py b/rhodecode/apps/admin/views/scheduler.py index 7f632ed5..bd501ca3 100644 --- a/rhodecode/apps/admin/views/scheduler.py +++ b/rhodecode/apps/admin/views/scheduler.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/sessions.py b/rhodecode/apps/admin/views/sessions.py index 1f2f5ac3..e5a83806 100644 --- a/rhodecode/apps/admin/views/sessions.py +++ b/rhodecode/apps/admin/views/sessions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py index 6c20c46b..4dfbe1f2 100644 --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/svn_config.py b/rhodecode/apps/admin/views/svn_config.py index 3de1b73e..102d8d4f 100644 --- a/rhodecode/apps/admin/views/svn_config.py +++ b/rhodecode/apps/admin/views/svn_config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/system_info.py b/rhodecode/apps/admin/views/system_info.py index a4346da6..9a798274 100644 --- a/rhodecode/apps/admin/views/system_info.py +++ b/rhodecode/apps/admin/views/system_info.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/user_groups.py b/rhodecode/apps/admin/views/user_groups.py index 993fe8e6..b2b28980 100644 --- a/rhodecode/apps/admin/views/user_groups.py +++ b/rhodecode/apps/admin/views/user_groups.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/admin/views/users.py b/rhodecode/apps/admin/views/users.py index a39ba0c3..65863914 100644 --- a/rhodecode/apps/admin/views/users.py +++ b/rhodecode/apps/admin/views/users.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/channelstream/__init__.py b/rhodecode/apps/channelstream/__init__.py index 75d9069b..aab02a16 100644 --- a/rhodecode/apps/channelstream/__init__.py +++ b/rhodecode/apps/channelstream/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/channelstream/views.py b/rhodecode/apps/channelstream/views.py index b8f234b8..e6c05757 100644 --- a/rhodecode/apps/channelstream/views.py +++ b/rhodecode/apps/channelstream/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/debug_style/__init__.py b/rhodecode/apps/debug_style/__init__.py index 6f7ca3ce..a844ba57 100644 --- a/rhodecode/apps/debug_style/__init__.py +++ b/rhodecode/apps/debug_style/__init__.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/debug_style/views.py b/rhodecode/apps/debug_style/views.py index 68f94ef1..ad131a9f 100644 --- a/rhodecode/apps/debug_style/views.py +++ b/rhodecode/apps/debug_style/views.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/__init__.py b/rhodecode/apps/file_store/__init__.py index a7254497..f0986bce 100755 --- a/rhodecode/apps/file_store/__init__.py +++ b/rhodecode/apps/file_store/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/backends/__init__.py b/rhodecode/apps/file_store/backends/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/file_store/backends/__init__.py +++ b/rhodecode/apps/file_store/backends/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/backends/base.py b/rhodecode/apps/file_store/backends/base.py index 7093c6ad..05e66827 100644 --- a/rhodecode/apps/file_store/backends/base.py +++ b/rhodecode/apps/file_store/backends/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/backends/filesystem.py b/rhodecode/apps/file_store/backends/filesystem.py index bff1689c..85c6ab51 100644 --- a/rhodecode/apps/file_store/backends/filesystem.py +++ b/rhodecode/apps/file_store/backends/filesystem.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/backends/filesystem_legacy.py b/rhodecode/apps/file_store/backends/filesystem_legacy.py index a278e8ed..4216175e 100644 --- a/rhodecode/apps/file_store/backends/filesystem_legacy.py +++ b/rhodecode/apps/file_store/backends/filesystem_legacy.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/backends/objectstore.py b/rhodecode/apps/file_store/backends/objectstore.py index f239deb5..ee8bef11 100644 --- a/rhodecode/apps/file_store/backends/objectstore.py +++ b/rhodecode/apps/file_store/backends/objectstore.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/config_keys.py b/rhodecode/apps/file_store/config_keys.py index 5e14af82..7b2ffe40 100644 --- a/rhodecode/apps/file_store/config_keys.py +++ b/rhodecode/apps/file_store/config_keys.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/exceptions.py b/rhodecode/apps/file_store/exceptions.py index a6701a0f..aea355e1 100755 --- a/rhodecode/apps/file_store/exceptions.py +++ b/rhodecode/apps/file_store/exceptions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/extensions.py b/rhodecode/apps/file_store/extensions.py index 9b91ec67..b8bcc05e 100644 --- a/rhodecode/apps/file_store/extensions.py +++ b/rhodecode/apps/file_store/extensions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/__init__.py b/rhodecode/apps/file_store/tests/__init__.py index d8288e80..674b62dc 100644 --- a/rhodecode/apps/file_store/tests/__init__.py +++ b/rhodecode/apps/file_store/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/test_filestore_backends.py b/rhodecode/apps/file_store/tests/test_filestore_backends.py index 54a50ca0..df0082c2 100644 --- a/rhodecode/apps/file_store/tests/test_filestore_backends.py +++ b/rhodecode/apps/file_store/tests/test_filestore_backends.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/test_filestore_filesystem_backend.py b/rhodecode/apps/file_store/tests/test_filestore_filesystem_backend.py index 496145ce..ad423b9d 100644 --- a/rhodecode/apps/file_store/tests/test_filestore_filesystem_backend.py +++ b/rhodecode/apps/file_store/tests/test_filestore_filesystem_backend.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/test_filestore_legacy_and_v2_compatability.py b/rhodecode/apps/file_store/tests/test_filestore_legacy_and_v2_compatability.py index 6298bf53..7d42e202 100644 --- a/rhodecode/apps/file_store/tests/test_filestore_legacy_and_v2_compatability.py +++ b/rhodecode/apps/file_store/tests/test_filestore_legacy_and_v2_compatability.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/test_filestore_legacy_backend.py b/rhodecode/apps/file_store/tests/test_filestore_legacy_backend.py index f9783e07..ec2547c9 100644 --- a/rhodecode/apps/file_store/tests/test_filestore_legacy_backend.py +++ b/rhodecode/apps/file_store/tests/test_filestore_legacy_backend.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/test_filestore_objectstore_backend.py b/rhodecode/apps/file_store/tests/test_filestore_objectstore_backend.py index b77fe865..3a37523d 100644 --- a/rhodecode/apps/file_store/tests/test_filestore_objectstore_backend.py +++ b/rhodecode/apps/file_store/tests/test_filestore_objectstore_backend.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/tests/test_upload_file.py b/rhodecode/apps/file_store/tests/test_upload_file.py index 87e3da61..82639943 100644 --- a/rhodecode/apps/file_store/tests/test_upload_file.py +++ b/rhodecode/apps/file_store/tests/test_upload_file.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/utils.py b/rhodecode/apps/file_store/utils.py index 0c4ed472..d08e4605 100755 --- a/rhodecode/apps/file_store/utils.py +++ b/rhodecode/apps/file_store/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/file_store/views.py b/rhodecode/apps/file_store/views.py index 8ddde743..fa71da92 100644 --- a/rhodecode/apps/file_store/views.py +++ b/rhodecode/apps/file_store/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/__init__.py b/rhodecode/apps/gist/__init__.py index 8f6f2d62..1c327136 100644 --- a/rhodecode/apps/gist/__init__.py +++ b/rhodecode/apps/gist/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/tests/__init__.py b/rhodecode/apps/gist/tests/__init__.py index 380ec147..d2a8391f 100644 --- a/rhodecode/apps/gist/tests/__init__.py +++ b/rhodecode/apps/gist/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/tests/test_admin_gists.py b/rhodecode/apps/gist/tests/test_admin_gists.py index b81c5229..c97aa962 100644 --- a/rhodecode/apps/gist/tests/test_admin_gists.py +++ b/rhodecode/apps/gist/tests/test_admin_gists.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/gist/views.py b/rhodecode/apps/gist/views.py index 757a2f1b..48aeb978 100644 --- a/rhodecode/apps/gist/views.py +++ b/rhodecode/apps/gist/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2023 RhodeCode GmbH +# Copyright (C) 2013-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/__init__.py b/rhodecode/apps/home/__init__.py index c40dfab6..77698ecf 100644 --- a/rhodecode/apps/home/__init__.py +++ b/rhodecode/apps/home/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/__init__.py b/rhodecode/apps/home/tests/__init__.py index d27eb1ac..af7abedc 100644 --- a/rhodecode/apps/home/tests/__init__.py +++ b/rhodecode/apps/home/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_goto_switched_data.py b/rhodecode/apps/home/tests/test_get_goto_switched_data.py index 551fcbe9..441e252e 100644 --- a/rhodecode/apps/home/tests/test_get_goto_switched_data.py +++ b/rhodecode/apps/home/tests/test_get_goto_switched_data.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_repo_list_data.py b/rhodecode/apps/home/tests/test_get_repo_list_data.py index bf6b7d31..0bc68cee 100644 --- a/rhodecode/apps/home/tests/test_get_repo_list_data.py +++ b/rhodecode/apps/home/tests/test_get_repo_list_data.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_user_data.py b/rhodecode/apps/home/tests/test_get_user_data.py index 2113b360..6017f0c0 100644 --- a/rhodecode/apps/home/tests/test_get_user_data.py +++ b/rhodecode/apps/home/tests/test_get_user_data.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_get_user_group_data.py b/rhodecode/apps/home/tests/test_get_user_group_data.py index 9fdb613c..73761538 100644 --- a/rhodecode/apps/home/tests/test_get_user_group_data.py +++ b/rhodecode/apps/home/tests/test_get_user_group_data.py @@ -1,23 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3 -# (only), as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# 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/ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/tests/test_home.py b/rhodecode/apps/home/tests/test_home.py index fdcfb634..84e6945f 100644 --- a/rhodecode/apps/home/tests/test_home.py +++ b/rhodecode/apps/home/tests/test_home.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/home/views.py b/rhodecode/apps/home/views.py index 1f4157f9..5867d4dc 100644 --- a/rhodecode/apps/home/views.py +++ b/rhodecode/apps/home/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/hovercards/__init__.py b/rhodecode/apps/hovercards/__init__.py index 021ac718..04f6c236 100644 --- a/rhodecode/apps/hovercards/__init__.py +++ b/rhodecode/apps/hovercards/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2023 RhodeCode GmbH +# Copyright (C) 2018-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/hovercards/views.py b/rhodecode/apps/hovercards/views.py index 22c044ea..5ce629f9 100644 --- a/rhodecode/apps/hovercards/views.py +++ b/rhodecode/apps/hovercards/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/__init__.py b/rhodecode/apps/journal/__init__.py index cc9d7f1a..75f6388a 100644 --- a/rhodecode/apps/journal/__init__.py +++ b/rhodecode/apps/journal/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/tests/__init__.py b/rhodecode/apps/journal/tests/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/journal/tests/__init__.py +++ b/rhodecode/apps/journal/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/tests/test_journal.py b/rhodecode/apps/journal/tests/test_journal.py index 221dd9b7..54454587 100644 --- a/rhodecode/apps/journal/tests/test_journal.py +++ b/rhodecode/apps/journal/tests/test_journal.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/journal/views.py b/rhodecode/apps/journal/views.py index 4fbf55e4..2638428d 100644 --- a/rhodecode/apps/journal/views.py +++ b/rhodecode/apps/journal/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/__init__.py b/rhodecode/apps/login/__init__.py index eed380b1..8447f15a 100644 --- a/rhodecode/apps/login/__init__.py +++ b/rhodecode/apps/login/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/tests/test_login.py b/rhodecode/apps/login/tests/test_login.py index 22c9ac8e..cf0c5b1b 100644 --- a/rhodecode/apps/login/tests/test_login.py +++ b/rhodecode/apps/login/tests/test_login.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/tests/test_password_reset.py b/rhodecode/apps/login/tests/test_password_reset.py index d4d26b4f..cd760e2f 100644 --- a/rhodecode/apps/login/tests/test_password_reset.py +++ b/rhodecode/apps/login/tests/test_password_reset.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/tests/test_register_captcha.py b/rhodecode/apps/login/tests/test_register_captcha.py index 75983819..c50cd893 100644 --- a/rhodecode/apps/login/tests/test_register_captcha.py +++ b/rhodecode/apps/login/tests/test_register_captcha.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/login/views.py b/rhodecode/apps/login/views.py index f13660d3..3c5a7202 100644 --- a/rhodecode/apps/login/views.py +++ b/rhodecode/apps/login/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/__init__.py b/rhodecode/apps/my_account/__init__.py index 754d2677..99652332 100644 --- a/rhodecode/apps/my_account/__init__.py +++ b/rhodecode/apps/my_account/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/__init__.py b/rhodecode/apps/my_account/tests/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/my_account/tests/__init__.py +++ b/rhodecode/apps/my_account/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py b/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py index 162221dc..cb483345 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py +++ b/rhodecode/apps/my_account/tests/test_my_account_auth_tokens.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_edit.py b/rhodecode/apps/my_account/tests/test_my_account_edit.py index 1fbf37ef..9d0552c0 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_edit.py +++ b/rhodecode/apps/my_account/tests/test_my_account_edit.py @@ -1,23 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3 -# (only), as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# 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/ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_emails.py b/rhodecode/apps/my_account/tests/test_my_account_emails.py index 4ca87b60..7888bd73 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_emails.py +++ b/rhodecode/apps/my_account/tests/test_my_account_emails.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_notifications.py b/rhodecode/apps/my_account/tests/test_my_account_notifications.py index a8259dd7..6c3e5dd9 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_notifications.py +++ b/rhodecode/apps/my_account/tests/test_my_account_notifications.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_password.py b/rhodecode/apps/my_account/tests/test_my_account_password.py index 23f5bdfa..2f95e05f 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_password.py +++ b/rhodecode/apps/my_account/tests/test_my_account_password.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_profile.py b/rhodecode/apps/my_account/tests/test_my_account_profile.py index fe8d9ac8..d8bd2f6d 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_profile.py +++ b/rhodecode/apps/my_account/tests/test_my_account_profile.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_simple_views.py b/rhodecode/apps/my_account/tests/test_my_account_simple_views.py index 1fb74f8c..06bc7807 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_simple_views.py +++ b/rhodecode/apps/my_account/tests/test_my_account_simple_views.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py b/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py index cf853330..302020d2 100644 --- a/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py +++ b/rhodecode/apps/my_account/tests/test_my_account_ssh_keys.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/views/__init__.py b/rhodecode/apps/my_account/views/__init__.py index e10695d4..a9ba9298 100644 --- a/rhodecode/apps/my_account/views/__init__.py +++ b/rhodecode/apps/my_account/views/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/views/my_account.py b/rhodecode/apps/my_account/views/my_account.py index 098655b5..36309f5a 100644 --- a/rhodecode/apps/my_account/views/my_account.py +++ b/rhodecode/apps/my_account/views/my_account.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/views/my_account_notifications.py b/rhodecode/apps/my_account/views/my_account_notifications.py index 30593873..eb78b2bf 100644 --- a/rhodecode/apps/my_account/views/my_account_notifications.py +++ b/rhodecode/apps/my_account/views/my_account_notifications.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/my_account/views/my_account_ssh_keys.py b/rhodecode/apps/my_account/views/my_account_ssh_keys.py index 8887f280..2a3c396a 100644 --- a/rhodecode/apps/my_account/views/my_account_ssh_keys.py +++ b/rhodecode/apps/my_account/views/my_account_ssh_keys.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ops/__init__.py b/rhodecode/apps/ops/__init__.py index 46566ba7..aa9cd114 100644 --- a/rhodecode/apps/ops/__init__.py +++ b/rhodecode/apps/ops/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ops/views.py b/rhodecode/apps/ops/views.py index 7b179b54..9e08f6ff 100644 --- a/rhodecode/apps/ops/views.py +++ b/rhodecode/apps/ops/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/__init__.py b/rhodecode/apps/repo_group/__init__.py index 7f0c6506..36193773 100644 --- a/rhodecode/apps/repo_group/__init__.py +++ b/rhodecode/apps/repo_group/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/__init__.py b/rhodecode/apps/repo_group/tests/__init__.py index c4d86aeb..ae06165a 100644 --- a/rhodecode/apps/repo_group/tests/__init__.py +++ b/rhodecode/apps/repo_group/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py b/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py index 25f9cbed..c4aed64c 100644 --- a/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py +++ b/rhodecode/apps/repo_group/tests/test_repo_groups_advanced.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py b/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py index abe00b8c..30867ca2 100644 --- a/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py +++ b/rhodecode/apps/repo_group/tests/test_repo_groups_permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py b/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py index 1a12425a..11e81cf1 100644 --- a/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py +++ b/rhodecode/apps/repo_group/tests/test_repo_groups_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/__init__.py b/rhodecode/apps/repo_group/views/__init__.py index cb83a28d..f3a2879b 100644 --- a/rhodecode/apps/repo_group/views/__init__.py +++ b/rhodecode/apps/repo_group/views/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/repo_group_advanced.py b/rhodecode/apps/repo_group/views/repo_group_advanced.py index 8d2071fe..a9968bb5 100644 --- a/rhodecode/apps/repo_group/views/repo_group_advanced.py +++ b/rhodecode/apps/repo_group/views/repo_group_advanced.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/repo_group_permissions.py b/rhodecode/apps/repo_group/views/repo_group_permissions.py index 075e486e..df47f553 100644 --- a/rhodecode/apps/repo_group/views/repo_group_permissions.py +++ b/rhodecode/apps/repo_group/views/repo_group_permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repo_group/views/repo_group_settings.py b/rhodecode/apps/repo_group/views/repo_group_settings.py index 0d89c5be..036365e7 100644 --- a/rhodecode/apps/repo_group/views/repo_group_settings.py +++ b/rhodecode/apps/repo_group/views/repo_group_settings.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py index b7d4de34..42453689 100644 --- a/rhodecode/apps/repository/__init__.py +++ b/rhodecode/apps/repository/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/__init__.py b/rhodecode/apps/repository/tests/__init__.py index c4d86aeb..ae06165a 100644 --- a/rhodecode/apps/repository/tests/__init__.py +++ b/rhodecode/apps/repository/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_pull_requests_list.py b/rhodecode/apps/repository/tests/test_pull_requests_list.py index 47c9d9ce..24bf0e9d 100644 --- a/rhodecode/apps/repository/tests/test_pull_requests_list.py +++ b/rhodecode/apps/repository/tests/test_pull_requests_list.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_bookmarks.py b/rhodecode/apps/repository/tests/test_repo_bookmarks.py index 1559a16e..9b72c6a7 100644 --- a/rhodecode/apps/repository/tests/test_repo_bookmarks.py +++ b/rhodecode/apps/repository/tests/test_repo_bookmarks.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_branches.py b/rhodecode/apps/repository/tests/test_repo_branches.py index 5dfc7019..4cb67e27 100644 --- a/rhodecode/apps/repository/tests/test_repo_branches.py +++ b/rhodecode/apps/repository/tests/test_repo_branches.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_changelog.py b/rhodecode/apps/repository/tests/test_repo_changelog.py index e0e64c3d..9bca23a5 100644 --- a/rhodecode/apps/repository/tests/test_repo_changelog.py +++ b/rhodecode/apps/repository/tests/test_repo_changelog.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_commit_comments.py b/rhodecode/apps/repository/tests/test_repo_commit_comments.py index af8e3ad6..097208ef 100644 --- a/rhodecode/apps/repository/tests/test_repo_commit_comments.py +++ b/rhodecode/apps/repository/tests/test_repo_commit_comments.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_commits.py b/rhodecode/apps/repository/tests/test_repo_commits.py index fdf6f9e7..67308e42 100644 --- a/rhodecode/apps/repository/tests/test_repo_commits.py +++ b/rhodecode/apps/repository/tests/test_repo_commits.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_compare.py b/rhodecode/apps/repository/tests/test_repo_compare.py index e4f489bb..7319837c 100644 --- a/rhodecode/apps/repository/tests/test_repo_compare.py +++ b/rhodecode/apps/repository/tests/test_repo_compare.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_compare_local.py b/rhodecode/apps/repository/tests/test_repo_compare_local.py index df824ab5..f691737f 100644 --- a/rhodecode/apps/repository/tests/test_repo_compare_local.py +++ b/rhodecode/apps/repository/tests/test_repo_compare_local.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py index 2d81aaad..6629046b 100644 --- a/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py +++ b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_feed.py b/rhodecode/apps/repository/tests/test_repo_feed.py index 991d73f1..7d8dcc43 100644 --- a/rhodecode/apps/repository/tests/test_repo_feed.py +++ b/rhodecode/apps/repository/tests/test_repo_feed.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_files.py b/rhodecode/apps/repository/tests/test_repo_files.py index c59aed27..20de791f 100644 --- a/rhodecode/apps/repository/tests/test_repo_files.py +++ b/rhodecode/apps/repository/tests/test_repo_files.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_forks.py b/rhodecode/apps/repository/tests/test_repo_forks.py index e52dc880..f5de1f79 100644 --- a/rhodecode/apps/repository/tests/test_repo_forks.py +++ b/rhodecode/apps/repository/tests/test_repo_forks.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_issue_tracker.py b/rhodecode/apps/repository/tests/test_repo_issue_tracker.py index d9f44865..e82d6950 100644 --- a/rhodecode/apps/repository/tests/test_repo_issue_tracker.py +++ b/rhodecode/apps/repository/tests/test_repo_issue_tracker.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_maintainance.py b/rhodecode/apps/repository/tests/test_repo_maintainance.py index 86e0ad49..9a348fef 100644 --- a/rhodecode/apps/repository/tests/test_repo_maintainance.py +++ b/rhodecode/apps/repository/tests/test_repo_maintainance.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_permissions.py b/rhodecode/apps/repository/tests/test_repo_permissions.py index 24186c5a..600be8a8 100644 --- a/rhodecode/apps/repository/tests/test_repo_permissions.py +++ b/rhodecode/apps/repository/tests/test_repo_permissions.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_pullrequests.py b/rhodecode/apps/repository/tests/test_repo_pullrequests.py index ad41a2cb..00f9f3d4 100644 --- a/rhodecode/apps/repository/tests/test_repo_pullrequests.py +++ b/rhodecode/apps/repository/tests/test_repo_pullrequests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_settings.py b/rhodecode/apps/repository/tests/test_repo_settings.py index d20fe3cb..0c8621a4 100644 --- a/rhodecode/apps/repository/tests/test_repo_settings.py +++ b/rhodecode/apps/repository/tests/test_repo_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py index dc8e8c6a..24a910ad 100644 --- a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py +++ b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_summary.py b/rhodecode/apps/repository/tests/test_repo_summary.py index a2a3ab92..92c9975e 100644 --- a/rhodecode/apps/repository/tests/test_repo_summary.py +++ b/rhodecode/apps/repository/tests/test_repo_summary.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_tags.py b/rhodecode/apps/repository/tests/test_repo_tags.py index 7c90ff7d..02ebce38 100644 --- a/rhodecode/apps/repository/tests/test_repo_tags.py +++ b/rhodecode/apps/repository/tests/test_repo_tags.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py index 63accea0..d5395569 100644 --- a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py +++ b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/tests/test_vcs_settings.py b/rhodecode/apps/repository/tests/test_vcs_settings.py index 0b05c64a..edb136d4 100644 --- a/rhodecode/apps/repository/tests/test_vcs_settings.py +++ b/rhodecode/apps/repository/tests/test_vcs_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/utils.py b/rhodecode/apps/repository/utils.py index 697ba0ad..08034a39 100644 --- a/rhodecode/apps/repository/utils.py +++ b/rhodecode/apps/repository/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/__init__.py b/rhodecode/apps/repository/views/__init__.py index e10695d4..a9ba9298 100644 --- a/rhodecode/apps/repository/views/__init__.py +++ b/rhodecode/apps/repository/views/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_artifacts.py b/rhodecode/apps/repository/views/repo_artifacts.py index 2501b606..04535984 100644 --- a/rhodecode/apps/repository/views/repo_artifacts.py +++ b/rhodecode/apps/repository/views/repo_artifacts.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_audit_logs.py b/rhodecode/apps/repository/views/repo_audit_logs.py index 73364dc4..0e07dafa 100644 --- a/rhodecode/apps/repository/views/repo_audit_logs.py +++ b/rhodecode/apps/repository/views/repo_audit_logs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_automation.py b/rhodecode/apps/repository/views/repo_automation.py index 0a2a80c3..b39da22d 100644 --- a/rhodecode/apps/repository/views/repo_automation.py +++ b/rhodecode/apps/repository/views/repo_automation.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_bookmarks.py b/rhodecode/apps/repository/views/repo_bookmarks.py index c9e93919..ae8aec5f 100644 --- a/rhodecode/apps/repository/views/repo_bookmarks.py +++ b/rhodecode/apps/repository/views/repo_bookmarks.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_branch_permissions.py b/rhodecode/apps/repository/views/repo_branch_permissions.py index 505a71fd..6628bb64 100644 --- a/rhodecode/apps/repository/views/repo_branch_permissions.py +++ b/rhodecode/apps/repository/views/repo_branch_permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_branches.py b/rhodecode/apps/repository/views/repo_branches.py index 5b20f5ba..0c6b6264 100644 --- a/rhodecode/apps/repository/views/repo_branches.py +++ b/rhodecode/apps/repository/views/repo_branches.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_caches.py b/rhodecode/apps/repository/views/repo_caches.py index 05df8b6d..d78f4cb4 100644 --- a/rhodecode/apps/repository/views/repo_caches.py +++ b/rhodecode/apps/repository/views/repo_caches.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_changelog.py b/rhodecode/apps/repository/views/repo_changelog.py index bf3f9509..3c82f5ad 100644 --- a/rhodecode/apps/repository/views/repo_changelog.py +++ b/rhodecode/apps/repository/views/repo_changelog.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_checks.py b/rhodecode/apps/repository/views/repo_checks.py index 3c9d7e28..b81f7dfe 100644 --- a/rhodecode/apps/repository/views/repo_checks.py +++ b/rhodecode/apps/repository/views/repo_checks.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py index 9b50b5bc..aeb2e082 100644 --- a/rhodecode/apps/repository/views/repo_commits.py +++ b/rhodecode/apps/repository/views/repo_commits.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_compare.py b/rhodecode/apps/repository/views/repo_compare.py index a14dc8e3..69448255 100644 --- a/rhodecode/apps/repository/views/repo_compare.py +++ b/rhodecode/apps/repository/views/repo_compare.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_feed.py b/rhodecode/apps/repository/views/repo_feed.py index a9ca9f5f..0254e5f6 100644 --- a/rhodecode/apps/repository/views/repo_feed.py +++ b/rhodecode/apps/repository/views/repo_feed.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py index fb2fad78..ab46c1b5 100644 --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_forks.py b/rhodecode/apps/repository/views/repo_forks.py index ef42f960..fd1a19f6 100644 --- a/rhodecode/apps/repository/views/repo_forks.py +++ b/rhodecode/apps/repository/views/repo_forks.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_maintainance.py b/rhodecode/apps/repository/views/repo_maintainance.py index 8a79a7fb..f2db5c13 100644 --- a/rhodecode/apps/repository/views/repo_maintainance.py +++ b/rhodecode/apps/repository/views/repo_maintainance.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_permissions.py b/rhodecode/apps/repository/views/repo_permissions.py index 1ba9c94e..db019f57 100644 --- a/rhodecode/apps/repository/views/repo_permissions.py +++ b/rhodecode/apps/repository/views/repo_permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py index 192f071e..6df293b5 100644 --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_review_rules.py b/rhodecode/apps/repository/views/repo_review_rules.py index 9170e822..385c17c3 100644 --- a/rhodecode/apps/repository/views/repo_review_rules.py +++ b/rhodecode/apps/repository/views/repo_review_rules.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings.py b/rhodecode/apps/repository/views/repo_settings.py index 889f686d..3109c8b1 100644 --- a/rhodecode/apps/repository/views/repo_settings.py +++ b/rhodecode/apps/repository/views/repo_settings.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_advanced.py b/rhodecode/apps/repository/views/repo_settings_advanced.py index 2703be83..8519e742 100644 --- a/rhodecode/apps/repository/views/repo_settings_advanced.py +++ b/rhodecode/apps/repository/views/repo_settings_advanced.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_fields.py b/rhodecode/apps/repository/views/repo_settings_fields.py index caab97c4..e7ae7ec3 100644 --- a/rhodecode/apps/repository/views/repo_settings_fields.py +++ b/rhodecode/apps/repository/views/repo_settings_fields.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_issue_trackers.py b/rhodecode/apps/repository/views/repo_settings_issue_trackers.py index 818d720c..545da8c4 100644 --- a/rhodecode/apps/repository/views/repo_settings_issue_trackers.py +++ b/rhodecode/apps/repository/views/repo_settings_issue_trackers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_remote.py b/rhodecode/apps/repository/views/repo_settings_remote.py index dc909fb9..82cb5ed0 100644 --- a/rhodecode/apps/repository/views/repo_settings_remote.py +++ b/rhodecode/apps/repository/views/repo_settings_remote.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_settings_vcs.py b/rhodecode/apps/repository/views/repo_settings_vcs.py index b8d8e0a1..1e96798f 100644 --- a/rhodecode/apps/repository/views/repo_settings_vcs.py +++ b/rhodecode/apps/repository/views/repo_settings_vcs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_strip.py b/rhodecode/apps/repository/views/repo_strip.py index e9c8ab87..9e02cf48 100644 --- a/rhodecode/apps/repository/views/repo_strip.py +++ b/rhodecode/apps/repository/views/repo_strip.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_summary.py b/rhodecode/apps/repository/views/repo_summary.py index 05a36f37..25565485 100644 --- a/rhodecode/apps/repository/views/repo_summary.py +++ b/rhodecode/apps/repository/views/repo_summary.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/repository/views/repo_tags.py b/rhodecode/apps/repository/views/repo_tags.py index 8895116c..4ce393d5 100644 --- a/rhodecode/apps/repository/views/repo_tags.py +++ b/rhodecode/apps/repository/views/repo_tags.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/search/__init__.py b/rhodecode/apps/search/__init__.py index 3a07efd3..a1d27e59 100644 --- a/rhodecode/apps/search/__init__.py +++ b/rhodecode/apps/search/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/search/tests/test_search.py b/rhodecode/apps/search/tests/test_search.py index 34d9f055..eaf6a4df 100644 --- a/rhodecode/apps/search/tests/test_search.py +++ b/rhodecode/apps/search/tests/test_search.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/search/views.py b/rhodecode/apps/search/views.py index 8024c757..664bf3ba 100644 --- a/rhodecode/apps/search/views.py +++ b/rhodecode/apps/search/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/__init__.py b/rhodecode/apps/ssh_support/__init__.py index 1efad2c1..9621d032 100644 --- a/rhodecode/apps/ssh_support/__init__.py +++ b/rhodecode/apps/ssh_support/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/config_keys.py b/rhodecode/apps/ssh_support/config_keys.py index 490b7fc5..26fead88 100644 --- a/rhodecode/apps/ssh_support/config_keys.py +++ b/rhodecode/apps/ssh_support/config_keys.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/events.py b/rhodecode/apps/ssh_support/events.py index fe15e8af..51b7dd7c 100644 --- a/rhodecode/apps/ssh_support/events.py +++ b/rhodecode/apps/ssh_support/events.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/__init__.py b/rhodecode/apps/ssh_support/lib/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/ssh_support/lib/__init__.py +++ b/rhodecode/apps/ssh_support/lib/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/backends/__init__.py b/rhodecode/apps/ssh_support/lib/backends/__init__.py index 01a7283e..0394baa0 100644 --- a/rhodecode/apps/ssh_support/lib/backends/__init__.py +++ b/rhodecode/apps/ssh_support/lib/backends/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/backends/base.py b/rhodecode/apps/ssh_support/lib/backends/base.py index 639a810e..0d824285 100644 --- a/rhodecode/apps/ssh_support/lib/backends/base.py +++ b/rhodecode/apps/ssh_support/lib/backends/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/backends/git.py b/rhodecode/apps/ssh_support/lib/backends/git.py index 26dba531..47ded5df 100644 --- a/rhodecode/apps/ssh_support/lib/backends/git.py +++ b/rhodecode/apps/ssh_support/lib/backends/git.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/backends/hg.py b/rhodecode/apps/ssh_support/lib/backends/hg.py index 3be6000e..0a99283b 100644 --- a/rhodecode/apps/ssh_support/lib/backends/hg.py +++ b/rhodecode/apps/ssh_support/lib/backends/hg.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/backends/svn.py b/rhodecode/apps/ssh_support/lib/backends/svn.py index 4e54e53a..d56b8be3 100644 --- a/rhodecode/apps/ssh_support/lib/backends/svn.py +++ b/rhodecode/apps/ssh_support/lib/backends/svn.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/ssh_wrapper_v1.py b/rhodecode/apps/ssh_support/lib/ssh_wrapper_v1.py index 1fd567ed..7fb1f1cb 100644 --- a/rhodecode/apps/ssh_support/lib/ssh_wrapper_v1.py +++ b/rhodecode/apps/ssh_support/lib/ssh_wrapper_v1.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/ssh_wrapper_v2.py b/rhodecode/apps/ssh_support/lib/ssh_wrapper_v2.py index 94034cfc..bbfa7d25 100644 --- a/rhodecode/apps/ssh_support/lib/ssh_wrapper_v2.py +++ b/rhodecode/apps/ssh_support/lib/ssh_wrapper_v2.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/lib/utils.py b/rhodecode/apps/ssh_support/lib/utils.py index bfe91082..c5ef3265 100644 --- a/rhodecode/apps/ssh_support/lib/utils.py +++ b/rhodecode/apps/ssh_support/lib/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/subscribers.py b/rhodecode/apps/ssh_support/subscribers.py index 9484599b..00447e47 100644 --- a/rhodecode/apps/ssh_support/subscribers.py +++ b/rhodecode/apps/ssh_support/subscribers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/__init__.py b/rhodecode/apps/ssh_support/tests/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/ssh_support/tests/__init__.py +++ b/rhodecode/apps/ssh_support/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/conftest.py b/rhodecode/apps/ssh_support/tests/conftest.py index 7c5a90cf..5797d900 100644 --- a/rhodecode/apps/ssh_support/tests/conftest.py +++ b/rhodecode/apps/ssh_support/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_server_git.py b/rhodecode/apps/ssh_support/tests/test_server_git.py index fc721340..9260f789 100644 --- a/rhodecode/apps/ssh_support/tests/test_server_git.py +++ b/rhodecode/apps/ssh_support/tests/test_server_git.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_server_hg.py b/rhodecode/apps/ssh_support/tests/test_server_hg.py index e9585eb5..d0521adc 100644 --- a/rhodecode/apps/ssh_support/tests/test_server_hg.py +++ b/rhodecode/apps/ssh_support/tests/test_server_hg.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_server_svn.py b/rhodecode/apps/ssh_support/tests/test_server_svn.py index e78d3149..a574cc7f 100644 --- a/rhodecode/apps/ssh_support/tests/test_server_svn.py +++ b/rhodecode/apps/ssh_support/tests/test_server_svn.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py b/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py index 108639ed..13ba4adf 100644 --- a/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py +++ b/rhodecode/apps/ssh_support/tests/test_ssh_authorized_keys_gen.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py b/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py index fcb5aa93..e5df0134 100644 --- a/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py +++ b/rhodecode/apps/ssh_support/tests/test_ssh_wrapper.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/ssh_support/utils.py b/rhodecode/apps/ssh_support/utils.py index a7cb3082..3d08e951 100644 --- a/rhodecode/apps/ssh_support/utils.py +++ b/rhodecode/apps/ssh_support/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/__init__.py b/rhodecode/apps/svn_support/__init__.py index 763f8ddb..8f3d20fc 100644 --- a/rhodecode/apps/svn_support/__init__.py +++ b/rhodecode/apps/svn_support/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/config_keys.py b/rhodecode/apps/svn_support/config_keys.py index f529035e..d7003991 100644 --- a/rhodecode/apps/svn_support/config_keys.py +++ b/rhodecode/apps/svn_support/config_keys.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/events.py b/rhodecode/apps/svn_support/events.py index 7c9ce70c..2185d100 100644 --- a/rhodecode/apps/svn_support/events.py +++ b/rhodecode/apps/svn_support/events.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/subscribers.py b/rhodecode/apps/svn_support/subscribers.py index ea83ac0c..5afef5b0 100644 --- a/rhodecode/apps/svn_support/subscribers.py +++ b/rhodecode/apps/svn_support/subscribers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py b/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py index a995e99b..f93b0079 100644 --- a/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py +++ b/rhodecode/apps/svn_support/tests/test_mod_dav_svn_config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/svn_support/utils.py b/rhodecode/apps/svn_support/utils.py index ec0d2a7f..ee15fe9e 100644 --- a/rhodecode/apps/svn_support/utils.py +++ b/rhodecode/apps/svn_support/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/__init__.py b/rhodecode/apps/user_group/__init__.py index 0fa8ac3d..2818fece 100644 --- a/rhodecode/apps/user_group/__init__.py +++ b/rhodecode/apps/user_group/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/tests/__init__.py b/rhodecode/apps/user_group/tests/__init__.py index c4927498..e73ac886 100644 --- a/rhodecode/apps/user_group/tests/__init__.py +++ b/rhodecode/apps/user_group/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/tests/test_user_groups.py b/rhodecode/apps/user_group/tests/test_user_groups.py index 5d5fd018..f1528c3b 100644 --- a/rhodecode/apps/user_group/tests/test_user_groups.py +++ b/rhodecode/apps/user_group/tests/test_user_groups.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/tests/test_user_groups_permissions.py b/rhodecode/apps/user_group/tests/test_user_groups_permissions.py index ca2a4fc3..66b24b8d 100644 --- a/rhodecode/apps/user_group/tests/test_user_groups_permissions.py +++ b/rhodecode/apps/user_group/tests/test_user_groups_permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group/views/__init__.py b/rhodecode/apps/user_group/views/__init__.py index 56d4a18f..b0139029 100644 --- a/rhodecode/apps/user_group/views/__init__.py +++ b/rhodecode/apps/user_group/views/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/__init__.py b/rhodecode/apps/user_group_profile/__init__.py index 47a28422..509e2d5e 100644 --- a/rhodecode/apps/user_group_profile/__init__.py +++ b/rhodecode/apps/user_group_profile/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/tests/__init__.py b/rhodecode/apps/user_group_profile/tests/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/user_group_profile/tests/__init__.py +++ b/rhodecode/apps/user_group_profile/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/tests/test_user_group.py b/rhodecode/apps/user_group_profile/tests/test_user_group.py index 4740375f..3679b1a2 100644 --- a/rhodecode/apps/user_group_profile/tests/test_user_group.py +++ b/rhodecode/apps/user_group_profile/tests/test_user_group.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_group_profile/views.py b/rhodecode/apps/user_group_profile/views.py index e32451d9..333ae22e 100644 --- a/rhodecode/apps/user_group_profile/views.py +++ b/rhodecode/apps/user_group_profile/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/__init__.py b/rhodecode/apps/user_profile/__init__.py index 154dd073..e52b1f52 100644 --- a/rhodecode/apps/user_profile/__init__.py +++ b/rhodecode/apps/user_profile/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/tests/__init__.py b/rhodecode/apps/user_profile/tests/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/apps/user_profile/tests/__init__.py +++ b/rhodecode/apps/user_profile/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/tests/test_users.py b/rhodecode/apps/user_profile/tests/test_users.py index 79b348c7..c31b0b83 100644 --- a/rhodecode/apps/user_profile/tests/test_users.py +++ b/rhodecode/apps/user_profile/tests/test_users.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/apps/user_profile/views.py b/rhodecode/apps/user_profile/views.py index 9ac6ef44..d0d82f9f 100644 --- a/rhodecode/apps/user_profile/views.py +++ b/rhodecode/apps/user_profile/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/__init__.py b/rhodecode/authentication/__init__.py index 07409194..00b46f1c 100644 --- a/rhodecode/authentication/__init__.py +++ b/rhodecode/authentication/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/base.py b/rhodecode/authentication/base.py index 29971248..75f53057 100644 --- a/rhodecode/authentication/base.py +++ b/rhodecode/authentication/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/interface.py b/rhodecode/authentication/interface.py index aa8a3bad..b741971d 100644 --- a/rhodecode/authentication/interface.py +++ b/rhodecode/authentication/interface.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/__init__.py b/rhodecode/authentication/plugins/__init__.py index a861bf2b..5b24a60c 100644 --- a/rhodecode/authentication/plugins/__init__.py +++ b/rhodecode/authentication/plugins/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_crowd.py b/rhodecode/authentication/plugins/auth_crowd.py index 613cc487..62f3959d 100644 --- a/rhodecode/authentication/plugins/auth_crowd.py +++ b/rhodecode/authentication/plugins/auth_crowd.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_headers.py b/rhodecode/authentication/plugins/auth_headers.py index 2e947543..0b4c3900 100644 --- a/rhodecode/authentication/plugins/auth_headers.py +++ b/rhodecode/authentication/plugins/auth_headers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_jasig_cas.py b/rhodecode/authentication/plugins/auth_jasig_cas.py index ec7b67c6..beb8deeb 100644 --- a/rhodecode/authentication/plugins/auth_jasig_cas.py +++ b/rhodecode/authentication/plugins/auth_jasig_cas.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_ldap.py b/rhodecode/authentication/plugins/auth_ldap.py index c4e72b4e..312c5ed4 100644 --- a/rhodecode/authentication/plugins/auth_ldap.py +++ b/rhodecode/authentication/plugins/auth_ldap.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_pam.py b/rhodecode/authentication/plugins/auth_pam.py index 52c18fb2..e440953a 100644 --- a/rhodecode/authentication/plugins/auth_pam.py +++ b/rhodecode/authentication/plugins/auth_pam.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_rhodecode.py b/rhodecode/authentication/plugins/auth_rhodecode.py index ffa355d6..1529faee 100644 --- a/rhodecode/authentication/plugins/auth_rhodecode.py +++ b/rhodecode/authentication/plugins/auth_rhodecode.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/plugins/auth_token.py b/rhodecode/authentication/plugins/auth_token.py index 243aaef0..03013373 100644 --- a/rhodecode/authentication/plugins/auth_token.py +++ b/rhodecode/authentication/plugins/auth_token.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/registry.py b/rhodecode/authentication/registry.py index 3bb38fd4..38cffa18 100644 --- a/rhodecode/authentication/registry.py +++ b/rhodecode/authentication/registry.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/routes.py b/rhodecode/authentication/routes.py index e9e4b969..ddae5fe3 100644 --- a/rhodecode/authentication/routes.py +++ b/rhodecode/authentication/routes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/schema.py b/rhodecode/authentication/schema.py index 49371064..21176a99 100644 --- a/rhodecode/authentication/schema.py +++ b/rhodecode/authentication/schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/tests/conftest.py b/rhodecode/authentication/tests/conftest.py index bf555e23..f04b28fc 100644 --- a/rhodecode/authentication/tests/conftest.py +++ b/rhodecode/authentication/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/tests/functional/test_settings.py b/rhodecode/authentication/tests/functional/test_settings.py index 3764ecdd..2bd67913 100644 --- a/rhodecode/authentication/tests/functional/test_settings.py +++ b/rhodecode/authentication/tests/functional/test_settings.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/authentication/views.py b/rhodecode/authentication/views.py index 4b86444d..4a26a04f 100644 --- a/rhodecode/authentication/views.py +++ b/rhodecode/authentication/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/__init__.py b/rhodecode/config/__init__.py index 11c251a7..5e62d987 100644 --- a/rhodecode/config/__init__.py +++ b/rhodecode/config/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/conf.py b/rhodecode/config/conf.py index 726d2838..eef40820 100644 --- a/rhodecode/config/conf.py +++ b/rhodecode/config/conf.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2023 RhodeCode GmbH +# Copyright (C) 2013-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/config_maker.py b/rhodecode/config/config_maker.py index 9155c472..3f71e878 100644 --- a/rhodecode/config/config_maker.py +++ b/rhodecode/config/config_maker.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/environment.py b/rhodecode/config/environment.py index acc2a76f..9542db3c 100644 --- a/rhodecode/config/environment.py +++ b/rhodecode/config/environment.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/jsroutes.py b/rhodecode/config/jsroutes.py index d10b0139..b19bbaeb 100644 --- a/rhodecode/config/jsroutes.py +++ b/rhodecode/config/jsroutes.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py index 8e34137c..57975239 100644 --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/patches.py b/rhodecode/config/patches.py index 04eb02aa..b6505cd1 100644 --- a/rhodecode/config/patches.py +++ b/rhodecode/config/patches.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/__init__.py b/rhodecode/config/rcextensions/__init__.py index ddb69559..ddb8d4dc 100644 --- a/rhodecode/config/rcextensions/__init__.py +++ b/rhodecode/config/rcextensions/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/__init__.py b/rhodecode/config/rcextensions/helpers/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/config/rcextensions/helpers/__init__.py +++ b/rhodecode/config/rcextensions/helpers/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/extra_fields.py b/rhodecode/config/rcextensions/helpers/extra_fields.py index 3bf746d2..3dbf7284 100644 --- a/rhodecode/config/rcextensions/helpers/extra_fields.py +++ b/rhodecode/config/rcextensions/helpers/extra_fields.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/extract_post_commits.py b/rhodecode/config/rcextensions/helpers/extract_post_commits.py index 9b2d0d9f..5beac048 100644 --- a/rhodecode/config/rcextensions/helpers/extract_post_commits.py +++ b/rhodecode/config/rcextensions/helpers/extract_post_commits.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/extract_pre_commits.py b/rhodecode/config/rcextensions/helpers/extract_pre_commits.py index 40702027..589da689 100644 --- a/rhodecode/config/rcextensions/helpers/extract_pre_commits.py +++ b/rhodecode/config/rcextensions/helpers/extract_pre_commits.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/extract_pre_files.py b/rhodecode/config/rcextensions/helpers/extract_pre_files.py index 9eb625c8..0839e211 100644 --- a/rhodecode/config/rcextensions/helpers/extract_pre_files.py +++ b/rhodecode/config/rcextensions/helpers/extract_pre_files.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/helpers/http_call.py b/rhodecode/config/rcextensions/helpers/http_call.py index b06dd9a0..cf7c2a28 100644 --- a/rhodecode/config/rcextensions/helpers/http_call.py +++ b/rhodecode/config/rcextensions/helpers/http_call.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/hooks.py b/rhodecode/config/rcextensions/hooks.py index 0cfd0447..72c9d501 100644 --- a/rhodecode/config/rcextensions/hooks.py +++ b/rhodecode/config/rcextensions/hooks.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/rcextensions/utils.py b/rhodecode/config/rcextensions/utils.py index d7f4b48d..31f2e27f 100644 --- a/rhodecode/config/rcextensions/utils.py +++ b/rhodecode/config/rcextensions/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/routing_links.py b/rhodecode/config/routing_links.py index 6a569383..3e45ff6c 100644 --- a/rhodecode/config/routing_links.py +++ b/rhodecode/config/routing_links.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/settings_maker.py b/rhodecode/config/settings_maker.py index 18b14d02..100f1505 100644 --- a/rhodecode/config/settings_maker.py +++ b/rhodecode/config/settings_maker.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/config/utils.py b/rhodecode/config/utils.py index e3d48d1c..82bf62b2 100644 --- a/rhodecode/config/utils.py +++ b/rhodecode/config/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/__init__.py b/rhodecode/events/__init__.py index 05903cbd..df5086e0 100644 --- a/rhodecode/events/__init__.py +++ b/rhodecode/events/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/base.py b/rhodecode/events/base.py index 9496ed79..d312989f 100644 --- a/rhodecode/events/base.py +++ b/rhodecode/events/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/interfaces.py b/rhodecode/events/interfaces.py index 9285f628..cd3a1657 100644 --- a/rhodecode/events/interfaces.py +++ b/rhodecode/events/interfaces.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/pullrequest.py b/rhodecode/events/pullrequest.py index e3e31cc4..97506e79 100644 --- a/rhodecode/events/pullrequest.py +++ b/rhodecode/events/pullrequest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/repo.py b/rhodecode/events/repo.py index 3794dd45..5e898a26 100644 --- a/rhodecode/events/repo.py +++ b/rhodecode/events/repo.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/repo_group.py b/rhodecode/events/repo_group.py index ed56c91d..210e9efd 100644 --- a/rhodecode/events/repo_group.py +++ b/rhodecode/events/repo_group.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/events/user.py b/rhodecode/events/user.py index 360b91be..43977f2b 100644 --- a/rhodecode/events/user.py +++ b/rhodecode/events/user.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/forms/__init__.py b/rhodecode/forms/__init__.py index 706707d1..9f04ea30 100644 --- a/rhodecode/forms/__init__.py +++ b/rhodecode/forms/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/__init__.py b/rhodecode/integrations/__init__.py index 215d9689..b1a75eb3 100644 --- a/rhodecode/integrations/__init__.py +++ b/rhodecode/integrations/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/registry.py b/rhodecode/integrations/registry.py index fb175063..8d408d7a 100644 --- a/rhodecode/integrations/registry.py +++ b/rhodecode/integrations/registry.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/routes.py b/rhodecode/integrations/routes.py index 8866811a..7173bf49 100644 --- a/rhodecode/integrations/routes.py +++ b/rhodecode/integrations/routes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/schema.py b/rhodecode/integrations/schema.py index 59cea360..38a9de3e 100644 --- a/rhodecode/integrations/schema.py +++ b/rhodecode/integrations/schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/tests/__init__.py b/rhodecode/integrations/tests/__init__.py index 0bdb7f25..e786a1a1 100644 --- a/rhodecode/integrations/tests/__init__.py +++ b/rhodecode/integrations/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/tests/test_integrations.py b/rhodecode/integrations/tests/test_integrations.py index 9fa47a9a..a11bb87a 100644 --- a/rhodecode/integrations/tests/test_integrations.py +++ b/rhodecode/integrations/tests/test_integrations.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/__init__.py b/rhodecode/integrations/types/__init__.py index a861bf2b..5b24a60c 100644 --- a/rhodecode/integrations/types/__init__.py +++ b/rhodecode/integrations/types/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/base.py b/rhodecode/integrations/types/base.py index b0642742..1ab2cdb0 100644 --- a/rhodecode/integrations/types/base.py +++ b/rhodecode/integrations/types/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/email.py b/rhodecode/integrations/types/email.py index 677d2e62..2da2acbc 100644 --- a/rhodecode/integrations/types/email.py +++ b/rhodecode/integrations/types/email.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/handlers/__init__.py b/rhodecode/integrations/types/handlers/__init__.py index a861bf2b..5b24a60c 100644 --- a/rhodecode/integrations/types/handlers/__init__.py +++ b/rhodecode/integrations/types/handlers/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/handlers/slack.py b/rhodecode/integrations/types/handlers/slack.py index 68749542..aaed64b6 100644 --- a/rhodecode/integrations/types/handlers/slack.py +++ b/rhodecode/integrations/types/handlers/slack.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/handlers/webhook.py b/rhodecode/integrations/types/handlers/webhook.py index 05d84651..4447b8b2 100644 --- a/rhodecode/integrations/types/handlers/webhook.py +++ b/rhodecode/integrations/types/handlers/webhook.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/slack.py b/rhodecode/integrations/types/slack.py index b927828e..3ad30bb8 100644 --- a/rhodecode/integrations/types/slack.py +++ b/rhodecode/integrations/types/slack.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/types/webhook.py b/rhodecode/integrations/types/webhook.py index 715db32b..f1838382 100644 --- a/rhodecode/integrations/types/webhook.py +++ b/rhodecode/integrations/types/webhook.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/integrations/views.py b/rhodecode/integrations/views.py index ee4d958f..038ab7a5 100644 --- a/rhodecode/integrations/views.py +++ b/rhodecode/integrations/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/__init__.py b/rhodecode/lib/__init__.py index 9db5f6aa..7500f496 100644 --- a/rhodecode/lib/__init__.py +++ b/rhodecode/lib/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/_vendor/__init__.py b/rhodecode/lib/_vendor/__init__.py index a118c37f..14e3802b 100644 --- a/rhodecode/lib/_vendor/__init__.py +++ b/rhodecode/lib/_vendor/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/_vendor/authomatic/six.py b/rhodecode/lib/_vendor/authomatic/six.py index c024aac1..defedb16 100755 --- a/rhodecode/lib/_vendor/authomatic/six.py +++ b/rhodecode/lib/_vendor/authomatic/six.py @@ -1,7 +1,5 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson +"""Utilities for writing code that runs on Python 2 and 3"""# Copyright (C) 2010-2015 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/rhodecode/lib/action_parser.py b/rhodecode/lib/action_parser.py index 6ff4efb2..ae3cc158 100644 --- a/rhodecode/lib/action_parser.py +++ b/rhodecode/lib/action_parser.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/api_utils.py b/rhodecode/lib/api_utils.py index 00e18c9f..92a06d7e 100644 --- a/rhodecode/lib/api_utils.py +++ b/rhodecode/lib/api_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/audit_logger.py b/rhodecode/lib/audit_logger.py index 43a1a7a6..e14d7d70 100644 --- a/rhodecode/lib/audit_logger.py +++ b/rhodecode/lib/audit_logger.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/auth.py b/rhodecode/lib/auth.py index 34ec7b40..3a2fe245 100644 --- a/rhodecode/lib/auth.py +++ b/rhodecode/lib/auth.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py index 45f74ae3..68b907e6 100644 --- a/rhodecode/lib/base.py +++ b/rhodecode/lib/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/caching_query.py b/rhodecode/lib/caching_query.py index a6ec8a27..b50012a7 100644 --- a/rhodecode/lib/caching_query.py +++ b/rhodecode/lib/caching_query.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/__init__.py b/rhodecode/lib/celerylib/__init__.py index b32d91d6..c241504d 100644 --- a/rhodecode/lib/celerylib/__init__.py +++ b/rhodecode/lib/celerylib/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/loader.py b/rhodecode/lib/celerylib/loader.py index deac2bfa..37e47bc5 100644 --- a/rhodecode/lib/celerylib/loader.py +++ b/rhodecode/lib/celerylib/loader.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/scheduler.py b/rhodecode/lib/celerylib/scheduler.py index 1a6a0557..1e91babd 100644 --- a/rhodecode/lib/celerylib/scheduler.py +++ b/rhodecode/lib/celerylib/scheduler.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/celerylib/utils.py b/rhodecode/lib/celerylib/utils.py index ba0079df..dcf5852f 100644 --- a/rhodecode/lib/celerylib/utils.py +++ b/rhodecode/lib/celerylib/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/channelstream.py b/rhodecode/lib/channelstream.py index 261f99dc..ea0c0846 100644 --- a/rhodecode/lib/channelstream.py +++ b/rhodecode/lib/channelstream.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/codeblocks.py b/rhodecode/lib/codeblocks.py index 25f053a7..f0aaf0a8 100644 --- a/rhodecode/lib/codeblocks.py +++ b/rhodecode/lib/codeblocks.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/colander_utils.py b/rhodecode/lib/colander_utils.py index 2506def4..7f3e3280 100644 --- a/rhodecode/lib/colander_utils.py +++ b/rhodecode/lib/colander_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/colored_formatter.py b/rhodecode/lib/colored_formatter.py index edeaab7c..2569f630 100644 --- a/rhodecode/lib/colored_formatter.py +++ b/rhodecode/lib/colored_formatter.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/config_utils.py b/rhodecode/lib/config_utils.py index eeafef5e..c93b97af 100644 --- a/rhodecode/lib/config_utils.py +++ b/rhodecode/lib/config_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/datelib.py b/rhodecode/lib/datelib.py index 1018a41b..d53e1960 100644 --- a/rhodecode/lib/datelib.py +++ b/rhodecode/lib/datelib.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py index c09155af..1f0c6031 100644 --- a/rhodecode/lib/db_manage.py +++ b/rhodecode/lib/db_manage.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/__init__.py b/rhodecode/lib/dbmigrate/__init__.py index 80b4f912..d04c1f90 100644 --- a/rhodecode/lib/dbmigrate/__init__.py +++ b/rhodecode/lib/dbmigrate/__init__.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/__init__.py b/rhodecode/lib/dbmigrate/schema/__init__.py index 7a7f3ca8..9fc12673 100644 --- a/rhodecode/lib/dbmigrate/schema/__init__.py +++ b/rhodecode/lib/dbmigrate/schema/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_1_0.py b/rhodecode/lib/dbmigrate/schema/db_1_1_0.py index 5fac86f0..69643acc 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_1_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_1_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_2_0.py b/rhodecode/lib/dbmigrate/schema/db_1_2_0.py index 28df9ab3..4fe5dc56 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_2_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_2_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_3_0.py b/rhodecode/lib/dbmigrate/schema/db_1_3_0.py index fb294e46..1dd53f3f 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_3_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_3_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_4_0.py b/rhodecode/lib/dbmigrate/schema/db_1_4_0.py index 4b840a48..f0b59eb6 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_4_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_4_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_5_0.py b/rhodecode/lib/dbmigrate/schema/db_1_5_0.py index abacbbc8..85429ad6 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_5_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_5_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_5_2.py b/rhodecode/lib/dbmigrate/schema/db_1_5_2.py index 2ddceddb..7bccc8ec 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_5_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_5_2.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_6_0.py b/rhodecode/lib/dbmigrate/schema/db_1_6_0.py index 6c5417e3..628e76e2 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_6_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_6_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_7_0.py b/rhodecode/lib/dbmigrate/schema/db_1_7_0.py index 72ab086a..2510bd87 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_7_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_7_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_1_8_0.py b/rhodecode/lib/dbmigrate/schema/db_1_8_0.py index 1c5f4150..62bc1a2b 100644 --- a/rhodecode/lib/dbmigrate/schema/db_1_8_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_1_8_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_0_0.py b/rhodecode/lib/dbmigrate/schema/db_2_0_0.py index cdb1bd64..c3b45252 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_0_1.py b/rhodecode/lib/dbmigrate/schema/db_2_0_1.py index 4328bc74..f65bd762 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_0_2.py b/rhodecode/lib/dbmigrate/schema/db_2_0_2.py index 85f0ef99..53a99272 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_0_2.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_1_0.py b/rhodecode/lib/dbmigrate/schema/db_2_1_0.py index 6829a340..e14ea2cf 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_1_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_1_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_2_0.py b/rhodecode/lib/dbmigrate/schema/db_2_2_0.py index c63c7da5..d5d1904c 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_2_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_2_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_2_3.py b/rhodecode/lib/dbmigrate/schema/db_2_2_3.py index 5399131f..ce972ae6 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_2_3.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_2_3.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py b/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py index 7a483757..6a8c77d8 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py b/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py index 88d287b2..6c9d5b45 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py b/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py index 7faadf87..6e4a3eb4 100644 --- a/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py index 2bef00c8..d5b6474f 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py b/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py index 39681446..70827865 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py index a28edb72..1b1b3f2c 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py b/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py index 10d4de80..e5115669 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py index 145b50ae..82a9c875 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py index c5caecff..bec59f09 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py index 14b77f22..c58cfeb9 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py b/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py index b893019c..0e380d54 100644 --- a/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py index 8843e836..4ed1cf24 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py index f506f713..1ead14d8 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py index a1e43f18..de645a4d 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py index 8010f94e..6cad7b0c 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py b/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py index 77a6095a..4d922f1a 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py index 458503f9..659d4cc1 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py index e463d63e..04f3dae4 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py b/rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py index 6904522b..5e347541 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py index f440c631..081650ae 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py index 96a65fbc..a8109ab4 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py index 90858ccf..efab6bc2 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py index 64d786eb..f17255c2 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py b/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py index fc76a631..50787654 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py index ac13d273..6bc089b9 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py index 1eb36c25..b0f658c4 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py b/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py index 56d6ae73..d931b3b8 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py b/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py index 68832b2a..4f3c9cc5 100644 --- a/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py +++ b/rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/utils.py b/rhodecode/lib/dbmigrate/utils.py index 32b3730b..b7c912f9 100644 --- a/rhodecode/lib/dbmigrate/utils.py +++ b/rhodecode/lib/dbmigrate/utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/dbmigrate/versions/__init__.py b/rhodecode/lib/dbmigrate/versions/__init__.py index ec5c7fc6..364c1869 100644 --- a/rhodecode/lib/dbmigrate/versions/__init__.py +++ b/rhodecode/lib/dbmigrate/versions/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py index a59db2de..f721cb05 100644 --- a/rhodecode/lib/diffs.py +++ b/rhodecode/lib/diffs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/enc_utils.py b/rhodecode/lib/enc_utils.py index 9a882dc1..53633b88 100644 --- a/rhodecode/lib/enc_utils.py +++ b/rhodecode/lib/enc_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/encrypt.py b/rhodecode/lib/encrypt.py index bdf859ce..89149b99 100644 --- a/rhodecode/lib/encrypt.py +++ b/rhodecode/lib/encrypt.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/exc_tracking.py b/rhodecode/lib/exc_tracking.py index 6feaf19a..75395b80 100644 --- a/rhodecode/lib/exc_tracking.py +++ b/rhodecode/lib/exc_tracking.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/exceptions.py b/rhodecode/lib/exceptions.py index 301e629f..37f8f122 100644 --- a/rhodecode/lib/exceptions.py +++ b/rhodecode/lib/exceptions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/ext_json_renderer.py b/rhodecode/lib/ext_json_renderer.py index 8af283a3..f712d86e 100644 --- a/rhodecode/lib/ext_json_renderer.py +++ b/rhodecode/lib/ext_json_renderer.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/feedgenerator/__init__.py b/rhodecode/lib/feedgenerator/__init__.py index 1769f9d6..cc5c3123 100644 --- a/rhodecode/lib/feedgenerator/__init__.py +++ b/rhodecode/lib/feedgenerator/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/graphmod.py b/rhodecode/lib/graphmod.py index 0dbe4336..5651cb6d 100644 --- a/rhodecode/lib/graphmod.py +++ b/rhodecode/lib/graphmod.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/hash_utils.py b/rhodecode/lib/hash_utils.py index 85e15f94..682337fe 100644 --- a/rhodecode/lib/hash_utils.py +++ b/rhodecode/lib/hash_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py index bfee147b..3bd9f2c1 100644 --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/hook_daemon/__init__.py b/rhodecode/lib/hook_daemon/__init__.py index c4d86aeb..ae06165a 100644 --- a/rhodecode/lib/hook_daemon/__init__.py +++ b/rhodecode/lib/hook_daemon/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/hook_daemon/celery_hooks_deamon.py b/rhodecode/lib/hook_daemon/celery_hooks_deamon.py index fc67b3cb..19cb5991 100644 --- a/rhodecode/lib/hook_daemon/celery_hooks_deamon.py +++ b/rhodecode/lib/hook_daemon/celery_hooks_deamon.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/hook_daemon/hook_module.py b/rhodecode/lib/hook_daemon/hook_module.py index 1a7e90e0..ea7e484e 100644 --- a/rhodecode/lib/hook_daemon/hook_module.py +++ b/rhodecode/lib/hook_daemon/hook_module.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/hooks_base.py b/rhodecode/lib/hooks_base.py index c44d5368..8931ab71 100644 --- a/rhodecode/lib/hooks_base.py +++ b/rhodecode/lib/hooks_base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2023 RhodeCode GmbH +# Copyright (C) 2013-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/hooks_utils.py b/rhodecode/lib/hooks_utils.py index 9aeaa5c6..6f96486e 100644 --- a/rhodecode/lib/hooks_utils.py +++ b/rhodecode/lib/hooks_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/html_filters.py b/rhodecode/lib/html_filters.py index 98d66c37..c5eab5c1 100644 --- a/rhodecode/lib/html_filters.py +++ b/rhodecode/lib/html_filters.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2023 RhodeCode GmbH +# Copyright (C) 2020-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/index/__init__.py b/rhodecode/lib/index/__init__.py index a03715d8..63838efa 100644 --- a/rhodecode/lib/index/__init__.py +++ b/rhodecode/lib/index/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/index/search_utils.py b/rhodecode/lib/index/search_utils.py index 4f682433..59da18a4 100644 --- a/rhodecode/lib/index/search_utils.py +++ b/rhodecode/lib/index/search_utils.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/index/whoosh.py b/rhodecode/lib/index/whoosh.py index cbae1e29..c59c569a 100644 --- a/rhodecode/lib/index/whoosh.py +++ b/rhodecode/lib/index/whoosh.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/index/whoosh_fallback_schema.py b/rhodecode/lib/index/whoosh_fallback_schema.py index 3e1506d4..49079897 100644 --- a/rhodecode/lib/index/whoosh_fallback_schema.py +++ b/rhodecode/lib/index/whoosh_fallback_schema.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/jsonalchemy.py b/rhodecode/lib/jsonalchemy.py index d992e7a2..d3a4b00e 100644 --- a/rhodecode/lib/jsonalchemy.py +++ b/rhodecode/lib/jsonalchemy.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/logging_formatter.py b/rhodecode/lib/logging_formatter.py index 6bc7b6c4..7cee8767 100644 --- a/rhodecode/lib/logging_formatter.py +++ b/rhodecode/lib/logging_formatter.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/markdown_ext.py b/rhodecode/lib/markdown_ext.py index 92be52a8..5353ccf0 100644 --- a/rhodecode/lib/markdown_ext.py +++ b/rhodecode/lib/markdown_ext.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/markup_renderer.py b/rhodecode/lib/markup_renderer.py index 1016ee3c..951cdd9b 100644 --- a/rhodecode/lib/markup_renderer.py +++ b/rhodecode/lib/markup_renderer.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/memory_lru_dict.py b/rhodecode/lib/memory_lru_dict.py index a3347cf7..c1a7e1eb 100644 --- a/rhodecode/lib/memory_lru_dict.py +++ b/rhodecode/lib/memory_lru_dict.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/__init__.py b/rhodecode/lib/middleware/__init__.py index 9159cefc..5b24a60c 100644 --- a/rhodecode/lib/middleware/__init__.py +++ b/rhodecode/lib/middleware/__init__.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/appenlight.py b/rhodecode/lib/middleware/appenlight.py index 5f9b9f5d..5beef0b3 100644 --- a/rhodecode/lib/middleware/appenlight.py +++ b/rhodecode/lib/middleware/appenlight.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/csrf.py b/rhodecode/lib/middleware/csrf.py index a89c5131..f0cdae00 100644 --- a/rhodecode/lib/middleware/csrf.py +++ b/rhodecode/lib/middleware/csrf.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/https_fixup.py b/rhodecode/lib/middleware/https_fixup.py index 66d77c2d..8ebb641e 100644 --- a/rhodecode/lib/middleware/https_fixup.py +++ b/rhodecode/lib/middleware/https_fixup.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/request_wrapper.py b/rhodecode/lib/middleware/request_wrapper.py index 68b21aba..d87511b1 100644 --- a/rhodecode/lib/middleware/request_wrapper.py +++ b/rhodecode/lib/middleware/request_wrapper.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/simplegit.py b/rhodecode/lib/middleware/simplegit.py index 9580f827..999c56eb 100644 --- a/rhodecode/lib/middleware/simplegit.py +++ b/rhodecode/lib/middleware/simplegit.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/simplehg.py b/rhodecode/lib/middleware/simplehg.py index 35f1f75a..07433332 100644 --- a/rhodecode/lib/middleware/simplehg.py +++ b/rhodecode/lib/middleware/simplehg.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/simplesvn.py b/rhodecode/lib/middleware/simplesvn.py index 67fe64bf..b67edce5 100644 --- a/rhodecode/lib/middleware/simplesvn.py +++ b/rhodecode/lib/middleware/simplesvn.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/simplevcs.py b/rhodecode/lib/middleware/simplevcs.py index bd6d8c16..664ee739 100644 --- a/rhodecode/lib/middleware/simplevcs.py +++ b/rhodecode/lib/middleware/simplevcs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/utils/__init__.py b/rhodecode/lib/middleware/utils/__init__.py index 9d269b75..36edc0fd 100644 --- a/rhodecode/lib/middleware/utils/__init__.py +++ b/rhodecode/lib/middleware/utils/__init__.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/utils/scm_app.py b/rhodecode/lib/middleware/utils/scm_app.py index 4acc81f7..063c3154 100644 --- a/rhodecode/lib/middleware/utils/scm_app.py +++ b/rhodecode/lib/middleware/utils/scm_app.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/utils/scm_app_http.py b/rhodecode/lib/middleware/utils/scm_app_http.py index 19be6777..da49d2a8 100644 --- a/rhodecode/lib/middleware/utils/scm_app_http.py +++ b/rhodecode/lib/middleware/utils/scm_app_http.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py b/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py index ff555fad..a036185c 100644 --- a/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py +++ b/rhodecode/lib/middleware/utils/wsgi_app_caller_client.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/middleware/vcs.py b/rhodecode/lib/middleware/vcs.py index 59b26c8f..8793f9aa 100644 --- a/rhodecode/lib/middleware/vcs.py +++ b/rhodecode/lib/middleware/vcs.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pagination.py b/rhodecode/lib/pagination.py index 42c03f18..fec607ef 100644 --- a/rhodecode/lib/pagination.py +++ b/rhodecode/lib/pagination.py @@ -1,6 +1,4 @@ - - -# Copyright (c) 2007-2012 Christoph Haas +# Copyright (C) 2007-2012 Christoph Haas # NOTE: MIT license based code, backported and edited by RhodeCode GmbH """ @@ -869,8 +867,7 @@ class _Page(list): return make_html_tag("a", text=text, href=target_url, **item["attrs"]) -# Below is RhodeCode custom code -# Copyright (C) 2010-2023 RhodeCode GmbH +# Below is RhodeCode custom code# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/partial_renderer.py b/rhodecode/lib/partial_renderer.py index affe241b..bdbb71a8 100644 --- a/rhodecode/lib/partial_renderer.py +++ b/rhodecode/lib/partial_renderer.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pidlock.py b/rhodecode/lib/pidlock.py index 069108db..98284e5d 100644 --- a/rhodecode/lib/pidlock.py +++ b/rhodecode/lib/pidlock.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/plugins/__init__.py b/rhodecode/lib/plugins/__init__.py index 9d88ca55..e786a1a1 100644 --- a/rhodecode/lib/plugins/__init__.py +++ b/rhodecode/lib/plugins/__init__.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/plugins/utils.py b/rhodecode/lib/plugins/utils.py index f12c3979..5175100f 100644 --- a/rhodecode/lib/plugins/utils.py +++ b/rhodecode/lib/plugins/utils.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pyramid_shell/__init__.py b/rhodecode/lib/pyramid_shell/__init__.py index 6ab59ad0..7162e3f1 100644 --- a/rhodecode/lib/pyramid_shell/__init__.py +++ b/rhodecode/lib/pyramid_shell/__init__.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/pyramid_utils.py b/rhodecode/lib/pyramid_utils.py index daaf32e3..68cbeb81 100644 --- a/rhodecode/lib/pyramid_utils.py +++ b/rhodecode/lib/pyramid_utils.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_cache/__init__.py b/rhodecode/lib/rc_cache/__init__.py index f93d125f..bbb9c6ec 100644 --- a/rhodecode/lib/rc_cache/__init__.py +++ b/rhodecode/lib/rc_cache/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2023 RhodeCode GmbH +# Copyright (C) 2015-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_cache/backends.py b/rhodecode/lib/rc_cache/backends.py index 04d4f724..22813c78 100644 --- a/rhodecode/lib/rc_cache/backends.py +++ b/rhodecode/lib/rc_cache/backends.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2023 RhodeCode GmbH +# Copyright (C) 2015-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_cache/region_meta.py b/rhodecode/lib/rc_cache/region_meta.py index b6322bb4..4c1fcbd1 100644 --- a/rhodecode/lib/rc_cache/region_meta.py +++ b/rhodecode/lib/rc_cache/region_meta.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2023 RhodeCode GmbH +# Copyright (C) 2015-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/add_artifact.py b/rhodecode/lib/rc_commands/add_artifact.py index 5bbe6d7a..432598d8 100644 --- a/rhodecode/lib/rc_commands/add_artifact.py +++ b/rhodecode/lib/rc_commands/add_artifact.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/ishell.py b/rhodecode/lib/rc_commands/ishell.py index 0be469b8..baef0028 100644 --- a/rhodecode/lib/rc_commands/ishell.py +++ b/rhodecode/lib/rc_commands/ishell.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/migrate_artifact.py b/rhodecode/lib/rc_commands/migrate_artifact.py index 9205daba..b9411e1f 100644 --- a/rhodecode/lib/rc_commands/migrate_artifact.py +++ b/rhodecode/lib/rc_commands/migrate_artifact.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/setup_rc.py b/rhodecode/lib/rc_commands/setup_rc.py index 18d133ac..123f3951 100644 --- a/rhodecode/lib/rc_commands/setup_rc.py +++ b/rhodecode/lib/rc_commands/setup_rc.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/rc_commands/upgrade_db.py b/rhodecode/lib/rc_commands/upgrade_db.py index 9c617430..1faae189 100644 --- a/rhodecode/lib/rc_commands/upgrade_db.py +++ b/rhodecode/lib/rc_commands/upgrade_db.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/repo_maintenance.py b/rhodecode/lib/repo_maintenance.py index d25f9241..5463abbd 100644 --- a/rhodecode/lib/repo_maintenance.py +++ b/rhodecode/lib/repo_maintenance.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/request.py b/rhodecode/lib/request.py index 1d89fe12..0d381485 100644 --- a/rhodecode/lib/request.py +++ b/rhodecode/lib/request.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/request_counter.py b/rhodecode/lib/request_counter.py index 0a74d028..cf650c26 100644 --- a/rhodecode/lib/request_counter.py +++ b/rhodecode/lib/request_counter.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/str_utils.py b/rhodecode/lib/str_utils.py index 437aabde..7f437786 100644 --- a/rhodecode/lib/str_utils.py +++ b/rhodecode/lib/str_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/string_renderer.py b/rhodecode/lib/string_renderer.py index eeb3ede7..31eb5131 100644 --- a/rhodecode/lib/string_renderer.py +++ b/rhodecode/lib/string_renderer.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/svn_txn_utils.py b/rhodecode/lib/svn_txn_utils.py index 5463bbb2..8fd8aa33 100644 --- a/rhodecode/lib/svn_txn_utils.py +++ b/rhodecode/lib/svn_txn_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/system_info.py b/rhodecode/lib/system_info.py index 524c9f30..41ade8da 100644 --- a/rhodecode/lib/system_info.py +++ b/rhodecode/lib/system_info.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/type_utils.py b/rhodecode/lib/type_utils.py index c4328cc4..210e366d 100644 --- a/rhodecode/lib/type_utils.py +++ b/rhodecode/lib/type_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/user_log_filter.py b/rhodecode/lib/user_log_filter.py index 206f0976..258a7cfd 100644 --- a/rhodecode/lib/user_log_filter.py +++ b/rhodecode/lib/user_log_filter.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/user_sessions.py b/rhodecode/lib/user_sessions.py index d51d0085..6dfeb554 100644 --- a/rhodecode/lib/user_sessions.py +++ b/rhodecode/lib/user_sessions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py index ea5ada46..1485dba6 100644 --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/utils2.py b/rhodecode/lib/utils2.py index 4030f998..11918581 100644 --- a/rhodecode/lib/utils2.py +++ b/rhodecode/lib/utils2.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/__init__.py b/rhodecode/lib/vcs/__init__.py index bce7c095..e03c4031 100644 --- a/rhodecode/lib/vcs/__init__.py +++ b/rhodecode/lib/vcs/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/__init__.py b/rhodecode/lib/vcs/backends/__init__.py index f357fb33..c6ce140e 100644 --- a/rhodecode/lib/vcs/backends/__init__.py +++ b/rhodecode/lib/vcs/backends/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/base.py b/rhodecode/lib/vcs/backends/base.py index b06e5914..2d0ea95d 100644 --- a/rhodecode/lib/vcs/backends/base.py +++ b/rhodecode/lib/vcs/backends/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/__init__.py b/rhodecode/lib/vcs/backends/git/__init__.py index e08cd427..ad13115f 100644 --- a/rhodecode/lib/vcs/backends/git/__init__.py +++ b/rhodecode/lib/vcs/backends/git/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/commit.py b/rhodecode/lib/vcs/backends/git/commit.py index b6a76dfc..89f90d98 100644 --- a/rhodecode/lib/vcs/backends/git/commit.py +++ b/rhodecode/lib/vcs/backends/git/commit.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/diff.py b/rhodecode/lib/vcs/backends/git/diff.py index d652799a..49219e2b 100644 --- a/rhodecode/lib/vcs/backends/git/diff.py +++ b/rhodecode/lib/vcs/backends/git/diff.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/inmemory.py b/rhodecode/lib/vcs/backends/git/inmemory.py index e02cbc79..4d7bc556 100644 --- a/rhodecode/lib/vcs/backends/git/inmemory.py +++ b/rhodecode/lib/vcs/backends/git/inmemory.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/git/repository.py b/rhodecode/lib/vcs/backends/git/repository.py index 73a1a060..1ab47525 100644 --- a/rhodecode/lib/vcs/backends/git/repository.py +++ b/rhodecode/lib/vcs/backends/git/repository.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/__init__.py b/rhodecode/lib/vcs/backends/hg/__init__.py index cefbe54a..1044acbe 100644 --- a/rhodecode/lib/vcs/backends/hg/__init__.py +++ b/rhodecode/lib/vcs/backends/hg/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/commit.py b/rhodecode/lib/vcs/backends/hg/commit.py index 2033efca..d79339c2 100644 --- a/rhodecode/lib/vcs/backends/hg/commit.py +++ b/rhodecode/lib/vcs/backends/hg/commit.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/diff.py b/rhodecode/lib/vcs/backends/hg/diff.py index 97a7f967..6f8045b6 100644 --- a/rhodecode/lib/vcs/backends/hg/diff.py +++ b/rhodecode/lib/vcs/backends/hg/diff.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/inmemory.py b/rhodecode/lib/vcs/backends/hg/inmemory.py index d7b28b4a..53ca6343 100644 --- a/rhodecode/lib/vcs/backends/hg/inmemory.py +++ b/rhodecode/lib/vcs/backends/hg/inmemory.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/hg/repository.py b/rhodecode/lib/vcs/backends/hg/repository.py index 7cc7ac38..dbe4f612 100644 --- a/rhodecode/lib/vcs/backends/hg/repository.py +++ b/rhodecode/lib/vcs/backends/hg/repository.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/__init__.py b/rhodecode/lib/vcs/backends/svn/__init__.py index d49340ba..03814eee 100644 --- a/rhodecode/lib/vcs/backends/svn/__init__.py +++ b/rhodecode/lib/vcs/backends/svn/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/commit.py b/rhodecode/lib/vcs/backends/svn/commit.py index 6d9289df..c7fbec84 100644 --- a/rhodecode/lib/vcs/backends/svn/commit.py +++ b/rhodecode/lib/vcs/backends/svn/commit.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/diff.py b/rhodecode/lib/vcs/backends/svn/diff.py index 0113460b..17b7490b 100644 --- a/rhodecode/lib/vcs/backends/svn/diff.py +++ b/rhodecode/lib/vcs/backends/svn/diff.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/inmemory.py b/rhodecode/lib/vcs/backends/svn/inmemory.py index b9b09c8c..09b742ed 100644 --- a/rhodecode/lib/vcs/backends/svn/inmemory.py +++ b/rhodecode/lib/vcs/backends/svn/inmemory.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/backends/svn/repository.py b/rhodecode/lib/vcs/backends/svn/repository.py index 9427b557..7ec4e9d3 100644 --- a/rhodecode/lib/vcs/backends/svn/repository.py +++ b/rhodecode/lib/vcs/backends/svn/repository.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py index 498a0eb6..75eb01cc 100644 --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/conf/__init__.py b/rhodecode/lib/vcs/conf/__init__.py index c4d86aeb..ae06165a 100644 --- a/rhodecode/lib/vcs/conf/__init__.py +++ b/rhodecode/lib/vcs/conf/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/conf/mtypes.py b/rhodecode/lib/vcs/conf/mtypes.py index fdc23dbc..8111c833 100644 --- a/rhodecode/lib/vcs/conf/mtypes.py +++ b/rhodecode/lib/vcs/conf/mtypes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/conf/settings.py b/rhodecode/lib/vcs/conf/settings.py index 8a8640a5..ca9d954b 100644 --- a/rhodecode/lib/vcs/conf/settings.py +++ b/rhodecode/lib/vcs/conf/settings.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/connection.py b/rhodecode/lib/vcs/connection.py index 12bc2304..a919ea50 100644 --- a/rhodecode/lib/vcs/connection.py +++ b/rhodecode/lib/vcs/connection.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/exceptions.py b/rhodecode/lib/vcs/exceptions.py index 55a1f838..fe4547b5 100644 --- a/rhodecode/lib/vcs/exceptions.py +++ b/rhodecode/lib/vcs/exceptions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/geventcurl.py b/rhodecode/lib/vcs/geventcurl.py index 814022c5..c1e5fffb 100644 --- a/rhodecode/lib/vcs/geventcurl.py +++ b/rhodecode/lib/vcs/geventcurl.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/nodes.py b/rhodecode/lib/vcs/nodes.py index cd74abfa..9ed11448 100644 --- a/rhodecode/lib/vcs/nodes.py +++ b/rhodecode/lib/vcs/nodes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/path.py b/rhodecode/lib/vcs/path.py index f365e0ed..97792a6d 100644 --- a/rhodecode/lib/vcs/path.py +++ b/rhodecode/lib/vcs/path.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/__init__.py b/rhodecode/lib/vcs/utils/__init__.py index e4516ed6..5c4fba8e 100644 --- a/rhodecode/lib/vcs/utils/__init__.py +++ b/rhodecode/lib/vcs/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/helpers.py b/rhodecode/lib/vcs/utils/helpers.py index 15ae1be3..379511f1 100644 --- a/rhodecode/lib/vcs/utils/helpers.py +++ b/rhodecode/lib/vcs/utils/helpers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2023 RhodeCode GmbH +# Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/imports.py b/rhodecode/lib/vcs/utils/imports.py index 609ae616..f70bbff2 100644 --- a/rhodecode/lib/vcs/utils/imports.py +++ b/rhodecode/lib/vcs/utils/imports.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/vcs/utils/paths.py b/rhodecode/lib/vcs/utils/paths.py index feffe8dd..04a9801d 100644 --- a/rhodecode/lib/vcs/utils/paths.py +++ b/rhodecode/lib/vcs/utils/paths.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/lib/view_utils.py b/rhodecode/lib/view_utils.py index 51135ad9..0cf63be1 100644 --- a/rhodecode/lib/view_utils.py +++ b/rhodecode/lib/view_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/__init__.py b/rhodecode/model/__init__.py index 2683acc7..1dbbbf91 100644 --- a/rhodecode/model/__init__.py +++ b/rhodecode/model/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/auth_token.py b/rhodecode/model/auth_token.py index abcc2456..d11f9bbe 100644 --- a/rhodecode/model/auth_token.py +++ b/rhodecode/model/auth_token.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2023 RhodeCode GmbH +# Copyright (C) 2013-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/changeset_status.py b/rhodecode/model/changeset_status.py index 8c05a3cc..e08216f1 100644 --- a/rhodecode/model/changeset_status.py +++ b/rhodecode/model/changeset_status.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py index 8b44a156..15c77d50 100644 --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py index 4d687678..12633c41 100644 --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py index bc82d930..4bbc79d5 100644 --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/gist.py b/rhodecode/model/gist.py index 3218306e..ce150fa1 100644 --- a/rhodecode/model/gist.py +++ b/rhodecode/model/gist.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2023 RhodeCode GmbH +# Copyright (C) 2013-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/integration.py b/rhodecode/model/integration.py index f53ff5bb..872bac00 100644 --- a/rhodecode/model/integration.py +++ b/rhodecode/model/integration.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/meta.py b/rhodecode/model/meta.py index 4ff3afc6..ea320254 100644 --- a/rhodecode/model/meta.py +++ b/rhodecode/model/meta.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py index acad4e6c..de95c674 100644 --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/permission.py b/rhodecode/model/permission.py index 8fcbe733..fdedce8b 100644 --- a/rhodecode/model/permission.py +++ b/rhodecode/model/permission.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py index e99fad38..22be808e 100644 --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py index af9fa47f..fc6d6708 100644 --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/repo_permission.py b/rhodecode/model/repo_permission.py index 39d4820c..c7d7a933 100644 --- a/rhodecode/model/repo_permission.py +++ b/rhodecode/model/repo_permission.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py index e4e5e564..c53c729f 100644 --- a/rhodecode/model/scm.py +++ b/rhodecode/model/scm.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/settings.py b/rhodecode/model/settings.py index 23433839..b9498f6d 100644 --- a/rhodecode/model/settings.py +++ b/rhodecode/model/settings.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/ssh_key.py b/rhodecode/model/ssh_key.py index b2d85556..b2cc2ce2 100644 --- a/rhodecode/model/ssh_key.py +++ b/rhodecode/model/ssh_key.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2023 RhodeCode GmbH +# Copyright (C) 2013-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/update.py b/rhodecode/model/update.py index e6087fa6..d6dcfd85 100644 --- a/rhodecode/model/update.py +++ b/rhodecode/model/update.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2023 RhodeCode GmbH +# Copyright (C) 2013-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py index 852e8aad..e539a26d 100644 --- a/rhodecode/model/user.py +++ b/rhodecode/model/user.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/user_group.py b/rhodecode/model/user_group.py index f58f06f5..da5a833a 100644 --- a/rhodecode/model/user_group.py +++ b/rhodecode/model/user_group.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/__init__.py b/rhodecode/model/validation_schema/__init__.py index 94c1253b..0838a1fb 100644 --- a/rhodecode/model/validation_schema/__init__.py +++ b/rhodecode/model/validation_schema/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/preparers.py b/rhodecode/model/validation_schema/preparers.py index 31303d0e..ce1fbe1b 100644 --- a/rhodecode/model/validation_schema/preparers.py +++ b/rhodecode/model/validation_schema/preparers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/__init__.py b/rhodecode/model/validation_schema/schemas/__init__.py index bbc264c9..7840a51d 100644 --- a/rhodecode/model/validation_schema/schemas/__init__.py +++ b/rhodecode/model/validation_schema/schemas/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/comment_schema.py b/rhodecode/model/validation_schema/schemas/comment_schema.py index 2a60e107..88542959 100644 --- a/rhodecode/model/validation_schema/schemas/comment_schema.py +++ b/rhodecode/model/validation_schema/schemas/comment_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2023 RhodeCode GmbH +# Copyright (C) 2017-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/gist_schema.py b/rhodecode/model/validation_schema/schemas/gist_schema.py index 37794623..f9a3e6b9 100644 --- a/rhodecode/model/validation_schema/schemas/gist_schema.py +++ b/rhodecode/model/validation_schema/schemas/gist_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/integration_schema.py b/rhodecode/model/validation_schema/schemas/integration_schema.py index ce6b8fa2..fe94e099 100644 --- a/rhodecode/model/validation_schema/schemas/integration_schema.py +++ b/rhodecode/model/validation_schema/schemas/integration_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/repo_group_schema.py b/rhodecode/model/validation_schema/schemas/repo_group_schema.py index c38cf9d2..36eaa7a5 100644 --- a/rhodecode/model/validation_schema/schemas/repo_group_schema.py +++ b/rhodecode/model/validation_schema/schemas/repo_group_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/repo_schema.py b/rhodecode/model/validation_schema/schemas/repo_schema.py index 40a3c43c..c1f426fc 100644 --- a/rhodecode/model/validation_schema/schemas/repo_schema.py +++ b/rhodecode/model/validation_schema/schemas/repo_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/reviewer_schema.py b/rhodecode/model/validation_schema/schemas/reviewer_schema.py index 2efc9753..7f809398 100644 --- a/rhodecode/model/validation_schema/schemas/reviewer_schema.py +++ b/rhodecode/model/validation_schema/schemas/reviewer_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/search_schema.py b/rhodecode/model/validation_schema/schemas/search_schema.py index 4f73ad54..7c280316 100644 --- a/rhodecode/model/validation_schema/schemas/search_schema.py +++ b/rhodecode/model/validation_schema/schemas/search_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/user_group_schema.py b/rhodecode/model/validation_schema/schemas/user_group_schema.py index b3daf73d..3367fa84 100644 --- a/rhodecode/model/validation_schema/schemas/user_group_schema.py +++ b/rhodecode/model/validation_schema/schemas/user_group_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/schemas/user_schema.py b/rhodecode/model/validation_schema/schemas/user_schema.py index 0431e414..5dee0227 100644 --- a/rhodecode/model/validation_schema/schemas/user_schema.py +++ b/rhodecode/model/validation_schema/schemas/user_schema.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/types.py b/rhodecode/model/validation_schema/types.py index 94fc9fd7..9b88ef7c 100644 --- a/rhodecode/model/validation_schema/types.py +++ b/rhodecode/model/validation_schema/types.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/utils.py b/rhodecode/model/validation_schema/utils.py index e4465530..bccb5a31 100644 --- a/rhodecode/model/validation_schema/utils.py +++ b/rhodecode/model/validation_schema/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/validators.py b/rhodecode/model/validation_schema/validators.py index 0f30f08b..d91ca7c9 100644 --- a/rhodecode/model/validation_schema/validators.py +++ b/rhodecode/model/validation_schema/validators.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validation_schema/widgets.py b/rhodecode/model/validation_schema/widgets.py index 119f8131..0814ff80 100644 --- a/rhodecode/model/validation_schema/widgets.py +++ b/rhodecode/model/validation_schema/widgets.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011-2023 RhodeCode GmbH +# Copyright (C) 2011-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/model/validators.py b/rhodecode/model/validators.py index 5236f82b..0d862b1f 100644 --- a/rhodecode/model/validators.py +++ b/rhodecode/model/validators.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py index 4bf7f2b8..9489ab70 100644 --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/__init__.py b/rhodecode/tests/config/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/config/__init__.py +++ b/rhodecode/tests/config/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_environment.py b/rhodecode/tests/config/test_environment.py index 1400d56c..22c38cf8 100644 --- a/rhodecode/tests/config/test_environment.py +++ b/rhodecode/tests/config/test_environment.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_routing_links.py b/rhodecode/tests/config/test_routing_links.py index bb9ada63..aec9964b 100644 --- a/rhodecode/tests/config/test_routing_links.py +++ b/rhodecode/tests/config/test_routing_links.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_sanitize_settings.py b/rhodecode/tests/config/test_sanitize_settings.py index 4161904d..92206509 100644 --- a/rhodecode/tests/config/test_sanitize_settings.py +++ b/rhodecode/tests/config/test_sanitize_settings.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/config/test_utils.py b/rhodecode/tests/config/test_utils.py index e9a18b86..285567d8 100644 --- a/rhodecode/tests/config/test_utils.py +++ b/rhodecode/tests/config/test_utils.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2012-2023 RhodeCode GmbH +# Copyright (C) 2012-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/database/__init__.py b/rhodecode/tests/database/__init__.py index c4d86aeb..ae06165a 100644 --- a/rhodecode/tests/database/__init__.py +++ b/rhodecode/tests/database/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/database/conftest.py b/rhodecode/tests/database/conftest.py index 528b1dbb..17e1943a 100644 --- a/rhodecode/tests/database/conftest.py +++ b/rhodecode/tests/database/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/database/test_creation.py b/rhodecode/tests/database/test_creation.py index 25ef7a98..a3c7262c 100755 --- a/rhodecode/tests/database/test_creation.py +++ b/rhodecode/tests/database/test_creation.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/database/test_migration.py b/rhodecode/tests/database/test_migration.py index 9a199bdf..63712a0e 100644 --- a/rhodecode/tests/database/test_migration.py +++ b/rhodecode/tests/database/test_migration.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/events/__init__.py b/rhodecode/tests/events/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/events/__init__.py +++ b/rhodecode/tests/events/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/events/conftest.py b/rhodecode/tests/events/conftest.py index 78aa002c..60d0a60f 100644 --- a/rhodecode/tests/events/conftest.py +++ b/rhodecode/tests/events/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/events/test_pullrequest.py b/rhodecode/tests/events/test_pullrequest.py index 9dd78da7..4a021d51 100644 --- a/rhodecode/tests/events/test_pullrequest.py +++ b/rhodecode/tests/events/test_pullrequest.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/events/test_repo.py b/rhodecode/tests/events/test_repo.py index 84b5eb7d..3b62545d 100644 --- a/rhodecode/tests/events/test_repo.py +++ b/rhodecode/tests/events/test_repo.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/fixtures/rc_fixture.py b/rhodecode/tests/fixtures/rc_fixture.py index 2fc5692a..a7838865 100644 --- a/rhodecode/tests/fixtures/rc_fixture.py +++ b/rhodecode/tests/fixtures/rc_fixture.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/functional/__init__.py b/rhodecode/tests/functional/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/functional/__init__.py +++ b/rhodecode/tests/functional/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/functional/test_bad_request_data.py b/rhodecode/tests/functional/test_bad_request_data.py index 43ebcfc3..bb459211 100644 --- a/rhodecode/tests/functional/test_bad_request_data.py +++ b/rhodecode/tests/functional/test_bad_request_data.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/functional/test_delegated_admin.py b/rhodecode/tests/functional/test_delegated_admin.py index e48cf835..12d79121 100644 --- a/rhodecode/tests/functional/test_delegated_admin.py +++ b/rhodecode/tests/functional/test_delegated_admin.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/functional/test_sessions.py b/rhodecode/tests/functional/test_sessions.py index 9270f1d8..5d0bda51 100644 --- a/rhodecode/tests/functional/test_sessions.py +++ b/rhodecode/tests/functional/test_sessions.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/integrations/conftest.py b/rhodecode/tests/integrations/conftest.py index fc880bfd..34ccf06d 100644 --- a/rhodecode/tests/integrations/conftest.py +++ b/rhodecode/tests/integrations/conftest.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/integrations/test_integration.py b/rhodecode/tests/integrations/test_integration.py index 8c76dbc5..70dc62e3 100644 --- a/rhodecode/tests/integrations/test_integration.py +++ b/rhodecode/tests/integrations/test_integration.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/integrations/test_slack.py b/rhodecode/tests/integrations/test_slack.py index 1b207426..98129b62 100644 --- a/rhodecode/tests/integrations/test_slack.py +++ b/rhodecode/tests/integrations/test_slack.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/integrations/test_webhook.py b/rhodecode/tests/integrations/test_webhook.py index 8779a809..fcc2e003 100644 --- a/rhodecode/tests/integrations/test_webhook.py +++ b/rhodecode/tests/integrations/test_webhook.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/__init__.py b/rhodecode/tests/lib/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/lib/__init__.py +++ b/rhodecode/tests/lib/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/auth_modules/test_auth_modules.py b/rhodecode/tests/lib/auth_modules/test_auth_modules.py index b213c40a..cd485f56 100644 --- a/rhodecode/tests/lib/auth_modules/test_auth_modules.py +++ b/rhodecode/tests/lib/auth_modules/test_auth_modules.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/__init__.py b/rhodecode/tests/lib/middleware/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/lib/middleware/__init__.py +++ b/rhodecode/tests/lib/middleware/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/mock_scm_app.py b/rhodecode/tests/lib/middleware/mock_scm_app.py index 8e4a1121..b6ec21c0 100644 --- a/rhodecode/tests/lib/middleware/mock_scm_app.py +++ b/rhodecode/tests/lib/middleware/mock_scm_app.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_appenlight.py b/rhodecode/tests/lib/middleware/test_appenlight.py index b284c7d3..c08d5405 100644 --- a/rhodecode/tests/lib/middleware/test_appenlight.py +++ b/rhodecode/tests/lib/middleware/test_appenlight.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplegit.py b/rhodecode/tests/lib/middleware/test_simplegit.py index c67de867..7c93ee91 100644 --- a/rhodecode/tests/lib/middleware/test_simplegit.py +++ b/rhodecode/tests/lib/middleware/test_simplegit.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplehg.py b/rhodecode/tests/lib/middleware/test_simplehg.py index 608a245d..3c8ef08f 100644 --- a/rhodecode/tests/lib/middleware/test_simplehg.py +++ b/rhodecode/tests/lib/middleware/test_simplehg.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplesvn.py b/rhodecode/tests/lib/middleware/test_simplesvn.py index 5e73634e..8ffe266c 100644 --- a/rhodecode/tests/lib/middleware/test_simplesvn.py +++ b/rhodecode/tests/lib/middleware/test_simplesvn.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_simplevcs.py b/rhodecode/tests/lib/middleware/test_simplevcs.py index 2ed7cfde..dde71564 100644 --- a/rhodecode/tests/lib/middleware/test_simplevcs.py +++ b/rhodecode/tests/lib/middleware/test_simplevcs.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_vcs.py b/rhodecode/tests/lib/middleware/test_vcs.py index e730356b..ee6e3cc2 100644 --- a/rhodecode/tests/lib/middleware/test_vcs.py +++ b/rhodecode/tests/lib/middleware/test_vcs.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/test_vcs_unavailable.py b/rhodecode/tests/lib/middleware/test_vcs_unavailable.py index f8d10e1a..a1e72d95 100644 --- a/rhodecode/tests/lib/middleware/test_vcs_unavailable.py +++ b/rhodecode/tests/lib/middleware/test_vcs_unavailable.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/__init__.py b/rhodecode/tests/lib/middleware/utils/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/lib/middleware/utils/__init__.py +++ b/rhodecode/tests/lib/middleware/utils/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py b/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py index 6530618c..eb8e8a74 100644 --- a/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py +++ b/rhodecode/tests/lib/middleware/utils/test_scm_app_http.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py b/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py index 9c08e0c7..71008eb5 100644 --- a/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py +++ b/rhodecode/tests/lib/middleware/utils/test_scm_app_http_chunking.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py b/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py index 1eb89e7f..4d244bb6 100644 --- a/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py +++ b/rhodecode/tests/lib/middleware/utils/test_wsgi_app_caller_client.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_action_parser.py b/rhodecode/tests/lib/test_action_parser.py index 54c9b63b..bb567bae 100644 --- a/rhodecode/tests/lib/test_action_parser.py +++ b/rhodecode/tests/lib/test_action_parser.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_archive_caches.py b/rhodecode/tests/lib/test_archive_caches.py index 9459cabb..262f5624 100644 --- a/rhodecode/tests/lib/test_archive_caches.py +++ b/rhodecode/tests/lib/test_archive_caches.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_audit_logger.py b/rhodecode/tests/lib/test_audit_logger.py index 2f507c3f..0790c678 100644 --- a/rhodecode/tests/lib/test_audit_logger.py +++ b/rhodecode/tests/lib/test_audit_logger.py @@ -1,6 +1,4 @@ -import collections - -# Copyright (C) 2010-2023 RhodeCode GmbH +import collections# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_auth_crypto_backend.py b/rhodecode/tests/lib/test_auth_crypto_backend.py index d4c578bf..f85f1506 100644 --- a/rhodecode/tests/lib/test_auth_crypto_backend.py +++ b/rhodecode/tests/lib/test_auth_crypto_backend.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_base.py b/rhodecode/tests/lib/test_base.py index 24023b2d..e5ccedbb 100644 --- a/rhodecode/tests/lib/test_base.py +++ b/rhodecode/tests/lib/test_base.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_caches.py b/rhodecode/tests/lib/test_caches.py index bda36879..018d4bf4 100644 --- a/rhodecode/tests/lib/test_caches.py +++ b/rhodecode/tests/lib/test_caches.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_codeblocks.py b/rhodecode/tests/lib/test_codeblocks.py index 9dfc61e8..b191b437 100644 --- a/rhodecode/tests/lib/test_codeblocks.py +++ b/rhodecode/tests/lib/test_codeblocks.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_colored_formatter.py b/rhodecode/tests/lib/test_colored_formatter.py index 49b5595d..c8040b27 100644 --- a/rhodecode/tests/lib/test_colored_formatter.py +++ b/rhodecode/tests/lib/test_colored_formatter.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_datelib.py b/rhodecode/tests/lib/test_datelib.py index aabab058..753dd09a 100644 --- a/rhodecode/tests/lib/test_datelib.py +++ b/rhodecode/tests/lib/test_datelib.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_db_manage.py b/rhodecode/tests/lib/test_db_manage.py index c586a7b8..e49ea99d 100644 --- a/rhodecode/tests/lib/test_db_manage.py +++ b/rhodecode/tests/lib/test_db_manage.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_dbmigrate.py b/rhodecode/tests/lib/test_dbmigrate.py index e0616400..9c9c5977 100644 --- a/rhodecode/tests/lib/test_dbmigrate.py +++ b/rhodecode/tests/lib/test_dbmigrate.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_diffs.py b/rhodecode/tests/lib/test_diffs.py index c38c7bb7..99587119 100644 --- a/rhodecode/tests/lib/test_diffs.py +++ b/rhodecode/tests/lib/test_diffs.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_diffs_context.py b/rhodecode/tests/lib/test_diffs_context.py index d385c982..48ef622a 100644 --- a/rhodecode/tests/lib/test_diffs_context.py +++ b/rhodecode/tests/lib/test_diffs_context.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_encrypt.py b/rhodecode/tests/lib/test_encrypt.py index a9e5d0dc..1d39d94d 100644 --- a/rhodecode/tests/lib/test_encrypt.py +++ b/rhodecode/tests/lib/test_encrypt.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_ext_json.py b/rhodecode/tests/lib/test_ext_json.py index c1b96a62..993d7756 100644 --- a/rhodecode/tests/lib/test_ext_json.py +++ b/rhodecode/tests/lib/test_ext_json.py @@ -1,5 +1,4 @@ -import dataclasses -# Copyright (C) 2010-2023 RhodeCode GmbH +import dataclasses# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_graphmod.py b/rhodecode/tests/lib/test_graphmod.py index 65641958..f8e81e27 100644 --- a/rhodecode/tests/lib/test_graphmod.py +++ b/rhodecode/tests/lib/test_graphmod.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_helpers.py b/rhodecode/tests/lib/test_helpers.py index 110af4a5..55a9f1a1 100644 --- a/rhodecode/tests/lib/test_helpers.py +++ b/rhodecode/tests/lib/test_helpers.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_hooks_base.py b/rhodecode/tests/lib/test_hooks_base.py index 5dd5645b..8f9bc781 100644 --- a/rhodecode/tests/lib/test_hooks_base.py +++ b/rhodecode/tests/lib/test_hooks_base.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_html_sanitizer.py b/rhodecode/tests/lib/test_html_sanitizer.py index b1b19222..5f7ea371 100644 --- a/rhodecode/tests/lib/test_html_sanitizer.py +++ b/rhodecode/tests/lib/test_html_sanitizer.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_jsonalchemy.py b/rhodecode/tests/lib/test_jsonalchemy.py index 53a0f3d7..21fa29ba 100644 --- a/rhodecode/tests/lib/test_jsonalchemy.py +++ b/rhodecode/tests/lib/test_jsonalchemy.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_mako_emails.py b/rhodecode/tests/lib/test_mako_emails.py index ba7d7de4..ead333c3 100644 --- a/rhodecode/tests/lib/test_mako_emails.py +++ b/rhodecode/tests/lib/test_mako_emails.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_markup_renderer.py b/rhodecode/tests/lib/test_markup_renderer.py index 7c8a0167..7230dadb 100644 --- a/rhodecode/tests/lib/test_markup_renderer.py +++ b/rhodecode/tests/lib/test_markup_renderer.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_search.py b/rhodecode/tests/lib/test_search.py index 7bfea9b3..5883a245 100644 --- a/rhodecode/tests/lib/test_search.py +++ b/rhodecode/tests/lib/test_search.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_search_utils.py b/rhodecode/tests/lib/test_search_utils.py index 5e053c3b..898921c2 100644 --- a/rhodecode/tests/lib/test_search_utils.py +++ b/rhodecode/tests/lib/test_search_utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/lib/test_utils.py b/rhodecode/tests/lib/test_utils.py index 6f68b38e..e46e38a2 100644 --- a/rhodecode/tests/lib/test_utils.py +++ b/rhodecode/tests/lib/test_utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/api.py b/rhodecode/tests/load/api.py index e639865d..57ecdba3 100644 --- a/rhodecode/tests/load/api.py +++ b/rhodecode/tests/load/api.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/http_performance.py b/rhodecode/tests/load/http_performance.py index ce64121b..3c34700d 100644 --- a/rhodecode/tests/load/http_performance.py +++ b/rhodecode/tests/load/http_performance.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/profile-mem.py b/rhodecode/tests/load/profile-mem.py index 96778bd1..a4e12673 100644 --- a/rhodecode/tests/load/profile-mem.py +++ b/rhodecode/tests/load/profile-mem.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/profile.py b/rhodecode/tests/load/profile.py index 5a6a1f5f..a534be2f 100644 --- a/rhodecode/tests/load/profile.py +++ b/rhodecode/tests/load/profile.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/time_urls.py b/rhodecode/tests/load/time_urls.py index e27bba72..771c2104 100644 --- a/rhodecode/tests/load/time_urls.py +++ b/rhodecode/tests/load/time_urls.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/load/vcs_performance.py b/rhodecode/tests/load/vcs_performance.py index f4ca7294..975bb074 100644 --- a/rhodecode/tests/load/vcs_performance.py +++ b/rhodecode/tests/load/vcs_performance.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/__init__.py b/rhodecode/tests/models/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/models/__init__.py +++ b/rhodecode/tests/models/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_gist_schema.py b/rhodecode/tests/models/schemas/test_gist_schema.py index ea49d080..fef43fa7 100644 --- a/rhodecode/tests/models/schemas/test_gist_schema.py +++ b/rhodecode/tests/models/schemas/test_gist_schema.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_integration_schema.py b/rhodecode/tests/models/schemas/test_integration_schema.py index e56e7b45..c4a7adc1 100644 --- a/rhodecode/tests/models/schemas/test_integration_schema.py +++ b/rhodecode/tests/models/schemas/test_integration_schema.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_repo_group_schema.py b/rhodecode/tests/models/schemas/test_repo_group_schema.py index 462db6c3..8b5ee448 100644 --- a/rhodecode/tests/models/schemas/test_repo_group_schema.py +++ b/rhodecode/tests/models/schemas/test_repo_group_schema.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_repo_schema.py b/rhodecode/tests/models/schemas/test_repo_schema.py index 09041f04..60c6230d 100644 --- a/rhodecode/tests/models/schemas/test_repo_schema.py +++ b/rhodecode/tests/models/schemas/test_repo_schema.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_schema_types.py b/rhodecode/tests/models/schemas/test_schema_types.py index a0456678..9592f53a 100644 --- a/rhodecode/tests/models/schemas/test_schema_types.py +++ b/rhodecode/tests/models/schemas/test_schema_types.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_user_schema.py b/rhodecode/tests/models/schemas/test_user_schema.py index 9d3c9d30..16fbe5f8 100644 --- a/rhodecode/tests/models/schemas/test_user_schema.py +++ b/rhodecode/tests/models/schemas/test_user_schema.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/schemas/test_user_usergroup_types.py b/rhodecode/tests/models/schemas/test_user_usergroup_types.py index de38368d..4a8f49ea 100644 --- a/rhodecode/tests/models/schemas/test_user_usergroup_types.py +++ b/rhodecode/tests/models/schemas/test_user_usergroup_types.py @@ -1,6 +1,4 @@ - - -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/__init__.py b/rhodecode/tests/models/settings/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/models/settings/__init__.py +++ b/rhodecode/tests/models/settings/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/test_issue_tracker.py b/rhodecode/tests/models/settings/test_issue_tracker.py index 5d2d826a..bec19f9f 100644 --- a/rhodecode/tests/models/settings/test_issue_tracker.py +++ b/rhodecode/tests/models/settings/test_issue_tracker.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/test_settings.py b/rhodecode/tests/models/settings/test_settings.py index 4f4eada8..5e3cb959 100644 --- a/rhodecode/tests/models/settings/test_settings.py +++ b/rhodecode/tests/models/settings/test_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/settings/test_vcs_settings.py b/rhodecode/tests/models/settings/test_vcs_settings.py index 9ca4653a..7ee6daad 100644 --- a/rhodecode/tests/models/settings/test_vcs_settings.py +++ b/rhodecode/tests/models/settings/test_vcs_settings.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_changeset_status.py b/rhodecode/tests/models/test_changeset_status.py index 9468259e..dd12add0 100644 --- a/rhodecode/tests/models/test_changeset_status.py +++ b/rhodecode/tests/models/test_changeset_status.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_changeset_status_group_voting.py b/rhodecode/tests/models/test_changeset_status_group_voting.py index fd841812..804443a5 100644 --- a/rhodecode/tests/models/test_changeset_status_group_voting.py +++ b/rhodecode/tests/models/test_changeset_status_group_voting.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_comment.py b/rhodecode/tests/models/test_comment.py index b0b9b28a..96682501 100644 --- a/rhodecode/tests/models/test_comment.py +++ b/rhodecode/tests/models/test_comment.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_db.py b/rhodecode/tests/models/test_db.py index 65df9e5e..7a077e00 100644 --- a/rhodecode/tests/models/test_db.py +++ b/rhodecode/tests/models/test_db.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_gist.py b/rhodecode/tests/models/test_gist.py index 26f5cf51..14603afd 100644 --- a/rhodecode/tests/models/test_gist.py +++ b/rhodecode/tests/models/test_gist.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_notifications.py b/rhodecode/tests/models/test_notifications.py index fee49a4f..7999f0a9 100644 --- a/rhodecode/tests/models/test_notifications.py +++ b/rhodecode/tests/models/test_notifications.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_permissions.py b/rhodecode/tests/models/test_permissions.py index 8722a774..10fe1d17 100644 --- a/rhodecode/tests/models/test_permissions.py +++ b/rhodecode/tests/models/test_permissions.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_pullrequest.py b/rhodecode/tests/models/test_pullrequest.py index 9a09c098..d32813c6 100644 --- a/rhodecode/tests/models/test_pullrequest.py +++ b/rhodecode/tests/models/test_pullrequest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_pullrequest_git.py b/rhodecode/tests/models/test_pullrequest_git.py index 4303c970..b0e570bb 100644 --- a/rhodecode/tests/models/test_pullrequest_git.py +++ b/rhodecode/tests/models/test_pullrequest_git.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_repo_groups.py b/rhodecode/tests/models/test_repo_groups.py index dee2a806..67cfc75e 100644 --- a/rhodecode/tests/models/test_repo_groups.py +++ b/rhodecode/tests/models/test_repo_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_repo_readme.py b/rhodecode/tests/models/test_repo_readme.py index 219dc97b..703dd14d 100644 --- a/rhodecode/tests/models/test_repo_readme.py +++ b/rhodecode/tests/models/test_repo_readme.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_repos.py b/rhodecode/tests/models/test_repos.py index 0ba88a4a..929ac100 100644 --- a/rhodecode/tests/models/test_repos.py +++ b/rhodecode/tests/models/test_repos.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_scm.py b/rhodecode/tests/models/test_scm.py index 3ea8fd37..134cfa2e 100644 --- a/rhodecode/tests/models/test_scm.py +++ b/rhodecode/tests/models/test_scm.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py b/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py index 83279902..67a89ee0 100644 --- a/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py +++ b/rhodecode/tests/models/test_user_group_permissions_on_repo_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_user_groups.py b/rhodecode/tests/models/test_user_groups.py index c21b6a31..5cdec301 100644 --- a/rhodecode/tests/models/test_user_groups.py +++ b/rhodecode/tests/models/test_user_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_user_permissions_on_repo_groups.py b/rhodecode/tests/models/test_user_permissions_on_repo_groups.py index 4cc81319..6bf022e4 100644 --- a/rhodecode/tests/models/test_user_permissions_on_repo_groups.py +++ b/rhodecode/tests/models/test_user_permissions_on_repo_groups.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/models/test_users.py b/rhodecode/tests/models/test_users.py index c4851c75..97eb2b29 100644 --- a/rhodecode/tests/models/test_users.py +++ b/rhodecode/tests/models/test_users.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/__init__.py b/rhodecode/tests/other/__init__.py index bdc53285..ae06165a 100644 --- a/rhodecode/tests/other/__init__.py +++ b/rhodecode/tests/other/__init__.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/test_base_controller.py b/rhodecode/tests/other/test_base_controller.py index bf689d56..7270adb6 100644 --- a/rhodecode/tests/other/test_base_controller.py +++ b/rhodecode/tests/other/test_base_controller.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/test_validators.py b/rhodecode/tests/other/test_validators.py index d4fd4842..ecb13d1f 100644 --- a/rhodecode/tests/other/test_validators.py +++ b/rhodecode/tests/other/test_validators.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/other/test_views_utils.py b/rhodecode/tests/other/test_views_utils.py index d8179d05..00834f94 100644 --- a/rhodecode/tests/other/test_views_utils.py +++ b/rhodecode/tests/other/test_views_utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/routes.py b/rhodecode/tests/routes.py index f0176744..fb6c30b7 100644 --- a/rhodecode/tests/routes.py +++ b/rhodecode/tests/routes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/scripts/test_concurency.py b/rhodecode/tests/scripts/test_concurency.py index cefaf893..45cb6072 100644 --- a/rhodecode/tests/scripts/test_concurency.py +++ b/rhodecode/tests/scripts/test_concurency.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/scripts/test_crawler.py b/rhodecode/tests/scripts/test_crawler.py index cd1d0725..4960f4d3 100644 --- a/rhodecode/tests/scripts/test_crawler.py +++ b/rhodecode/tests/scripts/test_crawler.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/server_utils.py b/rhodecode/tests/server_utils.py index d5aadc4b..14dbc798 100644 --- a/rhodecode/tests/server_utils.py +++ b/rhodecode/tests/server_utils.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/utils.py b/rhodecode/tests/utils.py index 64a5d8f4..ee280523 100644 --- a/rhodecode/tests/utils.py +++ b/rhodecode/tests/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/__init__.py b/rhodecode/tests/vcs/__init__.py index c4d86aeb..ae06165a 100644 --- a/rhodecode/tests/vcs/__init__.py +++ b/rhodecode/tests/vcs/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/conftest.py b/rhodecode/tests/vcs/conftest.py index 7e0313f8..5121ecdc 100644 --- a/rhodecode/tests/vcs/conftest.py +++ b/rhodecode/tests/vcs/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_archives.py b/rhodecode/tests/vcs/test_archives.py index a22ff43d..de0625b3 100644 --- a/rhodecode/tests/vcs/test_archives.py +++ b/rhodecode/tests/vcs/test_archives.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_branches.py b/rhodecode/tests/vcs/test_branches.py index 2cea7691..bdfa7e7a 100644 --- a/rhodecode/tests/vcs/test_branches.py +++ b/rhodecode/tests/vcs/test_branches.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_client_http.py b/rhodecode/tests/vcs/test_client_http.py index 2618dd24..27178b21 100644 --- a/rhodecode/tests/vcs/test_client_http.py +++ b/rhodecode/tests/vcs/test_client_http.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_commits.py b/rhodecode/tests/vcs/test_commits.py index df57ca1f..2be37a6a 100644 --- a/rhodecode/tests/vcs/test_commits.py +++ b/rhodecode/tests/vcs/test_commits.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_config.py b/rhodecode/tests/vcs/test_config.py index 4ecea4a2..5daf8961 100644 --- a/rhodecode/tests/vcs/test_config.py +++ b/rhodecode/tests/vcs/test_config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_diff.py b/rhodecode/tests/vcs/test_diff.py index 38b4a3f4..990a08eb 100644 --- a/rhodecode/tests/vcs/test_diff.py +++ b/rhodecode/tests/vcs/test_diff.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_exceptions.py b/rhodecode/tests/vcs/test_exceptions.py index 54704ccc..177e8dbc 100644 --- a/rhodecode/tests/vcs/test_exceptions.py +++ b/rhodecode/tests/vcs/test_exceptions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_filenodes_unicode_path.py b/rhodecode/tests/vcs/test_filenodes_unicode_path.py index 26a85850..807b74a6 100644 --- a/rhodecode/tests/vcs/test_filenodes_unicode_path.py +++ b/rhodecode/tests/vcs/test_filenodes_unicode_path.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_getitem.py b/rhodecode/tests/vcs/test_getitem.py index d95c7bd8..b0e3cd68 100644 --- a/rhodecode/tests/vcs/test_getitem.py +++ b/rhodecode/tests/vcs/test_getitem.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_getslice.py b/rhodecode/tests/vcs/test_getslice.py index 225aaf83..e82e3952 100644 --- a/rhodecode/tests/vcs/test_getslice.py +++ b/rhodecode/tests/vcs/test_getslice.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_git.py b/rhodecode/tests/vcs/test_git.py index 48e3c7fc..adfd5026 100644 --- a/rhodecode/tests/vcs/test_git.py +++ b/rhodecode/tests/vcs/test_git.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_hg.py b/rhodecode/tests/vcs/test_hg.py index cf7ce267..49f90868 100644 --- a/rhodecode/tests/vcs/test_hg.py +++ b/rhodecode/tests/vcs/test_hg.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py b/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py index c6b5f522..04cef689 100644 --- a/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py +++ b/rhodecode/tests/vcs/test_hg_vcsserver_cache_invalidation.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_inmemory.py b/rhodecode/tests/vcs/test_inmemory.py index e28bed33..2d7d49f1 100644 --- a/rhodecode/tests/vcs/test_inmemory.py +++ b/rhodecode/tests/vcs/test_inmemory.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_nodes.py b/rhodecode/tests/vcs/test_nodes.py index e745816e..77e23da5 100644 --- a/rhodecode/tests/vcs/test_nodes.py +++ b/rhodecode/tests/vcs/test_nodes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_repository.py b/rhodecode/tests/vcs/test_repository.py index 2ba82e49..beffb370 100644 --- a/rhodecode/tests/vcs/test_repository.py +++ b/rhodecode/tests/vcs/test_repository.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_svn.py b/rhodecode/tests/vcs/test_svn.py index 620c244f..9eb64898 100644 --- a/rhodecode/tests/vcs/test_svn.py +++ b/rhodecode/tests/vcs/test_svn.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_tags.py b/rhodecode/tests/vcs/test_tags.py index b5774ccb..306f0aca 100644 --- a/rhodecode/tests/vcs/test_tags.py +++ b/rhodecode/tests/vcs/test_tags.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_utils.py b/rhodecode/tests/vcs/test_utils.py index 0b79766b..c8aafed2 100644 --- a/rhodecode/tests/vcs/test_utils.py +++ b/rhodecode/tests/vcs/test_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/test_vcs.py b/rhodecode/tests/vcs/test_vcs.py index 3f9a89f8..6c0a5dfc 100644 --- a/rhodecode/tests/vcs/test_vcs.py +++ b/rhodecode/tests/vcs/test_vcs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs/utils.py b/rhodecode/tests/vcs/utils.py index f3565848..6fbd7776 100644 --- a/rhodecode/tests/vcs/utils.py +++ b/rhodecode/tests/vcs/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py index 76fc59ca..cef5230a 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_403.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py index 3271cb7f..4a6ccf2a 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_404.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py index 18610a4e..eea6187c 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_custom_auth_code_bad_code.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py b/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py index 1b0b8aeb..3c9b2b94 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py +++ b/rhodecode/tests/vcs_operations/test_vcs_calls_small_post_buffer.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py index 514e3f19..6e5159fb 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py b/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py index 400fac8d..bed5f3c5 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_branch_protection.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_clone.py b/rhodecode/tests/vcs_operations/test_vcs_operations_clone.py index 04d6d6ba..1c18bb22 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_clone.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_clone.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py index 53469d95..98908059 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_force_push.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_git.py b/rhodecode/tests/vcs_operations/test_vcs_operations_git.py index 561da0e1..141c871a 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_git.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_git.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py b/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py index c316fd2f..bde0a852 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_integrations_trigger.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py b/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py index 63a90a90..3aaa85a4 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_locking.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py b/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py index 9bddd51f..13606514 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_locking_custom_code.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py index fa469b0a..41748357 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_new_branch_push.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py index 899261b8..8e6ed6e4 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_rcextensions_push.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py index d5bff9a4..2b9b484c 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py @@ -1,5 +1,4 @@ - -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py b/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py index 4dc7a98a..6f4b8a9b 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py b/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py index ecee4f9c..fe2ed1ea 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_tag_push.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/translation.py b/rhodecode/translation.py index 782cf4ef..142c2e2c 100644 --- a/rhodecode/translation.py +++ b/rhodecode/translation.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2023 RhodeCode GmbH +# Copyright (C) 2016-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/tweens.py b/rhodecode/tweens.py index 10953e12..c1ab2f1d 100644 --- a/rhodecode/tweens.py +++ b/rhodecode/tweens.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 diff --git a/rhodecode/typing/__init__.py b/rhodecode/typing/__init__.py index c8080fa6..f61ec923 100644 --- a/rhodecode/typing/__init__.py +++ b/rhodecode/typing/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010-2023 RhodeCode GmbH +# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 From 8669543f89c800b52f0e5eea6a28e106c215cd61 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 16:40:10 +0100 Subject: [PATCH 47/99] fix(tests): on jenkins ensure re-creation of the path --- rhodecode/tests/vcs/test_git.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rhodecode/tests/vcs/test_git.py b/rhodecode/tests/vcs/test_git.py index adfd5026..684f7e8b 100644 --- a/rhodecode/tests/vcs/test_git.py +++ b/rhodecode/tests/vcs/test_git.py @@ -994,6 +994,8 @@ class TestLargeFileRepo(object): # Note: oid path depends on LFSOidStore.store_suffix. Once it will be changed update below line accordingly oid_destination = os.path.join(git_largefiles_store, f"objects/{oid[:2]}/{oid[2:4]}/{oid}") + spec_path = os.path.dirname(oid_destination) + os.makedirs(spec_path, exist_ok=True) shutil.copy(oid_path, oid_destination) lf_node = node.get_largefile_node() From 6741ba448646e67d5795897a58d8383748924fe0 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:03:51 +0100 Subject: [PATCH 48/99] deps: bumped test requirements --- requirements_test.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index f615a94a..7a111054 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -44,11 +44,11 @@ pytest-timeout==2.3.1 packaging==24.1 pluggy==1.5.0 -webtest==3.0.0 +webtest==3.0.1 beautifulsoup4==4.12.3 soupsieve==2.5 - waitress==3.0.0 - webob==1.8.8 + waitress==3.0.1 + webob==1.8.9 # RhodeCode test-data rc_testdata @ https://code.rhodecode.com/upstream/rc-testdata-dist/raw/77378e9097f700b4c1b9391b56199fe63566b5c9/rc_testdata-0.11.0.tar.gz#egg=rc_testdata From 087c283588a644ebf2babb7567cdaeb7c2577f0d Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:04:19 +0100 Subject: [PATCH 49/99] deps: bumped gevent==24.11.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c96364ea..a11fba55 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,7 +30,7 @@ celery==5.4.0 tzdata==2024.2 vine==5.1.0 channelstream==0.7.1 - gevent==24.10.3 + gevent==24.11.1 greenlet==3.1.1 zope.event==5.0.0 zope.interface==7.1.1 From da74f320fd7902095f3491af4491f4f93304a642 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:04:57 +0100 Subject: [PATCH 50/99] fold gevent --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a11fba55..a7a53257 100644 --- a/requirements.txt +++ b/requirements.txt @@ -90,7 +90,7 @@ formencode==2.1.0 fsspec==2024.9.0 gunicorn==23.0.0 packaging==24.1 -gevent==24.10.3 +gevent==24.11.1 greenlet==3.1.1 zope.event==5.0.0 zope.interface==7.1.1 From 836370ec9e1d501fc0a4da1d7ff8764dd6c4f435 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:05:16 +0100 Subject: [PATCH 51/99] deps: bumped webob==1.8.9 --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index a7a53257..4998e46c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,7 +44,7 @@ channelstream==0.7.1 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.8 + webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 pyramid-jinja2==2.10 @@ -59,7 +59,7 @@ channelstream==0.7.1 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.8 + webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 zope.deprecation==5.0.0 @@ -199,7 +199,7 @@ pyramid-mailer==0.15.1 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.8 + webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 repoze.sendmail==4.4.1 @@ -219,7 +219,7 @@ pyramid-mako==1.1.0 plaster==1.1.2 translationstring==1.4 venusian==3.0.0 - webob==1.8.8 + webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 python-ldap==3.4.3 From c003ef46c4a78b7c2d60799930897120ce682fac Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:06:20 +0100 Subject: [PATCH 52/99] deps: bumped redis==5.2.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4998e46c..9a8342e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -235,7 +235,7 @@ python3-saml==1.16.0 xmlsec==1.3.14 lxml==5.3.0 pyyaml==6.0.1 -redis==5.1.0 +redis==5.2.0 async-timeout==4.0.3 regex==2022.10.31 routes==2.5.1 From 9794c4c64fe02fe5f48d4e61da306acc4f466205 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:06:53 +0100 Subject: [PATCH 53/99] deps: bumped s3fs==2024.10.0 and fsspec==2024.10.0 --- requirements.txt | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9a8342e8..cfa4be93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -87,7 +87,7 @@ dogpile.cache==1.3.3 pbr==5.11.1 formencode==2.1.0 six==1.16.0 -fsspec==2024.9.0 +fsspec==2024.10.0 gunicorn==23.0.0 packaging==24.1 gevent==24.11.1 @@ -241,34 +241,38 @@ regex==2022.10.31 routes==2.5.1 repoze.lru==0.7 six==1.16.0 -s3fs==2024.9.0 - aiobotocore==2.13.0 - aiohttp==3.9.5 +s3fs==2024.10.0 + aiobotocore==2.15.2 + aiohttp==3.10.10 + aiohappyeyeballs==2.4.3 aiosignal==1.3.1 - frozenlist==1.4.1 + frozenlist==1.5.0 attrs==22.2.0 - frozenlist==1.4.1 - multidict==6.0.5 - yarl==1.9.4 + frozenlist==1.5.0 + multidict==6.1.0 + yarl==1.17.1 idna==3.4 - multidict==6.0.5 - aioitertools==0.11.0 - botocore==1.34.106 + multidict==6.1.0 + propcache==0.2.0 + aioitertools==0.12.0 + botocore==1.35.36 jmespath==1.0.1 python-dateutil==2.9.0.post0 six==1.16.0 urllib3==1.26.14 wrapt==1.16.0 - aiohttp==3.9.5 + aiohttp==3.10.10 + aiohappyeyeballs==2.4.3 aiosignal==1.3.1 - frozenlist==1.4.1 + frozenlist==1.5.0 attrs==22.2.0 - frozenlist==1.4.1 - multidict==6.0.5 - yarl==1.9.4 + frozenlist==1.5.0 + multidict==6.1.0 + yarl==1.17.1 idna==3.4 - multidict==6.0.5 - fsspec==2024.9.0 + multidict==6.1.0 + propcache==0.2.0 + fsspec==2024.10.0 simplejson==3.19.2 sshpubkeys==3.3.1 cryptography==43.0.3 From 7f5670ba72b72e79f720e419ddb61040b50364da Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:07:41 +0100 Subject: [PATCH 54/99] deps: bumped waitress==3.0.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cfa4be93..be708723 100644 --- a/requirements.txt +++ b/requirements.txt @@ -290,7 +290,7 @@ tzlocal==4.3 tempita==0.5.2 unidecode==1.3.6 urlobject==2.4.3 -waitress==3.0.0 +waitress==3.0.1 webhelpers2==2.1 markupsafe==3.0.2 six==1.16.0 From b6b0974b8c54bbaefae17aaec7c2c5abbf6b2200 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 12 Nov 2024 21:07:53 +0100 Subject: [PATCH 55/99] deps: bumped unidecode==1.3.8 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index be708723..798a47d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -288,7 +288,7 @@ tzlocal==4.3 pytz-deprecation-shim==0.1.0.post0 tzdata==2024.2 tempita==0.5.2 -unidecode==1.3.6 +unidecode==1.3.8 urlobject==2.4.3 waitress==3.0.1 webhelpers2==2.1 From 2b623ea7f2510b437a44c48055900a4bed4798db Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 13 Nov 2024 09:49:39 +0100 Subject: [PATCH 56/99] core: avoid using rhodecode.test packages inside main packages as tests are removed during build which can cause some problems in some edge case calls --- rhodecode/api/tests/conftest.py | 2 +- rhodecode/api/views/server_api.py | 56 +++++++++++++++++++++++++++++++ rhodecode/bootstrap.py | 41 ++++++++++++++++++++++ rhodecode/lib/db_manage.py | 2 +- rhodecode/tests/__init__.py | 43 ++++-------------------- 5 files changed, 106 insertions(+), 38 deletions(-) create mode 100644 rhodecode/bootstrap.py diff --git a/rhodecode/api/tests/conftest.py b/rhodecode/api/tests/conftest.py index 560afb10..dcd34619 100644 --- a/rhodecode/api/tests/conftest.py +++ b/rhodecode/api/tests/conftest.py @@ -21,12 +21,12 @@ import pytest from rhodecode.model.meta import Session from rhodecode.model.user import UserModel from rhodecode.model.auth_token import AuthTokenModel -from rhodecode.tests import TEST_USER_ADMIN_LOGIN @pytest.fixture(scope="class") def testuser_api(request, baseapp): cls = request.cls + from rhodecode.tests import TEST_USER_ADMIN_LOGIN # ADMIN USER cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN) diff --git a/rhodecode/api/views/server_api.py b/rhodecode/api/views/server_api.py index b09d1650..585668ac 100644 --- a/rhodecode/api/views/server_api.py +++ b/rhodecode/api/views/server_api.py @@ -213,6 +213,62 @@ def rescan_repos(request, apiuser, remove_obsolete=Optional(False)): 'Error occurred during rescan repositories action' ) +@jsonrpc_method() +def cleanup_repos(request, apiuser, remove_obsolete=Optional(False)): + """ + Triggers a rescan of the specified repositories. + + * If the ``remove_obsolete`` option is set, it also deletes repositories + that are found in the database but not on the file system, so called + "clean zombies". + + This command can only be run using an |authtoken| with admin rights to + the specified repository. + + This command takes the following options: + + :param apiuser: This is filled automatically from the |authtoken|. + :type apiuser: AuthUser + :param remove_obsolete: Deletes repositories from the database that + are not found on the filesystem. + :type remove_obsolete: Optional(``True`` | ``False``) + + Example output: + + .. code-block:: bash + + id : + result : { + 'added': [,...] + 'removed': [,...] + } + error : null + + Example error output: + + .. code-block:: bash + + id : + result : null + error : { + 'Error occurred during rescan repositories action' + } + + """ + if not has_superadmin_permission(apiuser): + raise JSONRPCForbidden() + + try: + rm_obsolete = Optional.extract(remove_obsolete) + added, removed = repo2db_mapper(ScmModel().repo_scan(), + remove_obsolete=rm_obsolete, force_hooks_rebuild=True) + return {'added': added, 'removed': removed} + except Exception: + log.exception('Failed to run repo rescann') + raise JSONRPCError( + 'Error occurred during rescan repositories action' + ) + @jsonrpc_method() def cleanup_sessions(request, apiuser, older_then=Optional(60)): diff --git a/rhodecode/bootstrap.py b/rhodecode/bootstrap.py new file mode 100644 index 00000000..c93742d9 --- /dev/null +++ b/rhodecode/bootstrap.py @@ -0,0 +1,41 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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/ + +# bootstrap data available for tests and setup clean install + +TEST_USER_ADMIN_LOGIN = 'test_admin' +TEST_USER_ADMIN_PASS = 'test12' +TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com' + +TEST_USER_REGULAR_LOGIN = 'test_regular' +TEST_USER_REGULAR_PASS = 'test12' +TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com' + +TEST_USER_REGULAR2_LOGIN = 'test_regular2' +TEST_USER_REGULAR2_PASS = 'test12' +TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com' + +HG_REPO = 'vcs_test_hg' +GIT_REPO = 'vcs_test_git' +SVN_REPO = 'vcs_test_svn' + +NEW_HG_REPO = 'vcs_test_hg_new' +NEW_GIT_REPO = 'vcs_test_git_new' + +HG_FORK = 'vcs_test_hg_fork' +GIT_FORK = 'vcs_test_git_fork' diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py index 1f0c6031..2b2a3caf 100644 --- a/rhodecode/lib/db_manage.py +++ b/rhodecode/lib/db_manage.py @@ -307,7 +307,7 @@ class DbManage(object): def create_test_admin_and_users(self): log.info('creating admin and regular test users') - from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \ + from rhodecode.bootstrap import TEST_USER_ADMIN_LOGIN, \ TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \ TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \ TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \ diff --git a/rhodecode/tests/__init__.py b/rhodecode/tests/__init__.py index f2f2a9a2..8188aee9 100644 --- a/rhodecode/tests/__init__.py +++ b/rhodecode/tests/__init__.py @@ -33,49 +33,20 @@ from rhodecode.lib import helpers as h from rhodecode.lib.helpers import flash from rhodecode.lib.str_utils import safe_str from rhodecode.lib.hash_utils import sha1_safe +from rhodecode.bootstrap import \ + TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \ + TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, TEST_USER_REGULAR_EMAIL, \ + TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL,\ + HG_REPO, GIT_REPO, SVN_REPO,\ + NEW_HG_REPO, NEW_GIT_REPO,\ + HG_FORK, GIT_FORK log = logging.getLogger(__name__) -__all__ = [ - 'get_new_dir', 'TestController', 'console_printer', - 'clear_cache_regions', - 'assert_session_flash', 'login_user', 'no_newline_id_generator', - 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'SVN_REPO', - 'NEW_HG_REPO', 'NEW_GIT_REPO', - 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS', - 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS', - 'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN', - 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', - 'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO', - 'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'SCM_TESTS', -] - # SOME GLOBALS FOR TESTS TEST_DIR = tempfile.gettempdir() -TEST_USER_ADMIN_LOGIN = 'test_admin' -TEST_USER_ADMIN_PASS = 'test12' -TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com' - -TEST_USER_REGULAR_LOGIN = 'test_regular' -TEST_USER_REGULAR_PASS = 'test12' -TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com' - -TEST_USER_REGULAR2_LOGIN = 'test_regular2' -TEST_USER_REGULAR2_PASS = 'test12' -TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com' - -HG_REPO = 'vcs_test_hg' -GIT_REPO = 'vcs_test_git' -SVN_REPO = 'vcs_test_svn' - -NEW_HG_REPO = 'vcs_test_hg_new' -NEW_GIT_REPO = 'vcs_test_git_new' - -HG_FORK = 'vcs_test_hg_fork' -GIT_FORK = 'vcs_test_git_fork' - ## VCS SCM_TESTS = ['hg', 'git'] uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple()))) From ef109d8f054e13c1eb6caa21e2d90f20b09d4a04 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 13 Nov 2024 13:39:21 +0100 Subject: [PATCH 57/99] feat(remap and rescan): added more relient remap and removal option, and also split the logic to either add or cleanup --- rhodecode/api/tests/test_cleanup_repos.py | 44 +++++ rhodecode/api/tests/test_rescan_repos.py | 10 +- rhodecode/api/views/server_api.py | 58 +++--- rhodecode/apps/admin/__init__.py | 17 +- .../apps/admin/tests/test_admin_repos.py | 8 +- rhodecode/apps/admin/views/settings.py | 35 +++- rhodecode/lib/utils.py | 171 +++++++++++------- rhodecode/lib/vcs/backends/__init__.py | 7 +- rhodecode/model/repo.py | 79 ++++---- rhodecode/model/repo_group.py | 13 +- rhodecode/subscribers.py | 2 +- .../admin/settings/settings_mapping.mako | 38 ++-- rhodecode/tests/fixtures/fixture_utils.py | 2 + .../tests/integrations/test_integration.py | 12 +- rhodecode/tests/lib/test_utils.py | 84 +++++++-- rhodecode/tests/routes.py | 3 +- 16 files changed, 375 insertions(+), 208 deletions(-) create mode 100644 rhodecode/api/tests/test_cleanup_repos.py diff --git a/rhodecode/api/tests/test_cleanup_repos.py b/rhodecode/api/tests/test_cleanup_repos.py new file mode 100644 index 00000000..8a2e29c7 --- /dev/null +++ b/rhodecode/api/tests/test_cleanup_repos.py @@ -0,0 +1,44 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 mock +import pytest + +from rhodecode.model.scm import ScmModel +from rhodecode.api.tests.utils import ( + build_data, api_call, assert_ok, assert_error, crash) + + +@pytest.mark.usefixtures("testuser_api", "app") +class TestCleanupRepos(object): + def test_api_cleanup_repos(self): + id_, params = build_data(self.apikey, 'cleanup_repos') + response = api_call(self.app, params) + + expected = {'removed': [], 'errors': []} + assert_ok(id_, expected, given=response.body) + + def test_api_cleanup_repos_error(self): + + id_, params = build_data(self.apikey, 'cleanup_repos', ) + + with mock.patch('rhodecode.lib.utils.repo2db_cleanup', side_effect=crash): + response = api_call(self.app, params) + + expected = 'Error occurred during repo storage cleanup action' + assert_error(id_, expected, given=response.body) diff --git a/rhodecode/api/tests/test_rescan_repos.py b/rhodecode/api/tests/test_rescan_repos.py index c18efb4b..36f2401f 100644 --- a/rhodecode/api/tests/test_rescan_repos.py +++ b/rhodecode/api/tests/test_rescan_repos.py @@ -19,7 +19,6 @@ import mock import pytest -from rhodecode.model.scm import ScmModel from rhodecode.api.tests.utils import ( build_data, api_call, assert_ok, assert_error, crash) @@ -30,13 +29,14 @@ class TestRescanRepos(object): id_, params = build_data(self.apikey, 'rescan_repos') response = api_call(self.app, params) - expected = {'added': [], 'removed': []} + expected = {'added': [], 'errors': []} assert_ok(id_, expected, given=response.body) - @mock.patch.object(ScmModel, 'repo_scan', crash) - def test_api_rescann_error(self): + def test_api_rescan_repos_error(self): id_, params = build_data(self.apikey, 'rescan_repos', ) - response = api_call(self.app, params) + + with mock.patch('rhodecode.lib.utils.repo2db_mapper', side_effect=crash): + response = api_call(self.app, params) expected = 'Error occurred during rescan repositories action' assert_error(id_, expected, given=response.body) diff --git a/rhodecode/api/views/server_api.py b/rhodecode/api/views/server_api.py index 585668ac..150e4660 100644 --- a/rhodecode/api/views/server_api.py +++ b/rhodecode/api/views/server_api.py @@ -18,14 +18,13 @@ import logging import itertools -import base64 from rhodecode.api import ( jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods) from rhodecode.api.utils import ( Optional, OAttr, has_superadmin_permission, get_user_or_error) -from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_repo_store_path +from rhodecode.lib.utils import get_rhodecode_repo_store_path from rhodecode.lib import system_info from rhodecode.lib import user_sessions from rhodecode.lib import exc_tracking @@ -33,9 +32,7 @@ from rhodecode.lib.ext_json import json from rhodecode.lib.utils2 import safe_int from rhodecode.model.db import UserIpMap from rhodecode.model.scm import ScmModel -from rhodecode.apps.file_store import utils as store_utils -from rhodecode.apps.file_store.exceptions import FileNotAllowedException, \ - FileOverSizeException + log = logging.getLogger(__name__) @@ -158,13 +155,10 @@ def get_ip(request, apiuser, userid=Optional(OAttr('apiuser'))): @jsonrpc_method() -def rescan_repos(request, apiuser, remove_obsolete=Optional(False)): +def rescan_repos(request, apiuser): """ Triggers a rescan of the specified repositories. - - * If the ``remove_obsolete`` option is set, it also deletes repositories - that are found in the database but not on the file system, so called - "clean zombies". + It returns list of added repositories, and errors during scan. This command can only be run using an |authtoken| with admin rights to the specified repository. @@ -173,9 +167,6 @@ def rescan_repos(request, apiuser, remove_obsolete=Optional(False)): :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser - :param remove_obsolete: Deletes repositories from the database that - are not found on the filesystem. - :type remove_obsolete: Optional(``True`` | ``False``) Example output: @@ -184,7 +175,7 @@ def rescan_repos(request, apiuser, remove_obsolete=Optional(False)): id : result : { 'added': [,...] - 'removed': [,...] + 'errors': [,...] } error : null @@ -199,28 +190,24 @@ def rescan_repos(request, apiuser, remove_obsolete=Optional(False)): } """ + from rhodecode.lib.utils import repo2db_mapper # re-import for testing patches + if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() try: - rm_obsolete = Optional.extract(remove_obsolete) - added, removed = repo2db_mapper(ScmModel().repo_scan(), - remove_obsolete=rm_obsolete, force_hooks_rebuild=True) - return {'added': added, 'removed': removed} + added, errors = repo2db_mapper(ScmModel().repo_scan(), force_hooks_rebuild=True) + return {'added': added, 'errors': errors} except Exception: - log.exception('Failed to run repo rescann') + log.exception('Failed to run repo rescan') raise JSONRPCError( 'Error occurred during rescan repositories action' ) @jsonrpc_method() -def cleanup_repos(request, apiuser, remove_obsolete=Optional(False)): +def cleanup_repos(request, apiuser): """ - Triggers a rescan of the specified repositories. - - * If the ``remove_obsolete`` option is set, it also deletes repositories - that are found in the database but not on the file system, so called - "clean zombies". + Triggers a cleanup of non-existing repositories or repository groups in filesystem. This command can only be run using an |authtoken| with admin rights to the specified repository. @@ -229,9 +216,6 @@ def cleanup_repos(request, apiuser, remove_obsolete=Optional(False)): :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser - :param remove_obsolete: Deletes repositories from the database that - are not found on the filesystem. - :type remove_obsolete: Optional(``True`` | ``False``) Example output: @@ -239,8 +223,8 @@ def cleanup_repos(request, apiuser, remove_obsolete=Optional(False)): id : result : { - 'added': [,...] - 'removed': [,...] + 'removed': [,...] + 'errors': [,...] } error : null @@ -251,22 +235,22 @@ def cleanup_repos(request, apiuser, remove_obsolete=Optional(False)): id : result : null error : { - 'Error occurred during rescan repositories action' + 'Error occurred during repo storage cleanup action' } """ + from rhodecode.lib.utils import repo2db_cleanup # re-import for testing patches + if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() try: - rm_obsolete = Optional.extract(remove_obsolete) - added, removed = repo2db_mapper(ScmModel().repo_scan(), - remove_obsolete=rm_obsolete, force_hooks_rebuild=True) - return {'added': added, 'removed': removed} + removed, errors = repo2db_cleanup() + return {'removed': removed, 'errors': errors} except Exception: - log.exception('Failed to run repo rescann') + log.exception('Failed to run repo storage cleanup') raise JSONRPCError( - 'Error occurred during rescan repositories action' + 'Error occurred during repo storage cleanup action' ) diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py index 4f53749b..838e1065 100644 --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -361,12 +361,21 @@ def admin_routes(config): renderer='rhodecode:templates/admin/settings/settings.mako') config.add_route( - name='admin_settings_mapping_update', - pattern='/settings/mapping/update') + name='admin_settings_mapping_create', + pattern='/settings/mapping/create') config.add_view( AdminSettingsView, - attr='settings_mapping_update', - route_name='admin_settings_mapping_update', request_method='POST', + attr='settings_mapping_create', + route_name='admin_settings_mapping_create', request_method='POST', + renderer='rhodecode:templates/admin/settings/settings.mako') + + config.add_route( + name='admin_settings_mapping_cleanup', + pattern='/settings/mapping/cleanup') + config.add_view( + AdminSettingsView, + attr='settings_mapping_cleanup', + route_name='admin_settings_mapping_cleanup', request_method='POST', renderer='rhodecode:templates/admin/settings/settings.mako') config.add_route( diff --git a/rhodecode/apps/admin/tests/test_admin_repos.py b/rhodecode/apps/admin/tests/test_admin_repos.py index e9e23286..18a4927f 100644 --- a/rhodecode/apps/admin/tests/test_admin_repos.py +++ b/rhodecode/apps/admin/tests/test_admin_repos.py @@ -110,9 +110,11 @@ class TestAdminRepos(object): repo_type=backend.alias, repo_description=description, csrf_token=csrf_token)) - - self.assert_repository_is_created_correctly( - repo_name, description, backend) + try: + self.assert_repository_is_created_correctly(repo_name, description, backend) + finally: + RepoModel().delete(numeric_repo) + Session().commit() @pytest.mark.parametrize("suffix", ['', '_ąćę'], ids=['', 'non-ascii']) def test_create_in_group( diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py index 4dfbe1f2..7f7c4abc 100644 --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -38,7 +38,7 @@ from rhodecode.lib.auth import ( LoginRequired, HasPermissionAllDecorator, CSRFRequired) from rhodecode.lib.celerylib import tasks, run_task from rhodecode.lib.str_utils import safe_str -from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_repo_store_path +from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_repo_store_path, repo2db_cleanup from rhodecode.lib.utils2 import str2bool, AttributeDict from rhodecode.lib.index import searcher_from_config @@ -233,13 +233,12 @@ class AdminSettingsView(BaseAppView): @LoginRequired() @HasPermissionAllDecorator('hg.admin') @CSRFRequired() - def settings_mapping_update(self): + def settings_mapping_create(self): _ = self.request.translate c = self.load_default_context() c.active = 'mapping' - rm_obsolete = self.request.POST.get('destroy', False) invalidate_cache = self.request.POST.get('invalidate', False) - log.debug('rescanning repo location with destroy obsolete=%s', rm_obsolete) + log.debug('rescanning repo location') if invalidate_cache: log.debug('invalidating all repositories cache') @@ -247,16 +246,34 @@ class AdminSettingsView(BaseAppView): ScmModel().mark_for_invalidation(repo.repo_name, delete=True) filesystem_repos = ScmModel().repo_scan() - added, removed = repo2db_mapper(filesystem_repos, rm_obsolete, force_hooks_rebuild=True) + added, errors = repo2db_mapper(filesystem_repos, force_hooks_rebuild=True) PermissionModel().trigger_permission_flush() def _repr(rm_repo): return ', '.join(map(safe_str, rm_repo)) or '-' - h.flash(_('Repositories successfully ' - 'rescanned added: %s ; removed: %s') % - (_repr(added), _repr(removed)), - category='success') + if errors: + h.flash(_('Errors during scan: {}').format(_repr(errors), ), category='error') + + h.flash(_('Repositories successfully scanned: Added: {}').format(_repr(added)), category='success') + raise HTTPFound(h.route_path('admin_settings_mapping')) + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @CSRFRequired() + def settings_mapping_cleanup(self): + _ = self.request.translate + c = self.load_default_context() + c.active = 'mapping' + log.debug('rescanning repo location') + + removed, errors = repo2db_cleanup() + PermissionModel().trigger_permission_flush() + + def _repr(rm_repo): + return ', '.join(map(safe_str, rm_repo)) or '-' + + h.flash(_('Repositories successfully scanned: Errors: {}, Added: {}').format(errors, _repr(removed)), category='success') raise HTTPFound(h.route_path('admin_settings_mapping')) @LoginRequired() diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py index 1485dba6..0d332b27 100644 --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -582,23 +582,19 @@ def map_groups(path): return group -def repo2db_mapper(initial_repo_list, remove_obsolete=False, force_hooks_rebuild=False): +def repo2db_mapper(initial_repo_list, force_hooks_rebuild=False): """ - maps all repos given in initial_repo_list, non existing repositories - are created, if remove_obsolete is True it also checks for db entries - that are not in initial_repo_list and removes them. - - :param initial_repo_list: list of repositories found by scanning methods - :param remove_obsolete: check for obsolete entries in database + maps all repos given in initial_repo_list, non-existing repositories + are created """ from rhodecode.model.repo import RepoModel - from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.settings import SettingsModel sa = meta.Session() repo_model = RepoModel() user = User.get_first_super_admin() added = [] + errors = [] # creation defaults defs = SettingsModel().get_default_repo_settings(strip_prefix=True) @@ -616,9 +612,7 @@ def repo2db_mapper(initial_repo_list, remove_obsolete=False, force_hooks_rebuild if not db_repo: log.info('repository `%s` not found in the database, creating now', name) added.append(name) - desc = (repo.description - if repo.description != 'unknown' - else '%s repository' % name) + desc = repo.description if repo.description != 'unknown' else f'{name} repository' db_repo = repo_model._create_repo( repo_name=name, @@ -633,76 +627,115 @@ def repo2db_mapper(initial_repo_list, remove_obsolete=False, force_hooks_rebuild state=Repository.STATE_CREATED ) sa.commit() + + try: + config = db_repo._config + config.set('extensions', 'largefiles', '') + scm_repo = db_repo.scm_instance(config=config) + except Exception: + log.error(traceback.format_exc()) + errors.append(f'getting vcs instance for {name} failed') + continue + + try: + db_repo.update_commit_cache(recursive=False) + except Exception: + log.error(traceback.format_exc()) + errors.append(f'update_commit_cache for {name} failed') + continue + + try: + scm_repo.install_hooks(force=force_hooks_rebuild) + except Exception: + log.error(traceback.format_exc()) + errors.append(f'install_hooks for {name} failed') + continue + + try: # we added that repo just now, and make sure we updated server info if db_repo.repo_type == 'git': - git_repo = db_repo.scm_instance() # update repository server-info log.debug('Running update server info') - git_repo._update_server_info(force=True) + scm_repo._update_server_info(force=True) + except Exception: + log.error(traceback.format_exc()) + errors.append(f'update_server_info for {name} failed') + continue - db_repo.update_commit_cache(recursive=False) + return added, errors - config = db_repo._config - config.set('extensions', 'largefiles', '') - repo = db_repo.scm_instance(config=config) - repo.install_hooks(force=force_hooks_rebuild) +def repo2db_cleanup(skip_repos=None, skip_groups=None): + from rhodecode.model.repo import RepoModel + from rhodecode.model.repo_group import RepoGroupModel + sa = meta.Session() removed = [] - if remove_obsolete: - # remove from database those repositories that are not in the filesystem - for repo in sa.query(Repository).all(): - if repo.repo_name not in list(initial_repo_list.keys()): - log.debug("Removing non-existing repository found in db `%s`", - repo.repo_name) - try: - RepoModel(sa).delete(repo, forks='detach', fs_remove=False) - sa.commit() - removed.append(repo.repo_name) - except Exception: - # don't hold further removals on error - log.error(traceback.format_exc()) - sa.rollback() + errors = [] - def splitter(full_repo_name): - _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1) - gr_name = None - if len(_parts) == 2: - gr_name = _parts[0] - return gr_name - initial_repo_group_list = [splitter(x) for x in - list(initial_repo_list.keys()) if splitter(x)] + all_repos = Repository.execute( + Repository.select(Repository)\ + .order_by(Repository.repo_name) + ).scalars() - # remove from database those repository groups that are not in the - # filesystem due to parent child relationships we need to delete them - # in a specific order of most nested first - all_groups = [x.group_name for x in sa.query(RepoGroup).all()] - def nested_sort(gr): - return len(gr.split('/')) - for group_name in sorted(all_groups, key=nested_sort, reverse=True): - if group_name not in initial_repo_group_list: - repo_group = RepoGroup.get_by_group_name(group_name) - if (repo_group.children.all() or - not RepoGroupModel().check_exist_filesystem( - group_name=group_name, exc_on_failure=False)): - continue + # remove from database those repositories that are not in the filesystem + for db_repo in all_repos: + db_repo_name = db_repo.repo_name + if skip_repos and db_repo_name in skip_repos: + log.debug('Skipping repo `%s`', db_repo_name) + continue + try: + instance = db_repo.scm_instance() + except Exception: + instance = None - log.info( - 'Removing non-existing repository group found in db `%s`', - group_name) - try: - RepoGroupModel(sa).delete(group_name, fs_remove=False) - sa.commit() - removed.append(group_name) - except Exception: - # don't hold further removals on error - log.exception( - 'Unable to remove repository group `%s`', - group_name) - sa.rollback() - raise + if not instance: + log.debug("Removing non-existing repository found in db `%s`", db_repo_name) + try: + RepoModel(sa).delete(db_repo, forks='detach', fs_remove=False, call_events=False) + sa.commit() + removed.append(db_repo_name) + except Exception: + # don't hold further removals on error + log.error(traceback.format_exc()) + sa.rollback() + errors.append(db_repo_name) + + # remove from database those repository groups that are not in the + # filesystem due to parent child relationships we need to delete them + # in a specific order of most nested first + all_groups = RepoGroup.execute( + RepoGroup.select(RepoGroup.group_name)\ + .order_by(RepoGroup.group_name) + ).scalars().all() + + def nested_sort(gr): + return len(gr.split('/')) + + for group_name in sorted(all_groups, key=nested_sort, reverse=True): + if skip_groups and group_name in skip_groups: + log.debug('Skipping repo group `%s`', group_name) + continue + + repo_group = RepoGroup.get_by_group_name(group_name) + + if repo_group.children.all() or not RepoGroupModel().check_exist_filesystem(group_name=group_name, exc_on_failure=False): + continue + + log.info('Removing non-existing repository group found in db `%s`', group_name) + + try: + RepoGroupModel(sa).delete(group_name, fs_remove=False, call_events=False) + sa.commit() + removed.append(group_name) + except Exception: + # don't hold further removals on error + log.exception('Unable to remove repository group `%s`',group_name) + sa.rollback() + errors.append(group_name) + + return removed, errors - return added, removed def deep_reload_package(package_name): """ @@ -829,7 +862,7 @@ def create_test_repositories(test_path, config): raise ImportError('Failed to import rc_testdata, ' 'please make sure this package is installed from requirements_test.txt') - from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO + from rhodecode.bootstrap import HG_REPO, GIT_REPO, SVN_REPO log.debug('making test vcs repositories at %s', test_path) diff --git a/rhodecode/lib/vcs/backends/__init__.py b/rhodecode/lib/vcs/backends/__init__.py index c6ce140e..1a56567e 100644 --- a/rhodecode/lib/vcs/backends/__init__.py +++ b/rhodecode/lib/vcs/backends/__init__.py @@ -58,9 +58,10 @@ def get_vcs_instance(repo_path, *args, **kwargs) -> typing.VCSRepo | None: raise VCSError(f"Given path {repo_path} is not a directory") except VCSError: log.exception( - 'Perhaps this repository is in db and not in ' - 'filesystem run rescan repositories with ' - '"destroy old data" option from admin panel') + 'Perhaps this repository is in db and not in filesystem.' + 'Run cleanup filesystem option from admin settings under Remap and rescan' + ) + return None return backend(repo_path=repo_path, *args, **kwargs) diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py index fc6d6708..b7b72d40 100644 --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -745,7 +745,7 @@ class RepoModel(BaseModel): log.error(traceback.format_exc()) raise - def delete(self, repo, forks=None, pull_requests=None, artifacts=None, fs_remove=True, cur_user=None): + def delete(self, repo, forks=None, pull_requests=None, artifacts=None, fs_remove=True, cur_user=None, call_events=True): """ Delete given repository, forks parameter defines what do do with attached forks. Throws AttachedForksError if deleted repo has attached @@ -760,47 +760,54 @@ class RepoModel(BaseModel): if not cur_user: cur_user = getattr(get_current_rhodecode_user(), 'username', None) repo = self._get_repo(repo) - if repo: - if forks == 'detach': - for r in repo.forks: - r.fork = None - self.sa.add(r) - elif forks == 'delete': - for r in repo.forks: - self.delete(r, forks='delete') - elif [f for f in repo.forks]: - raise AttachedForksError() + if not repo: + return False - # check for pull requests - pr_sources = repo.pull_requests_source - pr_targets = repo.pull_requests_target - if pull_requests != 'delete' and (pr_sources or pr_targets): - raise AttachedPullRequestsError() + if forks == 'detach': + for r in repo.forks: + r.fork = None + self.sa.add(r) + elif forks == 'delete': + for r in repo.forks: + self.delete(r, forks='delete') + elif [f for f in repo.forks]: + raise AttachedForksError() - artifacts_objs = repo.artifacts - if artifacts == 'delete': - for a in artifacts_objs: - self.sa.delete(a) - elif [a for a in artifacts_objs]: - raise AttachedArtifactsError() + # check for pull requests + pr_sources = repo.pull_requests_source + pr_targets = repo.pull_requests_target + if pull_requests != 'delete' and (pr_sources or pr_targets): + raise AttachedPullRequestsError() - old_repo_dict = repo.get_dict() + artifacts_objs = repo.artifacts + if artifacts == 'delete': + for a in artifacts_objs: + self.sa.delete(a) + elif [a for a in artifacts_objs]: + raise AttachedArtifactsError() + + old_repo_dict = repo.get_dict() + if call_events: events.trigger(events.RepoPreDeleteEvent(repo)) - try: - self.sa.delete(repo) - if fs_remove: - self._delete_filesystem_repo(repo) - else: - log.debug('skipping removal from filesystem') - old_repo_dict.update({ - 'deleted_by': cur_user, - 'deleted_on': time.time(), - }) + + try: + self.sa.delete(repo) + if fs_remove: + self._delete_filesystem_repo(repo) + else: + log.debug('skipping removal from filesystem') + old_repo_dict.update({ + 'deleted_by': cur_user, + 'deleted_on': time.time(), + }) + if call_events: hooks_base.delete_repository(**old_repo_dict) events.trigger(events.RepoDeleteEvent(repo)) - except Exception: - log.error(traceback.format_exc()) - raise + except Exception: + log.error(traceback.format_exc()) + raise + + return True def grant_user_permission(self, repo, user, perm): """ diff --git a/rhodecode/model/repo_group.py b/rhodecode/model/repo_group.py index c20950b0..b81afe8f 100644 --- a/rhodecode/model/repo_group.py +++ b/rhodecode/model/repo_group.py @@ -156,7 +156,7 @@ class RepoGroupModel(BaseModel): def check_exist_filesystem(self, group_name, exc_on_failure=True): create_path = os.path.join(self.repos_path, group_name) - log.debug('creating new group in %s', create_path) + log.debug('checking FS presence for repo group in %s', create_path) if os.path.isdir(create_path): if exc_on_failure: @@ -573,10 +573,11 @@ class RepoGroupModel(BaseModel): log.error(traceback.format_exc()) raise - def delete(self, repo_group, force_delete=False, fs_remove=True): + def delete(self, repo_group, force_delete=False, fs_remove=True, call_events=True): repo_group = self._get_repo_group(repo_group) if not repo_group: return False + repo_group_name = repo_group.group_name try: self.sa.delete(repo_group) if fs_remove: @@ -585,13 +586,15 @@ class RepoGroupModel(BaseModel): log.debug('skipping removal from filesystem') # Trigger delete event. - events.trigger(events.RepoGroupDeleteEvent(repo_group)) - return True + if call_events: + events.trigger(events.RepoGroupDeleteEvent(repo_group)) except Exception: - log.error('Error removing repo_group %s', repo_group) + log.error('Error removing repo_group %s', repo_group_name) raise + return True + def grant_user_permission(self, repo_group, user, perm): """ Grant permission for user on given repository group, or update diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py index 9489ab70..439aa8c7 100644 --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -121,7 +121,7 @@ def scan_repositories_if_enabled(event): from rhodecode.lib.utils import repo2db_mapper scm = ScmModel() repositories = scm.repo_scan(scm.repos_path) - repo2db_mapper(repositories, remove_obsolete=False) + repo2db_mapper(repositories) def write_metadata_if_needed(event): diff --git a/rhodecode/templates/admin/settings/settings_mapping.mako b/rhodecode/templates/admin/settings/settings_mapping.mako index 0e520e4e..164fdc0a 100644 --- a/rhodecode/templates/admin/settings/settings_mapping.mako +++ b/rhodecode/templates/admin/settings/settings_mapping.mako @@ -1,21 +1,16 @@ -${h.secure_form(h.route_path('admin_settings_mapping_update'), request=request)} +
-

${_('Import New Groups or Repositories')}

+

${_('Import new repository groups and repositories')}

- + ${h.secure_form(h.route_path('admin_settings_mapping_create'), request=request)}

- ${_('This function will scann all data under the current storage path location at')} ${c.storage_path} + ${_('This function will scan all data under the current storage path location at')} ${c.storage_path}
+ ${_('Each folder will be imported as a new repository group, and each repository found will be also imported to root level or corresponding repository group')}

-
- ${h.checkbox('destroy',True)} - -
- ${_('In case a repository or a group was deleted from the filesystem and it still exists in the database, check this option to remove obsolete data from the database.')} -
${h.checkbox('invalidate',True)} @@ -23,11 +18,28 @@ ${h.secure_form(h.route_path('admin_settings_mapping_update'), request=request)} ${_('Each cache data for repositories will be cleaned with this option selected. Use this to reload data and clear cache keys.')}
- ${h.submit('rescan',_('Rescan Filesystem'),class_="btn")} + ${h.submit('rescan',_('Scan filesystem'),class_="btn")}
- + ${h.end_form()}
-${h.end_form()} +
+
+

${_('Cleanup removed Repository Groups or Repositories')}

+
+
+ ${h.secure_form(h.route_path('admin_settings_mapping_cleanup'), request=request)} +

+ ${_('This function will scan all data under the current storage path location at')} ${c.storage_path} + ${_('Then it will remove all repository groups and repositories that are no longer present in the filesystem.')} +

+ +
+ ${h.submit('rescan',_('Cleanup filesystem'),class_="btn btn-danger")} +
+ ${h.end_form()} +
+
+ diff --git a/rhodecode/tests/fixtures/fixture_utils.py b/rhodecode/tests/fixtures/fixture_utils.py index 5caedd3f..8b64efc8 100644 --- a/rhodecode/tests/fixtures/fixture_utils.py +++ b/rhodecode/tests/fixtures/fixture_utils.py @@ -1558,6 +1558,7 @@ def stub_integration_settings(): @pytest.fixture() def repo_integration_stub(request, repo_stub, StubIntegrationType, stub_integration_settings): + repo_id = repo_stub.repo_id integration = IntegrationModel().create( StubIntegrationType, settings=stub_integration_settings, @@ -1571,6 +1572,7 @@ def repo_integration_stub(request, repo_stub, StubIntegrationType, stub_integrat @request.addfinalizer def cleanup(): IntegrationModel().delete(integration) + RepoModel().delete(repo_id) return integration diff --git a/rhodecode/tests/integrations/test_integration.py b/rhodecode/tests/integrations/test_integration.py index 70dc62e3..43adb423 100644 --- a/rhodecode/tests/integrations/test_integration.py +++ b/rhodecode/tests/integrations/test_integration.py @@ -20,23 +20,21 @@ import time import pytest from rhodecode import events +from rhodecode.model.repo import RepoModel from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.model.db import Session, Integration from rhodecode.model.integration import IntegrationModel class TestDeleteScopesDeletesIntegrations(object): - def test_delete_repo_with_integration_deletes_integration( - self, repo_integration_stub): - - Session().delete(repo_integration_stub.repo) + def test_delete_repo_with_integration_deletes_integration(self, repo_integration_stub): + RepoModel().delete(repo_integration_stub.repo) Session().commit() Session().expire_all() integration = Integration.get(repo_integration_stub.integration_id) assert integration is None - def test_delete_repo_group_with_integration_deletes_integration( - self, repogroup_integration_stub): + def test_delete_repo_group_with_integration_deletes_integration(self, repogroup_integration_stub): Session().delete(repogroup_integration_stub.repo_group) Session().commit() @@ -52,7 +50,7 @@ def counter(): global count val = count count += 1 - return '{}_{}'.format(val, time.time()) + return f'{val}_{time.time()}' @pytest.fixture() diff --git a/rhodecode/tests/lib/test_utils.py b/rhodecode/tests/lib/test_utils.py index e46e38a2..0a1b5851 100644 --- a/rhodecode/tests/lib/test_utils.py +++ b/rhodecode/tests/lib/test_utils.py @@ -18,20 +18,25 @@ import multiprocessing import os +import shutil import mock import py import pytest +import rhodecode from rhodecode.lib import caching_query from rhodecode.lib import utils from rhodecode.lib.str_utils import safe_bytes from rhodecode.model import settings from rhodecode.model import db from rhodecode.model import meta +from rhodecode.model.meta import Session from rhodecode.model.repo import RepoModel from rhodecode.model.repo_group import RepoGroupModel +from rhodecode.model.scm import ScmModel from rhodecode.model.settings import UiSetting, SettingsModel +from rhodecode.tests.fixtures.fixture_pyramid import rhodecode_factory from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode_tools.lib.hash_utils import md5_safe from rhodecode.lib.ext_json import json @@ -230,7 +235,7 @@ def platform_encodes_filenames(): return path_with_latin1 != read_path -def test_repo2db_mapper_groups(repo_groups): +def test_repo2db_cleaner_removes_zombie_groups(repo_groups): session = meta.Session() zombie_group, parent_group, child_group = repo_groups zombie_path = os.path.join( @@ -238,10 +243,9 @@ def test_repo2db_mapper_groups(repo_groups): os.rmdir(zombie_path) # Avoid removing test repos when calling repo2db_mapper - repo_list = { - repo.repo_name: 'test' for repo in session.query(db.Repository).all() - } - utils.repo2db_mapper(repo_list, remove_obsolete=True) + repo_list = [repo.repo_name for repo in session.query(db.Repository).all()] + + utils.repo2db_cleanup(skip_repos=repo_list) groups_in_db = session.query(db.RepoGroup).all() assert child_group in groups_in_db @@ -249,20 +253,68 @@ def test_repo2db_mapper_groups(repo_groups): assert zombie_path not in groups_in_db -def test_repo2db_mapper_enables_largefiles(backend): + +@pytest.mark.backends("hg", "git", "svn") +def test_repo2db_cleaner_removes_zombie_repos(backend): repo = backend.create_repo() - repo_list = {repo.repo_name: 'test'} - with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm_mock: - utils.repo2db_mapper(repo_list, remove_obsolete=False) - _, kwargs = scm_mock.call_args - assert kwargs['config'].get('extensions', 'largefiles') == '' + zombie_path = repo.repo_full_path + shutil.rmtree(zombie_path) + + removed, errors = utils.repo2db_cleanup() + assert len(removed) == 1 + assert not errors + + +def test_repo2db_mapper_adds_new_repos(request, backend): + repo = backend.create_repo() + cleanup_repos = [] + cleanup_groups = [] + for num in range(5): + copy_repo_name = f'{repo.repo_name}-{num}' + copy_repo_path = f'{repo.repo_full_path}-{num}' + + shutil.copytree(repo.repo_full_path, copy_repo_path) + cleanup_repos.append(copy_repo_name) + + for gr_num in range(5): + gr_name = f'my_gr_{gr_num}' + dest_gr = os.path.join(os.path.dirname(repo.repo_full_path), gr_name) + os.makedirs(dest_gr, exist_ok=True) + + copy_repo_name = f'{gr_name}/{repo.repo_name}-{gr_num}' + copy_repo_path = f'{dest_gr}/{repo.repo_name}-{gr_num}' + + shutil.copytree(repo.repo_full_path, copy_repo_path) + cleanup_repos.append(copy_repo_name) + cleanup_groups.append(gr_name) + + repo_list = ScmModel().repo_scan() + + added, errors = utils.repo2db_mapper(repo_list) + Session().commit() + assert not errors + + assert len(added) == 10 + + @request.addfinalizer + def cleanup(): + for _repo in cleanup_repos: + del_result = RepoModel().delete(_repo, call_events=False) + Session().commit() + assert del_result is True + + for _repo_group in cleanup_groups: + del_result = RepoGroupModel().delete(_repo_group, force_delete=True, call_events=False) + Session().commit() + assert del_result is True -@pytest.mark.backends("git", "svn") def test_repo2db_mapper_installs_hooks_for_repos_in_db(backend): repo = backend.create_repo() repo_list = {repo.repo_name: 'test'} - utils.repo2db_mapper(repo_list, remove_obsolete=False) + added, errors = utils.repo2db_mapper(repo_list) + assert not errors + assert repo.scm_instance().get_hooks_info() == {'pre_version': rhodecode.__version__, 'post_version': rhodecode.__version__} @pytest.mark.backends("git", "svn") @@ -271,7 +323,9 @@ def test_repo2db_mapper_installs_hooks_for_newly_added_repos(backend): RepoModel().delete(repo, fs_remove=False) meta.Session().commit() repo_list = {repo.repo_name: repo.scm_instance()} - utils.repo2db_mapper(repo_list, remove_obsolete=False) + added, errors = utils.repo2db_mapper(repo_list) + assert not errors + assert len(added) == 1 class TestPasswordChanged(object): @@ -453,7 +507,7 @@ class TestGetEnabledHooks(object): def test_obfuscate_url_pw(): from rhodecode.lib.utils2 import obfuscate_url_pw - engine = u'/home/repos/malmö' + engine = '/home/repos/malmö' assert obfuscate_url_pw(engine) diff --git a/rhodecode/tests/routes.py b/rhodecode/tests/routes.py index fb6c30b7..696df030 100644 --- a/rhodecode/tests/routes.py +++ b/rhodecode/tests/routes.py @@ -182,7 +182,8 @@ def get_url_defs(): "admin_settings_vcs_svn_pattern_delete": ADMIN_PREFIX + "/settings/vcs/svn_pattern_delete", "admin_settings_mapping": ADMIN_PREFIX + "/settings/mapping", - "admin_settings_mapping_update": ADMIN_PREFIX + "/settings/mapping/update", + "admin_settings_mapping_create": ADMIN_PREFIX + "/settings/mapping/create", + "admin_settings_mapping_cleanup": ADMIN_PREFIX + "/settings/mapping/cleanup", "admin_settings_visual": ADMIN_PREFIX + "/settings/visual", "admin_settings_visual_update": ADMIN_PREFIX + "/settings/visual/update", "admin_settings_issuetracker": ADMIN_PREFIX + "/settings/issue-tracker", From d88fc5856d09321e06fc1c3426c9d64e9b24d7b9 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 13 Nov 2024 14:07:06 +0100 Subject: [PATCH 58/99] configs: added info how to set cache_license to redis --- configs/development.ini | 30 ++++++++++++++++++++++++++++++ configs/production.ini | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/configs/development.ini b/configs/development.ini index 096ebc45..129cadcf 100644 --- a/configs/development.ini +++ b/configs/development.ini @@ -550,6 +550,36 @@ rc_cache.cache_repo.expiration_time = 2592000 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} #rc_cache.cache_repo.arguments.key_prefix = custom-prefix- +; ********************************************* +; `cache_license` cache for storing license info +; for simplicity use rc.file_namespace backend, +; for performance and scale use rc.redis +; ********************************************* +rc_cache.cache_license.backend = dogpile.cache.rc.file_namespace +rc_cache.cache_license.expiration_time = 300 +; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set +#rc_cache.cache_license.arguments.filename = /tmp/cache_general_db + +; alternative `cache_license` redis backend with distributed lock +#rc_cache.cache_license.backend = dogpile.cache.rc.redis +#rc_cache.cache_license.expiration_time = 300 + +; redis_expiration_time needs to be greater then expiration_time +#rc_cache.cache_license.arguments.redis_expiration_time = 360 + +#rc_cache.cache_license.arguments.host = localhost +#rc_cache.cache_license.arguments.port = 6379 +#rc_cache.cache_license.arguments.db = 0 +#rc_cache.cache_license.arguments.socket_timeout = 30 +; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends +#rc_cache.cache_license.arguments.distributed_lock = true + +; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen +#rc_cache.cache_license.arguments.lock_auto_renewal = true + +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_license.arguments.key_prefix = custom-prefix- + ; ############## ; BEAKER SESSION diff --git a/configs/production.ini b/configs/production.ini index 073943ca..4312e3ed 100644 --- a/configs/production.ini +++ b/configs/production.ini @@ -518,6 +518,36 @@ rc_cache.cache_repo.expiration_time = 2592000 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} #rc_cache.cache_repo.arguments.key_prefix = custom-prefix- +; ********************************************* +; `cache_license` cache for storing license info +; for simplicity use rc.file_namespace backend, +; for performance and scale use rc.redis +; ********************************************* +rc_cache.cache_license.backend = dogpile.cache.rc.file_namespace +rc_cache.cache_license.expiration_time = 300 +; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set +#rc_cache.cache_license.arguments.filename = /tmp/cache_general_db + +; alternative `cache_license` redis backend with distributed lock +#rc_cache.cache_license.backend = dogpile.cache.rc.redis +#rc_cache.cache_license.expiration_time = 300 + +; redis_expiration_time needs to be greater then expiration_time +#rc_cache.cache_license.arguments.redis_expiration_time = 360 + +#rc_cache.cache_license.arguments.host = localhost +#rc_cache.cache_license.arguments.port = 6379 +#rc_cache.cache_license.arguments.db = 0 +#rc_cache.cache_license.arguments.socket_timeout = 30 +; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends +#rc_cache.cache_license.arguments.distributed_lock = true + +; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen +#rc_cache.cache_license.arguments.lock_auto_renewal = true + +; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key} +#rc_cache.cache_license.arguments.key_prefix = custom-prefix- + ; ############## ; BEAKER SESSION From 6ef8e63fda891e616ba60234f12e0b44b88390a8 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 13 Nov 2024 14:07:23 +0100 Subject: [PATCH 59/99] deps: bumped psycopg2==2.10.0 for python 3.13 compat --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 798a47d2..6757f222 100644 --- a/requirements.txt +++ b/requirements.txt @@ -180,7 +180,7 @@ premailer==3.10.0 idna==3.4 urllib3==1.26.14 psutil==5.9.8 -psycopg2==2.9.9 +psycopg2==2.9.10 py-bcrypt==0.4 pycmarkgfm==1.2.0 cffi==1.17.1 From 11d505480b72a1002ccf919bd71fb221cde9da0a Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 13 Nov 2024 14:09:28 +0100 Subject: [PATCH 60/99] deps: bumped venusian==3.1.0 for python 3.12 compat --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6757f222..48b36fd2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,7 +43,7 @@ channelstream==0.7.1 pastedeploy==3.1.0 plaster==1.1.2 translationstring==1.4 - venusian==3.0.0 + venusian==3.1.0 webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 @@ -58,7 +58,7 @@ channelstream==0.7.1 pastedeploy==3.1.0 plaster==1.1.2 translationstring==1.4 - venusian==3.0.0 + venusian==3.1.0 webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 @@ -198,7 +198,7 @@ pyramid-mailer==0.15.1 pastedeploy==3.1.0 plaster==1.1.2 translationstring==1.4 - venusian==3.0.0 + venusian==3.1.0 webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 @@ -218,7 +218,7 @@ pyramid-mako==1.1.0 pastedeploy==3.1.0 plaster==1.1.2 translationstring==1.4 - venusian==3.0.0 + venusian==3.1.0 webob==1.8.9 zope.deprecation==5.0.0 zope.interface==7.1.1 From 104671c5e7cc0d1e97dde9e0068f8c022b8a601e Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Thu, 14 Nov 2024 20:15:11 +0100 Subject: [PATCH 61/99] feat(events): added context into events so we can track additional context of calling it --- rhodecode/events/__init__.py | 27 +-- rhodecode/events/base.py | 57 +++--- rhodecode/events/interfaces.py | 13 +- rhodecode/events/pullrequest.py | 170 +++++++++-------- rhodecode/events/repo.py | 323 +++++++++++++++----------------- rhodecode/events/repo_group.py | 53 +++--- rhodecode/events/user.py | 37 ++-- 7 files changed, 343 insertions(+), 337 deletions(-) diff --git a/rhodecode/events/__init__.py b/rhodecode/events/__init__.py index df5086e0..f82cb4ec 100644 --- a/rhodecode/events/__init__.py +++ b/rhodecode/events/__init__.py @@ -18,13 +18,10 @@ import logging -from rhodecode.events.base import ( - RhodeCodeIntegrationEvent, - RhodecodeEvent -) +from rhodecode.events.base import RhodeCodeIntegrationEvent, RhodecodeEvent from rhodecode.events.base import ( # pragma: no cover - FtsBuild + FtsBuild, ) from rhodecode.events.user import ( # pragma: no cover @@ -37,11 +34,16 @@ from rhodecode.events.user import ( # pragma: no cover from rhodecode.events.repo import ( # pragma: no cover RepoEvent, - RepoCommitCommentEvent, RepoCommitCommentEditEvent, - RepoPreCreateEvent, RepoCreateEvent, - RepoPreDeleteEvent, RepoDeleteEvent, - RepoPrePushEvent, RepoPushEvent, - RepoPrePullEvent, RepoPullEvent, + RepoCommitCommentEvent, + RepoCommitCommentEditEvent, + RepoPreCreateEvent, + RepoCreateEvent, + RepoPreDeleteEvent, + RepoDeleteEvent, + RepoPrePushEvent, + RepoPushEvent, + RepoPrePullEvent, + RepoPullEvent, ) from rhodecode.events.repo_group import ( # pragma: no cover @@ -77,12 +79,13 @@ def trigger(event, registry=None): from pyramid.threadlocal import get_current_registry event_name = event.__class__ - log.debug('event %s sent for execution', event_name) + log.debug("event %s sent for execution", event_name) registry = registry or get_current_registry() registry.notify(event) - log.debug('event %s triggered using registry %s', event_name, registry) + log.debug("event %s triggered using registry %s", event_name, registry) # Send the events to integrations directly from rhodecode.integrations import integrations_event_handler + if isinstance(event, RhodeCodeIntegrationEvent): integrations_event_handler(event) diff --git a/rhodecode/events/base.py b/rhodecode/events/base.py index d312989f..019c2ca6 100644 --- a/rhodecode/events/base.py +++ b/rhodecode/events/base.py @@ -26,10 +26,7 @@ from rhodecode.lib.utils2 import AttributeDict # this is a user object to be used for events caused by the system (eg. shell) -SYSTEM_USER = AttributeDict(dict( - username='__SYSTEM__', - user_id='__SYSTEM_ID__' -)) +SYSTEM_USER = AttributeDict(dict(username="__SYSTEM__", user_id="__SYSTEM_ID__")) log = logging.getLogger(__name__) @@ -38,16 +35,18 @@ class RhodecodeEvent(object): """ Base event class for all RhodeCode events """ - name = "RhodeCodeEvent" - no_url_set = '' - def __init__(self, request=None, actor=None): + name = "RhodeCodeEvent" + no_url_set = "" + + def __init__(self, request=None, actor=None, context=None): self._request = request self._actor = actor + self._context = context self.utc_timestamp = datetime.datetime.utcnow() def __repr__(self): - return '<{}:({})>'.format(self.__class__.__name__, self.name) + return f"<{self.__class__.__name__}:(name={self.name}, context={self._context})>" def get_request(self): if self._request: @@ -63,11 +62,11 @@ class RhodecodeEvent(object): if not self.request: return - user = getattr(self.request, 'user', None) + user = getattr(self.request, "user", None) if user: return user - api_user = getattr(self.request, 'rpc_user', None) + api_user = getattr(self.request, "rpc_user", None) if api_user: return api_user @@ -80,15 +79,17 @@ class RhodecodeEvent(object): return self._actor auth_user = self.auth_user - log.debug('Got integration actor: %s', auth_user) + log.debug("Got integration actor: %s", auth_user) if isinstance(auth_user, AuthUser): instance = auth_user.get_instance() # we can't find this DB user... if not instance: - return AttributeDict(dict( - username=auth_user.username, - user_id=auth_user.user_id, - )) + return AttributeDict( + dict( + username=auth_user.username, + user_id=auth_user.user_id, + ) + ) elif auth_user: return auth_user return SYSTEM_USER @@ -98,29 +99,26 @@ class RhodecodeEvent(object): auth_user = self.auth_user if auth_user: return auth_user.ip_addr - return '' + return "" @property def server_url(self): if self.request: try: - return self.request.route_url('home') + return self.request.route_url("home") except Exception: - log.exception('Failed to fetch URL for server') + log.exception("Failed to fetch URL for server") return self.no_url_set return self.no_url_set def as_dict(self): data = { - 'name': self.name, - 'utc_timestamp': self.utc_timestamp, - 'actor_ip': self.actor_ip, - 'actor': { - 'username': self.actor.username, - 'user_id': self.actor.user_id - }, - 'server_url': self.server_url + "name": self.name, + "utc_timestamp": self.utc_timestamp, + "actor_ip": self.actor_ip, + "actor": {"username": self.actor.username, "user_id": self.actor.user_id}, + "server_url": self.server_url, } return data @@ -129,13 +127,14 @@ class RhodeCodeIntegrationEvent(RhodecodeEvent): """ Special subclass for Integration events """ - description = '' + + description = "" class FtsBuild(RhodecodeEvent): """ This event will be triggered when FTS Build is triggered """ - name = 'fts-build' - display_name = 'Start FTS Build' + name = "fts-build" + display_name = "Start FTS Build" diff --git a/rhodecode/events/interfaces.py b/rhodecode/events/interfaces.py index cd3a1657..14120d13 100644 --- a/rhodecode/events/interfaces.py +++ b/rhodecode/events/interfaces.py @@ -24,20 +24,23 @@ class IUserRegistered(Interface): An event type that is emitted whenever a new user registers a user account. """ - user = Attribute('The user object.') - session = Attribute('The session while processing the register form post.') + + user = Attribute("The user object.") + session = Attribute("The session while processing the register form post.") class IUserPreCreate(Interface): """ An event type that is emitted before a new user object is created. """ - user_data = Attribute('Data used to create the new user') + + user_data = Attribute("Data used to create the new user") class IUserPreUpdate(Interface): """ An event type that is emitted before a user object is updated. """ - user = Attribute('The not yet updated user object') - user_data = Attribute('Data used to update the user') + + user = Attribute("The not yet updated user object") + user_data = Attribute("Data used to update the user") diff --git a/rhodecode/events/pullrequest.py b/rhodecode/events/pullrequest.py index 97506e79..9551e0a8 100644 --- a/rhodecode/events/pullrequest.py +++ b/rhodecode/events/pullrequest.py @@ -19,7 +19,7 @@ import logging from rhodecode.translation import lazy_ugettext -from rhodecode.events.repo import (RepoEvent, _commits_as_dict, _issues_as_dict) +from rhodecode.events.repo import RepoEvent, _commits_as_dict, _issues_as_dict log = logging.getLogger(__name__) @@ -30,45 +30,44 @@ class PullRequestEvent(RepoEvent): :param pullrequest: a :class:`PullRequest` instance """ - name = 'pullrequest-event' - display_name = lazy_ugettext('pullrequest generic event') - description = lazy_ugettext('All events within a context of a pull request') - def __init__(self, pullrequest): - super().__init__(pullrequest.target_repo) + name = "pullrequest-event" + display_name = lazy_ugettext("pullrequest generic event") + description = lazy_ugettext("All events within a context of a pull request") + + def __init__(self, pullrequest, context=None): + super().__init__(pullrequest.target_repo, context=context) self.pullrequest = pullrequest + self.context = self._context def as_dict(self): from rhodecode.lib.utils2 import md5_safe from rhodecode.model.pull_request import PullRequestModel + data = super().as_dict() - commits = _commits_as_dict( - self, - commit_ids=self.pullrequest.revisions, - repos=[self.pullrequest.source_repo] - ) + commits = _commits_as_dict(self, commit_ids=self.pullrequest.revisions, repos=[self.pullrequest.source_repo]) issues = _issues_as_dict(commits) # calculate hashes of all commits for unique identifier of commits # inside that pull request - commits_hash = md5_safe(':'.join(x.get('raw_id', '') for x in commits)) + commits_hash = md5_safe(":".join(x.get("raw_id", "") for x in commits)) - data.update({ - 'pullrequest': { - 'title': self.pullrequest.title, - 'issues': issues, - 'pull_request_id': self.pullrequest.pull_request_id, - 'url': PullRequestModel().get_url( - self.pullrequest, request=self.request), - 'permalink_url': PullRequestModel().get_url( - self.pullrequest, request=self.request, permalink=True), - 'shadow_url': PullRequestModel().get_shadow_clone_url( - self.pullrequest, request=self.request), - 'status': self.pullrequest.calculated_review_status(), - 'commits_uid': commits_hash, - 'commits': commits, + data.update( + { + "pullrequest": { + "title": self.pullrequest.title, + "issues": issues, + "pull_request_id": self.pullrequest.pull_request_id, + "url": PullRequestModel().get_url(self.pullrequest, request=self.request), + "permalink_url": PullRequestModel().get_url(self.pullrequest, request=self.request, permalink=True), + "shadow_url": PullRequestModel().get_shadow_clone_url(self.pullrequest, request=self.request), + "status": self.pullrequest.calculated_review_status(), + "commits_uid": commits_hash, + "commits": commits, + }, + "context": self.context, } - }) + ) return data @@ -77,9 +76,10 @@ class PullRequestCreateEvent(PullRequestEvent): An instance of this class is emitted as an :term:`event` after a pull request is created. """ - name = 'pullrequest-create' - display_name = lazy_ugettext('pullrequest created') - description = lazy_ugettext('Event triggered after pull request was created') + + name = "pullrequest-create" + display_name = lazy_ugettext("pullrequest created") + description = lazy_ugettext("Event triggered after pull request was created") class PullRequestCloseEvent(PullRequestEvent): @@ -87,9 +87,10 @@ class PullRequestCloseEvent(PullRequestEvent): An instance of this class is emitted as an :term:`event` after a pull request is closed. """ - name = 'pullrequest-close' - display_name = lazy_ugettext('pullrequest closed') - description = lazy_ugettext('Event triggered after pull request was closed') + + name = "pullrequest-close" + display_name = lazy_ugettext("pullrequest closed") + description = lazy_ugettext("Event triggered after pull request was closed") class PullRequestUpdateEvent(PullRequestEvent): @@ -97,9 +98,10 @@ class PullRequestUpdateEvent(PullRequestEvent): An instance of this class is emitted as an :term:`event` after a pull request's commits have been updated. """ - name = 'pullrequest-update' - display_name = lazy_ugettext('pullrequest commits updated') - description = lazy_ugettext('Event triggered after pull requests was updated') + + name = "pullrequest-update" + display_name = lazy_ugettext("pullrequest commits updated") + description = lazy_ugettext("Event triggered after pull requests was updated") class PullRequestReviewEvent(PullRequestEvent): @@ -107,13 +109,13 @@ class PullRequestReviewEvent(PullRequestEvent): An instance of this class is emitted as an :term:`event` after a pull request review has changed. A status defines new status of review. """ - name = 'pullrequest-review' - display_name = lazy_ugettext('pullrequest review changed') - description = lazy_ugettext('Event triggered after a review status of a ' - 'pull requests has changed to other.') - def __init__(self, pullrequest, status): - super().__init__(pullrequest) + name = "pullrequest-review" + display_name = lazy_ugettext("pullrequest review changed") + description = lazy_ugettext("Event triggered after a review status of a pull requests has changed to other.") + + def __init__(self, pullrequest, status, context=None): + super().__init__(pullrequest, context=context) self.status = status @@ -122,10 +124,10 @@ class PullRequestMergeEvent(PullRequestEvent): An instance of this class is emitted as an :term:`event` after a pull request is merged. """ - name = 'pullrequest-merge' - display_name = lazy_ugettext('pullrequest merged') - description = lazy_ugettext('Event triggered after a successful merge operation ' - 'was executed on a pull request') + + name = "pullrequest-merge" + display_name = lazy_ugettext("pullrequest merged") + description = lazy_ugettext("Event triggered after a successful merge operation was executed on a pull request") class PullRequestCommentEvent(PullRequestEvent): @@ -133,37 +135,39 @@ class PullRequestCommentEvent(PullRequestEvent): An instance of this class is emitted as an :term:`event` after a pull request comment is created. """ - name = 'pullrequest-comment' - display_name = lazy_ugettext('pullrequest commented') - description = lazy_ugettext('Event triggered after a comment was made on a code ' - 'in the pull request') - def __init__(self, pullrequest, comment): - super().__init__(pullrequest) + name = "pullrequest-comment" + display_name = lazy_ugettext("pullrequest commented") + description = lazy_ugettext("Event triggered after a comment was made on a code in the pull request") + + def __init__(self, pullrequest, comment, context=None): + super().__init__(pullrequest, context=context) self.comment = comment def as_dict(self): from rhodecode.model.comment import CommentsModel + data = super().as_dict() status = None if self.comment.status_change: status = self.comment.review_status - data.update({ - 'comment': { - 'status': status, - 'text': self.comment.text, - 'type': self.comment.comment_type, - 'file': self.comment.f_path, - 'line': self.comment.line_no, - 'version': self.comment.last_version, - 'url': CommentsModel().get_url( - self.comment, request=self.request), - 'permalink_url': CommentsModel().get_url( - self.comment, request=self.request, permalink=True), + data.update( + { + "comment": { + "status": status, + "text": self.comment.text, + "type": self.comment.comment_type, + "file": self.comment.f_path, + "line": self.comment.line_no, + "version": self.comment.last_version, + "url": CommentsModel().get_url(self.comment, request=self.request), + "permalink_url": CommentsModel().get_url(self.comment, request=self.request, permalink=True), + }, + "context": self.context, } - }) + ) return data @@ -172,10 +176,10 @@ class PullRequestCommentEditEvent(PullRequestEvent): An instance of this class is emitted as an :term:`event` after a pull request comment is edited. """ - name = 'pullrequest-comment-edit' - display_name = lazy_ugettext('pullrequest comment edited') - description = lazy_ugettext('Event triggered after a comment was edited on a code ' - 'in the pull request') + + name = "pullrequest-comment-edit" + display_name = lazy_ugettext("pullrequest comment edited") + description = lazy_ugettext("Event triggered after a comment was edited on a code in the pull request") def __init__(self, pullrequest, comment): super().__init__(pullrequest) @@ -183,24 +187,26 @@ class PullRequestCommentEditEvent(PullRequestEvent): def as_dict(self): from rhodecode.model.comment import CommentsModel + data = super().as_dict() status = None if self.comment.status_change: status = self.comment.review_status - data.update({ - 'comment': { - 'status': status, - 'text': self.comment.text, - 'type': self.comment.comment_type, - 'file': self.comment.f_path, - 'line': self.comment.line_no, - 'version': self.comment.last_version, - 'url': CommentsModel().get_url( - self.comment, request=self.request), - 'permalink_url': CommentsModel().get_url( - self.comment, request=self.request, permalink=True), + data.update( + { + "comment": { + "status": status, + "text": self.comment.text, + "type": self.comment.comment_type, + "file": self.comment.f_path, + "line": self.comment.line_no, + "version": self.comment.last_version, + "url": CommentsModel().get_url(self.comment, request=self.request), + "permalink_url": CommentsModel().get_url(self.comment, request=self.request, permalink=True), + }, + "context": self.context } - }) + ) return data diff --git a/rhodecode/events/repo.py b/rhodecode/events/repo.py index 5e898a26..757d0a2f 100644 --- a/rhodecode/events/repo.py +++ b/rhodecode/events/repo.py @@ -37,12 +37,11 @@ def _commits_as_dict(event, commit_ids, repos): :param repos: a list of repos to check """ from rhodecode.lib.utils2 import extract_mentioned_users - from rhodecode.lib.helpers import ( - urlify_commit_message, process_patterns, chop_at_smart) + from rhodecode.lib.helpers import urlify_commit_message, process_patterns, chop_at_smart from rhodecode.model.repo import RepoModel if not repos: - raise Exception('no repo defined') + raise Exception("no repo defined") if not isinstance(repos, (tuple, list)): repos = [repos] @@ -63,37 +62,31 @@ def _commits_as_dict(event, commit_ids, repos): try: # use copy of needed_commits since we modify it while iterating for commit_id in list(needed_commits): - if commit_id.startswith('tag=>'): + if commit_id.startswith("tag=>"): raw_id = commit_id[5:] cs_data = { - 'raw_id': commit_id, 'short_id': commit_id, - 'branch': None, - 'git_ref_change': 'tag_add', - 'message': f'Added new tag {raw_id}', - 'author': event.actor.full_contact, - 'date': datetime.datetime.now(), - 'refs': { - 'branches': [], - 'bookmarks': [], - 'tags': [] - } + "raw_id": commit_id, + "short_id": commit_id, + "branch": None, + "git_ref_change": "tag_add", + "message": f"Added new tag {raw_id}", + "author": event.actor.full_contact, + "date": datetime.datetime.now(), + "refs": {"branches": [], "bookmarks": [], "tags": []}, } commits.append(cs_data) - elif commit_id.startswith('delete_branch=>'): + elif commit_id.startswith("delete_branch=>"): raw_id = commit_id[15:] cs_data = { - 'raw_id': commit_id, 'short_id': commit_id, - 'branch': None, - 'git_ref_change': 'branch_delete', - 'message': f'Deleted branch {raw_id}', - 'author': event.actor.full_contact, - 'date': datetime.datetime.now(), - 'refs': { - 'branches': [], - 'bookmarks': [], - 'tags': [] - } + "raw_id": commit_id, + "short_id": commit_id, + "branch": None, + "git_ref_change": "branch_delete", + "message": f"Deleted branch {raw_id}", + "author": event.actor.full_contact, + "date": datetime.datetime.now(), + "refs": {"branches": [], "bookmarks": [], "tags": []}, } commits.append(cs_data) @@ -104,50 +97,45 @@ def _commits_as_dict(event, commit_ids, repos): continue # maybe its in next repo cs_data = cs.__json__() - cs_data['refs'] = cs._get_refs() + cs_data["refs"] = cs._get_refs() - cs_data['mentions'] = extract_mentioned_users(cs_data['message']) - cs_data['reviewers'] = reviewers - cs_data['url'] = RepoModel().get_commit_url( - repo, cs_data['raw_id'], request=event.request) - cs_data['permalink_url'] = RepoModel().get_commit_url( - repo, cs_data['raw_id'], request=event.request, - permalink=True) - urlified_message, issues_data, errors = process_patterns( - cs_data['message'], repo.repo_name) - cs_data['issues'] = issues_data - cs_data['message_html'] = urlify_commit_message( - cs_data['message'], repo.repo_name) - cs_data['message_html_title'] = chop_at_smart( - cs_data['message'], '\n', suffix_if_chopped='...') + cs_data["mentions"] = extract_mentioned_users(cs_data["message"]) + cs_data["reviewers"] = reviewers + cs_data["url"] = RepoModel().get_commit_url(repo, cs_data["raw_id"], request=event.request) + cs_data["permalink_url"] = RepoModel().get_commit_url( + repo, cs_data["raw_id"], request=event.request, permalink=True + ) + urlified_message, issues_data, errors = process_patterns(cs_data["message"], repo.repo_name) + cs_data["issues"] = issues_data + cs_data["message_html"] = urlify_commit_message(cs_data["message"], repo.repo_name) + cs_data["message_html_title"] = chop_at_smart(cs_data["message"], "\n", suffix_if_chopped="...") commits.append(cs_data) needed_commits.remove(commit_id) except Exception: - log.exception('Failed to extract commits data') + log.exception("Failed to extract commits data") # we don't send any commits when crash happens, only full list # matters we short circuit then. return [] # we failed to remove all needed_commits from all repositories if needed_commits: - raise ValueError(f'Unexpectedly not found {needed_commits} in all available repos {repos}') + raise ValueError(f"Unexpectedly not found {needed_commits} in all available repos {repos}") - missing_commits = set(commit_ids) - set(c['raw_id'] for c in commits) + missing_commits = set(commit_ids) - set(c["raw_id"] for c in commits) if missing_commits: - log.error('Inconsistent repository state. ' - 'Missing commits: %s', ', '.join(missing_commits)) + log.error("Inconsistent repository state. " "Missing commits: %s", ", ".join(missing_commits)) return commits def _issues_as_dict(commits): - """ Helper function to serialize issues from commits """ + """Helper function to serialize issues from commits""" issues = {} for commit in commits: - for issue in commit['issues']: - issues[issue['id']] = issue + for issue in commit["issues"]: + issues[issue["id"]] = issue return issues @@ -156,33 +144,36 @@ class RepoEvent(RhodeCodeIntegrationEvent): Base class for events acting on a repository. """ - def __init__(self, repo, actor=None): + def __init__(self, repo, actor=None, context=None): """ :param repo: a :class:`Repository` instance """ - super().__init__(actor=actor) + super().__init__(actor=actor, context=context) self.repo = repo + self.context = self._context def as_dict(self): from rhodecode.model.repo import RepoModel + data = super().as_dict() extra_fields = collections.OrderedDict() for field in self.repo.extra_fields: extra_fields[field.field_key] = field.field_value - data.update({ - 'repo': { - 'repo_id': self.repo.repo_id, - 'repo_name': self.repo.repo_name, - 'repo_type': self.repo.repo_type, - 'url': RepoModel().get_url( - self.repo, request=self.request), - 'permalink_url': RepoModel().get_url( - self.repo, request=self.request, permalink=True), - 'extra_fields': extra_fields + data.update( + { + "repo": { + "repo_id": self.repo.repo_id, + "repo_name": self.repo.repo_name, + "repo_type": self.repo.repo_type, + "url": RepoModel().get_url(self.repo, request=self.request), + "permalink_url": RepoModel().get_url(self.repo, request=self.request, permalink=True), + "extra_fields": extra_fields, + }, + "context": self.context, } - }) + ) return data @@ -192,32 +183,32 @@ class RepoCommitCommentEvent(RepoEvent): on repository commit. """ - name = 'repo-commit-comment' - display_name = lazy_ugettext('repository commit comment') - description = lazy_ugettext('Event triggered after a comment was made ' - 'on commit inside a repository') + name = "repo-commit-comment" + display_name = lazy_ugettext("repository commit comment") + description = lazy_ugettext("Event triggered after a comment was made on commit inside a repository") - def __init__(self, repo, commit, comment): - super().__init__(repo) + def __init__(self, repo, commit, comment, context=None): + super().__init__(repo, context=context) self.commit = commit self.comment = comment def as_dict(self): data = super().as_dict() - data['commit'] = { - 'commit_id': self.commit.raw_id, - 'commit_message': self.commit.message, - 'commit_branch': self.commit.branch, + data["commit"] = { + "commit_id": self.commit.raw_id, + "commit_message": self.commit.message, + "commit_branch": self.commit.branch, } - data['comment'] = { - 'comment_id': self.comment.comment_id, - 'comment_text': self.comment.text, - 'comment_type': self.comment.comment_type, - 'comment_f_path': self.comment.f_path, - 'comment_line_no': self.comment.line_no, - 'comment_version': self.comment.last_version, + data["comment"] = { + "comment_id": self.comment.comment_id, + "comment_text": self.comment.text, + "comment_type": self.comment.comment_type, + "comment_f_path": self.comment.f_path, + "comment_line_no": self.comment.line_no, + "comment_version": self.comment.last_version, } + data["contex"] = self.context return data @@ -227,32 +218,32 @@ class RepoCommitCommentEditEvent(RepoEvent): on repository commit. """ - name = 'repo-commit-edit-comment' - display_name = lazy_ugettext('repository commit edit comment') - description = lazy_ugettext('Event triggered after a comment was edited ' - 'on commit inside a repository') + name = "repo-commit-edit-comment" + display_name = lazy_ugettext("repository commit edit comment") + description = lazy_ugettext("Event triggered after a comment was edited on commit inside a repository") - def __init__(self, repo, commit, comment): - super().__init__(repo) + def __init__(self, repo, commit, comment, context=None): + super().__init__(repo, context=context) self.commit = commit self.comment = comment def as_dict(self): data = super().as_dict() - data['commit'] = { - 'commit_id': self.commit.raw_id, - 'commit_message': self.commit.message, - 'commit_branch': self.commit.branch, + data["commit"] = { + "commit_id": self.commit.raw_id, + "commit_message": self.commit.message, + "commit_branch": self.commit.branch, } - data['comment'] = { - 'comment_id': self.comment.comment_id, - 'comment_text': self.comment.text, - 'comment_type': self.comment.comment_type, - 'comment_f_path': self.comment.f_path, - 'comment_line_no': self.comment.line_no, - 'comment_version': self.comment.last_version, + data["comment"] = { + "comment_id": self.comment.comment_id, + "comment_text": self.comment.text, + "comment_type": self.comment.comment_type, + "comment_f_path": self.comment.f_path, + "comment_line_no": self.comment.line_no, + "comment_version": self.comment.last_version, } + data["context"] = "context" return data @@ -261,9 +252,10 @@ class RepoPreCreateEvent(RepoEvent): An instance of this class is emitted as an :term:`event` before a repo is created. """ - name = 'repo-pre-create' - display_name = lazy_ugettext('repository pre create') - description = lazy_ugettext('Event triggered before repository is created') + + name = "repo-pre-create" + display_name = lazy_ugettext("repository pre create") + description = lazy_ugettext("Event triggered before repository is created") class RepoCreateEvent(RepoEvent): @@ -271,9 +263,10 @@ class RepoCreateEvent(RepoEvent): An instance of this class is emitted as an :term:`event` whenever a repo is created. """ - name = 'repo-create' - display_name = lazy_ugettext('repository created') - description = lazy_ugettext('Event triggered after repository was created') + + name = "repo-create" + display_name = lazy_ugettext("repository created") + description = lazy_ugettext("Event triggered after repository was created") class RepoPreDeleteEvent(RepoEvent): @@ -281,9 +274,10 @@ class RepoPreDeleteEvent(RepoEvent): An instance of this class is emitted as an :term:`event` whenever a repo is created. """ - name = 'repo-pre-delete' - display_name = lazy_ugettext('repository pre delete') - description = lazy_ugettext('Event triggered before a repository is deleted') + + name = "repo-pre-delete" + display_name = lazy_ugettext("repository pre delete") + description = lazy_ugettext("Event triggered before a repository is deleted") class RepoDeleteEvent(RepoEvent): @@ -291,43 +285,45 @@ class RepoDeleteEvent(RepoEvent): An instance of this class is emitted as an :term:`event` whenever a repo is created. """ - name = 'repo-delete' - display_name = lazy_ugettext('repository deleted') - description = lazy_ugettext('Event triggered after repository was deleted') + + name = "repo-delete" + display_name = lazy_ugettext("repository deleted") + description = lazy_ugettext("Event triggered after repository was deleted") class RepoVCSEvent(RepoEvent): """ Base class for events triggered by the VCS """ - name = '' - display_name = 'generic_vcs_event' - def __init__(self, repo_name, extras): + name = "" + display_name = "generic_vcs_event" + + def __init__(self, repo_name, extras, context=None): self.repo = Repository.get_by_repo_name(repo_name) if not self.repo: - raise Exception(f'repo by this name {repo_name} does not exist') + raise Exception(f"repo by this name {repo_name} does not exist") self.extras = extras - super().__init__(self.repo) + super().__init__(self.repo, context=context) @property def actor(self): - if self.extras.get('username'): - return User.get_by_username(self.extras['username']) + if self.extras.get("username"): + return User.get_by_username(self.extras["username"]) @property def actor_ip(self): - if self.extras.get('ip'): - return self.extras['ip'] + if self.extras.get("ip"): + return self.extras["ip"] @property def server_url(self): - if self.extras.get('server_url'): - return self.extras['server_url'] + if self.extras.get("server_url"): + return self.extras["server_url"] @property def request(self): - return self.extras.get('request') or self.get_request() + return self.extras.get("request") or self.get_request() class RepoPrePullEvent(RepoVCSEvent): @@ -335,9 +331,10 @@ class RepoPrePullEvent(RepoVCSEvent): An instance of this class is emitted as an :term:`event` before commits are pulled from a repo. """ - name = 'repo-pre-pull' - display_name = lazy_ugettext('repository pre pull') - description = lazy_ugettext('Event triggered before repository code is pulled') + + name = "repo-pre-pull" + display_name = lazy_ugettext("repository pre pull") + description = lazy_ugettext("Event triggered before repository code is pulled") class RepoPullEvent(RepoVCSEvent): @@ -345,9 +342,10 @@ class RepoPullEvent(RepoVCSEvent): An instance of this class is emitted as an :term:`event` after commits are pulled from a repo. """ - name = 'repo-pull' - display_name = lazy_ugettext('repository pull') - description = lazy_ugettext('Event triggered after repository code was pulled') + + name = "repo-pull" + display_name = lazy_ugettext("repository pull") + description = lazy_ugettext("Event triggered after repository code was pulled") class RepoPrePushEvent(RepoVCSEvent): @@ -355,10 +353,10 @@ class RepoPrePushEvent(RepoVCSEvent): An instance of this class is emitted as an :term:`event` before commits are pushed to a repo. """ - name = 'repo-pre-push' - display_name = lazy_ugettext('repository pre push') - description = lazy_ugettext('Event triggered before the code is ' - 'pushed to a repository') + + name = "repo-pre-push" + display_name = lazy_ugettext("repository pre push") + description = lazy_ugettext("Event triggered before the code is pushed to a repository") class RepoPushEvent(RepoVCSEvent): @@ -368,13 +366,13 @@ class RepoPushEvent(RepoVCSEvent): :param extras: (optional) dict of data from proxied VCS actions """ - name = 'repo-push' - display_name = lazy_ugettext('repository push') - description = lazy_ugettext('Event triggered after the code was ' - 'pushed to a repository') - def __init__(self, repo_name, pushed_commit_ids, extras): - super().__init__(repo_name, extras) + name = "repo-push" + display_name = lazy_ugettext("repository push") + description = lazy_ugettext("Event triggered after the code was pushed to a repository") + + def __init__(self, repo_name, pushed_commit_ids, extras, context=None): + super().__init__(repo_name, extras, context=context) self.pushed_commit_ids = pushed_commit_ids self.new_refs = extras.new_refs @@ -382,63 +380,48 @@ class RepoPushEvent(RepoVCSEvent): data = super().as_dict() def branch_url(branch_name): - return '{}/changelog?branch={}'.format( - data['repo']['url'], branch_name) + return "{}/changelog?branch={}".format(data["repo"]["url"], branch_name) def tag_url(tag_name): - return '{}/files/{}/'.format( - data['repo']['url'], tag_name) + return "{}/files/{}/".format(data["repo"]["url"], tag_name) - commits = _commits_as_dict( - self, commit_ids=self.pushed_commit_ids, repos=[self.repo]) + commits = _commits_as_dict(self, commit_ids=self.pushed_commit_ids, repos=[self.repo]) last_branch = None for commit in reversed(commits): - commit['branch'] = commit['branch'] or last_branch - last_branch = commit['branch'] + commit["branch"] = commit["branch"] or last_branch + last_branch = commit["branch"] issues = _issues_as_dict(commits) branches = set() tags = set() for commit in commits: - if commit['refs']['tags']: - for tag in commit['refs']['tags']: + if commit["refs"]["tags"]: + for tag in commit["refs"]["tags"]: tags.add(tag) - if commit['branch']: - branches.add(commit['branch']) + if commit["branch"]: + branches.add(commit["branch"]) # maybe we have branches in new_refs ? try: - branches = branches.union(set(self.new_refs['branches'])) + branches = branches.union(set(self.new_refs["branches"])) except Exception: pass - branches = [ - { - 'name': branch, - 'url': branch_url(branch) - } - for branch in branches - ] + branches = [{"name": branch, "url": branch_url(branch)} for branch in branches] # maybe we have branches in new_refs ? try: - tags = tags.union(set(self.new_refs['tags'])) + tags = tags.union(set(self.new_refs["tags"])) except Exception: pass - tags = [ - { - 'name': tag, - 'url': tag_url(tag) - } - for tag in tags - ] + tags = [{"name": tag, "url": tag_url(tag)} for tag in tags] - data['push'] = { - 'commits': commits, - 'issues': issues, - 'branches': branches, - 'tags': tags, + data["push"] = { + "commits": commits, + "issues": issues, + "branches": branches, + "tags": tags, } return data diff --git a/rhodecode/events/repo_group.py b/rhodecode/events/repo_group.py index 210e9efd..9b28c937 100644 --- a/rhodecode/events/repo_group.py +++ b/rhodecode/events/repo_group.py @@ -29,27 +29,31 @@ class RepoGroupEvent(RhodeCodeIntegrationEvent): """ Base class for events acting on a repository group. - :param repo: a :class:`RepositoryGroup` instance + :param repo_group: a :class:`RepositoryGroup` instance """ - def __init__(self, repo_group): - super().__init__() + def __init__(self, repo_group, context=None): + super().__init__(context=context) self.repo_group = repo_group + self.context = self._context def as_dict(self): data = super().as_dict() - data.update({ - 'repo_group': { - 'group_id': self.repo_group.group_id, - 'group_name': self.repo_group.group_name, - 'group_parent_id': self.repo_group.group_parent_id, - 'group_description': self.repo_group.group_description, - 'user_id': self.repo_group.user_id, - 'created_by': self.repo_group.user.username, - 'created_on': self.repo_group.created_on, - 'enable_locking': self.repo_group.enable_locking, + data.update( + { + "repo_group": { + "group_id": self.repo_group.group_id, + "group_name": self.repo_group.group_name, + "group_parent_id": self.repo_group.group_parent_id, + "group_description": self.repo_group.group_description, + "user_id": self.repo_group.user_id, + "created_by": self.repo_group.user.username, + "created_on": self.repo_group.created_on, + "enable_locking": self.repo_group.enable_locking, + }, + "context": self.context } - }) + ) return data @@ -58,9 +62,10 @@ class RepoGroupCreateEvent(RepoGroupEvent): An instance of this class is emitted as an :term:`event` whenever a repository group is created. """ - name = 'repo-group-create' - display_name = lazy_ugettext('repository group created') - description = lazy_ugettext('Event triggered after a repository group was created') + + name = "repo-group-create" + display_name = lazy_ugettext("repository group created") + description = lazy_ugettext("Event triggered after a repository group was created") class RepoGroupDeleteEvent(RepoGroupEvent): @@ -68,9 +73,10 @@ class RepoGroupDeleteEvent(RepoGroupEvent): An instance of this class is emitted as an :term:`event` whenever a repository group is deleted. """ - name = 'repo-group-delete' - display_name = lazy_ugettext('repository group deleted') - description = lazy_ugettext('Event triggered after a repository group was deleted') + + name = "repo-group-delete" + display_name = lazy_ugettext("repository group deleted") + description = lazy_ugettext("Event triggered after a repository group was deleted") class RepoGroupUpdateEvent(RepoGroupEvent): @@ -78,6 +84,7 @@ class RepoGroupUpdateEvent(RepoGroupEvent): An instance of this class is emitted as an :term:`event` whenever a repository group is updated. """ - name = 'repo-group-update' - display_name = lazy_ugettext('repository group update') - description = lazy_ugettext('Event triggered after a repository group was updated') + + name = "repo-group-update" + display_name = lazy_ugettext("repository group update") + description = lazy_ugettext("Event triggered after a repository group was updated") diff --git a/rhodecode/events/user.py b/rhodecode/events/user.py index 43977f2b..afcaa3ef 100644 --- a/rhodecode/events/user.py +++ b/rhodecode/events/user.py @@ -21,8 +21,7 @@ from zope.interface import implementer from rhodecode.translation import lazy_ugettext from rhodecode.events.base import RhodecodeEvent, RhodeCodeIntegrationEvent -from rhodecode.events.interfaces import ( - IUserRegistered, IUserPreCreate, IUserPreUpdate) +from rhodecode.events.interfaces import IUserRegistered, IUserPreCreate, IUserPreUpdate log = logging.getLogger(__name__) @@ -33,8 +32,9 @@ class UserRegistered(RhodeCodeIntegrationEvent): An instance of this class is emitted as an :term:`event` whenever a user account is registered. """ - name = 'user-register' - display_name = lazy_ugettext('user registered') + + name = "user-register" + display_name = lazy_ugettext("user registered") def __init__(self, user, session): super().__init__() @@ -48,8 +48,9 @@ class UserPreCreate(RhodeCodeIntegrationEvent): An instance of this class is emitted as an :term:`event` before a new user object is created. """ - name = 'user-pre-create' - display_name = lazy_ugettext('user pre create') + + name = "user-pre-create" + display_name = lazy_ugettext("user pre create") def __init__(self, user_data): super().__init__() @@ -62,8 +63,9 @@ class UserPostCreate(RhodeCodeIntegrationEvent): An instance of this class is emitted as an :term:`event` after a new user object is created. """ - name = 'user-post-create' - display_name = lazy_ugettext('user post create') + + name = "user-post-create" + display_name = lazy_ugettext("user post create") def __init__(self, user_data): super().__init__() @@ -76,8 +78,9 @@ class UserPreUpdate(RhodeCodeIntegrationEvent): An instance of this class is emitted as an :term:`event` before a user object is updated. """ - name = 'user-pre-update' - display_name = lazy_ugettext('user pre update') + + name = "user-pre-update" + display_name = lazy_ugettext("user pre update") def __init__(self, user, user_data): super().__init__() @@ -87,8 +90,8 @@ class UserPreUpdate(RhodeCodeIntegrationEvent): class UserPermissionsChange(RhodecodeEvent): """ - This event should be triggered on an event that permissions of user might changed. - Currently this should be triggered on: + This event should be triggered on an event that permissions of user might be changed. + Currently, this should be triggered on: - user added/removed from user group - repo permissions changed @@ -96,9 +99,11 @@ class UserPermissionsChange(RhodecodeEvent): - user group permissions changed """ - name = 'user-permissions-change' - display_name = lazy_ugettext('user permissions change') - def __init__(self, user_ids): - super().__init__() + name = "user-permissions-change" + display_name = lazy_ugettext("user permissions change") + + def __init__(self, user_ids, context=None): + super().__init__(context=context) self.user_ids = user_ids + self.context = self._context From 4bd1cec6151e070e8d0dd407f670977070d9a2fc Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 09:50:40 +0100 Subject: [PATCH 62/99] fix(git): moved certain operations into vcsserver --- Makefile | 7 +++++++ rhodecode/lib/vcs/backends/git/repository.py | 20 ++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index d20aca27..0ae38701 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,13 @@ test-only: py.test -x -vv -r xw -p no:sugar \ rhodecode/tests/database +.PHONY: test-simple +# test-simple: Run tests only for main test suite witout coverage +test-simple: + PYTHONHASHSEED=random \ + py.test -x -vv -r xw -p no:sugar \ + --ignore=rhodecode/tests/vcs_operations \ + --ignore=rhodecode/tests/database # >>> Docs commands diff --git a/rhodecode/lib/vcs/backends/git/repository.py b/rhodecode/lib/vcs/backends/git/repository.py index 1ab47525..c633ccb7 100644 --- a/rhodecode/lib/vcs/backends/git/repository.py +++ b/rhodecode/lib/vcs/backends/git/repository.py @@ -608,8 +608,7 @@ class GitRepository(BaseRepository): return commit_id1 if self != repo2: - commits = self._remote.get_missing_revs( - commit_id1, commit_id2, repo2.path) + commits = self._remote.get_missing_revs(commit_id1, commit_id2, repo2.path) if commits: commit = repo2.get_commit(commits[-1]) if commit.parents: @@ -620,9 +619,7 @@ class GitRepository(BaseRepository): # no commits from other repo, ancestor_id is the commit_id2 ancestor_id = commit_id2 else: - output, __ = self.run_git_command( - ['merge-base', commit_id1, commit_id2]) - ancestor_id = self.COMMIT_ID_PAT.findall(output)[0] + ancestor_id = self._remote.get_common_ancestor(commit_id1, commit_id2) log.debug('Found common ancestor with sha: %s', ancestor_id) @@ -630,23 +627,22 @@ class GitRepository(BaseRepository): def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None): repo1 = self - ancestor_id = None if commit_id1 == commit_id2: commits = [] elif repo1 != repo2: - missing_ids = self._remote.get_missing_revs(commit_id1, commit_id2, - repo2.path) + missing_ids = self._remote.get_missing_revs(commit_id1, commit_id2, repo2.path) commits = [ repo2.get_commit(commit_id=commit_id, pre_load=pre_load) for commit_id in reversed(missing_ids)] else: - output, __ = repo1.run_git_command( - ['log', '--reverse', '--pretty=format: %H', '-s', - f'{commit_id1}..{commit_id2}']) + compare_commits = self._remote.compare_commits( + commit_id1, commit_id2 + ) commits = [ repo1.get_commit(commit_id=commit_id, pre_load=pre_load) - for commit_id in self.COMMIT_ID_PAT.findall(output)] + for commit_id in compare_commits] + return commits From 51075d8b3451398f73e3878e7771926949633d88 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 09:50:54 +0100 Subject: [PATCH 63/99] chore(pyroutes): updated JS routes --- rhodecode/public/js/rhodecode/routes.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js index 37a985a0..364eeeec 100644 --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -41,6 +41,9 @@ function registerRCRoutes() { pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []); pyroutes.register('admin_scheduler', '/_admin/scheduler', []); pyroutes.register('admin_scheduler_show_tasks', '/_admin/scheduler/_tasks', []); + pyroutes.register('admin_security', '/_admin/security', []); + pyroutes.register('admin_security_modify_allowed_vcs_client_versions', '/_admin/security/modify/allowed_vcs_client_versions', []); + pyroutes.register('admin_security_update', '/_admin/security/audit/update', []); pyroutes.register('admin_settings', '/_admin/settings', []); pyroutes.register('admin_settings_email', '/_admin/settings/email', []); pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []); @@ -62,7 +65,8 @@ function registerRCRoutes() { pyroutes.register('admin_settings_license', '/_admin/settings/license', []); pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []); pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []); - pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []); + pyroutes.register('admin_settings_mapping_cleanup', '/_admin/settings/mapping/cleanup', []); + pyroutes.register('admin_settings_mapping_create', '/_admin/settings/mapping/create', []); pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []); pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []); pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []); @@ -86,7 +90,6 @@ function registerRCRoutes() { pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []); pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []); pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []); - pyroutes.register('admin_security_modify_allowed_vcs_client_versions', '/_admin/security/modify/allowed_vcs_client_versions', []); pyroutes.register('apiv2', '/_admin/api', []); pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']); pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']); From e9354d8f71c288be90f90879f4bfcbbb0f44d9e2 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 11:18:05 +0100 Subject: [PATCH 64/99] deps: bumped pymysql==1.1.1 for security fix --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 48b36fd2..3d843016 100644 --- a/requirements.txt +++ b/requirements.txt @@ -187,7 +187,7 @@ pycmarkgfm==1.2.0 pycparser==2.22 pycryptodome==3.17 pycurl==7.45.3 -pymysql==1.0.3 +pymysql==1.1.1 pyotp==2.8.0 pyparsing==3.1.1 pyramid-mailer==0.15.1 From ca7a43e76852ef23a018dde4ae692c0cb8dd36d8 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 11:18:53 +0100 Subject: [PATCH 65/99] deps: bumped jinja2==3.1.3 for security fix --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3d843016..9ac34116 100644 --- a/requirements.txt +++ b/requirements.txt @@ -48,7 +48,7 @@ channelstream==0.7.1 zope.deprecation==5.0.0 zope.interface==7.1.1 pyramid-jinja2==2.10 - jinja2==3.1.2 + jinja2==3.1.3 markupsafe==3.0.2 markupsafe==3.0.2 pyramid==2.0.2 @@ -122,7 +122,7 @@ nbconvert==7.7.3 six==1.16.0 webencodings==0.5.1 defusedxml==0.7.1 - jinja2==3.1.2 + jinja2==3.1.3 markupsafe==3.0.2 jupyter_core==5.3.1 platformdirs==3.10.0 From 7914ab3938647349b88e3fc51dc926c93a3ec5ea Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 11:24:13 +0100 Subject: [PATCH 66/99] deps: bumped urllib3==1.26.19 for security fix --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9ac34116..6b547ddc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -69,7 +69,7 @@ channelstream==0.7.1 certifi==2022.12.7 charset-normalizer==3.1.0 idna==3.4 - urllib3==1.26.14 + urllib3==1.26.19 ws4py==0.5.1 deform==2.0.15 chameleon==3.10.2 @@ -178,7 +178,7 @@ premailer==3.10.0 certifi==2022.12.7 charset-normalizer==3.1.0 idna==3.4 - urllib3==1.26.14 + urllib3==1.26.19 psutil==5.9.8 psycopg2==2.9.10 py-bcrypt==0.4 @@ -259,7 +259,7 @@ s3fs==2024.10.0 jmespath==1.0.1 python-dateutil==2.9.0.post0 six==1.16.0 - urllib3==1.26.14 + urllib3==1.26.19 wrapt==1.16.0 aiohttp==3.10.10 aiohappyeyeballs==2.4.3 From 7acb39bd3937cb24fa07ccff57f69bfbaddaff22 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 11:24:31 +0100 Subject: [PATCH 67/99] deps: bumped certifi==2024.7.4 for security fix --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6b547ddc..4c473830 100644 --- a/requirements.txt +++ b/requirements.txt @@ -66,7 +66,7 @@ channelstream==0.7.1 python-dateutil==2.9.0.post0 six==1.16.0 requests==2.28.2 - certifi==2022.12.7 + certifi==2024.7.4 charset-normalizer==3.1.0 idna==3.4 urllib3==1.26.19 @@ -175,7 +175,7 @@ premailer==3.10.0 cssutils==2.6.0 lxml==5.3.0 requests==2.28.2 - certifi==2022.12.7 + certifi==2024.7.4 charset-normalizer==3.1.0 idna==3.4 urllib3==1.26.19 From f1905e33ba8aee61477de81c7c7d0a94716c727b Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 11:24:44 +0100 Subject: [PATCH 68/99] deps: bumped tornado==6.4.1 for security fix --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4c473830..da766b9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -138,7 +138,7 @@ nbconvert==7.7.3 python-dateutil==2.9.0.post0 six==1.16.0 pyzmq==25.0.0 - tornado==6.2 + tornado==6.4.1 traitlets==5.14.3 jupyter_core==5.3.1 platformdirs==3.10.0 From e7c3598707bc0568a84c02ccee212a09352a268e Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 14:32:51 +0100 Subject: [PATCH 69/99] deps: bumped requests==2.32.3 --- requirements.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index da766b9f..7aadb482 100644 --- a/requirements.txt +++ b/requirements.txt @@ -65,11 +65,11 @@ channelstream==0.7.1 zope.deprecation==5.0.0 python-dateutil==2.9.0.post0 six==1.16.0 - requests==2.28.2 - certifi==2024.7.4 - charset-normalizer==3.1.0 - idna==3.4 - urllib3==1.26.19 + requests==2.32.3 + certifi==2024.8.30 + charset-normalizer==3.4.0 + idna==3.10 + urllib3==1.26.20 ws4py==0.5.1 deform==2.0.15 chameleon==3.10.2 @@ -174,11 +174,11 @@ premailer==3.10.0 cssselect==1.2.0 cssutils==2.6.0 lxml==5.3.0 - requests==2.28.2 - certifi==2024.7.4 - charset-normalizer==3.1.0 - idna==3.4 - urllib3==1.26.19 + requests==2.32.3 + certifi==2024.8.30 + charset-normalizer==3.4.0 + idna==3.10 + urllib3==1.26.20 psutil==5.9.8 psycopg2==2.9.10 py-bcrypt==0.4 From 1f985db0ecae9133caf0a9aaebd50eced82dca04 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 14:33:06 +0100 Subject: [PATCH 70/99] deps: bumped rhodecode-tools to 4.1.0 --- requirements_rc_tools.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_rc_tools.txt b/requirements_rc_tools.txt index fc3d7f6f..088bd984 100644 --- a/requirements_rc_tools.txt +++ b/requirements_rc_tools.txt @@ -1,3 +1,3 @@ ## rhodecode-tools, special case, use file://PATH.tar.gz#egg=rhodecode-tools==X.Y.Z, to test local version -rhodecode-tools @ https://code.rhodecode.com/_file_store/download/0-a47f315d-1289-41bf-af77-ea052f6a9f77.0.0.tar.gz#egg=rhodecode-tools -rhodecode-tools==4.0.0 +rhodecode-tools @ https://code.rhodecode.com/_file_store/download/0-51fb13c9-6175-480c-b9dc-630c7601d47c.1.0.tar.gz#egg=rhodecode-tools +rhodecode-tools==4.1.0 From af95b9ec4471fb920f8d00e4f8a5703fbb207800 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 14:33:19 +0100 Subject: [PATCH 71/99] deps: bumped idna==3.10 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7aadb482..4cf2fad5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -251,7 +251,7 @@ s3fs==2024.10.0 frozenlist==1.5.0 multidict==6.1.0 yarl==1.17.1 - idna==3.4 + idna==3.10 multidict==6.1.0 propcache==0.2.0 aioitertools==0.12.0 @@ -269,7 +269,7 @@ s3fs==2024.10.0 frozenlist==1.5.0 multidict==6.1.0 yarl==1.17.1 - idna==3.4 + idna==3.10 multidict==6.1.0 propcache==0.2.0 fsspec==2024.10.0 From e9d433e7cb09fb9c85820d1fbc14f8e35d719b59 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 14:33:37 +0100 Subject: [PATCH 72/99] deps: bumped debug deps --- requirements.txt | 2 +- requirements_debug.txt | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4cf2fad5..ff579804 100644 --- a/requirements.txt +++ b/requirements.txt @@ -259,7 +259,7 @@ s3fs==2024.10.0 jmespath==1.0.1 python-dateutil==2.9.0.post0 six==1.16.0 - urllib3==1.26.19 + urllib3==1.26.20 wrapt==1.16.0 aiohttp==3.10.10 aiohappyeyeballs==2.4.3 diff --git a/requirements_debug.txt b/requirements_debug.txt index a8d517a4..57f1d02d 100644 --- a/requirements_debug.txt +++ b/requirements_debug.txt @@ -15,10 +15,8 @@ pyramid-debugtoolbar flake8 ruff -pipdeptree==2.7.1 +pipdeptree invoke==2.0.0 -bumpversion==0.6.0 -bump2version==1.0.1 docutils-stubs types-redis From 638a00b50d04d368e591ceb903900550f5f6893d Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Fri, 15 Nov 2024 22:43:15 +0100 Subject: [PATCH 73/99] fix(config): ensure better defaults for celery queue --- rhodecode/config/config_maker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhodecode/config/config_maker.py b/rhodecode/config/config_maker.py index 3f71e878..1b1b8bf4 100644 --- a/rhodecode/config/config_maker.py +++ b/rhodecode/config/config_maker.py @@ -165,7 +165,7 @@ def sanitize_settings_and_apply_defaults(global_config, settings): ) # celery - broker_url = settings_maker.make_setting('celery.broker_url', 'redis://redis:6379/8') + broker_url = settings_maker.make_setting('celery.broker_url', 'redis://redis:6379/8', default_when_empty=True) settings_maker.make_setting('celery.result_backend', broker_url) settings_maker.make_setting('exception_tracker.send_email', False, parser='bool') From 5e2ead8185e62852fe884e2c132a215d4d0aca6a Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 18 Nov 2024 11:37:23 +0100 Subject: [PATCH 74/99] deps: bumped jinja2==3.1.4 for security issues fix --- docs/requirements_docs.txt | 2 +- requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/requirements_docs.txt b/docs/requirements_docs.txt index d20a433f..a50d22a1 100644 --- a/docs/requirements_docs.txt +++ b/docs/requirements_docs.txt @@ -8,4 +8,4 @@ pygments==2.18.0 docutils<0.19 markupsafe==2.1.3 -jinja2==3.1.2 +jinja2==3.1.4 diff --git a/requirements.txt b/requirements.txt index ff579804..1858074b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -48,7 +48,7 @@ channelstream==0.7.1 zope.deprecation==5.0.0 zope.interface==7.1.1 pyramid-jinja2==2.10 - jinja2==3.1.3 + jinja2==3.1.4 markupsafe==3.0.2 markupsafe==3.0.2 pyramid==2.0.2 @@ -122,7 +122,7 @@ nbconvert==7.7.3 six==1.16.0 webencodings==0.5.1 defusedxml==0.7.1 - jinja2==3.1.3 + jinja2==3.1.4 markupsafe==3.0.2 jupyter_core==5.3.1 platformdirs==3.10.0 From b8b4f82fc87da5e8619684867e063cf9808422f6 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 19 Nov 2024 09:31:17 +0100 Subject: [PATCH 75/99] deps: bumped aiohttp==3.11.4 for security fix --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1858074b..18624eac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -243,7 +243,7 @@ routes==2.5.1 six==1.16.0 s3fs==2024.10.0 aiobotocore==2.15.2 - aiohttp==3.10.10 + aiohttp==3.11.4 aiohappyeyeballs==2.4.3 aiosignal==1.3.1 frozenlist==1.5.0 @@ -261,7 +261,7 @@ s3fs==2024.10.0 six==1.16.0 urllib3==1.26.20 wrapt==1.16.0 - aiohttp==3.10.10 + aiohttp==3.11.4 aiohappyeyeballs==2.4.3 aiosignal==1.3.1 frozenlist==1.5.0 From 5a872cd17ba94fd646cb3570690b3620ddf16078 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 19 Nov 2024 09:34:02 +0100 Subject: [PATCH 76/99] deps: bumped attrs==24.2.0 --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 18624eac..6b1e179f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -146,7 +146,7 @@ nbconvert==7.7.3 nbformat==5.9.2 fastjsonschema==2.18.0 jsonschema==4.18.6 - attrs==22.2.0 + attrs==24.2.0 pyrsistent==0.19.3 jupyter_core==5.3.1 platformdirs==3.10.0 @@ -156,7 +156,7 @@ nbconvert==7.7.3 nbformat==5.9.2 fastjsonschema==2.18.0 jsonschema==4.18.6 - attrs==22.2.0 + attrs==24.2.0 pyrsistent==0.19.3 jupyter_core==5.3.1 platformdirs==3.10.0 @@ -247,7 +247,7 @@ s3fs==2024.10.0 aiohappyeyeballs==2.4.3 aiosignal==1.3.1 frozenlist==1.5.0 - attrs==22.2.0 + attrs==24.2.0 frozenlist==1.5.0 multidict==6.1.0 yarl==1.17.1 @@ -265,7 +265,7 @@ s3fs==2024.10.0 aiohappyeyeballs==2.4.3 aiosignal==1.3.1 frozenlist==1.5.0 - attrs==22.2.0 + attrs==24.2.0 frozenlist==1.5.0 multidict==6.1.0 yarl==1.17.1 From 7732089421baa57e54df5196368f1c075498ce86 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 19 Nov 2024 09:35:28 +0100 Subject: [PATCH 77/99] deps: bumped deps for s3fs --- requirements.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6b1e179f..c60beaf2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -250,7 +250,8 @@ s3fs==2024.10.0 attrs==24.2.0 frozenlist==1.5.0 multidict==6.1.0 - yarl==1.17.1 + propcache==0.2.0 + yarl==1.17.2 idna==3.10 multidict==6.1.0 propcache==0.2.0 @@ -268,7 +269,8 @@ s3fs==2024.10.0 attrs==24.2.0 frozenlist==1.5.0 multidict==6.1.0 - yarl==1.17.1 + propcache==0.2.0 + yarl==1.17.2 idna==3.10 multidict==6.1.0 propcache==0.2.0 From 5e2968e3774a73cb26d0ec6cac1e95690d27b358 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Tue, 19 Nov 2024 14:09:56 +0100 Subject: [PATCH 78/99] deps: bumped pycryptodome==3.21.0 for security issue --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c60beaf2..ecfdc84c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -185,7 +185,7 @@ py-bcrypt==0.4 pycmarkgfm==1.2.0 cffi==1.17.1 pycparser==2.22 -pycryptodome==3.17 +pycryptodome==3.21.0 pycurl==7.45.3 pymysql==1.1.1 pyotp==2.8.0 From 1149dc9093cdc365db8642ddd04b6b1f567c3142 Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Fri, 15 Nov 2024 12:14:52 +0200 Subject: [PATCH 79/99] feat(hide recaptcha secrets): masked sensitive information related to reCaptcha configuration. --- docs/release-notes/release-notes-5.4.0.rst | 1 + rhodecode/templates/admin/auth/plugin_settings.mako | 12 ++++++++++++ .../templates/admin/settings/settings_global.mako | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/release-notes-5.4.0.rst b/docs/release-notes/release-notes-5.4.0.rst index 379d2fb9..6d5634b2 100644 --- a/docs/release-notes/release-notes-5.4.0.rst +++ b/docs/release-notes/release-notes-5.4.0.rst @@ -32,6 +32,7 @@ Fixes ^^^^^ - Fixed UI visualization issues with long commit messages +- Fixed masking for some fields in admin settings Upgrade notes diff --git a/rhodecode/templates/admin/auth/plugin_settings.mako b/rhodecode/templates/admin/auth/plugin_settings.mako index 07cba2f0..de19936a 100644 --- a/rhodecode/templates/admin/auth/plugin_settings.mako +++ b/rhodecode/templates/admin/auth/plugin_settings.mako @@ -143,4 +143,16 @@ $(document).ready(function() { }); + diff --git a/rhodecode/templates/admin/settings/settings_global.mako b/rhodecode/templates/admin/settings/settings_global.mako index 92e2a557..348f5f11 100644 --- a/rhodecode/templates/admin/settings/settings_global.mako +++ b/rhodecode/templates/admin/settings/settings_global.mako @@ -68,7 +68,7 @@ ${h.secure_form(h.route_path('admin_settings_global_update'), request=request)}
- ${h.text('rhodecode_captcha_public_key',size=60)} + ${h.text('rhodecode_captcha_public_key',size=60, type="password")}
@@ -80,7 +80,7 @@ ${h.secure_form(h.route_path('admin_settings_global_update'), request=request)}
- ${h.text('rhodecode_captcha_private_key',size=60)} + ${h.text('rhodecode_captcha_private_key',size=60, type="password")}
From 3d4c26c911fee852a8cc9ed72aed39d787d5d809 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Thu, 21 Nov 2024 21:37:25 +0100 Subject: [PATCH 80/99] core(project): updated pyproject structure for formatting/linting --- pyproject.toml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 599ef4f8..bd7c559f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ 'Operating System :: OS Independent', 'Topic :: Software Development :: Version Control', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ] [project.entry-points."paste.app_factory"] @@ -65,7 +65,15 @@ dependencies = {file = ["requirements.txt"]} optional-dependencies.tests = {file = ["requirements_test.txt"]} [tool.ruff] -lint.select = [ +# Line length max same as Black. +line-length = 120 + +# Assume Python 3.11 +target-version = "py311" + + +[tool.ruff.lint] +select = [ # Pyflakes "F", # Pycodestyle @@ -74,17 +82,15 @@ lint.select = [ # isort "I001" ] -lint.ignore = [ + +ignore = [ "E501", # line too long, handled by black ] -# Same as Black. -line-length = 120 [tool.ruff.lint.isort] known-first-party = ["rhodecode"] [tool.ruff.format] - # Like Black, use double quotes for strings. quote-style = "double" From f0d42a9cb250f34bde7ab7cb5a314504bb699017 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Sun, 24 Nov 2024 20:30:59 +0100 Subject: [PATCH 81/99] deps: fixed deps for security fixes --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index ecfdc84c..43947508 100644 --- a/requirements.txt +++ b/requirements.txt @@ -137,8 +137,8 @@ nbconvert==7.7.3 traitlets==5.14.3 python-dateutil==2.9.0.post0 six==1.16.0 - pyzmq==25.0.0 - tornado==6.4.1 + pyzmq==26.2.0 + tornado==6.4.2 traitlets==5.14.3 jupyter_core==5.3.1 platformdirs==3.10.0 From a780ffc2fa7933bc72d1e3650087c8c6df842cf8 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 27 Nov 2024 12:55:54 +0100 Subject: [PATCH 82/99] fix(docs): fixed cli usage docs --- docs/tools/tools-cli.rst | 102 ++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/docs/tools/tools-cli.rst b/docs/tools/tools-cli.rst index 87fbedde..3d5eac1b 100644 --- a/docs/tools/tools-cli.rst +++ b/docs/tools/tools-cli.rst @@ -12,6 +12,26 @@ The commands available with |RCT| can be split into three categories: - Local configuration commands used to help set up your |RCT| configuration. +.. _tools-rhodecode-list-instance: + +rhodecode-list-instances +------------------------ + +Use this command to list the instance details configured in the +:file:`/etc/rhodecode/conf/.rhoderc` file. + +.. code-block:: bash + + $ ./rcstack cli cmd rhodecode-list-instances --config=/etc/rhodecode/conf/.rhoderc + [instance:production] - Config only + API-HOST: https://some.url.com + API-KEY: some.auth.token + + [instance:development] - Config only + API-HOST: http://some.ip.address + API-KEY: some.auth.token + + rhodecode-tools --------------- @@ -163,6 +183,7 @@ Example usage: removing gist /home/brian/repos/.rc_gist_store/5 removing gist /home/brian/repos/.rc_gist_store/8FtCKdcbRKmEvRzTVsEt + rhodecode-cleanup-repos ----------------------- @@ -201,30 +222,37 @@ Example usage: .. code-block:: bash - # Cleaning up repos using tools installed with RCE 350 and above - $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-cleanup-repos \ - --instance-name=enterprise-4 --older-than=1d - Scanning for repositories in /home/brian/repos... - preparing to remove [2] found repositories older than 1 day, 0:00:00 (1d) + + # create a .rhoderc file in your host config directory (in :file:`config/_shared/.rhoderc`): + + [instance:rcstack-instance] + api_host = http://rhodecode:10020 + api_key = + repo_dir = /var/opt/rhodecode_repo_store + + # Run rcstack cli + ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc + + checking if config files needs bootstrapping + Scanning for repositories in /var/opt/rhodecode_repo_store... the following repositories will be deleted completely: - * REMOVED: 2015-08-05 00:23:18 | /home/brian/repos/rm__20150805_002318_831 - * REMOVED: 2015-08-04 01:22:10 | /home/brian/repos/rm__20150804_012210_336 + * REMOVED: 2015-08-05 00:23:18 | /var/opt/rhodecode_repo_store/rm__20150805_002318_831 + * REMOVED: 2015-08-04 01:22:10 | /var/opt/rhodecode_repo_store/rm__20150804_012210_336 are you sure you want to remove them? [y/N]: # Clean up repos older than 1 year - # If using virtualenv and pre RCE 350 tools installation - (venv)$ rhodecode-cleanup-repos --instance-name=enterprise-1 \ - --older-than=365d + ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --older-than=365d - Scanning for repositories in /home/brian/repos... + checking if config files needs bootstrapping + Scanning for repositories in /var/opt/rhodecode_repo_store... preparing to remove [343] found repositories older than 365 days # clean up repos older than 3 days - # If using virtualenv and pre RCE 350 tools installation - (venv)$ rhodecode-cleanup-repos --instance-name=enterprise-1 \ - --older-than=3d - Scanning for repositories in /home/brian/repos... + ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --older-than=3d + + checking if config files needs bootstrapping + Scanning for repositories in /var/opt/rhodecode_repo_store... preparing to remove [3] found repositories older than 3 days .. _tools-config: @@ -420,9 +448,8 @@ Example usage: } # Cat a file and pipe to gist - # in RCE 3.5.0 tools and above - $ cat ~/.rhoderc | ~/.rccontrol/{instance-id}/profile/bin/rhodecode-gist \ - --instance-name=enterprise-4 -d '.rhoderc copy' create + + $ cat ~/.rhoderc | ./rcstack cli cmd rhodecode-gist --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc -d '.rhoderc copy' create { "error": null, "id": 9253, @@ -512,41 +539,18 @@ Example usage: .. code-block:: bash - # Run the indexer - $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-index \ - --instance-name=enterprise-4 + # Create the indexing mapping file + $ ./rcstack cli cmd rhodecode-index --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --create-mapping search_mapping.ini - # Run indexer based on search_mapping.ini file - # This is using pre-350 virtualenv - (venv)$ rhodecode-index --instance-name=enterprise-1 + # Run the indexer + $ ./rcstack cli cmd rhodecode-index --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc + + # Run indexer based on search_mapping.ini file using rhodecode-tools virtualenv + (venv)$ rhodecode-index --instance-name=rcstack-instance # Index from the command line without creating # the .rhoderc file - $ rhodecode-index --apikey=key --apihost=http://rhodecode.server \ - --instance-name=enterprise-2 --save-config - - # Create the indexing mapping file - $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-index \ - --create-mapping search_mapping.ini --instance-name=enterprise-4 - -.. _tools-rhodecode-list-instance: - -rhodecode-list-instances ------------------------- - -Use this command to list the instance details configured in the -:file:`~/.rhoderc` file. - -.. code-block:: bash - - $ .rccontrol/enterprise-1/profile/bin/rhodecode-list-instances - [instance:production] - Config only - API-HOST: https://some.url.com - API-KEY: some.auth.token - - [instance:development] - Config only - API-HOST: http://some.ip.address - API-KEY: some.auth.token + $ rhodecode-index --apikey=key --apihost=http://rhodecode.server --instance-name=rcstack-instance --save-config .. _tools-setup-config: From 31ff8f7384c47aebe5254bec09a4616f1bca38e2 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 27 Nov 2024 13:10:42 +0100 Subject: [PATCH 83/99] fix(db-calls): fixed issues with caches calculations when passed search fields were empty --- rhodecode/model/db.py | 58 ++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py index 12633c41..d8cdbda7 100644 --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -35,7 +35,7 @@ import collections import pyotp from sqlalchemy import ( - or_, and_, not_, func, cast, TypeDecorator, event, select, + or_, and_, not_, func, cast, TypeDecorator, event, select, delete, true, false, null, union_all, Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column, Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary, @@ -273,6 +273,22 @@ class BaseModel(object): stmt = select(cls) return stmt + @classmethod + def delete(cls, custom_cls=None): + """ + stmt = cls.delete().where(cls.user_id==1) + # optionally + stmt = cls.delete(User).where(cls.user_id==1) + result = cls.execute(stmt) + """ + + if custom_cls: + stmt = delete(custom_cls) + else: + stmt = delete(cls) + return stmt + + @classmethod def execute(cls, stmt): return Session().execute(stmt) @@ -1075,28 +1091,26 @@ class User(Base, BaseModel): @classmethod def get(cls, user_id, cache=False): if not user_id: - return + return None q = cls.select().where(cls.user_id == user_id) if cache: - q = q.options( - FromCache("sql_cache_short", f"get_users_{user_id}")) + q = q.options(FromCache("sql_cache_short", f"get_users_{user_id}")) return cls.execute(q).scalar_one_or_none() @classmethod - def get_by_username(cls, username, case_insensitive=False, - cache=False): + def get_by_username(cls, username, case_insensitive=False, cache=False): + if not username: + return None if case_insensitive: - q = cls.select().where( - func.lower(cls.username) == func.lower(username)) + q = cls.select().where(func.lower(cls.username) == func.lower(username)) else: q = cls.select().where(cls.username == username) if cache: hash_key = _hash_key(username) - q = q.options( - FromCache("sql_cache_short", f"get_user_by_name_{hash_key}")) + q = q.options(FromCache("sql_cache_short", f"get_user_by_name_{hash_key}")) return cls.execute(q).scalar_one_or_none() @@ -1125,6 +1139,8 @@ class User(Base, BaseModel): @classmethod def get_by_email(cls, email, case_insensitive=False, cache=False): + if not email: + return None if case_insensitive: q = cls.select().where(func.lower(cls.email) == func.lower(email)) @@ -1133,8 +1149,7 @@ class User(Base, BaseModel): if cache: email_key = _hash_key(email) - q = q.options( - FromCache("sql_cache_short", f"get_email_key_{email_key}")) + q = q.options(FromCache("sql_cache_short", f"get_email_key_{email_key}")) ret = cls.execute(q).scalar_one_or_none() @@ -1147,8 +1162,8 @@ class User(Base, BaseModel): q = q.where(UserEmailMap.email == email) q = q.options(joinedload(UserEmailMap.user)) if cache: - q = q.options( - FromCache("sql_cache_short", f"get_email_map_key_{email_key}")) + email_key = _hash_key(email) + q = q.options(FromCache("sql_cache_short", f"get_email_map_key_{email_key}")) result = cls.execute(q).scalar_one_or_none() ret = getattr(result, 'user', None) @@ -1642,11 +1657,12 @@ class UserGroup(Base, BaseModel): return f"<{self.cls_name}('id:{self.users_group_id}:{self.users_group_name}')>" @classmethod - def get_by_group_name(cls, group_name, cache=False, - case_insensitive=False): + def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): + if not group_name: + return None + if case_insensitive: - q = cls.query().filter(func.lower(cls.users_group_name) == - func.lower(group_name)) + q = cls.query().filter(func.lower(cls.users_group_name) == func.lower(group_name)) else: q = cls.query().filter(cls.users_group_name == group_name) @@ -2968,9 +2984,11 @@ class RepoGroup(Base, BaseModel): @classmethod def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): + if not group_name: + return None + if case_insensitive: - gr = cls.query().filter(func.lower(cls.group_name) - == func.lower(group_name)) + gr = cls.query().filter(func.lower(cls.group_name) == func.lower(group_name)) else: gr = cls.query().filter(cls.group_name == group_name) if cache: From f687bfc994a8cb37d2e3b0a4b52bb2419c610ec6 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 27 Nov 2024 13:14:40 +0100 Subject: [PATCH 84/99] ui: changed calls for edit caches to use repo id instead of a name for safer calls to repos which paths have special characters --- rhodecode/templates/admin/repos/repo_edit_caches.mako | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhodecode/templates/admin/repos/repo_edit_caches.mako b/rhodecode/templates/admin/repos/repo_edit_caches.mako index 0cff90e3..a9c36be0 100644 --- a/rhodecode/templates/admin/repos/repo_edit_caches.mako +++ b/rhodecode/templates/admin/repos/repo_edit_caches.mako @@ -10,7 +10,7 @@ ${_('Cache purge can be automated by such api call. Can be called periodically in crontab etc.')}
- ${h.api_call_example(method='invalidate_cache', args={"repoid": c.rhodecode_db_repo.repo_name})} + ${h.api_call_example(method='invalidate_cache', args={"repoid": c.rhodecode_db_repo.repo_id})}

From d08633d2208a4dd1ec00c3e6bc3b8c6670126e06 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 27 Nov 2024 19:35:02 +0100 Subject: [PATCH 85/99] fix(encoding for file): fixed support of non utf-8 files in all backends --- rhodecode/__init__.py | 19 +- rhodecode/api/tests/test_get_repo_comments.py | 3 +- rhodecode/api/views/repo_api.py | 6 +- .../apps/repository/views/repo_changelog.py | 14 +- .../apps/repository/views/repo_commits.py | 6 +- rhodecode/apps/repository/views/repo_files.py | 144 +- rhodecode/config/utils.py | 1 - rhodecode/lib/codeblocks.py | 10 +- rhodecode/lib/diffs.py | 13 +- rhodecode/lib/hooks_base.py | 1 - rhodecode/lib/str_utils.py | 9 +- rhodecode/lib/vcs/__init__.py | 2 +- rhodecode/lib/vcs/backends/base.py | 1633 ++++++++--------- rhodecode/lib/vcs/backends/git/commit.py | 295 ++- rhodecode/lib/vcs/backends/git/repository.py | 2 +- rhodecode/lib/vcs/backends/hg/commit.py | 228 ++- rhodecode/lib/vcs/backends/hg/repository.py | 5 +- rhodecode/lib/vcs/backends/svn/commit.py | 182 +- rhodecode/lib/vcs/backends/svn/repository.py | 17 +- rhodecode/lib/vcs/client_http.py | 5 +- rhodecode/lib/vcs/compat.py | 2 +- rhodecode/lib/vcs/conf/settings.py | 3 - rhodecode/lib/vcs/exceptions.py | 4 - rhodecode/lib/vcs/nodes.py | 386 ++-- rhodecode/lib/vcs_common.py | 46 + rhodecode/model/gist.py | 2 +- rhodecode/model/pull_request.py | 10 +- rhodecode/model/repo.py | 61 +- rhodecode/model/scm.py | 11 +- rhodecode/tests/config/test_routing_links.py | 2 +- rhodecode/tests/models/test_repos.py | 4 +- rhodecode/tests/vcs/test_archives.py | 16 +- rhodecode/tests/vcs/test_commits.py | 54 +- .../vcs/test_filenodes_listing_and_caches.py | 227 +++ rhodecode/tests/vcs/test_git.py | 179 +- rhodecode/tests/vcs/test_hg.py | 311 ++-- rhodecode/tests/vcs/test_inmemory.py | 14 +- rhodecode/tests/vcs/test_nodes.py | 26 +- rhodecode/tests/vcs/test_svn.py | 10 +- rhodecode/tests/vcs/test_tags.py | 4 +- .../test_vcs_operations_bad_client.py | 4 +- .../vcs_operations/test_vcs_operations_svn.py | 3 - 42 files changed, 2001 insertions(+), 1973 deletions(-) create mode 100644 rhodecode/lib/vcs_common.py create mode 100644 rhodecode/tests/vcs/test_filenodes_listing_and_caches.py diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py index 4a06569f..732b082b 100644 --- a/rhodecode/__init__.py +++ b/rhodecode/__init__.py @@ -45,13 +45,20 @@ CELERY_EAGER = False # link to config for pyramid CONFIG = {} +class NotGivenMeta: + + def __repr__(self): + return 'NotGivenObject()' + __str__ = __repr__ + +NotGiven = NotGivenMeta() class ConfigGet: - NotGiven = object() - def _get_val_or_missing(self, key, missing): + @classmethod + def _get_val_or_missing(cls, key, missing): if key not in CONFIG: - if missing == self.NotGiven: + if missing != NotGiven: return missing # we don't get key, we don't get missing value, return nothing similar as config.get(key) return None @@ -74,6 +81,12 @@ class ConfigGet: val = self._get_val_or_missing(key, missing) return str2bool(val) + def get_list(self, key, missing=NotGiven): + from rhodecode.lib.type_utils import aslist + val = self._get_val_or_missing(key, missing) + return aslist(val, sep=',') + + # Populated with the settings dictionary from application init in # rhodecode.conf.environment.load_pyramid_environment PYRAMID_SETTINGS = {} diff --git a/rhodecode/api/tests/test_get_repo_comments.py b/rhodecode/api/tests/test_get_repo_comments.py index a5d5d44e..09375f4c 100644 --- a/rhodecode/api/tests/test_get_repo_comments.py +++ b/rhodecode/api/tests/test_get_repo_comments.py @@ -19,6 +19,7 @@ import pytest +from rhodecode.lib.str_utils import safe_str from rhodecode.model.db import User, ChangesetComment from rhodecode.model.meta import Session from rhodecode.model.comment import CommentsModel @@ -36,7 +37,7 @@ def make_repo_comments_factory(request): commit = repo.scm_instance()[0] commit_id = commit.raw_id - file_0 = commit.affected_files[0] + file_0 = safe_str(commit.affected_files[0]) comments = [] # general diff --git a/rhodecode/api/views/repo_api.py b/rhodecode/api/views/repo_api.py index f4fac27e..60ee6b64 100644 --- a/rhodecode/api/views/repo_api.py +++ b/rhodecode/api/views/repo_api.py @@ -317,8 +317,7 @@ def get_repo_changeset(request, apiuser, repoid, revision, ','.join(_changes_details_types))) vcs_repo = repo.scm_instance() - pre_load = ['author', 'branch', 'date', 'message', 'parents', - 'status', '_commit', '_file_paths'] + pre_load = ['author', 'branch', 'date', 'message', 'parents', 'status', '_commit'] try: commit = repo.get_commit(commit_id=revision, pre_load=pre_load) @@ -376,8 +375,7 @@ def get_repo_changesets(request, apiuser, repoid, start_rev, limit, ','.join(_changes_details_types))) limit = int(limit) - pre_load = ['author', 'branch', 'date', 'message', 'parents', - 'status', '_commit', '_file_paths'] + pre_load = ['author', 'branch', 'date', 'message', 'parents', 'status', '_commit'] vcs_repo = repo.scm_instance() # SVN needs a special case to distinguish its index and commit id diff --git a/rhodecode/apps/repository/views/repo_changelog.py b/rhodecode/apps/repository/views/repo_changelog.py index 3c82f5ad..d5878a25 100644 --- a/rhodecode/apps/repository/views/repo_changelog.py +++ b/rhodecode/apps/repository/views/repo_changelog.py @@ -33,7 +33,7 @@ from rhodecode.lib.auth import ( from rhodecode.lib.graphmod import _colored, _dagwalker from rhodecode.lib.helpers import RepoPage from rhodecode.lib.utils2 import str2bool -from rhodecode.lib.str_utils import safe_int, safe_str +from rhodecode.lib.str_utils import safe_int, safe_str, safe_bytes from rhodecode.lib.vcs.exceptions import ( RepositoryError, CommitDoesNotExistError, CommitError, NodeDoesNotExistError, EmptyRepositoryError) @@ -204,10 +204,9 @@ class RepoChangelogView(RepoAppView): log.debug('generating changelog for path %s', f_path) # get the history for the file ! base_commit = self.rhodecode_vcs_repo.get_commit(commit_id) - + bytes_path = safe_bytes(f_path) try: - collection = base_commit.get_path_history( - f_path, limit=hist_limit, pre_load=pre_load) + collection = base_commit.get_path_history(bytes_path, limit=hist_limit, pre_load=pre_load) if collection and partial_xhr: # for ajax call we remove first one since we're looking # at it right now in the context of a file commit @@ -216,7 +215,7 @@ class RepoChangelogView(RepoAppView): # this node is not present at tip! try: commit = self._get_commit_or_redirect(commit_id) - collection = commit.get_path_history(f_path) + collection = commit.get_path_history(bytes_path) except RepositoryError as e: h.flash(safe_str(e), category='warning') redirect_url = h.route_path( @@ -310,9 +309,8 @@ class RepoChangelogView(RepoAppView): log.exception(safe_str(e)) raise HTTPFound( h.route_path('repo_commits', repo_name=self.db_repo_name)) - - collection = base_commit.get_path_history( - f_path, limit=hist_limit, pre_load=pre_load) + bytes_path = safe_bytes(f_path) + collection = base_commit.get_path_history(bytes_path, limit=hist_limit, pre_load=pre_load) collection = list(reversed(collection)) else: collection = self.rhodecode_vcs_repo.get_commits( diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py index aeb2e082..76acd50f 100644 --- a/rhodecode/apps/repository/views/repo_commits.py +++ b/rhodecode/apps/repository/views/repo_commits.py @@ -89,8 +89,7 @@ class RepoCommitsView(RepoAppView): commit_range = commit_id_range.split('...')[:2] try: - pre_load = ['affected_files', 'author', 'branch', 'date', - 'message', 'parents'] + pre_load = ['author', 'branch', 'date', 'message', 'parents'] if self.rhodecode_vcs_repo.alias == 'hg': pre_load += ['hidden', 'obsolete', 'phase'] @@ -100,8 +99,7 @@ class RepoCommitsView(RepoAppView): pre_load=pre_load, translate_tags=False) commits = list(commits) else: - commits = [self.rhodecode_vcs_repo.get_commit( - commit_id=commit_id_range, pre_load=pre_load)] + commits = [self.rhodecode_vcs_repo.get_commit(commit_id=commit_id_range, pre_load=pre_load)] c.commit_ranges = commits if not c.commit_ranges: diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py index ab46c1b5..e1ba3261 100644 --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -187,12 +187,14 @@ class RepoFilesView(RepoAppView): default_commit_id = self.db_repo.landing_ref_name default_f_path = '/' - commit_id = self.request.matchdict.get( - 'commit_id', default_commit_id) + commit_id = self.request.matchdict.get('commit_id', default_commit_id) f_path = self._get_f_path(self.request.matchdict, default_f_path) - return commit_id, f_path - def _get_default_encoding(self, c): + bytes_path = safe_bytes(f_path) + return commit_id, f_path, bytes_path + + @classmethod + def _get_default_encoding(cls, c): enc_list = getattr(c, 'default_encodings', []) return enc_list[0] if enc_list else 'UTF-8' @@ -361,21 +363,21 @@ class RepoFilesView(RepoAppView): from rhodecode import CONFIG _ = self.request.translate self.load_default_context() - default_at_path = '/' - fname = self.request.matchdict['fname'] + subrepos = self.request.GET.get('subrepos') == 'true' with_hash = str2bool(self.request.GET.get('with_hash', '1')) + + default_at_path = '/' + fname = self.request.matchdict['fname'] at_path = self.request.GET.get('at_path') or default_at_path if not self.db_repo.enable_downloads: return Response(_('Downloads disabled')) try: - commit_id, ext, fileformat, content_type = \ - _get_archive_spec(fname) + commit_id, ext, file_format, content_type = _get_archive_spec(fname) except ValueError: - return Response(_('Unknown archive type for: `{}`').format( - h.escape(fname))) + return Response(_('Unknown archive type for: `{}`').format(h.escape(fname))) try: commit = self.rhodecode_vcs_repo.get_commit(commit_id) @@ -391,7 +393,7 @@ class RepoFilesView(RepoAppView): raise HTTPFound(self.request.current_route_path(fname=fname)) try: - at_path = commit.get_node(at_path).path or default_at_path + at_path = commit.get_node(safe_bytes(at_path)).path or default_at_path except Exception: return Response(_('No node at path {} for this repository').format(h.escape(at_path))) @@ -440,7 +442,7 @@ class RepoFilesView(RepoAppView): with d_cache.get_lock(reentrant_lock_key): try: commit.archive_repo(archive_name_key, archive_dir_name=archive_dir_name, - kind=fileformat, subrepos=subrepos, + kind=file_format, subrepos=subrepos, archive_at_path=at_path, cache_config=d_cache_conf) except ImproperArchiveTypeError: return _('Unknown archive type') @@ -484,7 +486,7 @@ class RepoFilesView(RepoAppView): if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]: commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id) try: - node = commit.get_node(f_path) + node = commit.get_node(safe_bytes(f_path)) if node.is_dir(): raise NodeError(f'{node} path is a {type(node)} not a file') except NodeDoesNotExistError: @@ -648,7 +650,7 @@ class RepoFilesView(RepoAppView): # overwrite auto rendering by setting this GET flag c.renderer = view_name == 'repo_files:rendered' or not self.request.GET.get('no-render', False) - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() c.commit = self._get_commit_or_redirect(commit_id) c.branch = self.request.GET.get('branch', None) @@ -657,7 +659,8 @@ class RepoFilesView(RepoAppView): # files or dirs try: - c.file = c.commit.get_node(f_path, pre_load=['is_binary', 'size', 'data']) + + c.file = c.commit.get_node(bytes_path, pre_load=['is_binary', 'size', 'data']) c.file_author = True c.file_tree = '' @@ -666,11 +669,9 @@ class RepoFilesView(RepoAppView): try: prev_commit = c.commit.prev(c.branch) c.prev_commit = prev_commit - c.url_prev = h.route_path( - 'repo_files', repo_name=self.db_repo_name, - commit_id=prev_commit.raw_id, f_path=f_path) + c.url_prev = h.route_path('repo_files', repo_name=self.db_repo_name, commit_id=prev_commit.raw_id, f_path=f_path) if c.branch: - c.url_prev += '?branch=%s' % c.branch + c.url_prev += f'?branch={c.branch}' except (CommitDoesNotExistError, VCSError): c.url_prev = '#' c.prev_commit = EmptyCommit() @@ -679,11 +680,9 @@ class RepoFilesView(RepoAppView): try: next_commit = c.commit.next(c.branch) c.next_commit = next_commit - c.url_next = h.route_path( - 'repo_files', repo_name=self.db_repo_name, - commit_id=next_commit.raw_id, f_path=f_path) + c.url_next = h.route_path('repo_files', repo_name=self.db_repo_name, commit_id=next_commit.raw_id, f_path=f_path) if c.branch: - c.url_next += '?branch=%s' % c.branch + c.url_next += f'?branch={c.branch}' except (CommitDoesNotExistError, VCSError): c.url_next = '#' c.next_commit = EmptyCommit() @@ -739,8 +738,7 @@ class RepoFilesView(RepoAppView): c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev) c.readme_data, c.readme_file = \ - self._get_readme_data(self.db_repo, c.visual.default_renderer, - c.commit.raw_id, f_path) + self._get_readme_data(self.db_repo, c.visual.default_renderer, c.commit.raw_id, bytes_path) except RepositoryError as e: h.flash(h.escape(safe_str(e)), category='error') @@ -759,24 +757,24 @@ class RepoFilesView(RepoAppView): def repo_files_annotated_previous(self): self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, bytes_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) prev_commit_id = commit.raw_id line_anchor = self.request.GET.get('line_anchor') is_file = False try: - _file = commit.get_node(f_path) + _file = commit.get_node(bytes_path) is_file = _file.is_file() except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError): pass if is_file: - history = commit.get_path_history(f_path) + history = commit.get_path_history(bytes_path) prev_commit_id = history[1].raw_id \ if len(history) > 1 else prev_commit_id prev_url = h.route_path( 'repo_files:annotated', repo_name=self.db_repo_name, - commit_id=prev_commit_id, f_path=f_path, + commit_id=prev_commit_id, f_path=bytes_path, _anchor=f'L{line_anchor}') raise HTTPFound(prev_url) @@ -792,10 +790,10 @@ class RepoFilesView(RepoAppView): """ c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) try: - dir_node = commit.get_node(f_path) + dir_node = commit.get_node(bytes_path) except RepositoryError as e: return Response(f'error: {h.escape(safe_str(e))}') @@ -816,9 +814,9 @@ class RepoFilesView(RepoAppView): safe_path = f_name.replace('"', '\\"') encoded_path = urllib.parse.quote(f_name) - headers = "attachment; " \ - "filename=\"{}\"; " \ - "filename*=UTF-8\'\'{}".format(safe_path, encoded_path) + headers = f"attachment; " \ + f"filename=\"{safe_path}\"; " \ + f"filename*=UTF-8\'\'{encoded_path}" return header_safe_str(headers) @@ -832,9 +830,9 @@ class RepoFilesView(RepoAppView): """ c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) - file_node = self._get_filenode_or_redirect(commit, f_path) + file_node = self._get_filenode_or_redirect(commit, bytes_path) raw_mimetype_mapping = { # map original mimetype to a mimetype used for "show as raw" @@ -892,9 +890,9 @@ class RepoFilesView(RepoAppView): def repo_file_download(self): c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) - file_node = self._get_filenode_or_redirect(commit, f_path) + file_node = self._get_filenode_or_redirect(commit, bytes_path) if self.request.GET.get('lf'): # only if lf get flag is passed, we download this file @@ -920,8 +918,7 @@ class RepoFilesView(RepoAppView): def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path): - cache_seconds = safe_int( - rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time')) + cache_seconds = rhodecode.ConfigGet().get_int('rc_cache.cache_repo.expiration_time') cache_on = cache_seconds > 0 log.debug( 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`' @@ -933,21 +930,17 @@ class RepoFilesView(RepoAppView): @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on) def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path): - log.debug('Generating cached nodelist for repo_id:%s, %s, %s', - _repo_id, commit_id, f_path) + log.debug('Generating cached nodelist for repo_id:%s, %s, %s', _repo_id, commit_id, f_path) try: _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path) except (RepositoryError, CommitDoesNotExistError, Exception) as e: log.exception(safe_str(e)) h.flash(h.escape(safe_str(e)), category='error') - raise HTTPFound(h.route_path( - 'repo_files', repo_name=self.db_repo_name, - commit_id='tip', f_path='/')) + raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name, commit_id='tip', f_path='/')) return _d + _f - result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id, - commit_id, f_path) + result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path) return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result) @LoginRequired() @@ -956,7 +949,7 @@ class RepoFilesView(RepoAppView): def repo_nodelist(self): self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) metadata = self._get_nodelist_at_commit( @@ -996,10 +989,10 @@ class RepoFilesView(RepoAppView): if commits is None: pre_load = ["author", "branch"] try: - commits = tip.get_path_history(f_path, pre_load=pre_load) + commits = tip.get_path_history(safe_bytes(f_path), pre_load=pre_load) except (NodeDoesNotExistError, CommitError): # this node is not present at tip! - commits = commit_obj.get_path_history(f_path, pre_load=pre_load) + commits = commit_obj.get_path_history(safe_bytes(f_path), pre_load=pre_load) history = [] commits_group = ([], _("Changesets")) @@ -1040,9 +1033,9 @@ class RepoFilesView(RepoAppView): def repo_file_history(self): self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) - file_node = self._get_filenode_or_redirect(commit, f_path) + file_node = self._get_filenode_or_redirect(commit, bytes_path) if file_node.is_file(): file_history, _hist = self._get_node_history(commit, f_path) @@ -1083,9 +1076,9 @@ class RepoFilesView(RepoAppView): def repo_file_authors(self): c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) - file_node = self._get_filenode_or_redirect(commit, f_path) + file_node = self._get_filenode_or_redirect(commit, bytes_path) if not file_node.is_file(): raise HTTPBadRequest() @@ -1124,10 +1117,9 @@ class RepoFilesView(RepoAppView): def repo_files_check_head(self): self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + self._is_valid_head(commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name) new_path = self.request.POST.get('path') operation = self.request.POST.get('operation') @@ -1138,12 +1130,10 @@ class RepoFilesView(RepoAppView): try: commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id) # NOTE(dan): construct whole path without leading / - file_node = commit_obj.get_node(new_f_path) - if file_node is not None: + file_node = commit_obj.get_node(safe_bytes(new_f_path)) + if file_node: path_exist = new_f_path - except EmptyRepositoryError: - pass - except Exception: + except (EmptyRepositoryError, NodeDoesNotExistError): pass return { @@ -1158,7 +1148,7 @@ class RepoFilesView(RepoAppView): def repo_files_remove_file(self): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() _branch_name, _sha_commit_id, is_head = \ @@ -1169,7 +1159,7 @@ class RepoFilesView(RepoAppView): self.check_branch_permission(_branch_name) c.commit = self._get_commit_or_redirect(commit_id) - c.file = self._get_filenode_or_redirect(c.commit, f_path) + c.file = self._get_filenode_or_redirect(c.commit, bytes_path) c.default_message = _( 'Deleted file {} via RhodeCode Enterprise').format(f_path) @@ -1184,7 +1174,7 @@ class RepoFilesView(RepoAppView): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() _branch_name, _sha_commit_id, is_head = \ @@ -1195,10 +1185,9 @@ class RepoFilesView(RepoAppView): self.check_branch_permission(_branch_name) c.commit = self._get_commit_or_redirect(commit_id) - c.file = self._get_filenode_or_redirect(c.commit, f_path) + c.file = self._get_filenode_or_redirect(c.commit, bytes_path) - c.default_message = _( - 'Deleted file {} via RhodeCode Enterprise').format(f_path) + c.default_message = _('Deleted file {} via RhodeCode Enterprise').format(f_path) c.f_path = f_path node_path = f_path author = self._rhodecode_db_user.full_contact @@ -1232,7 +1221,7 @@ class RepoFilesView(RepoAppView): def repo_files_edit_file(self): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() _branch_name, _sha_commit_id, is_head = \ @@ -1243,7 +1232,7 @@ class RepoFilesView(RepoAppView): self.check_branch_permission(_branch_name, commit_id=commit_id) c.commit = self._get_commit_or_redirect(commit_id) - c.file = self._get_filenode_or_redirect(c.commit, f_path) + c.file = self._get_filenode_or_redirect(c.commit, bytes_path) if c.file.is_binary: files_url = h.route_path( @@ -1263,12 +1252,12 @@ class RepoFilesView(RepoAppView): def repo_files_update_file(self): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() c.commit = self._get_commit_or_redirect(commit_id) - c.file = self._get_filenode_or_redirect(c.commit, f_path) + c.file = self._get_filenode_or_redirect(c.commit, bytes_path) if c.file.is_binary: raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name, @@ -1345,7 +1334,7 @@ class RepoFilesView(RepoAppView): def repo_files_add_file(self): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() @@ -1381,7 +1370,7 @@ class RepoFilesView(RepoAppView): def repo_files_create_file(self): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() @@ -1450,8 +1439,7 @@ class RepoFilesView(RepoAppView): author=author, ) - h.flash(_('Successfully committed new file `{}`').format( - h.escape(node_path)), category='success') + h.flash(_('Successfully committed new file `{}`').format(h.escape(node_path)), category='success') default_redirect_url = h.route_path( 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id) @@ -1475,7 +1463,7 @@ class RepoFilesView(RepoAppView): def repo_files_upload_file(self): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() @@ -1604,7 +1592,7 @@ class RepoFilesView(RepoAppView): def repo_files_replace_file(self): _ = self.request.translate c = self.load_default_context() - commit_id, f_path = self._get_commit_and_path() + commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() diff --git a/rhodecode/config/utils.py b/rhodecode/config/utils.py index 82bf62b2..c2d1fbf8 100644 --- a/rhodecode/config/utils.py +++ b/rhodecode/config/utils.py @@ -39,7 +39,6 @@ def configure_vcs(config): conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol.v2'] conf.settings.HOOKS_HOST = config['vcs.hooks.host'] - conf.settings.DEFAULT_ENCODINGS = config['default_encoding'] conf.settings.ALIASES[:] = config['vcs.backends'] conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version'] diff --git a/rhodecode/lib/codeblocks.py b/rhodecode/lib/codeblocks.py index f0aaf0a8..0a475ae2 100644 --- a/rhodecode/lib/codeblocks.py +++ b/rhodecode/lib/codeblocks.py @@ -478,7 +478,9 @@ class DiffSet(object): log.debug('rendering diff for %r', patch['filename']) source_filename = patch['original_filename'] + source_filename_bytes = patch['original_filename_bytes'] target_filename = patch['filename'] + target_filename_bytes = patch['filename_bytes'] source_lexer = plain_text_lexer target_lexer = plain_text_lexer @@ -491,12 +493,12 @@ class DiffSet(object): if (source_filename and patch['operation'] in ('D', 'M') and source_filename not in self.source_nodes): self.source_nodes[source_filename] = ( - self.source_node_getter(source_filename)) + self.source_node_getter(source_filename_bytes)) if (target_filename and patch['operation'] in ('A', 'M') and target_filename not in self.target_nodes): self.target_nodes[target_filename] = ( - self.target_node_getter(target_filename)) + self.target_node_getter(target_filename_bytes)) elif hl_mode == self.HL_FAST: source_lexer = self._get_lexer_for_filename(source_filename) @@ -558,6 +560,7 @@ class DiffSet(object): }) file_chunks = patch['chunks'][1:] + for i, hunk in enumerate(file_chunks, 1): hunkbit = self.parse_hunk(hunk, source_file, target_file) hunkbit.source_file_path = source_file_path @@ -593,12 +596,13 @@ class DiffSet(object): return filediff def parse_hunk(self, hunk, source_file, target_file): + result = AttributeDict(dict( source_start=hunk['source_start'], source_length=hunk['source_length'], target_start=hunk['target_start'], target_length=hunk['target_length'], - section_header=hunk['section_header'], + section_header=safe_str(hunk['section_header']), lines=[], )) before, after = [], [] diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py index f721cb05..ff310961 100644 --- a/rhodecode/lib/diffs.py +++ b/rhodecode/lib/diffs.py @@ -455,7 +455,9 @@ class DiffProcessor(object): return arg for chunk in self._diff.chunks(): + bytes_head = chunk.header head = chunk.header_as_str + log.debug('parsing diff chunk %r', chunk) raw_diff = chunk.raw @@ -598,10 +600,17 @@ class DiffProcessor(object): chunks.insert(0, frag) - original_filename = safe_str(head['a_path']) + original_filename = head['a_path'] + original_filename_bytes = bytes_head['a_path'] + + filename = head['b_path'] + filename_bytes = bytes_head['b_path'] + _files.append({ 'original_filename': original_filename, - 'filename': safe_str(head['b_path']), + 'original_filename_bytes': original_filename_bytes, + 'filename': filename, + 'filename_bytes': filename_bytes, 'old_revision': head['a_blob_id'], 'new_revision': head['b_blob_id'], 'chunks': chunks, diff --git a/rhodecode/lib/hooks_base.py b/rhodecode/lib/hooks_base.py index 8931ab71..bb10ee9f 100644 --- a/rhodecode/lib/hooks_base.py +++ b/rhodecode/lib/hooks_base.py @@ -84,7 +84,6 @@ def check_locked_repo(extras, check_same_user=True): user = User.get_by_username(extras.username) output = '' if extras.locked_by[0] and (not check_same_user or user.user_id != extras.locked_by[0]): - locked_by = User.get(extras.locked_by[0]).username reason = extras.locked_by[2] # this exception is interpreted in git/hg middlewares and based diff --git a/rhodecode/lib/str_utils.py b/rhodecode/lib/str_utils.py index 7f437786..97aa6bd5 100644 --- a/rhodecode/lib/str_utils.py +++ b/rhodecode/lib/str_utils.py @@ -67,10 +67,7 @@ def base64_to_str(text: str | bytes) -> str: def get_default_encodings() -> list[str]: - return aslist(rhodecode.CONFIG.get('default_encoding', 'utf8'), sep=',') - - -DEFAULT_ENCODINGS = get_default_encodings() + return rhodecode.ConfigGet().get_list('default_encoding', missing='utf8') def safe_str(str_, to_encoding=None) -> str: @@ -87,7 +84,7 @@ def safe_str(str_, to_encoding=None) -> str: if not isinstance(str_, bytes): return str(str_) - to_encoding = to_encoding or DEFAULT_ENCODINGS + to_encoding = to_encoding or get_default_encodings() if not isinstance(to_encoding, (list, tuple)): to_encoding = [to_encoding] @@ -120,7 +117,7 @@ def safe_bytes(str_, from_encoding=None) -> bytes: for enc in from_encoding: try: return str_.encode(enc) - except UnicodeDecodeError: + except (UnicodeDecodeError, UnicodeEncodeError): pass return str_.encode(from_encoding[0], 'replace') diff --git a/rhodecode/lib/vcs/__init__.py b/rhodecode/lib/vcs/__init__.py index e03c4031..5c3aabd3 100644 --- a/rhodecode/lib/vcs/__init__.py +++ b/rhodecode/lib/vcs/__init__.py @@ -139,7 +139,7 @@ class CurlSession(object): try: curl.perform() - except pycurl.error as exc: + except pycurl.error: log.error('Failed to call endpoint url: %s using pycurl', url) raise diff --git a/rhodecode/lib/vcs/backends/base.py b/rhodecode/lib/vcs/backends/base.py index 2d0ea95d..a44e7621 100644 --- a/rhodecode/lib/vcs/backends/base.py +++ b/rhodecode/lib/vcs/backends/base.py @@ -19,6 +19,7 @@ """ Base module for all VCS systems """ + import os import re import time @@ -39,19 +40,25 @@ from rhodecode.lib.utils2 import safe_str, CachedProperty from rhodecode.lib.vcs.utils import author_name, author_email from rhodecode.lib.vcs.conf import settings from rhodecode.lib.vcs.exceptions import ( - CommitError, EmptyRepositoryError, NodeAlreadyAddedError, - NodeAlreadyChangedError, NodeAlreadyExistsError, NodeAlreadyRemovedError, - NodeDoesNotExistError, NodeNotChangedError, VCSError, - ImproperArchiveTypeError, BranchDoesNotExistError, CommitDoesNotExistError, - RepositoryError) + CommitError, + EmptyRepositoryError, + NodeAlreadyAddedError, + NodeAlreadyChangedError, + NodeAlreadyExistsError, + NodeAlreadyRemovedError, + NodeDoesNotExistError, + NodeNotChangedError, + VCSError, + ImproperArchiveTypeError, + BranchDoesNotExistError, + CommitDoesNotExistError, + RepositoryError, +) log = logging.getLogger(__name__) - -FILEMODE_DEFAULT = 0o100644 -FILEMODE_EXECUTABLE = 0o100755 -EMPTY_COMMIT_ID = '0' * 40 +EMPTY_COMMIT_ID = "0" * 40 @dataclasses.dataclass @@ -67,12 +74,12 @@ class Reference: @property def branch(self): - if self.type == 'branch': + if self.type == "branch": return self.name @property def bookmark(self): - if self.type == 'book': + if self.type == "book": return self.name @property @@ -80,11 +87,7 @@ class Reference: return reference_to_unicode(self) def asdict(self): - return dict( - type=self.type, - name=self.name, - commit_id=self.commit_id - ) + return dict(type=self.type, name=self.name, commit_id=self.commit_id) def unicode_to_reference(raw: str): @@ -93,7 +96,7 @@ def unicode_to_reference(raw: str): If unicode evaluates to False it returns None. """ if raw: - refs = raw.split(':') + refs = raw.split(":") return Reference(*refs) else: return None @@ -105,7 +108,7 @@ def reference_to_unicode(ref: Reference): If reference is None it returns None. """ if ref: - return ':'.join(ref) + return ":".join(ref) else: return None @@ -194,47 +197,44 @@ class UpdateFailureReason(object): class MergeResponse(object): - # uses .format(**metadata) for variables MERGE_STATUS_MESSAGES = { - MergeFailureReason.NONE: lazy_ugettext( - 'This pull request can be automatically merged.'), + MergeFailureReason.NONE: lazy_ugettext("This pull request can be automatically merged."), MergeFailureReason.UNKNOWN: lazy_ugettext( - 'This pull request cannot be merged because of an unhandled exception. ' - '{exception}'), + "This pull request cannot be merged because of an unhandled exception. " "{exception}" + ), MergeFailureReason.MERGE_FAILED: lazy_ugettext( - 'This pull request cannot be merged because of merge conflicts. {unresolved_files}'), + "This pull request cannot be merged because of merge conflicts. {unresolved_files}" + ), MergeFailureReason.PUSH_FAILED: lazy_ugettext( - 'This pull request could not be merged because push to ' - 'target:`{target}@{merge_commit}` failed.'), + "This pull request could not be merged because push to " "target:`{target}@{merge_commit}` failed." + ), MergeFailureReason.TARGET_IS_NOT_HEAD: lazy_ugettext( - 'This pull request cannot be merged because the target ' - '`{target_ref.name}` is not a head.'), + "This pull request cannot be merged because the target " "`{target_ref.name}` is not a head." + ), MergeFailureReason.HG_SOURCE_HAS_MORE_BRANCHES: lazy_ugettext( - 'This pull request cannot be merged because the source contains ' - 'more branches than the target.'), + "This pull request cannot be merged because the source contains " "more branches than the target." + ), MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS: lazy_ugettext( - 'This pull request cannot be merged because the target `{target_ref.name}` ' - 'has multiple heads: `{heads}`.'), + "This pull request cannot be merged because the target `{target_ref.name}` " + "has multiple heads: `{heads}`." + ), MergeFailureReason.TARGET_IS_LOCKED: lazy_ugettext( - 'This pull request cannot be merged because the target repository is ' - 'locked by {locked_by}.'), - + "This pull request cannot be merged because the target repository is " "locked by {locked_by}." + ), MergeFailureReason.MISSING_TARGET_REF: lazy_ugettext( - 'This pull request cannot be merged because the target ' - 'reference `{target_ref.name}` is missing.'), + "This pull request cannot be merged because the target " "reference `{target_ref.name}` is missing." + ), MergeFailureReason.MISSING_SOURCE_REF: lazy_ugettext( - 'This pull request cannot be merged because the source ' - 'reference `{source_ref.name}` is missing.'), + "This pull request cannot be merged because the source " "reference `{source_ref.name}` is missing." + ), MergeFailureReason.SUBREPO_MERGE_FAILED: lazy_ugettext( - 'This pull request cannot be merged because of conflicts related ' - 'to sub repositories.'), - + "This pull request cannot be merged because of conflicts related " "to sub repositories." + ), # Deprecations MergeFailureReason._DEPRECATED_MISSING_COMMIT: lazy_ugettext( - 'This pull request cannot be merged because the target or the ' - 'source reference is missing.'), - + "This pull request cannot be merged because the target or the " "source reference is missing." + ), } def __init__(self, possible, executed, merge_ref: Reference, failure_reason, metadata=None): @@ -245,19 +245,20 @@ class MergeResponse(object): self.metadata = metadata or {} def __repr__(self): - return f'' + return f"" def __eq__(self, other): same_instance = isinstance(other, self.__class__) - return same_instance \ - and self.possible == other.possible \ - and self.executed == other.executed \ - and self.failure_reason == other.failure_reason + return ( + same_instance + and self.possible == other.possible + and self.executed == other.executed + and self.failure_reason == other.failure_reason + ) @property def label(self): - label_dict = dict((v, k) for k, v in MergeFailureReason.__dict__.items() if - not k.startswith('_')) + label_dict = dict((v, k) for k, v in MergeFailureReason.__dict__.items() if not k.startswith("_")) return label_dict.get(self.failure_reason) @property @@ -270,13 +271,12 @@ class MergeResponse(object): try: return msg.format(**self.metadata) except Exception: - log.exception('Failed to format %s message', self) + log.exception("Failed to format %s message", self) return msg def asdict(self): data = {} - for k in ['possible', 'executed', 'merge_ref', 'failure_reason', - 'merge_status_message']: + for k in ["possible", "executed", "merge_ref", "failure_reason", "merge_status_message"]: data[k] = getattr(self, k) return data @@ -289,607 +289,7 @@ class SourceRefMissing(ValueError): pass -class BaseRepository(object): - """ - Base Repository for final backends - - .. attribute:: DEFAULT_BRANCH_NAME - - name of default branch (i.e. "trunk" for svn, "master" for git etc. - - .. attribute:: commit_ids - - list of all available commit ids, in ascending order - - .. attribute:: path - - absolute path to the repository - - .. attribute:: bookmarks - - Mapping from name to :term:`Commit ID` of the bookmark. Empty in case - there are no bookmarks or the backend implementation does not support - bookmarks. - - .. attribute:: tags - - Mapping from name to :term:`Commit ID` of the tag. - - """ - - DEFAULT_BRANCH_NAME = None - DEFAULT_CONTACT = "Unknown" - DEFAULT_DESCRIPTION = "unknown" - EMPTY_COMMIT_ID = '0' * 40 - COMMIT_ID_PAT = re.compile(r'[0-9a-fA-F]{40}') - - path = None - - _is_empty = None - _commit_ids = {} - - def __init__(self, repo_path, config=None, create=False, **kwargs): - """ - Initializes repository. Raises RepositoryError if repository could - not be find at the given ``repo_path`` or directory at ``repo_path`` - exists and ``create`` is set to True. - - :param repo_path: local path of the repository - :param config: repository configuration - :param create=False: if set to True, would try to create repository. - :param src_url=None: if set, should be proper url from which repository - would be cloned; requires ``create`` parameter to be set to True - - raises RepositoryError if src_url is set and create evaluates to - False - """ - raise NotImplementedError - - def __repr__(self): - return f'<{self.__class__.__name__} at {self.path}>' - - def __len__(self): - return self.count() - - def __eq__(self, other): - same_instance = isinstance(other, self.__class__) - return same_instance and other.path == self.path - - def __ne__(self, other): - return not self.__eq__(other) - - def get_create_shadow_cache_pr_path(self, db_repo): - path = db_repo.cached_diffs_dir - if not os.path.exists(path): - os.makedirs(path, 0o755) - return path - - @classmethod - def get_default_config(cls, default=None): - config = Config() - if default and isinstance(default, list): - for section, key, val in default: - config.set(section, key, val) - return config - - @LazyProperty - def _remote(self): - raise NotImplementedError - - def _heads(self, branch=None): - return [] - - @LazyProperty - def EMPTY_COMMIT(self): - return EmptyCommit(self.EMPTY_COMMIT_ID) - - @LazyProperty - def alias(self): - for k, v in settings.BACKENDS.items(): - if v.split('.')[-1] == str(self.__class__.__name__): - return k - - @LazyProperty - def name(self): - return safe_str(os.path.basename(self.path)) - - @LazyProperty - def description(self): - raise NotImplementedError - - def refs(self): - """ - returns a `dict` with branches, bookmarks, tags, and closed_branches - for this repository - """ - return dict( - branches=self.branches, - branches_closed=self.branches_closed, - tags=self.tags, - bookmarks=self.bookmarks - ) - - @LazyProperty - def branches(self): - """ - A `dict` which maps branch names to commit ids. - """ - raise NotImplementedError - - @LazyProperty - def branches_closed(self): - """ - A `dict` which maps tags names to commit ids. - """ - raise NotImplementedError - - @LazyProperty - def bookmarks(self): - """ - A `dict` which maps tags names to commit ids. - """ - raise NotImplementedError - - @LazyProperty - def tags(self): - """ - A `dict` which maps tags names to commit ids. - """ - raise NotImplementedError - - @LazyProperty - def size(self): - """ - Returns combined size in bytes for all repository files - """ - tip = self.get_commit() - return tip.size - - def size_at_commit(self, commit_id): - commit = self.get_commit(commit_id) - return commit.size - - def _check_for_empty(self): - no_commits = len(self._commit_ids) == 0 - if no_commits: - # check on remote to be sure - return self._remote.is_empty() - else: - return False - - def is_empty(self): - if rhodecode.is_test: - return self._check_for_empty() - - if self._is_empty is None: - # cache empty for production, but not tests - self._is_empty = self._check_for_empty() - - return self._is_empty - - @staticmethod - def check_url(url, config): - """ - Function will check given url and try to verify if it's a valid - link. - """ - raise NotImplementedError - - @staticmethod - def is_valid_repository(path): - """ - Check if given `path` contains a valid repository of this backend - """ - raise NotImplementedError - - # ========================================================================== - # COMMITS - # ========================================================================== - - @CachedProperty - def commit_ids(self): - raise NotImplementedError - - def append_commit_id(self, commit_id): - if commit_id not in self.commit_ids: - self._rebuild_cache(self.commit_ids + [commit_id]) - - # clear cache - self._invalidate_prop_cache('commit_ids') - self._is_empty = False - - def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, - translate_tag=None, maybe_unreachable=False, reference_obj=None): - """ - Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx` - are both None, most recent commit is returned. - - :param pre_load: Optional. List of commit attributes to load. - - :raises ``EmptyRepositoryError``: if there are no commits - """ - raise NotImplementedError - - def __iter__(self): - for commit_id in self.commit_ids: - yield self.get_commit(commit_id=commit_id) - - def get_commits( - self, start_id=None, end_id=None, start_date=None, end_date=None, - branch_name=None, show_hidden=False, pre_load=None, translate_tags=None): - """ - Returns iterator of `BaseCommit` objects from start to end - not inclusive. This should behave just like a list, ie. end is not - inclusive. - - :param start_id: None or str, must be a valid commit id - :param end_id: None or str, must be a valid commit id - :param start_date: - :param end_date: - :param branch_name: - :param show_hidden: - :param pre_load: - :param translate_tags: - """ - raise NotImplementedError - - def __getitem__(self, key): - """ - Allows index based access to the commit objects of this repository. - """ - pre_load = ["author", "branch", "date", "message", "parents"] - if isinstance(key, slice): - return self._get_range(key, pre_load) - return self.get_commit(commit_idx=key, pre_load=pre_load) - - def _get_range(self, slice_obj, pre_load): - for commit_id in self.commit_ids.__getitem__(slice_obj): - yield self.get_commit(commit_id=commit_id, pre_load=pre_load) - - def count(self): - return len(self.commit_ids) - - def tag(self, name, user, commit_id=None, message=None, date=None, **opts): - """ - Creates and returns a tag for the given ``commit_id``. - - :param name: name for new tag - :param user: full username, i.e.: "Joe Doe " - :param commit_id: commit id for which new tag would be created - :param message: message of the tag's commit - :param date: date of tag's commit - - :raises TagAlreadyExistError: if tag with same name already exists - """ - raise NotImplementedError - - def remove_tag(self, name, user, message=None, date=None): - """ - Removes tag with the given ``name``. - - :param name: name of the tag to be removed - :param user: full username, i.e.: "Joe Doe " - :param message: message of the tag's removal commit - :param date: date of tag's removal commit - - :raises TagDoesNotExistError: if tag with given name does not exists - """ - raise NotImplementedError - - def get_diff( - self, commit1, commit2, path=None, ignore_whitespace=False, - context=3, path1=None): - """ - Returns (git like) *diff*, as plain text. Shows changes introduced by - `commit2` since `commit1`. - - :param commit1: Entry point from which diff is shown. Can be - ``self.EMPTY_COMMIT`` - in this case, patch showing all - the changes since empty state of the repository until `commit2` - :param commit2: Until which commit changes should be shown. - :param path: Can be set to a path of a file to create a diff of that - file. If `path1` is also set, this value is only associated to - `commit2`. - :param ignore_whitespace: If set to ``True``, would not show whitespace - changes. Defaults to ``False``. - :param context: How many lines before/after changed lines should be - shown. Defaults to ``3``. - :param path1: Can be set to a path to associate with `commit1`. This - parameter works only for backends which support diff generation for - different paths. Other backends will raise a `ValueError` if `path1` - is set and has a different value than `path`. - :param file_path: filter this diff by given path pattern - """ - raise NotImplementedError - - def strip(self, commit_id, branch=None): - """ - Strip given commit_id from the repository - """ - raise NotImplementedError - - def get_common_ancestor(self, commit_id1, commit_id2, repo2): - """ - Return a latest common ancestor commit if one exists for this repo - `commit_id1` vs `commit_id2` from `repo2`. - - :param commit_id1: Commit it from this repository to use as a - target for the comparison. - :param commit_id2: Source commit id to use for comparison. - :param repo2: Source repository to use for comparison. - """ - raise NotImplementedError - - def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None): - """ - Compare this repository's revision `commit_id1` with `commit_id2`. - - Returns a tuple(commits, ancestor) that would be merged from - `commit_id2`. Doing a normal compare (``merge=False``), ``None`` - will be returned as ancestor. - - :param commit_id1: Commit it from this repository to use as a - target for the comparison. - :param commit_id2: Source commit id to use for comparison. - :param repo2: Source repository to use for comparison. - :param merge: If set to ``True`` will do a merge compare which also - returns the common ancestor. - :param pre_load: Optional. List of commit attributes to load. - """ - raise NotImplementedError - - def merge(self, repo_id, workspace_id, target_ref, source_repo, source_ref, - user_name='', user_email='', message='', dry_run=False, - use_rebase=False, close_branch=False): - """ - Merge the revisions specified in `source_ref` from `source_repo` - onto the `target_ref` of this repository. - - `source_ref` and `target_ref` are named tupls with the following - fields `type`, `name` and `commit_id`. - - Returns a MergeResponse named tuple with the following fields - 'possible', 'executed', 'source_commit', 'target_commit', - 'merge_commit'. - - :param repo_id: `repo_id` target repo id. - :param workspace_id: `workspace_id` unique identifier. - :param target_ref: `target_ref` points to the commit on top of which - the `source_ref` should be merged. - :param source_repo: The repository that contains the commits to be - merged. - :param source_ref: `source_ref` points to the topmost commit from - the `source_repo` which should be merged. - :param user_name: Merge commit `user_name`. - :param user_email: Merge commit `user_email`. - :param message: Merge commit `message`. - :param dry_run: If `True` the merge will not take place. - :param use_rebase: If `True` commits from the source will be rebased - on top of the target instead of being merged. - :param close_branch: If `True` branch will be close before merging it - """ - if dry_run: - message = message or settings.MERGE_DRY_RUN_MESSAGE - user_email = user_email or settings.MERGE_DRY_RUN_EMAIL - user_name = user_name or settings.MERGE_DRY_RUN_USER - else: - if not user_name: - raise ValueError('user_name cannot be empty') - if not user_email: - raise ValueError('user_email cannot be empty') - if not message: - raise ValueError('message cannot be empty') - - try: - return self._merge_repo( - repo_id, workspace_id, target_ref, source_repo, - source_ref, message, user_name, user_email, dry_run=dry_run, - use_rebase=use_rebase, close_branch=close_branch) - except RepositoryError as exc: - log.exception('Unexpected failure when running merge, dry-run=%s', dry_run) - return MergeResponse( - False, False, None, MergeFailureReason.UNKNOWN, - metadata={'exception': str(exc)}) - - def _merge_repo(self, repo_id, workspace_id, target_ref, - source_repo, source_ref, merge_message, - merger_name, merger_email, dry_run=False, - use_rebase=False, close_branch=False): - """Internal implementation of merge.""" - raise NotImplementedError - - def _maybe_prepare_merge_workspace( - self, repo_id, workspace_id, target_ref, source_ref): - """ - Create the merge workspace. - - :param workspace_id: `workspace_id` unique identifier. - """ - raise NotImplementedError - - @classmethod - def _get_legacy_shadow_repository_path(cls, repo_path, workspace_id): - """ - Legacy version that was used before. We still need it for - backward compat - """ - return os.path.join( - os.path.dirname(repo_path), - f'.__shadow_{os.path.basename(repo_path)}_{workspace_id}') - - @classmethod - def _get_shadow_repository_path(cls, repo_path, repo_id, workspace_id): - # The name of the shadow repository must start with '.', so it is - # skipped by 'rhodecode.lib.utils.get_filesystem_repos'. - legacy_repository_path = cls._get_legacy_shadow_repository_path(repo_path, workspace_id) - if os.path.exists(legacy_repository_path): - return legacy_repository_path - else: - return os.path.join( - os.path.dirname(repo_path), - f'.__shadow_repo_{repo_id}_{workspace_id}') - - def cleanup_merge_workspace(self, repo_id, workspace_id): - """ - Remove merge workspace. - - This function MUST not fail in case there is no workspace associated to - the given `workspace_id`. - - :param workspace_id: `workspace_id` unique identifier. - """ - shadow_repository_path = self._get_shadow_repository_path( - self.path, repo_id, workspace_id) - shadow_repository_path_del = '{}.{}.delete'.format( - shadow_repository_path, time.time()) - - # move the shadow repo, so it never conflicts with the one used. - # we use this method because shutil.rmtree had some edge case problems - # removing symlinked repositories - if not os.path.isdir(shadow_repository_path): - return - - shutil.move(shadow_repository_path, shadow_repository_path_del) - try: - shutil.rmtree(shadow_repository_path_del, ignore_errors=False) - except Exception: - log.exception('Failed to gracefully remove shadow repo under %s', - shadow_repository_path_del) - shutil.rmtree(shadow_repository_path_del, ignore_errors=True) - - # ========== # - # COMMIT API # - # ========== # - - @LazyProperty - def in_memory_commit(self): - """ - Returns :class:`InMemoryCommit` object for this repository. - """ - raise NotImplementedError - - # ======================== # - # UTILITIES FOR SUBCLASSES # - # ======================== # - - def _validate_diff_commits(self, commit1, commit2): - """ - Validates that the given commits are related to this repository. - - Intended as a utility for sub classes to have a consistent validation - of input parameters in methods like :meth:`get_diff`. - """ - self._validate_commit(commit1) - self._validate_commit(commit2) - if (isinstance(commit1, EmptyCommit) and - isinstance(commit2, EmptyCommit)): - raise ValueError("Cannot compare two empty commits") - - def _validate_commit(self, commit): - if not isinstance(commit, BaseCommit): - raise TypeError( - "%s is not of type BaseCommit" % repr(commit)) - if commit.repository != self and not isinstance(commit, EmptyCommit): - raise ValueError( - "Commit %s must be a valid commit from this repository %s, " - "related to this repository instead %s." % - (commit, self, commit.repository)) - - def _validate_commit_id(self, commit_id): - if not isinstance(commit_id, str): - raise TypeError(f"commit_id must be a string value got {type(commit_id)} instead") - - def _validate_commit_idx(self, commit_idx): - if not isinstance(commit_idx, int): - raise TypeError(f"commit_idx must be a numeric value, got {type(commit_idx)}") - - def _validate_branch_name(self, branch_name): - if branch_name and branch_name not in self.branches_all: - msg = (f"Branch {branch_name} not found in {self}") - raise BranchDoesNotExistError(msg) - - # - # Supporting deprecated API parts - # TODO: johbo: consider to move this into a mixin - # - - @property - def EMPTY_CHANGESET(self): - warnings.warn( - "Use EMPTY_COMMIT or EMPTY_COMMIT_ID instead", DeprecationWarning) - return self.EMPTY_COMMIT_ID - - @property - def revisions(self): - warnings.warn("Use commits attribute instead", DeprecationWarning) - return self.commit_ids - - @revisions.setter - def revisions(self, value): - warnings.warn("Use commits attribute instead", DeprecationWarning) - self.commit_ids = value - - def get_changeset(self, revision=None, pre_load=None): - warnings.warn("Use get_commit instead", DeprecationWarning) - commit_id = None - commit_idx = None - if isinstance(revision, str): - commit_id = revision - else: - commit_idx = revision - return self.get_commit( - commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load) - - def get_changesets( - self, start=None, end=None, start_date=None, end_date=None, - branch_name=None, pre_load=None): - warnings.warn("Use get_commits instead", DeprecationWarning) - start_id = self._revision_to_commit(start) - end_id = self._revision_to_commit(end) - return self.get_commits( - start_id=start_id, end_id=end_id, start_date=start_date, - end_date=end_date, branch_name=branch_name, pre_load=pre_load) - - def _revision_to_commit(self, revision): - """ - Translates a revision to a commit_id - - Helps to support the old changeset based API which allows to use - commit ids and commit indices interchangeable. - """ - if revision is None: - return revision - - if isinstance(revision, str): - commit_id = revision - else: - commit_id = self.commit_ids[revision] - return commit_id - - @property - def in_memory_changeset(self): - warnings.warn("Use in_memory_commit instead", DeprecationWarning) - return self.in_memory_commit - - def get_path_permissions(self, username): - """ - Returns a path permission checker or None if not supported - - :param username: session user name - :return: an instance of BasePathPermissionChecker or None - """ - return None - - def install_hooks(self, force=False): - return self._remote.install_hooks(force) - - def get_hooks_info(self): - return self._remote.get_hooks_info() - - def vcsserver_invalidate_cache(self, delete=False): - return self._remote.vcsserver_invalidate_cache(delete) - - -class BaseCommit(object): +class BaseCommit: """ Each backend should implement it's commit representation. @@ -933,6 +333,7 @@ class BaseCommit(object): list of parent commits """ + repository = None branch = None @@ -942,7 +343,7 @@ class BaseCommit(object): value as ``None``. """ - _ARCHIVE_PREFIX_TEMPLATE = '{repo_name}-{short_id}' + _ARCHIVE_PREFIX_TEMPLATE = "{repo_name}-{short_id}" """ This template is used to generate a default prefix for repository archives if no prefix has been specified. @@ -952,7 +353,7 @@ class BaseCommit(object): return self.__str__() def __str__(self): - return f'<{self.__class__.__name__} at {self.idx}:{self.short_id}>' + return f"<{self.__class__.__name__} at {self.idx}:{self.short_id}>" def __eq__(self, other): same_instance = isinstance(other, self.__class__) @@ -962,26 +363,26 @@ class BaseCommit(object): parents = [] try: for parent in self.parents: - parents.append({'raw_id': parent.raw_id}) + parents.append({"raw_id": parent.raw_id}) except NotImplementedError: # empty commit doesn't have parents implemented pass return { - 'short_id': self.short_id, - 'raw_id': self.raw_id, - 'revision': self.idx, - 'message': self.message, - 'date': self.date, - 'author': self.author, - 'parents': parents, - 'branch': self.branch + "short_id": self.short_id, + "raw_id": self.raw_id, + "revision": self.idx, + "message": self.message, + "date": self.date, + "author": self.author, + "parents": parents, + "branch": self.branch, } def __getstate__(self): d = self.__dict__.copy() - d.pop('_remote', None) - d.pop('repository', None) + d.pop("_remote", None) + d.pop("repository", None) return d def get_remote(self): @@ -992,9 +393,9 @@ class BaseCommit(object): def _get_refs(self): return { - 'branches': [self.branch] if self.branch else [], - 'bookmarks': getattr(self, 'bookmarks', []), - 'tags': self.tags + "branches": [self.branch] if self.branch else [], + "bookmarks": getattr(self, "bookmarks", []), + "tags": self.tags, } @LazyProperty @@ -1118,7 +519,7 @@ class BaseCommit(object): """ raise NotImplementedError - def is_link(self, path): + def is_link(self, path: bytes): """ Returns ``True`` if given `path` is a symlink """ @@ -1154,25 +555,24 @@ class BaseCommit(object): """ raise NotImplementedError - def get_path_commit(self, path, pre_load=None): + def get_path_commit(self, path: bytes, pre_load=None): """ Returns last commit of the file at the given `path`. - :param pre_load: Optional. List of commit attributes to load. """ commits = self.get_path_history(path, limit=1, pre_load=pre_load) if not commits: raise RepositoryError( - 'Failed to fetch history for path {}. ' - 'Please check if such path exists in your repository'.format( - path)) + f"Failed to fetch history for path {path}. Please check if such path exists in your repository" + ) return commits[0] - def get_path_history(self, path, limit=None, pre_load=None): + def get_path_history(self, path: bytes, limit=None, pre_load=None): """ Returns history of file as reversed list of :class:`BaseCommit` objects for which file at given `path` has been modified. + :param path: file path or dir path :param limit: Optional. Allows to limit the size of the returned history. This is intended as a hint to the underlying backend, so that it can apply optimizations depending on the limit. @@ -1180,16 +580,17 @@ class BaseCommit(object): """ raise NotImplementedError - def get_file_annotate(self, path, pre_load=None): + def get_file_annotate(self, path: bytes, pre_load=None): """ Returns a generator of four element tuples with lineno, sha, commit lazy loader and line + :param path: file path :param pre_load: Optional. List of commit attributes to load. """ raise NotImplementedError - def get_nodes(self, path, pre_load=None): + def get_nodes(self, path: bytes, pre_load=None): """ Returns combined ``DirNode`` and ``FileNode`` objects list representing state of commit at the given ``path``. @@ -1199,7 +600,7 @@ class BaseCommit(object): """ raise NotImplementedError - def get_node(self, path): + def get_node(self, path: bytes, pre_load=None): """ Returns ``Node`` object from the given ``path``. @@ -1208,16 +609,24 @@ class BaseCommit(object): """ raise NotImplementedError - def get_largefile_node(self, path): + def get_largefile_node(self, path: bytes): """ Returns the path to largefile from Mercurial/Git-lfs storage. or None if it's not a largefile node """ return None - def archive_repo(self, archive_name_key, kind='tgz', subrepos=None, - archive_dir_name=None, write_metadata=False, mtime=None, - archive_at_path='/', cache_config=None): + def archive_repo( + self, + archive_name_key, + kind="tgz", + subrepos=None, + archive_dir_name=None, + write_metadata=False, + mtime=None, + archive_at_path="/", + cache_config=None, + ): """ Creates an archive containing the contents of the repository. @@ -1237,27 +646,26 @@ class BaseCommit(object): cache_config = cache_config or {} allowed_kinds = [x[0] for x in settings.ARCHIVE_SPECS] if kind not in allowed_kinds: - raise ImproperArchiveTypeError( - f'Archive kind ({kind}) not supported use one of {allowed_kinds}') + raise ImproperArchiveTypeError(f"Archive kind ({kind}) not supported use one of {allowed_kinds}") archive_dir_name = self._validate_archive_prefix(archive_dir_name) mtime = mtime is not None or time.mktime(self.date.timetuple()) commit_id = self.raw_id return self.repository._remote.archive_repo( - archive_name_key, kind, mtime, archive_at_path, - archive_dir_name, commit_id, cache_config) + archive_name_key, kind, mtime, archive_at_path, archive_dir_name, commit_id, cache_config + ) def _validate_archive_prefix(self, archive_dir_name): if archive_dir_name is None: archive_dir_name = self._ARCHIVE_PREFIX_TEMPLATE.format( - repo_name=safe_str(self.repository.name), - short_id=self.short_id) + repo_name=safe_str(self.repository.name), short_id=self.short_id + ) elif not isinstance(archive_dir_name, str): raise ValueError(f"archive_dir_name is not str object but: {type(archive_dir_name)}") - elif archive_dir_name.startswith('/'): + elif archive_dir_name.startswith("/"): raise VCSError("Prefix cannot start with leading slash") - elif archive_dir_name.strip() == '': + elif archive_dir_name.strip() == "": raise VCSError("Prefix cannot be empty") elif not archive_dir_name.isascii(): raise VCSError("Prefix cannot contain non ascii characters") @@ -1268,7 +676,7 @@ class BaseCommit(object): """ Returns ``RootNode`` object for this commit. """ - return self.get_node('') + return self.get_node(b"") def next(self, branch=None): """ @@ -1292,8 +700,7 @@ class BaseCommit(object): def _find_next(self, indexes, branch=None): if branch and self.branch != branch: - raise VCSError('Branch option used on commit not belonging ' - 'to that branch') + raise VCSError("Branch option used on commit not belonging " "to that branch") for next_idx in indexes: commit = self.repository.get_commit(commit_idx=next_idx) @@ -1307,33 +714,9 @@ class BaseCommit(object): Returns a `Diff` object representing the change made by this commit. """ parent = self.first_parent - diff = self.repository.get_diff( - parent, self, - ignore_whitespace=ignore_whitespace, - context=context) + diff = self.repository.get_diff(parent, self, ignore_whitespace=ignore_whitespace, context=context) return diff - @LazyProperty - def added(self): - """ - Returns list of added ``FileNode`` objects. - """ - raise NotImplementedError - - @LazyProperty - def changed(self): - """ - Returns list of modified ``FileNode`` objects. - """ - raise NotImplementedError - - @LazyProperty - def removed(self): - """ - Returns list of removed ``FileNode`` objects. - """ - raise NotImplementedError - @LazyProperty def size(self): """ @@ -1341,7 +724,7 @@ class BaseCommit(object): """ return sum(node.size for node in self.get_filenodes_generator()) - def walk(self, topurl=''): + def walk(self, top_url=b""): """ Similar to os.walk method. Insted of filesystem it walks through commit starting at given ``topurl``. Returns generator of tuples @@ -1349,10 +732,10 @@ class BaseCommit(object): """ from rhodecode.lib.vcs.nodes import DirNode - if isinstance(topurl, DirNode): - top_node = topurl + if isinstance(top_url, DirNode): + top_node = top_url else: - top_node = self.get_node(topurl) + top_node = self.get_node(top_url) has_default_pre_load = False if isinstance(top_node, DirNode): @@ -1381,15 +764,20 @@ class BaseCommit(object): def no_node_at_path(self, path): return NodeDoesNotExistError( - f"There is no file nor directory at the given path: " - f"`{safe_str(path)}` at commit {self.short_id}") + f"There is no file nor directory at the given path: " f"`{safe_str(path)}` at commit {self.short_id}" + ) - def _fix_path(self, path: str) -> str: + @classmethod + def _fix_path(cls, path: bytes) -> bytes: """ - Paths are stored without trailing slash so we need to get rid off it if - needed. + Paths are stored without trailing slash so we need to get rid off it if needed. + It also validates that path is a bytestring for support of mixed encodings... """ - return safe_str(path).rstrip('/') + + if not isinstance(path, bytes): + raise ValueError(f'path=`{safe_str(path)}` must be bytes') + + return path.rstrip(b"/") # # Deprecated API based on changesets @@ -1410,17 +798,644 @@ class BaseCommit(object): return self.get_path_commit(path) -class BaseChangesetClass(type): +class BaseRepository(object): + """ + Base Repository for final backends + .. attribute:: DEFAULT_BRANCH_NAME + + name of default branch (i.e. "trunk" for svn, "master" for git etc. + + .. attribute:: commit_ids + + list of all available commit ids, in ascending order + + .. attribute:: path + + absolute path to the repository + + .. attribute:: bookmarks + + Mapping from name to :term:`Commit ID` of the bookmark. Empty in case + there are no bookmarks or the backend implementation does not support + bookmarks. + + .. attribute:: tags + + Mapping from name to :term:`Commit ID` of the tag. + + """ + + DEFAULT_BRANCH_NAME = None + DEFAULT_CONTACT = "Unknown" + DEFAULT_DESCRIPTION = "unknown" + EMPTY_COMMIT_ID = "0" * 40 + COMMIT_ID_PAT = re.compile(r"[0-9a-fA-F]{40}") + + path = None + + _is_empty = None + _commit_ids = {} + + def __init__(self, repo_path, config=None, create=False, **kwargs): + """ + Initializes repository. Raises RepositoryError if repository could + not be find at the given ``repo_path`` or directory at ``repo_path`` + exists and ``create`` is set to True. + + :param repo_path: local path of the repository + :param config: repository configuration + :param create=False: if set to True, would try to create repository. + :param src_url=None: if set, should be proper url from which repository + would be cloned; requires ``create`` parameter to be set to True - + raises RepositoryError if src_url is set and create evaluates to + False + """ + raise NotImplementedError + + def __repr__(self): + return f"<{self.__class__.__name__} at {self.path}>" + + def __len__(self): + return self.count() + + def __eq__(self, other): + same_instance = isinstance(other, self.__class__) + return same_instance and other.path == self.path + + def __ne__(self, other): + return not self.__eq__(other) + + @classmethod + def get_create_shadow_cache_pr_path(cls, db_repo): + path = db_repo.cached_diffs_dir + if not os.path.exists(path): + os.makedirs(path, 0o755) + return path + + @classmethod + def get_default_config(cls, default=None): + config = Config() + if default and isinstance(default, list): + for section, key, val in default: + config.set(section, key, val) + return config + + @LazyProperty + def _remote(self): + raise NotImplementedError + + def _heads(self, branch=None): + return [] + + @LazyProperty + def EMPTY_COMMIT(self): + return EmptyCommit(self.EMPTY_COMMIT_ID) + + @LazyProperty + def alias(self): + for k, v in settings.BACKENDS.items(): + if v.split(".")[-1] == str(self.__class__.__name__): + return k + + @LazyProperty + def name(self): + return safe_str(os.path.basename(self.path)) + + @LazyProperty + def description(self): + raise NotImplementedError + + def refs(self): + """ + returns a `dict` with branches, bookmarks, tags, and closed_branches + for this repository + """ + return dict( + branches=self.branches, branches_closed=self.branches_closed, tags=self.tags, bookmarks=self.bookmarks + ) + + @LazyProperty + def branches(self): + """ + A `dict` which maps branch names to commit ids. + """ + raise NotImplementedError + + @LazyProperty + def branches_closed(self): + """ + A `dict` which maps tags names to commit ids. + """ + raise NotImplementedError + + @LazyProperty + def bookmarks(self): + """ + A `dict` which maps tags names to commit ids. + """ + raise NotImplementedError + + @LazyProperty + def tags(self): + """ + A `dict` which maps tags names to commit ids. + """ + raise NotImplementedError + + @LazyProperty + def size(self): + """ + Returns combined size in bytes for all repository files + """ + tip = self.get_commit() + return tip.size + + def size_at_commit(self, commit_id): + commit = self.get_commit(commit_id) + return commit.size + + def _check_for_empty(self): + no_commits = len(self._commit_ids) == 0 + if no_commits: + # check on remote to be sure + return self._remote.is_empty() + else: + return False + + def is_empty(self): + if rhodecode.is_test: + return self._check_for_empty() + + if self._is_empty is None: + # cache empty for production, but not tests + self._is_empty = self._check_for_empty() + + return self._is_empty + + @staticmethod + def check_url(url, config): + """ + Function will check given url and try to verify if it's a valid + link. + """ + raise NotImplementedError + + @staticmethod + def is_valid_repository(path): + """ + Check if given `path` contains a valid repository of this backend + """ + raise NotImplementedError + + # ========================================================================== + # COMMITS + # ========================================================================== + + @CachedProperty + def commit_ids(self): + raise NotImplementedError + + def append_commit_id(self, commit_id): + if commit_id not in self.commit_ids: + self._rebuild_cache(self.commit_ids + [commit_id]) + + # clear cache + self._invalidate_prop_cache("commit_ids") + self._is_empty = False + + def get_commit( + self, + commit_id=None, + commit_idx=None, + pre_load=None, + translate_tag=None, + maybe_unreachable=False, + reference_obj=None, + ) -> BaseCommit: + """ + Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx` + are both None, most recent commit is returned. + + :param pre_load: Optional. List of commit attributes to load. + + :raises ``EmptyRepositoryError``: if there are no commits + """ + raise NotImplementedError + + def __iter__(self): + for commit_id in self.commit_ids: + yield self.get_commit(commit_id=commit_id) + + def get_commits( + self, + start_id=None, + end_id=None, + start_date=None, + end_date=None, + branch_name=None, + show_hidden=False, + pre_load=None, + translate_tags=None, + ): + """ + Returns iterator of `BaseCommit` objects from start to end + not inclusive. This should behave just like a list, ie. end is not + inclusive. + + :param start_id: None or str, must be a valid commit id + :param end_id: None or str, must be a valid commit id + :param start_date: + :param end_date: + :param branch_name: + :param show_hidden: + :param pre_load: + :param translate_tags: + """ + raise NotImplementedError + + def __getitem__(self, key): + """ + Allows index based access to the commit objects of this repository. + """ + pre_load = ["author", "branch", "date", "message", "parents"] + if isinstance(key, slice): + return self._get_range(key, pre_load) + return self.get_commit(commit_idx=key, pre_load=pre_load) + + def _get_range(self, slice_obj, pre_load): + for commit_id in self.commit_ids.__getitem__(slice_obj): + yield self.get_commit(commit_id=commit_id, pre_load=pre_load) + + def count(self): + return len(self.commit_ids) + + def tag(self, name, user, commit_id=None, message=None, date=None, **opts): + """ + Creates and returns a tag for the given ``commit_id``. + + :param name: name for new tag + :param user: full username, i.e.: "Joe Doe " + :param commit_id: commit id for which new tag would be created + :param message: message of the tag's commit + :param date: date of tag's commit + + :raises TagAlreadyExistError: if tag with same name already exists + """ + raise NotImplementedError + + def remove_tag(self, name, user, message=None, date=None): + """ + Removes tag with the given ``name``. + + :param name: name of the tag to be removed + :param user: full username, i.e.: "Joe Doe " + :param message: message of the tag's removal commit + :param date: date of tag's removal commit + + :raises TagDoesNotExistError: if tag with given name does not exists + """ + raise NotImplementedError + + def get_diff(self, commit1, commit2, path=None, ignore_whitespace=False, context=3, path1=None): + """ + Returns (git like) *diff*, as plain text. Shows changes introduced by + `commit2` since `commit1`. + + :param commit1: Entry point from which diff is shown. Can be + ``self.EMPTY_COMMIT`` - in this case, patch showing all + the changes since empty state of the repository until `commit2` + :param commit2: Until which commit changes should be shown. + :param path: Can be set to a path of a file to create a diff of that + file. If `path1` is also set, this value is only associated to + `commit2`. + :param ignore_whitespace: If set to ``True``, would not show whitespace + changes. Defaults to ``False``. + :param context: How many lines before/after changed lines should be + shown. Defaults to ``3``. + :param path1: Can be set to a path to associate with `commit1`. This + parameter works only for backends which support diff generation for + different paths. Other backends will raise a `ValueError` if `path1` + is set and has a different value than `path`. + :param file_path: filter this diff by given path pattern + """ + raise NotImplementedError + + def strip(self, commit_id, branch=None): + """ + Strip given commit_id from the repository + """ + raise NotImplementedError + + def get_common_ancestor(self, commit_id1, commit_id2, repo2): + """ + Return a latest common ancestor commit if one exists for this repo + `commit_id1` vs `commit_id2` from `repo2`. + + :param commit_id1: Commit it from this repository to use as a + target for the comparison. + :param commit_id2: Source commit id to use for comparison. + :param repo2: Source repository to use for comparison. + """ + raise NotImplementedError + + def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None): + """ + Compare this repository's revision `commit_id1` with `commit_id2`. + + Returns a tuple(commits, ancestor) that would be merged from + `commit_id2`. Doing a normal compare (``merge=False``), ``None`` + will be returned as ancestor. + + :param commit_id1: Commit it from this repository to use as a + target for the comparison. + :param commit_id2: Source commit id to use for comparison. + :param repo2: Source repository to use for comparison. + :param merge: If set to ``True`` will do a merge compare which also + returns the common ancestor. + :param pre_load: Optional. List of commit attributes to load. + """ + raise NotImplementedError + + def merge( + self, + repo_id, + workspace_id, + target_ref, + source_repo, + source_ref, + user_name="", + user_email="", + message="", + dry_run=False, + use_rebase=False, + close_branch=False, + ): + """ + Merge the revisions specified in `source_ref` from `source_repo` + onto the `target_ref` of this repository. + + `source_ref` and `target_ref` are named tupls with the following + fields `type`, `name` and `commit_id`. + + Returns a MergeResponse named tuple with the following fields + 'possible', 'executed', 'source_commit', 'target_commit', + 'merge_commit'. + + :param repo_id: `repo_id` target repo id. + :param workspace_id: `workspace_id` unique identifier. + :param target_ref: `target_ref` points to the commit on top of which + the `source_ref` should be merged. + :param source_repo: The repository that contains the commits to be + merged. + :param source_ref: `source_ref` points to the topmost commit from + the `source_repo` which should be merged. + :param user_name: Merge commit `user_name`. + :param user_email: Merge commit `user_email`. + :param message: Merge commit `message`. + :param dry_run: If `True` the merge will not take place. + :param use_rebase: If `True` commits from the source will be rebased + on top of the target instead of being merged. + :param close_branch: If `True` branch will be close before merging it + """ + if dry_run: + message = message or settings.MERGE_DRY_RUN_MESSAGE + user_email = user_email or settings.MERGE_DRY_RUN_EMAIL + user_name = user_name or settings.MERGE_DRY_RUN_USER + else: + if not user_name: + raise ValueError("user_name cannot be empty") + if not user_email: + raise ValueError("user_email cannot be empty") + if not message: + raise ValueError("message cannot be empty") + + try: + return self._merge_repo( + repo_id, + workspace_id, + target_ref, + source_repo, + source_ref, + message, + user_name, + user_email, + dry_run=dry_run, + use_rebase=use_rebase, + close_branch=close_branch, + ) + except RepositoryError as exc: + log.exception("Unexpected failure when running merge, dry-run=%s", dry_run) + return MergeResponse(False, False, None, MergeFailureReason.UNKNOWN, metadata={"exception": str(exc)}) + + def _merge_repo( + self, + repo_id, + workspace_id, + target_ref, + source_repo, + source_ref, + merge_message, + merger_name, + merger_email, + dry_run=False, + use_rebase=False, + close_branch=False, + ): + """Internal implementation of merge.""" + raise NotImplementedError + + def _maybe_prepare_merge_workspace(self, repo_id, workspace_id, target_ref, source_ref): + """ + Create the merge workspace. + + :param workspace_id: `workspace_id` unique identifier. + """ + raise NotImplementedError + + @classmethod + def _get_legacy_shadow_repository_path(cls, repo_path, workspace_id): + """ + Legacy version that was used before. We still need it for + backward compat + """ + return os.path.join(os.path.dirname(repo_path), f".__shadow_{os.path.basename(repo_path)}_{workspace_id}") + + @classmethod + def _get_shadow_repository_path(cls, repo_path, repo_id, workspace_id): + # The name of the shadow repository must start with '.', so it is + # skipped by 'rhodecode.lib.utils.get_filesystem_repos'. + legacy_repository_path = cls._get_legacy_shadow_repository_path(repo_path, workspace_id) + if os.path.exists(legacy_repository_path): + return legacy_repository_path + else: + return os.path.join(os.path.dirname(repo_path), f".__shadow_repo_{repo_id}_{workspace_id}") + + def cleanup_merge_workspace(self, repo_id, workspace_id): + """ + Remove merge workspace. + + This function MUST not fail in case there is no workspace associated to + the given `workspace_id`. + + :param workspace_id: `workspace_id` unique identifier. + """ + shadow_repository_path = self._get_shadow_repository_path(self.path, repo_id, workspace_id) + shadow_repository_path_del = "{}.{}.delete".format(shadow_repository_path, time.time()) + + # move the shadow repo, so it never conflicts with the one used. + # we use this method because shutil.rmtree had some edge case problems + # removing symlinked repositories + if not os.path.isdir(shadow_repository_path): + return + + shutil.move(shadow_repository_path, shadow_repository_path_del) + try: + shutil.rmtree(shadow_repository_path_del, ignore_errors=False) + except Exception: + log.exception("Failed to gracefully remove shadow repo under %s", shadow_repository_path_del) + shutil.rmtree(shadow_repository_path_del, ignore_errors=True) + + # ========== # + # COMMIT API # + # ========== # + + @LazyProperty + def in_memory_commit(self): + """ + Returns :class:`InMemoryCommit` object for this repository. + """ + raise NotImplementedError + + # ======================== # + # UTILITIES FOR SUBCLASSES # + # ======================== # + + def _validate_diff_commits(self, commit1, commit2): + """ + Validates that the given commits are related to this repository. + + Intended as a utility for sub classes to have a consistent validation + of input parameters in methods like :meth:`get_diff`. + """ + self._validate_commit(commit1) + self._validate_commit(commit2) + if isinstance(commit1, EmptyCommit) and isinstance(commit2, EmptyCommit): + raise ValueError("Cannot compare two empty commits") + + def _validate_commit(self, commit): + if not isinstance(commit, BaseCommit): + raise TypeError("%s is not of type BaseCommit" % repr(commit)) + if commit.repository != self and not isinstance(commit, EmptyCommit): + raise ValueError( + "Commit %s must be a valid commit from this repository %s, " + "related to this repository instead %s." % (commit, self, commit.repository) + ) + + def _validate_commit_id(self, commit_id): + if not isinstance(commit_id, str): + raise TypeError(f"commit_id must be a string value got {type(commit_id)} instead") + + def _validate_commit_idx(self, commit_idx): + if not isinstance(commit_idx, int): + raise TypeError(f"commit_idx must be a numeric value, got {type(commit_idx)}") + + def _validate_branch_name(self, branch_name): + if branch_name and branch_name not in self.branches_all: + msg = f"Branch {branch_name} not found in {self}" + raise BranchDoesNotExistError(msg) + + # + # Supporting deprecated API parts + # TODO: johbo: consider to move this into a mixin + # + + @property + def EMPTY_CHANGESET(self): + warnings.warn("Use EMPTY_COMMIT or EMPTY_COMMIT_ID instead", DeprecationWarning) + return self.EMPTY_COMMIT_ID + + @property + def revisions(self): + warnings.warn("Use commits attribute instead", DeprecationWarning) + return self.commit_ids + + @revisions.setter + def revisions(self, value): + warnings.warn("Use commits attribute instead", DeprecationWarning) + self.commit_ids = value + + def get_changeset(self, revision=None, pre_load=None): + warnings.warn("Use get_commit instead", DeprecationWarning) + commit_id = None + commit_idx = None + if isinstance(revision, str): + commit_id = revision + else: + commit_idx = revision + return self.get_commit(commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load) + + def get_changesets(self, start=None, end=None, start_date=None, end_date=None, branch_name=None, pre_load=None): + warnings.warn("Use get_commits instead", DeprecationWarning) + start_id = self._revision_to_commit(start) + end_id = self._revision_to_commit(end) + return self.get_commits( + start_id=start_id, + end_id=end_id, + start_date=start_date, + end_date=end_date, + branch_name=branch_name, + pre_load=pre_load, + ) + + def _revision_to_commit(self, revision): + """ + Translates a revision to a commit_id + + Helps to support the old changeset based API which allows to use + commit ids and commit indices interchangeable. + """ + if revision is None: + return revision + + if isinstance(revision, str): + commit_id = revision + else: + commit_id = self.commit_ids[revision] + return commit_id + + @property + def in_memory_changeset(self): + warnings.warn("Use in_memory_commit instead", DeprecationWarning) + return self.in_memory_commit + + def get_path_permissions(self, username): + """ + Returns a path permission checker or None if not supported + + :param username: session user name + :return: an instance of BasePathPermissionChecker or None + """ + return None + + def install_hooks(self, force=False): + return self._remote.install_hooks(force) + + def get_hooks_info(self): + return self._remote.get_hooks_info() + + def vcsserver_invalidate_cache(self, delete=False): + return self._remote.vcsserver_invalidate_cache(delete) + + +class BaseChangesetClass(type): def __instancecheck__(self, instance): return isinstance(instance, BaseCommit) class BaseChangeset(BaseCommit, metaclass=BaseChangesetClass): - def __new__(cls, *args, **kwargs): - warnings.warn( - "Use BaseCommit instead of BaseChangeset", DeprecationWarning) + warnings.warn("Use BaseCommit instead of BaseChangeset", DeprecationWarning) return super().__new__(cls, *args, **kwargs) @@ -1441,8 +1456,7 @@ class BaseInMemoryCommit(object): list of ``FileNode`` objects marked as *changed* ``removed`` - list of ``FileNode`` or ``RemovedFileNode`` objects marked to be - *removed* + list of ``FileNode`` objects marked to be *removed* ``parents`` list of :class:`BaseCommit` instances representing parents of @@ -1469,9 +1483,7 @@ class BaseInMemoryCommit(object): # Check if not already marked as *added* first for node in filenodes: if node.path in (n.path for n in self.added): - raise NodeAlreadyAddedError( - "Such FileNode %s is already marked for addition" - % node.path) + raise NodeAlreadyAddedError(f"Such FileNode {node.path} is already marked for addition") for node in filenodes: self.added.append(node) @@ -1490,24 +1502,19 @@ class BaseInMemoryCommit(object): """ for node in filenodes: if node.path in (n.path for n in self.removed): - raise NodeAlreadyRemovedError( - "Node at %s is already marked as removed" % node.path) + raise NodeAlreadyRemovedError("Node at %s is already marked as removed" % node.path) try: self.repository.get_commit() except EmptyRepositoryError: - raise EmptyRepositoryError( - "Nothing to change - try to *add* new nodes rather than " - "changing them") + raise EmptyRepositoryError("Nothing to change - try to *add* new nodes rather than " "changing them") for node in filenodes: if node.path in (n.path for n in self.changed): - raise NodeAlreadyChangedError( - "Node at '%s' is already marked as changed" % node.path) + raise NodeAlreadyChangedError("Node at '%s' is already marked as changed" % node.path) self.changed.append(node) def remove(self, *filenodes): """ - Marks given ``FileNode`` (or ``RemovedFileNode``) objects to be - *removed* in next commit. + Marks given ``FileNode`` objects to be *removed* in next commit. :raises ``NodeAlreadyRemovedError``: if node has been already marked to be *removed* @@ -1516,11 +1523,9 @@ class BaseInMemoryCommit(object): """ for node in filenodes: if node.path in (n.path for n in self.removed): - raise NodeAlreadyRemovedError( - "Node is already marked to for removal at %s" % node.path) + raise NodeAlreadyRemovedError("Node is already marked to for removal at %s" % node.path) if node.path in (n.path for n in self.changed): - raise NodeAlreadyChangedError( - "Node is already marked to be changed at %s" % node.path) + raise NodeAlreadyChangedError("Node is already marked to be changed at %s" % node.path) # We only mark node as *removed* - real removal is done by # commit method self.removed.append(node) @@ -1575,12 +1580,11 @@ class BaseInMemoryCommit(object): for p in parents: for node in self.added: try: - p.get_node(node.path) + p.get_node(node.bytes_path) except NodeDoesNotExistError: pass else: - raise NodeAlreadyExistsError( - f"Node `{node.path}` already exists at {p}") + raise NodeAlreadyExistsError(f"Node `{node.path}` already exists at {p}") # Check nodes marked as changed missing = set(self.changed) @@ -1590,7 +1594,7 @@ class BaseInMemoryCommit(object): for p in parents: for node in self.changed: try: - old = p.get_node(node.path) + old = p.get_node(node.bytes_path) missing.remove(node) # if content actually changed, remove node from not_changed if old.content != node.content: @@ -1598,24 +1602,23 @@ class BaseInMemoryCommit(object): except NodeDoesNotExistError: pass if self.changed and missing: - raise NodeDoesNotExistError( - f"Node `{node.path}` marked as modified but missing in parents: {parents}") + raise NodeDoesNotExistError(f"Node `{node.path}` marked as modified but missing in parents: {parents}") if self.changed and not_changed: raise NodeNotChangedError( - "Node `%s` wasn't actually changed (parents: %s)" - % (not_changed.pop().path, parents)) + "Node `%s` wasn't actually changed (parents: %s)" % (not_changed.pop().path, parents) + ) # Check nodes marked as removed if self.removed and not parents: raise NodeDoesNotExistError( - "Cannot remove node at %s as there " - "were no parents specified" % self.removed[0].path) + "Cannot remove node at %s as there " "were no parents specified" % self.removed[0].path + ) really_removed = set() for p in parents: for node in self.removed: try: - p.get_node(node.path) + p.get_node(node.bytes_path) really_removed.add(node) except CommitError: pass @@ -1623,8 +1626,8 @@ class BaseInMemoryCommit(object): if not_removed: # TODO: johbo: This code branch does not seem to be covered raise NodeDoesNotExistError( - "Cannot remove node at %s from " - "following parents: %s" % (not_removed, parents)) + "Cannot remove node at %s from " "following parents: %s" % (not_removed, parents) + ) def commit(self, message, author, parents=None, branch=None, date=None, **kwargs): """ @@ -1652,16 +1655,13 @@ class BaseInMemoryCommit(object): class BaseInMemoryChangesetClass(type): - def __instancecheck__(self, instance): return isinstance(instance, BaseInMemoryCommit) class BaseInMemoryChangeset(BaseInMemoryCommit, metaclass=BaseInMemoryChangesetClass): - def __new__(cls, *args, **kwargs): - warnings.warn( - "Use BaseCommit instead of BaseInMemoryCommit", DeprecationWarning) + warnings.warn("Use BaseCommit instead of BaseInMemoryCommit", DeprecationWarning) return super().__new__(cls, *args, **kwargs) @@ -1671,9 +1671,7 @@ class EmptyCommit(BaseCommit): an EmptyCommit """ - def __init__( - self, commit_id=EMPTY_COMMIT_ID, repo=None, alias=None, idx=-1, - message='', author='', date=None): + def __init__(self, commit_id=EMPTY_COMMIT_ID, repo=None, alias=None, idx=-1, message="", author="", date=None): self._empty_commit_id = commit_id # TODO: johbo: Solve idx parameter, default value does not make # too much sense @@ -1697,6 +1695,7 @@ class EmptyCommit(BaseCommit): def branch(self): if self.alias: from rhodecode.lib.vcs.backends import get_backend + return get_backend(self.alias).DEFAULT_BRANCH_NAME @LazyProperty @@ -1711,7 +1710,7 @@ class EmptyCommit(BaseCommit): return self def get_file_content(self, path) -> bytes: - return b'' + return b"" def get_file_content_streamed(self, path): yield self.get_file_content(path) @@ -1721,27 +1720,29 @@ class EmptyCommit(BaseCommit): class EmptyChangesetClass(type): - def __instancecheck__(self, instance): return isinstance(instance, EmptyCommit) class EmptyChangeset(EmptyCommit, metaclass=EmptyChangesetClass): - def __new__(cls, *args, **kwargs): - warnings.warn( - "Use EmptyCommit instead of EmptyChangeset", DeprecationWarning) + warnings.warn("Use EmptyCommit instead of EmptyChangeset", DeprecationWarning) return super(EmptyCommit, cls).__new__(cls, *args, **kwargs) - def __init__(self, cs=EMPTY_COMMIT_ID, repo=None, requested_revision=None, - alias=None, revision=-1, message='', author='', date=None): + def __init__( + self, + cs=EMPTY_COMMIT_ID, + repo=None, + requested_revision=None, + alias=None, + revision=-1, + message="", + author="", + date=None, + ): if requested_revision is not None: - warnings.warn( - "Parameter requested_revision not supported anymore", - DeprecationWarning) - super().__init__( - commit_id=cs, repo=repo, alias=alias, idx=revision, - message=message, author=author, date=date) + warnings.warn("Parameter requested_revision not supported anymore", DeprecationWarning) + super().__init__(commit_id=cs, repo=repo, alias=alias, idx=revision, message=message, author=author, date=date) @property def revision(self): @@ -1760,11 +1761,11 @@ class EmptyRepository(BaseRepository): def get_diff(self, *args, **kwargs): from rhodecode.lib.vcs.backends.git.diff import GitDiff - return GitDiff(b'') + + return GitDiff(b"") class CollectionGenerator(object): - def __init__(self, repo, commit_ids, collection_size=None, pre_load=None, translate_tag=None): self.repo = repo self.commit_ids = commit_ids @@ -1786,26 +1787,22 @@ class CollectionGenerator(object): """ Allows backends to override the way commits are generated. """ - return self.repo.get_commit( - commit_id=commit_id, pre_load=self.pre_load, - translate_tag=self.translate_tag) + return self.repo.get_commit(commit_id=commit_id, pre_load=self.pre_load, translate_tag=self.translate_tag) def __getitem__(self, key): """Return either a single element by index, or a sliced collection.""" if isinstance(key, slice): - commit_ids = self.commit_ids[key.start:key.stop] + commit_ids = self.commit_ids[key.start : key.stop] else: # single item commit_ids = self.commit_ids[key] - return self.__class__( - self.repo, commit_ids, pre_load=self.pre_load, - translate_tag=self.translate_tag) + return self.__class__(self.repo, commit_ids, pre_load=self.pre_load, translate_tag=self.translate_tag) def __repr__(self): - return '' % (self.__len__()) + return "" % (self.__len__()) class Config(object): @@ -1826,8 +1823,7 @@ class Config(object): return clone def __repr__(self): - return ''.format( - len(self._values), hex(id(self))) + return "".format(len(self._values), hex(id(self))) def items(self, section): return self._values.get(section, {}).items() @@ -1844,7 +1840,7 @@ class Config(object): def drop_option(self, section, option): if section not in self._values: - raise ValueError(f'Section {section} does not exist') + raise ValueError(f"Section {section} does not exist") del self._values[section][option] def serialize(self): @@ -1855,8 +1851,7 @@ class Config(object): items = [] for section in self._values: for option, value in self._values[section].items(): - items.append( - (safe_str(section), safe_str(option), safe_str(value))) + items.append((safe_str(section), safe_str(option), safe_str(value))) return items @@ -1867,12 +1862,13 @@ class Diff(object): Subclasses have to provide a backend specific value for :attr:`_header_re` and :attr:`_meta_re`. """ + _meta_re = None - _header_re: bytes = re.compile(br"") + _header_re: bytes = re.compile(rb"") def __init__(self, raw_diff: bytes): if not isinstance(raw_diff, bytes): - raise Exception(f'raw_diff must be bytes - got {type(raw_diff)}') + raise Exception(f"raw_diff must be bytes - got {type(raw_diff)}") self.raw = memoryview(raw_diff) @@ -1886,7 +1882,7 @@ class Diff(object): we can detect last chunk as this was also has special rule """ - diff_parts = (b'\n' + bytes(self.raw)).split(b'\ndiff --git') + diff_parts = (b"\n" + bytes(self.raw)).split(b"\ndiff --git") chunks = diff_parts[1:] total_chunks = len(chunks) @@ -1894,44 +1890,46 @@ class Diff(object): def diff_iter(_chunks): for cur_chunk, chunk in enumerate(_chunks, start=1): yield DiffChunk(chunk, self, cur_chunk == total_chunks) + return diff_iter(chunks) class DiffChunk(object): - def __init__(self, chunk: bytes, diff_obj: Diff, is_last_chunk: bool): self.diff_obj = diff_obj # since we split by \ndiff --git that part is lost from original diff # we need to re-apply it at the end, EXCEPT ! if it's last chunk if not is_last_chunk: - chunk += b'\n' + chunk += b"\n" header_re = self.diff_obj.get_header_re() + match = header_re.match(chunk) self.header = match.groupdict() - self.diff = chunk[match.end():] + self.diff = chunk[match.end() :] self.raw = chunk @property def header_as_str(self): if self.header: + def safe_str_on_bytes(val): if isinstance(val, bytes): return safe_str(val) return val + return {safe_str(k): safe_str_on_bytes(v) for k, v in self.header.items()} def __repr__(self): - return f'DiffChunk({self.header_as_str})' + return f"DiffChunk({self.header_as_str})" class BasePathPermissionChecker(object): - @staticmethod def create_from_patterns(includes, excludes): - if includes and '*' in includes and not excludes: + if includes and "*" in includes and not excludes: return AllPathPermissionChecker() - elif excludes and '*' in excludes: + elif excludes and "*" in excludes: return NonePathPermissionChecker() else: return PatternPathPermissionChecker(includes, excludes) @@ -1945,7 +1943,6 @@ class BasePathPermissionChecker(object): class AllPathPermissionChecker(BasePathPermissionChecker): - @property def has_full_access(self): return True @@ -1955,7 +1952,6 @@ class AllPathPermissionChecker(BasePathPermissionChecker): class NonePathPermissionChecker(BasePathPermissionChecker): - @property def has_full_access(self): return False @@ -1965,18 +1961,15 @@ class NonePathPermissionChecker(BasePathPermissionChecker): class PatternPathPermissionChecker(BasePathPermissionChecker): - def __init__(self, includes, excludes): self.includes = includes self.excludes = excludes - self.includes_re = [] if not includes else [ - re.compile(fnmatch.translate(pattern)) for pattern in includes] - self.excludes_re = [] if not excludes else [ - re.compile(fnmatch.translate(pattern)) for pattern in excludes] + self.includes_re = [] if not includes else [re.compile(fnmatch.translate(pattern)) for pattern in includes] + self.excludes_re = [] if not excludes else [re.compile(fnmatch.translate(pattern)) for pattern in excludes] @property def has_full_access(self): - return '*' in self.includes and not self.excludes + return "*" in self.includes and not self.excludes def has_access(self, path): for regex in self.excludes_re: diff --git a/rhodecode/lib/vcs/backends/git/commit.py b/rhodecode/lib/vcs/backends/git/commit.py index 89f90d98..2b658135 100644 --- a/rhodecode/lib/vcs/backends/git/commit.py +++ b/rhodecode/lib/vcs/backends/git/commit.py @@ -21,8 +21,8 @@ GIT commit module """ import io -import stat import configparser +import logging from itertools import chain from zope.cachedescriptors.property import Lazy as LazyProperty @@ -32,9 +32,16 @@ from rhodecode.lib.str_utils import safe_bytes, safe_str from rhodecode.lib.vcs.backends import base from rhodecode.lib.vcs.exceptions import CommitError, NodeDoesNotExistError from rhodecode.lib.vcs.nodes import ( - FileNode, DirNode, NodeKind, RootNode, SubModuleNode, - ChangedFileNodesGenerator, AddedFileNodesGenerator, - RemovedFileNodesGenerator, LargeFileNode) + FileNode, + DirNode, + NodeKind, + RootNode, + SubModuleNode, + LargeFileNode, +) +from rhodecode.lib.vcs_common import FILEMODE_LINK + +log = logging.getLogger(__name__) class GitCommit(base.BaseCommit): @@ -50,13 +57,11 @@ class GitCommit(base.BaseCommit): # done through a more complex tree walk on parents "status", # mercurial specific property not supported here - "_file_paths", + "obsolete", # mercurial specific property not supported here - 'obsolete', + "phase", # mercurial specific property not supported here - 'phase', - # mercurial specific property not supported here - 'hidden' + "hidden", ] def __init__(self, repository, raw_id, idx, pre_load=None): @@ -69,17 +74,16 @@ class GitCommit(base.BaseCommit): self._set_bulk_properties(pre_load) # caches - self._stat_modes = {} # stat info for paths - self._paths = {} # path processed with parse_tree self.nodes = {} + self._path_mode_cache = {} # path stats cache, e.g filemode etc + self._path_type_cache = {} # path type dir/file/link etc cache + self._submodules = None def _set_bulk_properties(self, pre_load): - if not pre_load: return - pre_load = [entry for entry in pre_load - if entry not in self._filter_pre_load] + pre_load = [entry for entry in pre_load if entry not in self._filter_pre_load] if not pre_load: return @@ -102,7 +106,7 @@ class GitCommit(base.BaseCommit): @LazyProperty def _tree_id(self): - return self._remote[self._commit['tree']]['id'] + return self._remote[self._commit["tree"]]["id"] @LazyProperty def id(self): @@ -134,13 +138,12 @@ class GitCommit(base.BaseCommit): """ Returns modified, added, removed, deleted files for current commit """ - return self.changed, self.added, self.removed + added, modified, deleted = self._changes_cache + return list(modified), list(modified), list(deleted) @LazyProperty def tags(self): - tags = [safe_str(name) for name, - commit_id in self.repository.tags.items() - if commit_id == self.raw_id] + tags = [safe_str(name) for name, commit_id in self.repository.tags.items() if commit_id == self.raw_id] return tags @LazyProperty @@ -161,47 +164,33 @@ class GitCommit(base.BaseCommit): branches = self._remote.branch(self.raw_id) return self._set_branch(branches) - def _get_tree_id_for_path(self, path): + def _get_path_tree_id_and_type(self, path: bytes): - path = safe_str(path) - if path in self._paths: - return self._paths[path] + if path in self._path_type_cache: + return self._path_type_cache[path] - tree_id = self._tree_id + if path == b"": + self._path_type_cache[b""] = [self._tree_id, NodeKind.DIR] + return self._path_type_cache[path] - path = path.strip('/') - if path == '': - data = [tree_id, "tree"] - self._paths[''] = data - return data - - tree_id, tree_type, tree_mode = \ - self._remote.tree_and_type_for_path(self.raw_id, path) + tree_id, tree_type, tree_mode = self._remote.tree_and_type_for_path(self.raw_id, path) if tree_id is None: raise self.no_node_at_path(path) - self._paths[path] = [tree_id, tree_type] - self._stat_modes[path] = tree_mode + self._path_type_cache[path] = [tree_id, tree_type] + self._path_mode_cache[path] = tree_mode - if path not in self._paths: - raise self.no_node_at_path(path) - - return self._paths[path] + return self._path_type_cache[path] def _get_kind(self, path): - tree_id, type_ = self._get_tree_id_for_path(path) - if type_ == 'blob': - return NodeKind.FILE - elif type_ == 'tree': - return NodeKind.DIR - elif type_ == 'link': - return NodeKind.SUBMODULE - return None + path = self._fix_path(path) + _, path_type = self._get_path_tree_id_and_type(path) + return path_type def _assert_is_path(self, path): path = self._fix_path(path) if self._get_kind(path) != NodeKind.FILE: - raise CommitError(f"File does not exist for commit {self.raw_id} at '{path}'") + raise CommitError(f"File at path={path} does not exist for commit {self.raw_id}") return path def _get_file_nodes(self): @@ -237,15 +226,19 @@ class GitCommit(base.BaseCommit): path = self._assert_is_path(path) # ensure path is traversed - self._get_tree_id_for_path(path) + self._get_path_tree_id_and_type(path) - return self._stat_modes[path] + return self._path_mode_cache[path] - def is_link(self, path): - return stat.S_ISLNK(self.get_file_mode(path)) + def is_link(self, path: bytes): + path = self._assert_is_path(path) + if path not in self._path_mode_cache: + self._path_mode_cache[path] = self._remote.fctx_flags(self.raw_id, path) + + return self._path_mode_cache[path] == FILEMODE_LINK def is_node_binary(self, path): - tree_id, _ = self._get_tree_id_for_path(path) + tree_id, _ = self._get_path_tree_id_and_type(path) return self._remote.is_binary(tree_id) def node_md5_hash(self, path): @@ -256,19 +249,19 @@ class GitCommit(base.BaseCommit): """ Returns content of the file at given `path`. """ - tree_id, _ = self._get_tree_id_for_path(path) + tree_id, _ = self._get_path_tree_id_and_type(path) return self._remote.blob_as_pretty_string(tree_id) def get_file_content_streamed(self, path): - tree_id, _ = self._get_tree_id_for_path(path) - stream_method = getattr(self._remote, 'stream:blob_as_pretty_string') + tree_id, _ = self._get_path_tree_id_and_type(path) + stream_method = getattr(self._remote, "stream:blob_as_pretty_string") return stream_method(tree_id) def get_file_size(self, path): """ Returns size of the file at given `path`. """ - tree_id, _ = self._get_tree_id_for_path(path) + tree_id, _ = self._get_path_tree_id_and_type(path) return self._remote.blob_raw_length(tree_id) def get_path_history(self, path, limit=None, pre_load=None): @@ -276,12 +269,9 @@ class GitCommit(base.BaseCommit): Returns history of file as reversed list of `GitCommit` objects for which file at given `path` has been modified. """ - path = self._assert_is_path(path) - hist = self._remote.node_history(self.raw_id, path, limit) - return [ - self.repository.get_commit(commit_id=commit_id, pre_load=pre_load) - for commit_id in hist] + history = self._remote.node_history(self.raw_id, path, limit) + return [self.repository.get_commit(commit_id=commit_id, pre_load=pre_load) for commit_id in history] def get_file_annotate(self, path, pre_load=None): """ @@ -293,95 +283,105 @@ class GitCommit(base.BaseCommit): for ln_no, commit_id, content in result: yield ( - ln_no, commit_id, + ln_no, + commit_id, lambda: self.repository.get_commit(commit_id=commit_id, pre_load=pre_load), - content) + content, + ) - def get_nodes(self, path, pre_load=None): + def get_nodes(self, path: bytes, pre_load=None): if self._get_kind(path) != NodeKind.DIR: - raise CommitError( - f"Directory does not exist for commit {self.raw_id} at '{path}'") + raise CommitError(f"Directory does not exist for commit {self.raw_id} at '{path}'") path = self._fix_path(path) - tree_id, _ = self._get_tree_id_for_path(path) + # call and check tree_id for this path + tree_id, _ = self._get_path_tree_id_and_type(path) - dirnodes = [] - filenodes = [] + path_nodes = [] - # extracted tree ID gives us our files... - str_path = safe_str(path) # libgit operates on bytes - for name, stat_, id_, type_ in self._remote.tree_items(tree_id): - if type_ == 'link': - url = self._get_submodule_url('/'.join((str_path, name))) - dirnodes.append(SubModuleNode( - name, url=url, commit=id_, alias=self.repository.alias)) - continue + for bytes_name, stat_, tree_item_id, node_kind in self._remote.tree_items(tree_id): + if node_kind is None: + raise CommitError(f"Requested object type={node_kind} cannot be determined") - if str_path != '': - obj_path = '/'.join((str_path, name)) + if path != b"": + obj_path = b"/".join((path, bytes_name)) else: - obj_path = name - if obj_path not in self._stat_modes: - self._stat_modes[obj_path] = stat_ + obj_path = bytes_name - if type_ == 'tree': - dirnodes.append(DirNode(safe_bytes(obj_path), commit=self)) - elif type_ == 'blob': - filenodes.append(FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load)) + # cache file mode for git, since we have it already + if obj_path not in self._path_mode_cache: + self._path_mode_cache[obj_path] = stat_ + + # cache type + if node_kind not in self._path_type_cache: + self._path_type_cache[obj_path] = [tree_item_id, node_kind] + + entry = None + if obj_path in self.nodes: + entry = self.nodes[obj_path] else: - raise CommitError(f"Requested object should be Tree or Blob, is {type_}") + if node_kind == NodeKind.SUBMODULE: + url = self._get_submodule_url(b"/".join((path, bytes_name))) + entry= SubModuleNode(bytes_name, url=url, commit=tree_item_id, alias=self.repository.alias) + elif node_kind == NodeKind.DIR: + entry = DirNode(safe_bytes(obj_path), commit=self) + elif node_kind == NodeKind.FILE: + entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load) - nodes = dirnodes + filenodes - for node in nodes: - if node.path not in self.nodes: - self.nodes[node.path] = node - nodes.sort() - return nodes + if entry: + self.nodes[obj_path] = entry + path_nodes.append(entry) - def get_node(self, path, pre_load=None): + path_nodes.sort() + return path_nodes + + def get_node(self, path: bytes, pre_load=None): path = self._fix_path(path) - if path not in self.nodes: - try: - tree_id, type_ = self._get_tree_id_for_path(path) - except CommitError: - raise NodeDoesNotExistError( - f"Cannot find one of parents' directories for a given " - f"path: {path}") - if type_ in ['link', 'commit']: + # use cached, if we have one + if path in self.nodes: + return self.nodes[path] + + try: + tree_id, path_type = self._get_path_tree_id_and_type(path) + except CommitError: + raise NodeDoesNotExistError(f"Cannot find one of parents' directories for a given path: {path}") + + if path == b"": + node = RootNode(commit=self) + else: + if path_type == NodeKind.SUBMODULE: url = self._get_submodule_url(path) - node = SubModuleNode(path, url=url, commit=tree_id, - alias=self.repository.alias) - elif type_ == 'tree': - if path == '': - node = RootNode(commit=self) - else: - node = DirNode(safe_bytes(path), commit=self) - elif type_ == 'blob': + node = SubModuleNode(path, url=url, commit=tree_id, alias=self.repository.alias) + elif path_type == NodeKind.DIR: + node = DirNode(safe_bytes(path), commit=self) + elif path_type == NodeKind.FILE: node = FileNode(safe_bytes(path), commit=self, pre_load=pre_load) - self._stat_modes[path] = node.mode + self._path_mode_cache[path] = node.mode else: raise self.no_node_at_path(path) - # cache node - self.nodes[path] = node - + # cache node + self.nodes[path] = node return self.nodes[path] - def get_largefile_node(self, path): - tree_id, _ = self._get_tree_id_for_path(path) + def get_largefile_node(self, path: bytes): + tree_id, _ = self._get_path_tree_id_and_type(path) pointer_spec = self._remote.is_large_file(tree_id) if pointer_spec: # content of that file regular FileNode is the hash of largefile - file_id = pointer_spec.get('oid_hash') - if self._remote.in_largefiles_store(file_id): - lf_path = self._remote.store_path(file_id) - return LargeFileNode(safe_bytes(lf_path), commit=self, org_path=path) + file_id = pointer_spec.get("oid_hash") + if not self._remote.in_largefiles_store(file_id): + log.warning(f'Largefile oid={file_id} not found in store') + return None + + lf_path = self._remote.store_path(file_id) + return LargeFileNode(safe_bytes(lf_path), commit=self, org_path=path) @LazyProperty - def affected_files(self): + def affected_files(self) -> list[bytes]: """ Gets a fast accessible file changes for given commit """ @@ -389,7 +389,7 @@ class GitCommit(base.BaseCommit): return list(added.union(modified).union(deleted)) @LazyProperty - def _changes_cache(self): + def _changes_cache(self) -> tuple[set, set, set]: added = set() modified = set() deleted = set() @@ -416,53 +416,22 @@ class GitCommit(base.BaseCommit): :param status: one of: *added*, *modified* or *deleted* """ added, modified, deleted = self._changes_cache - return sorted({ - 'added': list(added), - 'modified': list(modified), - 'deleted': list(deleted)}[status] - ) - - @LazyProperty - def added(self): - """ - Returns list of added ``FileNode`` objects. - """ - if not self.parents: - return list(self._get_file_nodes()) - return AddedFileNodesGenerator(self.added_paths, self) + return sorted({"added": list(added), "modified": list(modified), "deleted": list(deleted)}[status]) @LazyProperty def added_paths(self): - return [n for n in self._get_paths_for_status('added')] - - @LazyProperty - def changed(self): - """ - Returns list of modified ``FileNode`` objects. - """ - if not self.parents: - return [] - return ChangedFileNodesGenerator(self.changed_paths, self) + return [n for n in self._get_paths_for_status("added")] @LazyProperty def changed_paths(self): - return [n for n in self._get_paths_for_status('modified')] - - @LazyProperty - def removed(self): - """ - Returns list of removed ``FileNode`` objects. - """ - if not self.parents: - return [] - return RemovedFileNodesGenerator(self.removed_paths, self) + return [n for n in self._get_paths_for_status("modified")] @LazyProperty def removed_paths(self): - return [n for n in self._get_paths_for_status('deleted')] + return [n for n in self._get_paths_for_status("deleted")] - def _get_submodule_url(self, submodule_path): - git_modules_path = '.gitmodules' + def _get_submodule_url(self, submodule_path: bytes): + git_modules_path = b".gitmodules" if self._submodules is None: self._submodules = {} @@ -476,9 +445,9 @@ class GitCommit(base.BaseCommit): parser.read_file(io.StringIO(submodules_node.str_content)) for section in parser.sections(): - path = parser.get(section, 'path') - url = parser.get(section, 'url') + path = parser.get(section, "path") + url = parser.get(section, "url") if path and url: - self._submodules[path.strip('/')] = url + self._submodules[safe_bytes(path).strip(b"/")] = url - return self._submodules.get(submodule_path.strip('/')) + return self._submodules.get(submodule_path.strip(b"/")) diff --git a/rhodecode/lib/vcs/backends/git/repository.py b/rhodecode/lib/vcs/backends/git/repository.py index c633ccb7..f3c4c4d7 100644 --- a/rhodecode/lib/vcs/backends/git/repository.py +++ b/rhodecode/lib/vcs/backends/git/repository.py @@ -425,7 +425,7 @@ class GitRepository(BaseRepository): return def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, - translate_tag=True, maybe_unreachable=False, reference_obj=None): + translate_tag=True, maybe_unreachable=False, reference_obj=None) -> GitCommit: """ Returns `GitCommit` object representing commit from git repository at the given `commit_id` or head (most recent commit) if None given. diff --git a/rhodecode/lib/vcs/backends/hg/commit.py b/rhodecode/lib/vcs/backends/hg/commit.py index d79339c2..03b5bfd9 100644 --- a/rhodecode/lib/vcs/backends/hg/commit.py +++ b/rhodecode/lib/vcs/backends/hg/commit.py @@ -20,20 +20,25 @@ HG commit module """ -import os +import logging from zope.cachedescriptors.property import Lazy as LazyProperty from rhodecode.lib.datelib import utcdate_fromtimestamp from rhodecode.lib.str_utils import safe_bytes, safe_str -from rhodecode.lib.vcs import path as vcspath from rhodecode.lib.vcs.backends import base from rhodecode.lib.vcs.exceptions import CommitError from rhodecode.lib.vcs.nodes import ( - AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode, - NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode, - LargeFileNode) -from rhodecode.lib.vcs.utils.paths import get_dirs_for_path + DirNode, + FileNode, + NodeKind, + RootNode, + SubModuleNode, + LargeFileNode, +) +from rhodecode.lib.vcs_common import FILEMODE_LINK + +log = logging.getLogger(__name__) class MercurialCommit(base.BaseCommit): @@ -59,13 +64,13 @@ class MercurialCommit(base.BaseCommit): # caches self.nodes = {} - self._stat_modes = {} # stat info for paths + self._path_mode_cache = {} # path stats cache, e.g filemode etc + self._path_type_cache = {} # path type dir/file/link etc cache def _set_bulk_properties(self, pre_load): if not pre_load: return - pre_load = [entry for entry in pre_load - if entry not in self._filter_pre_load] + pre_load = [entry for entry in pre_load if entry not in self._filter_pre_load] if not pre_load: return @@ -86,8 +91,7 @@ class MercurialCommit(base.BaseCommit): @LazyProperty def tags(self): - tags = [name for name, commit_id in self.repository.tags.items() - if commit_id == self.raw_id] + tags = [name for name, commit_id in self.repository.tags.items() if commit_id == self.raw_id] return tags @LazyProperty @@ -96,9 +100,7 @@ class MercurialCommit(base.BaseCommit): @LazyProperty def bookmarks(self): - bookmarks = [ - name for name, commit_id in self.repository.bookmarks.items() - if commit_id == self.raw_id] + bookmarks = [name for name, commit_id in self.repository.bookmarks.items() if commit_id == self.raw_id] return bookmarks @LazyProperty @@ -122,27 +124,13 @@ class MercurialCommit(base.BaseCommit): """ Returns modified, added, removed, deleted files for current commit """ - return self._remote.ctx_status(self.raw_id) - - @LazyProperty - def _file_paths(self): - return self._remote.ctx_list(self.raw_id) - - @LazyProperty - def _dir_paths(self): - dir_paths = [''] - dir_paths.extend(list(set(get_dirs_for_path(*self._file_paths)))) - - return dir_paths - - @LazyProperty - def _paths(self): - return self._dir_paths + self._file_paths + modified, added, deleted, *_ = self._remote.ctx_status(self.raw_id) + return modified, added, deleted @LazyProperty def id(self): if self.last: - return 'tip' + return "tip" return self.short_id @LazyProperty @@ -150,8 +138,7 @@ class MercurialCommit(base.BaseCommit): return self.raw_id[:12] def _make_commits(self, commit_ids, pre_load=None): - return [self.repository.get_commit(commit_id=commit_id, pre_load=pre_load) - for commit_id in commit_ids] + return [self.repository.get_commit(commit_id=commit_id, pre_load=pre_load) for commit_id in commit_ids] @LazyProperty def parents(self): @@ -163,10 +150,10 @@ class MercurialCommit(base.BaseCommit): def _get_phase_text(self, phase_id): return { - 0: 'public', - 1: 'draft', - 2: 'secret', - }.get(phase_id) or '' + 0: "public", + 1: "draft", + 2: "secret", + }.get(phase_id) or "" @LazyProperty def phase(self): @@ -195,17 +182,14 @@ class MercurialCommit(base.BaseCommit): def _get_kind(self, path): path = self._fix_path(path) - if path in self._file_paths: - return NodeKind.FILE - elif path in self._dir_paths: - return NodeKind.DIR - else: - raise CommitError(f"Node does not exist at the given path '{path}'") + path_type = self._get_path_type(path) + return path_type - def _assert_is_path(self, path) -> str: + def _assert_is_path(self, path) -> str | bytes: path = self._fix_path(path) + if self._get_kind(path) != NodeKind.FILE: - raise CommitError(f"File does not exist for commit {self.raw_id} at '{path}'") + raise CommitError(f"File at path={path} does not exist for commit {self.raw_id}") return path @@ -214,20 +198,17 @@ class MercurialCommit(base.BaseCommit): Returns stat mode of the file at the given ``path``. """ path = self._assert_is_path(path) + if path not in self._path_mode_cache: + self._path_mode_cache[path] = self._remote.fctx_flags(self.raw_id, path) - if path not in self._stat_modes: - self._stat_modes[path] = self._remote.fctx_flags(self.raw_id, path) + return self._path_mode_cache[path] - if 'x' in self._stat_modes[path]: - return base.FILEMODE_EXECUTABLE - return base.FILEMODE_DEFAULT - - def is_link(self, path): + def is_link(self, path: bytes): path = self._assert_is_path(path) - if path not in self._stat_modes: - self._stat_modes[path] = self._remote.fctx_flags(self.raw_id, path) + if path not in self._path_mode_cache: + self._path_mode_cache[path] = self._remote.fctx_flags(self.raw_id, path) - return 'l' in self._stat_modes[path] + return self._path_mode_cache[path] == FILEMODE_LINK def is_node_binary(self, path): path = self._assert_is_path(path) @@ -246,7 +227,7 @@ class MercurialCommit(base.BaseCommit): def get_file_content_streamed(self, path): path = self._assert_is_path(path) - stream_method = getattr(self._remote, 'stream:fctx_node_data') + stream_method = getattr(self._remote, "stream:fctx_node_data") return stream_method(self.raw_id, path) def get_file_size(self, path): @@ -262,10 +243,8 @@ class MercurialCommit(base.BaseCommit): for which file at given ``path`` has been modified. """ path = self._assert_is_path(path) - hist = self._remote.node_history(self.raw_id, path, limit) - return [ - self.repository.get_commit(commit_id=commit_id, pre_load=pre_load) - for commit_id in hist] + history = self._remote.node_history(self.raw_id, path, limit) + return [self.repository.get_commit(commit_id=commit_id, pre_load=pre_load) for commit_id in history] def get_file_annotate(self, path, pre_load=None): """ @@ -276,11 +255,13 @@ class MercurialCommit(base.BaseCommit): for ln_no, commit_id, content in result: yield ( - ln_no, commit_id, + ln_no, + commit_id, lambda: self.repository.get_commit(commit_id=commit_id, pre_load=pre_load), - content) + content, + ) - def get_nodes(self, path, pre_load=None): + def get_nodes(self, path: bytes, pre_load=None): """ Returns combined ``DirNode`` and ``FileNode`` objects list representing state of commit at the given ``path``. If node at the given ``path`` @@ -288,59 +269,86 @@ class MercurialCommit(base.BaseCommit): """ if self._get_kind(path) != NodeKind.DIR: - raise CommitError( - f"Directory does not exist for idx {self.raw_id} at '{path}'") + raise CommitError(f"Directory does not exist for idx {self.raw_id} at '{path}'") path = self._fix_path(path) - filenodes = [ - FileNode(safe_bytes(f), commit=self, pre_load=pre_load) for f in self._file_paths - if os.path.dirname(f) == path] - # TODO: johbo: Check if this can be done in a more obvious way - dirs = path == '' and '' or [ - d for d in self._dir_paths - if d and vcspath.dirname(d) == path] - dirnodes = [ - DirNode(safe_bytes(d), commit=self) for d in dirs - if os.path.dirname(d) == path] + path_nodes = [] - alias = self.repository.alias - for k, vals in self._submodules.items(): - if vcspath.dirname(k) == path: - loc = vals[0] - commit = vals[1] - dirnodes.append(SubModuleNode(k, url=loc, commit=commit, alias=alias)) + for obj_path, node_kind in self._remote.dir_items(self.raw_id, path): - nodes = dirnodes + filenodes - for node in nodes: - if node.path not in self.nodes: - self.nodes[node.path] = node - nodes.sort() + if node_kind is None: + raise CommitError(f"Requested object type={node_kind} cannot be mapped to a proper type") - return nodes + # TODO: implement it ?? + stat_ = None + # # cache file mode + # if obj_path not in self._path_mode_cache: + # self._path_mode_cache[obj_path] = stat_ - def get_node(self, path, pre_load=None): + # cache type + if node_kind not in self._path_type_cache: + self._path_type_cache[obj_path] = node_kind + + entry = None + if obj_path in self.nodes: + entry = self.nodes[obj_path] + else: + if node_kind == NodeKind.DIR: + entry = DirNode(safe_bytes(obj_path), commit=self) + elif node_kind == NodeKind.FILE: + entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load) + if entry: + self.nodes[obj_path] = entry + path_nodes.append(entry) + + path_nodes.sort() + return path_nodes + + def get_node(self, path: bytes, pre_load=None): """ Returns `Node` object from the given `path`. If there is no node at the given `path`, `NodeDoesNotExistError` would be raised. """ path = self._fix_path(path) - if path not in self.nodes: - if path in self._file_paths: + # use cached, if we have one + if path in self.nodes: + return self.nodes[path] + + path_type = self._get_path_type(path) + if path == b"": + node = RootNode(commit=self) + else: + if path_type == NodeKind.DIR: + node = DirNode(safe_bytes(path), commit=self) + elif path_type == NodeKind.FILE: node = FileNode(safe_bytes(path), commit=self, pre_load=pre_load) - elif path in self._dir_paths: - if path == '': - node = RootNode(commit=self) - else: - node = DirNode(safe_bytes(path), commit=self) + self._path_mode_cache[path] = node.mode else: raise self.no_node_at_path(path) - - # cache node - self.nodes[path] = node + # cache node + self.nodes[path] = node return self.nodes[path] - def get_largefile_node(self, path): + def _get_path_type(self, path: bytes): + if path in self._path_type_cache: + return self._path_type_cache[path] + + if path == b"": + self._path_type_cache[b""] = NodeKind.DIR + return NodeKind.DIR + + path_type, flags = self._remote.get_path_type(self.raw_id, path) + + if not path_type: + raise self.no_node_at_path(path) + + self._path_type_cache[path] = path_type + self._path_mode_cache[path] = flags + + return self._path_type_cache[path] + + def get_largefile_node(self, path: bytes): pointer_spec = self._remote.is_large_file(self.raw_id, path) if pointer_spec: # content of that file regular FileNode is the hash of largefile @@ -363,40 +371,20 @@ class MercurialCommit(base.BaseCommit): return self._remote.ctx_substate(self.raw_id) @LazyProperty - def affected_files(self): + def affected_files(self) -> list[bytes]: """ Gets a fast accessible file changes for given commit """ return self._remote.ctx_files(self.raw_id) - @property - def added(self): - """ - Returns list of added ``FileNode`` objects. - """ - return AddedFileNodesGenerator(self.added_paths, self) - @LazyProperty def added_paths(self): return [n for n in self.status[1]] - @property - def changed(self): - """ - Returns list of modified ``FileNode`` objects. - """ - return ChangedFileNodesGenerator(self.changed_paths, self) - @LazyProperty def changed_paths(self): return [n for n in self.status[0]] - @property - def removed(self): - """ - Returns list of removed ``FileNode`` objects. - """ - return RemovedFileNodesGenerator(self.removed_paths, self) @LazyProperty def removed_paths(self): diff --git a/rhodecode/lib/vcs/backends/hg/repository.py b/rhodecode/lib/vcs/backends/hg/repository.py index dbe4f612..ecd65ac2 100644 --- a/rhodecode/lib/vcs/backends/hg/repository.py +++ b/rhodecode/lib/vcs/backends/hg/repository.py @@ -450,7 +450,7 @@ class MercurialRepository(BaseRepository): return os.path.join(self.path, '.hg', '.hgrc') def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, - translate_tag=None, maybe_unreachable=False, reference_obj=None): + translate_tag=None, maybe_unreachable=False, reference_obj=None) -> MercurialCommit: """ Returns ``MercurialCommit`` object representing repository's commit at the given `commit_id` or `commit_idx`. @@ -598,8 +598,7 @@ class MercurialRepository(BaseRepository): """ Create a local clone of the current repo. """ - self._remote.clone(self.path, clone_path, update_after_clone=True, - hooks=False) + self._remote.clone(self.path, clone_path, update_after_clone=True, hooks=False) def _update(self, revision, clean=False): """ diff --git a/rhodecode/lib/vcs/backends/svn/commit.py b/rhodecode/lib/vcs/backends/svn/commit.py index c7fbec84..4d26e731 100644 --- a/rhodecode/lib/vcs/backends/svn/commit.py +++ b/rhodecode/lib/vcs/backends/svn/commit.py @@ -19,8 +19,7 @@ """ SVN commit module """ - - +import logging import dateutil.parser from zope.cachedescriptors.property import Lazy as LazyProperty @@ -28,9 +27,10 @@ from rhodecode.lib.str_utils import safe_bytes, safe_str from rhodecode.lib.vcs import nodes, path as vcspath from rhodecode.lib.vcs.backends import base from rhodecode.lib.vcs.exceptions import CommitError +from vcsserver.lib.vcs_common import NodeKind, FILEMODE_EXECUTABLE, FILEMODE_DEFAULT, FILEMODE_LINK +_SVN_PROP_TRUE = "*" - -_SVN_PROP_TRUE = '*' +log = logging.getLogger(__name__) class SubversionCommit(base.BaseCommit): @@ -53,15 +53,16 @@ class SubversionCommit(base.BaseCommit): # which knows how to translate commit index and commit id self.raw_id = commit_id self.short_id = commit_id - self.id = f'r{commit_id}' + self.id = f"r{commit_id}" - # TODO: Implement the following placeholder attributes self.nodes = {} + self._path_mode_cache = {} # path stats cache, e.g filemode etc + self._path_type_cache = {} # path type dir/file/link etc cache self.tags = [] @property def author(self): - return safe_str(self._properties.get('svn:author')) + return safe_str(self._properties.get("svn:author")) @property def date(self): @@ -69,7 +70,7 @@ class SubversionCommit(base.BaseCommit): @property def message(self): - return safe_str(self._properties.get('svn:log')) + return safe_str(self._properties.get("svn:log")) @LazyProperty def _properties(self): @@ -91,19 +92,46 @@ class SubversionCommit(base.BaseCommit): return [child] return [] - def get_file_mode(self, path: bytes): + def _calculate_file_mode(self, path: bytes): # Note: Subversion flags files which are executable with a special # property `svn:executable` which is set to the value ``"*"``. - if self._get_file_property(path, 'svn:executable') == _SVN_PROP_TRUE: - return base.FILEMODE_EXECUTABLE + if self._get_file_property(path, "svn:executable") == _SVN_PROP_TRUE: + return FILEMODE_EXECUTABLE else: - return base.FILEMODE_DEFAULT + return FILEMODE_DEFAULT - def is_link(self, path): + def get_file_mode(self, path: bytes): + path = self._fix_path(path) + + if path not in self._path_mode_cache: + self._path_mode_cache[path] = self._calculate_file_mode(path) + + return self._path_mode_cache[path] + + def _get_path_type(self, path: bytes): + if path in self._path_type_cache: + return self._path_type_cache[path] + + if path == b"": + self._path_type_cache[b""] = NodeKind.DIR + return NodeKind.DIR + + path_type = self._remote.get_node_type(self._svn_rev, path) + + if not path_type: + raise self.no_node_at_path(path) + + #flags = None + self._path_type_cache[path] = path_type + #self._path_mode_cache[path] = flags + + return self._path_type_cache[path] + + def is_link(self, path: bytes): # Note: Subversion has a flag for special files, the content of the # file contains the type of that file. - if self._get_file_property(path, 'svn:special') == _SVN_PROP_TRUE: - return self.get_file_content(path).startswith(b'link') + if self._get_file_property(path, "svn:special") == _SVN_PROP_TRUE: + return self.get_file_content(path).startswith(b"link") return False def is_node_binary(self, path): @@ -115,8 +143,7 @@ class SubversionCommit(base.BaseCommit): return self._remote.md5_hash(self._svn_rev, safe_str(path)) def _get_file_property(self, path, name): - file_properties = self._remote.node_properties( - safe_str(path), self._svn_rev) + file_properties = self._remote.node_properties(safe_str(path), self._svn_rev) return file_properties.get(name) def get_file_content(self, path): @@ -126,7 +153,7 @@ class SubversionCommit(base.BaseCommit): def get_file_content_streamed(self, path): path = self._fix_path(path) - stream_method = getattr(self._remote, 'stream:get_file_content') + stream_method = getattr(self._remote, "stream:get_file_content") return stream_method(self._svn_rev, safe_str(path)) def get_file_size(self, path): @@ -134,11 +161,9 @@ class SubversionCommit(base.BaseCommit): return self._remote.get_file_size(self._svn_rev, safe_str(path)) def get_path_history(self, path, limit=None, pre_load=None): - path = safe_str(self._fix_path(path)) - history = self._remote.node_history(path, self._svn_rev, limit) - return [ - self.repository.get_commit(commit_id=str(svn_rev)) - for svn_rev in history] + path = self._fix_path(path) + history = self._remote.node_history(self._svn_rev, safe_str(path), limit) + return [self.repository.get_commit(commit_id=str(svn_rev)) for svn_rev in history] def get_file_annotate(self, path, pre_load=None): result = self._remote.file_annotate(safe_str(path), self._svn_rev) @@ -146,67 +171,78 @@ class SubversionCommit(base.BaseCommit): for zero_based_line_no, svn_rev, content in result: commit_id = str(svn_rev) line_no = zero_based_line_no + 1 - yield ( - line_no, - commit_id, - lambda: self.repository.get_commit(commit_id=commit_id), - content) + yield line_no, commit_id, lambda: self.repository.get_commit(commit_id=commit_id), content - def get_node(self, path, pre_load=None): + def get_node(self, path: bytes, pre_load=None): path = self._fix_path(path) - if path not in self.nodes: - if path == '': - node = nodes.RootNode(commit=self) + # use cached, if we have one + if path in self.nodes: + return self.nodes[path] + + path_type = self._get_path_type(path) + if path == b"": + node = nodes.RootNode(commit=self) + else: + if path_type == NodeKind.DIR: + node = nodes.DirNode(safe_bytes(path), commit=self) + elif path_type == NodeKind.FILE: + node = nodes.FileNode(safe_bytes(path), commit=self, pre_load=pre_load) + self._path_mode_cache[path] = node.mode else: - node_type = self._remote.get_node_type(self._svn_rev, safe_str(path)) - if node_type == 'dir': - node = nodes.DirNode(safe_bytes(path), commit=self) - elif node_type == 'file': - node = nodes.FileNode(safe_bytes(path), commit=self, pre_load=pre_load) - else: - raise self.no_node_at_path(path) + raise self.no_node_at_path(path) - self.nodes[path] = node + self.nodes[path] = node return self.nodes[path] - def get_nodes(self, path, pre_load=None): + def get_nodes(self, path: bytes, pre_load=None): if self._get_kind(path) != nodes.NodeKind.DIR: - raise CommitError( - f"Directory does not exist for commit {self.raw_id} at '{path}'") - path = safe_str(self._fix_path(path)) + raise CommitError(f"Directory does not exist for commit {self.raw_id} at '{path}'") + path = self._fix_path(path) path_nodes = [] - for name, kind in self._remote.get_nodes(self._svn_rev, path): - node_path = vcspath.join(path, name) - if kind == 'dir': - node = nodes.DirNode(safe_bytes(node_path), commit=self) - elif kind == 'file': - node = nodes.FileNode(safe_bytes(node_path), commit=self, pre_load=pre_load) - else: - raise ValueError(f"Node kind {kind} not supported.") - self.nodes[node_path] = node - path_nodes.append(node) + for name, node_kind in self._remote.get_nodes(self._svn_rev, path): + obj_path = vcspath.join(path, name) + if node_kind is None: + raise CommitError(f"Requested object type={node_kind} cannot be determined") + + # TODO: implement it ?? + stat_ = None + # # cache file mode + # if obj_path not in self._path_mode_cache: + # self._path_mode_cache[obj_path] = stat_ + + # cache type + if node_kind not in self._path_type_cache: + self._path_type_cache[obj_path] = node_kind + + entry = None + if obj_path in self.nodes: + entry = self.nodes[obj_path] + else: + if node_kind == NodeKind.DIR: + entry = nodes.DirNode(safe_bytes(obj_path), commit=self) + elif node_kind == NodeKind.FILE: + entry = nodes.FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load) + if entry: + self.nodes[obj_path] = entry + path_nodes.append(entry) + + path_nodes.sort() return path_nodes def _get_kind(self, path): path = self._fix_path(path) - kind = self._remote.get_node_type(self._svn_rev, path) - if kind == 'file': - return nodes.NodeKind.FILE - elif kind == 'dir': - return nodes.NodeKind.DIR - else: - raise CommitError( - f"Node does not exist at the given path '{path}'") + path_type = self._get_path_type(path) + return path_type @LazyProperty def _changes_cache(self): return self._remote.revision_changes(self._svn_rev) @LazyProperty - def affected_files(self): + def affected_files(self) -> list[bytes]: changed_files = set() for files in self._changes_cache.values(): changed_files.update(files) @@ -216,29 +252,17 @@ class SubversionCommit(base.BaseCommit): def id(self): return self.raw_id - @property - def added(self): - return nodes.AddedFileNodesGenerator(self.added_paths, self) - @LazyProperty def added_paths(self): - return [n for n in self._changes_cache['added']] - - @property - def changed(self): - return nodes.ChangedFileNodesGenerator(self.changed_paths, self) + return [n for n in self._changes_cache["added"]] @LazyProperty def changed_paths(self): - return [n for n in self._changes_cache['changed']] - - @property - def removed(self): - return nodes.RemovedFileNodesGenerator(self.removed_paths, self) + return [n for n in self._changes_cache["changed"]] @LazyProperty def removed_paths(self): - return [n for n in self._changes_cache['removed']] + return [n for n in self._changes_cache["removed"]] def _date_from_svn_properties(properties): @@ -248,7 +272,7 @@ def _date_from_svn_properties(properties): :return: :class:`datetime.datetime` instance. The object is naive. """ - aware_date = dateutil.parser.parse(properties.get('svn:date')) + aware_date = dateutil.parser.parse(properties.get("svn:date")) # final_date = aware_date.astimezone(dateutil.tz.tzlocal()) final_date = aware_date return final_date.replace(tzinfo=None) diff --git a/rhodecode/lib/vcs/backends/svn/repository.py b/rhodecode/lib/vcs/backends/svn/repository.py index 7ec4e9d3..97e91066 100644 --- a/rhodecode/lib/vcs/backends/svn/repository.py +++ b/rhodecode/lib/vcs/backends/svn/repository.py @@ -30,7 +30,7 @@ from zope.cachedescriptors.property import Lazy as LazyProperty from collections import OrderedDict from rhodecode.lib.datelib import date_astimestamp -from rhodecode.lib.str_utils import safe_str +from rhodecode.lib.str_utils import safe_str, safe_bytes from rhodecode.lib.utils2 import CachedProperty from rhodecode.lib.vcs import connection, path as vcspath from rhodecode.lib.vcs.backends import base @@ -157,16 +157,18 @@ class SubversionRepository(base.BaseRepository): for pattern in self._patterns_from_section(config_section): pattern = vcspath.sanitize(pattern) + bytes_pattern = safe_bytes(pattern) + tip = self.get_commit() try: - if pattern.endswith('*'): - basedir = tip.get_node(vcspath.dirname(pattern)) + if bytes_pattern.endswith(b'*'): + basedir = tip.get_node(vcspath.dirname(bytes_pattern)) directories = basedir.dirs else: - directories = (tip.get_node(pattern), ) + directories = (tip.get_node(bytes_pattern), ) except NodeDoesNotExistError: continue - found_items.update((safe_str(n.path), self.commit_ids[-1]) for n in directories) + found_items.update((dir_node.str_path, self.commit_ids[-1]) for dir_node in directories) def get_name(item): return item[0] @@ -216,7 +218,7 @@ class SubversionRepository(base.BaseRepository): def _get_commit_idx(self, commit_id): try: svn_rev = int(commit_id) - except: + except Exception: # TODO: johbo: this might be only one case, HEAD, check this svn_rev = self._remote.lookup(commit_id) commit_idx = svn_rev - 1 @@ -321,8 +323,7 @@ class SubversionRepository(base.BaseRepository): # TODO: johbo: Reconsider impact of DEFAULT_BRANCH_NAME here if branch_name not in [None, self.DEFAULT_BRANCH_NAME]: svn_rev = int(self.commit_ids[-1]) - commit_ids = self._remote.node_history( - path=branch_name, revision=svn_rev, limit=None) + commit_ids = self._remote.node_history(svn_rev, branch_name, None) commit_ids = [str(i) for i in reversed(commit_ids)] if start_pos or end_pos: diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py index 75eb01cc..59ed0110 100644 --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -27,14 +27,13 @@ import time import urllib.request import urllib.error import urllib.parse -import urllib.parse import uuid import traceback import pycurl import msgpack import requests -from requests.packages.urllib3.util.retry import Retry +from urllib3.util.retry import Retry import rhodecode from rhodecode.lib import rc_cache @@ -287,7 +286,7 @@ class RemoteRepo(object): 'fctx_size', 'stream:fctx_node_data', 'blob_raw_length', 'node_history', 'revision', 'tree_items', - 'ctx_list', 'ctx_branch', 'ctx_description', + 'ctx_branch', 'ctx_description', 'bulk_request', 'assert_correct_path', 'is_path_valid_repository', diff --git a/rhodecode/lib/vcs/compat.py b/rhodecode/lib/vcs/compat.py index 8a6bbc41..82daea14 100644 --- a/rhodecode/lib/vcs/compat.py +++ b/rhodecode/lib/vcs/compat.py @@ -1 +1 @@ -from pyramid.compat import configparser \ No newline at end of file +from pyramid.compat import configparser diff --git a/rhodecode/lib/vcs/conf/settings.py b/rhodecode/lib/vcs/conf/settings.py index ca9d954b..6318cc9f 100644 --- a/rhodecode/lib/vcs/conf/settings.py +++ b/rhodecode/lib/vcs/conf/settings.py @@ -20,9 +20,6 @@ Internal settings for vcs-lib """ -# list of default encoding used in safe_str methods -DEFAULT_ENCODINGS = ['utf8'] - # Compatibility version when creating SVN repositories. None means newest. # Other available options are: pre-1.4-compatible, pre-1.5-compatible, diff --git a/rhodecode/lib/vcs/exceptions.py b/rhodecode/lib/vcs/exceptions.py index fe4547b5..5af82684 100644 --- a/rhodecode/lib/vcs/exceptions.py +++ b/rhodecode/lib/vcs/exceptions.py @@ -102,10 +102,6 @@ class NodeError(VCSError): pass -class RemovedFileNodeError(NodeError): - pass - - class NodeAlreadyExistsError(CommittingError): pass diff --git a/rhodecode/lib/vcs/nodes.py b/rhodecode/lib/vcs/nodes.py index 9ed11448..be70067c 100644 --- a/rhodecode/lib/vcs/nodes.py +++ b/rhodecode/lib/vcs/nodes.py @@ -19,6 +19,7 @@ """ Module holding everything related to vcs nodes, with vcs2 architecture. """ + import functools import os import stat @@ -29,84 +30,26 @@ from rhodecode.config.conf import LANGUAGES_EXTENSIONS_MAP from rhodecode.lib.str_utils import safe_str, safe_bytes from rhodecode.lib.hash_utils import md5 from rhodecode.lib.vcs import path as vcspath -from rhodecode.lib.vcs.backends.base import EmptyCommit, FILEMODE_DEFAULT +from rhodecode.lib.vcs.backends.base import EmptyCommit from rhodecode.lib.vcs.conf.mtypes import get_mimetypes_db -from rhodecode.lib.vcs.exceptions import NodeError, RemovedFileNodeError +from rhodecode.lib.vcs.exceptions import NodeError +from rhodecode.lib.vcs_common import NodeKind, FILEMODE_DEFAULT -LARGEFILE_PREFIX = '.hglf' - - -class NodeKind: - SUBMODULE = -1 - DIR = 1 - FILE = 2 - LARGEFILE = 3 +LARGEFILE_PREFIX = ".hglf" class NodeState: - ADDED = 'added' - CHANGED = 'changed' - NOT_CHANGED = 'not changed' - REMOVED = 'removed' + ADDED = "added" + CHANGED = "changed" + NOT_CHANGED = "not changed" + REMOVED = "removed" -#TODO: not sure if that should be bytes or str ? + +# TODO: not sure if that should be bytes or str ? # most probably bytes because content should be bytes and we check it -BIN_BYTE_MARKER = b'\0' +BIN_BYTE_MARKER = b"\0" -class NodeGeneratorBase(object): - """ - Base class for removed added and changed filenodes, it's a lazy generator - class that will create filenodes only on iteration or call - - The len method doesn't need to create filenodes at all - """ - - def __init__(self, current_paths, cs): - self.cs = cs - self.current_paths = current_paths - - def __call__(self): - return [n for n in self] - - def __getitem__(self, key): - if isinstance(key, slice): - for p in self.current_paths[key.start:key.stop]: - yield self.cs.get_node(p) - - def __len__(self): - return len(self.current_paths) - - def __iter__(self): - for p in self.current_paths: - yield self.cs.get_node(p) - - -class AddedFileNodesGenerator(NodeGeneratorBase): - """ - Class holding added files for current commit - """ - - -class ChangedFileNodesGenerator(NodeGeneratorBase): - """ - Class holding changed files for current commit - """ - - -class RemovedFileNodesGenerator(NodeGeneratorBase): - """ - Class holding removed files for current commit - """ - def __iter__(self): - for p in self.current_paths: - yield RemovedFileNode(path=safe_bytes(p)) - - def __getitem__(self, key): - if isinstance(key, slice): - for p in self.current_paths[key.start:key.stop]: - yield RemovedFileNode(path=safe_bytes(p)) - @functools.total_ordering class Node(object): @@ -119,21 +62,22 @@ class Node(object): only. Moreover, every single node is identified by the ``path`` attribute, so it cannot end with slash, too. Otherwise, path could lead to mistakes. """ + # RTLO marker allows swapping text, and certain # security attacks could be used with this - RTLO_MARKER = "\u202E" + RTLO_MARKER = "\u202e" commit = None def __init__(self, path: bytes, kind): self._validate_path(path) # can throw exception if path is invalid - self.bytes_path = path.rstrip(b'/') # store for __repr__ - self.path = safe_str(self.bytes_path) # we store paths as str + self.bytes_path: bytes = path.rstrip(b"/") # store for mixed encoding, and raw version + self.str_path: str = safe_str(self.bytes_path) # we store paths as str + self.path: str = self.str_path - if self.bytes_path == b'' and kind != NodeKind.DIR: - raise NodeError("Only DirNode and its subclasses may be " - "initialized with empty path") + if self.bytes_path == b"" and kind != NodeKind.DIR: + raise NodeError("Only DirNode and its subclasses may be initialized with empty path") self.kind = kind if self.is_root() and not self.is_dir(): @@ -142,7 +86,7 @@ class Node(object): def __eq__(self, other): if type(self) is not type(other): return False - for attr in ['name', 'path', 'kind']: + for attr in ["name", "path", "kind"]: if getattr(self, attr) != getattr(other, attr): return False if self.is_file(): @@ -166,22 +110,9 @@ class Node(object): if self.path > other.path: return False - # def __cmp__(self, other): - # """ - # Comparator using name of the node, needed for quick list sorting. - # """ - # - # kind_cmp = cmp(self.kind, other.kind) - # if kind_cmp: - # if isinstance(self, SubModuleNode): - # # we make submodules equal to dirnode for "sorting" purposes - # return NodeKind.DIR - # return kind_cmp - # return cmp(self.name, other.name) - def __repr__(self): - maybe_path = getattr(self, 'path', 'UNKNOWN_PATH') - return f'<{self.__class__.__name__} {maybe_path!r}>' + maybe_path = getattr(self, "path", "UNKNOWN_PATH") + return f"<{self.__class__.__name__} {maybe_path!r}>" def __str__(self): return self.name @@ -189,29 +120,27 @@ class Node(object): def _validate_path(self, path: bytes): self._assert_bytes(path) - if path.startswith(b'/'): + if path.startswith(b"/"): raise NodeError( f"Cannot initialize Node objects with slash at " f"the beginning as only relative paths are supported. " - f"Got {path}") + f"Got {path}" + ) - def _assert_bytes(self, value): + @classmethod + def _assert_bytes(cls, value): if not isinstance(value, bytes): raise TypeError(f"Bytes required as input, got {type(value)} of {value}.") @LazyProperty def parent(self): - parent_path = self.get_parent_path() + parent_path: bytes = self.get_parent_path() if parent_path: if self.commit: return self.commit.get_node(parent_path) return DirNode(parent_path) return None - @LazyProperty - def str_path(self) -> str: - return safe_str(self.path) - @LazyProperty def has_rtlo(self): """Detects if a path has right-to-left-override marker""" @@ -223,10 +152,10 @@ class Node(object): Returns name of the directory from full path of this vcs node. Empty string is returned if there's no directory in the path """ - _parts = self.path.rstrip('/').rsplit('/', 1) + _parts = self.path.rstrip("/").rsplit("/", 1) if len(_parts) == 2: return _parts[0] - return '' + return "" @LazyProperty def name(self): @@ -234,7 +163,7 @@ class Node(object): Returns name of the node so if its path then only last part is returned. """ - return self.path.rstrip('/').split('/')[-1] + return self.str_path.rstrip("/").split("/")[-1] @property def kind(self): @@ -242,12 +171,12 @@ class Node(object): @kind.setter def kind(self, kind): - if hasattr(self, '_kind'): + if hasattr(self, "_kind"): raise NodeError("Cannot change node's kind") else: self._kind = kind # Post setter check (path's trailing slash) - if self.path.endswith('/'): + if self.str_path.endswith("/"): raise NodeError("Node's path cannot end with slash") def get_parent_path(self) -> bytes: @@ -255,8 +184,8 @@ class Node(object): Returns node's parent path or empty string if node is root. """ if self.is_root(): - return b'' - str_path = vcspath.dirname(self.path.rstrip('/')) + '/' + return b"" + str_path = vcspath.dirname(self.bytes_path.rstrip(b"/")) + b"/" return safe_bytes(str_path) @@ -278,7 +207,7 @@ class Node(object): """ Returns ``True`` if node is a root node and ``False`` otherwise. """ - return self.kind == NodeKind.DIR and self.path == '' + return self.kind == NodeKind.DIR and self.path == "" def is_submodule(self): """ @@ -292,29 +221,13 @@ class Node(object): Returns ``True`` if node's kind is ``NodeKind.LARGEFILE``, ``False`` otherwise """ - return self.kind == NodeKind.LARGEFILE + return self.kind == NodeKind.LARGE_FILE def is_link(self): if self.commit: - return self.commit.is_link(self.path) + return self.commit.is_link(self.bytes_path) return False - @LazyProperty - def added(self): - return self.state is NodeState.ADDED - - @LazyProperty - def changed(self): - return self.state is NodeState.CHANGED - - @LazyProperty - def not_changed(self): - return self.state is NodeState.NOT_CHANGED - - @LazyProperty - def removed(self): - return self.state is NodeState.REMOVED - class FileNode(Node): """ @@ -325,6 +238,7 @@ class FileNode(Node): :attribute: commit: if given, first time content is accessed, callback :attribute: mode: stat mode for a node. Default is `FILEMODE_DEFAULT`. """ + _filter_pre_load = [] def __init__(self, path: bytes, content: bytes | None = None, commit=None, mode=None, pre_load=None): @@ -359,7 +273,7 @@ class FileNode(Node): return self.content == other.content def __hash__(self): - raw_id = getattr(self.commit, 'raw_id', '') + raw_id = getattr(self.commit, "raw_id", "") return hash((self.path, raw_id)) def __lt__(self, other): @@ -369,33 +283,32 @@ class FileNode(Node): return self.content < other.content def __repr__(self): - short_id = getattr(self.commit, 'short_id', '') - return f'<{self.__class__.__name__} path={self.path!r}, short_id={short_id}>' + short_id = getattr(self.commit, "short_id", "") + return f"<{self.__class__.__name__} path={self.str_path!r}, short_id={short_id}>" def _set_bulk_properties(self, pre_load): if not pre_load: return - pre_load = [entry for entry in pre_load - if entry not in self._filter_pre_load] + pre_load = [entry for entry in pre_load if entry not in self._filter_pre_load] if not pre_load: return remote = self.commit.get_remote() - result = remote.bulk_file_request(self.commit.raw_id, self.path, pre_load) + result = remote.bulk_file_request(self.commit.raw_id, self.bytes_path, pre_load) for attr, value in result.items(): if attr == "flags": - self.__dict__['mode'] = safe_str(value) + self.__dict__["mode"] = safe_str(value) elif attr == "size": - self.__dict__['size'] = value + self.__dict__["size"] = value elif attr == "data": - self.__dict__['_content'] = value + self.__dict__["_content"] = value elif attr == "is_binary": - self.__dict__['is_binary'] = value + self.__dict__["is_binary"] = value elif attr == "md5": - self.__dict__['md5'] = value + self.__dict__["md5"] = value else: - raise ValueError(f'Unsupported attr in bulk_property: {attr}') + raise ValueError(f"Unsupported attr in bulk_property: {attr}") @LazyProperty def mode(self): @@ -404,7 +317,7 @@ class FileNode(Node): use value given at initialization or `FILEMODE_DEFAULT` (default). """ if self.commit: - mode = self.commit.get_file_mode(self.path) + mode = self.commit.get_file_mode(self.bytes_path) else: mode = self._mode return mode @@ -416,7 +329,7 @@ class FileNode(Node): """ if self.commit: if self._content is None: - self._content = self.commit.get_file_content(self.path) + self._content = self.commit.get_file_content(self.bytes_path) content = self._content else: content = self._content @@ -427,7 +340,7 @@ class FileNode(Node): Returns lazily content of the FileNode. """ if self.commit: - content = self.commit.get_file_content(self.path) + content = self.commit.get_file_content(self.bytes_path) else: content = self._content return content @@ -438,7 +351,7 @@ class FileNode(Node): vcsserver without loading it to memory. """ if self.commit: - return self.commit.get_file_content_streamed(self.path) + return self.commit.get_file_content_streamed(self.bytes_path) raise NodeError("Cannot retrieve stream_bytes without related commit attribute") def metadata_uncached(self): @@ -462,7 +375,7 @@ class FileNode(Node): """ content = self.raw_bytes if content and not isinstance(content, bytes): - raise ValueError(f'Content is of type {type(content)} instead of bytes') + raise ValueError(f"Content is of type {type(content)} instead of bytes") return content @LazyProperty @@ -472,27 +385,21 @@ class FileNode(Node): @LazyProperty def size(self): if self.commit: - return self.commit.get_file_size(self.path) - raise NodeError( - "Cannot retrieve size of the file without related " - "commit attribute") + return self.commit.get_file_size(self.bytes_path) + raise NodeError("Cannot retrieve size of the file without related commit attribute") @LazyProperty def message(self): if self.commit: return self.last_commit.message - raise NodeError( - "Cannot retrieve message of the file without related " - "commit attribute") + raise NodeError("Cannot retrieve message of the file without related " "commit attribute") @LazyProperty def last_commit(self): if self.commit: pre_load = ["author", "date", "message", "parents"] - return self.commit.get_path_commit(self.path, pre_load=pre_load) - raise NodeError( - "Cannot retrieve last commit of the file without " - "related commit attribute") + return self.commit.get_path_commit(self.bytes_path, pre_load=pre_load) + raise NodeError("Cannot retrieve last commit of the file without related commit attribute") def get_mimetype(self): """ @@ -502,28 +409,27 @@ class FileNode(Node): attribute to indicate that type should *NOT* be calculated). """ - if hasattr(self, '_mimetype'): - if (isinstance(self._mimetype, (tuple, list)) and - len(self._mimetype) == 2): + if hasattr(self, "_mimetype"): + if isinstance(self._mimetype, (tuple, list)) and len(self._mimetype) == 2: return self._mimetype else: - raise NodeError('given _mimetype attribute must be an 2 ' - 'element list or tuple') + raise NodeError("given _mimetype attribute must be an 2 element list or tuple") db = get_mimetypes_db() mtype, encoding = db.guess_type(self.name) if mtype is None: if not self.is_largefile() and self.is_binary: - mtype = 'application/octet-stream' + mtype = "application/octet-stream" encoding = None else: - mtype = 'text/plain' + mtype = "text/plain" encoding = None # try with pygments try: from pygments.lexers import get_lexer_for_filename + mt = get_lexer_for_filename(self.name).mimetypes except Exception: mt = None @@ -544,18 +450,17 @@ class FileNode(Node): @LazyProperty def mimetype_main(self): - return self.mimetype.split('/')[0] + return self.mimetype.split("/")[0] @classmethod def get_lexer(cls, filename, content=None): from pygments import lexers - extension = filename.split('.')[-1] + extension = filename.split(".")[-1] lexer = None try: - lexer = lexers.guess_lexer_for_filename( - filename, content, stripnl=False) + lexer = lexers.guess_lexer_for_filename(filename, content, stripnl=False) except lexers.ClassNotFound: pass @@ -580,7 +485,7 @@ class FileNode(Node): content, name and mimetype. """ # TODO: this is more proper, but super heavy on investigating the type based on the content - #self.get_lexer(self.name, self.content) + # self.get_lexer(self.name, self.content) return self.get_lexer(self.name) @@ -597,8 +502,8 @@ class FileNode(Node): Returns a list of commit for this file in which the file was changed """ if self.commit is None: - raise NodeError('Unable to get commit for this FileNode') - return self.commit.get_path_history(self.path) + raise NodeError("Unable to get commit for this FileNode") + return self.commit.get_path_history(self.bytes_path) @LazyProperty def annotate(self): @@ -606,22 +511,9 @@ class FileNode(Node): Returns a list of three element tuples with lineno, commit and line """ if self.commit is None: - raise NodeError('Unable to get commit for this FileNode') + raise NodeError("Unable to get commit for this FileNode") pre_load = ["author", "date", "message", "parents"] - return self.commit.get_file_annotate(self.path, pre_load=pre_load) - - @LazyProperty - def state(self): - if not self.commit: - raise NodeError( - "Cannot check state of the node if it's not " - "linked with commit") - elif self.path in (node.path for node in self.commit.added): - return NodeState.ADDED - elif self.path in (node.path for node in self.commit.changed): - return NodeState.CHANGED - else: - return NodeState.NOT_CHANGED + return self.commit.get_file_annotate(self.bytes_path, pre_load=pre_load) @LazyProperty def is_binary(self): @@ -629,7 +521,7 @@ class FileNode(Node): Returns True if file has binary content. """ if self.commit: - return self.commit.is_node_binary(self.path) + return self.commit.is_node_binary(self.bytes_path) else: raw_bytes = self._content return bool(raw_bytes and BIN_BYTE_MARKER in raw_bytes) @@ -641,7 +533,7 @@ class FileNode(Node): """ if self.commit: - return self.commit.node_md5_hash(self.path) + return self.commit.node_md5_hash(self.bytes_path) else: raw_bytes = self._content # TODO: this sucks, we're computing md5 on potentially super big stream data... @@ -650,7 +542,7 @@ class FileNode(Node): @LazyProperty def extension(self): """Returns filenode extension""" - return self.name.split('.')[-1] + return self.name.split(".")[-1] @property def is_executable(self): @@ -667,15 +559,15 @@ class FileNode(Node): LF store. """ if self.commit: - return self.commit.get_largefile_node(self.path) + return self.commit.get_largefile_node(self.bytes_path) def count_lines(self, content: str | bytes, count_empty=False): if isinstance(content, str): - newline_marker = '\n' + newline_marker = "\n" elif isinstance(content, bytes): - newline_marker = b'\n' + newline_marker = b"\n" else: - raise ValueError('content must be bytes or str got {type(content)} instead') + raise ValueError("content must be bytes or str got {type(content)} instead") if count_empty: all_lines = 0 @@ -704,33 +596,6 @@ class FileNode(Node): return all_lines, empty_lines -class RemovedFileNode(FileNode): - """ - Dummy FileNode class - trying to access any public attribute except path, - name, kind or state (or methods/attributes checking those two) would raise - RemovedFileNodeError. - """ - ALLOWED_ATTRIBUTES = [ - 'name', 'path', 'state', 'is_root', 'is_file', 'is_dir', 'kind', - 'added', 'changed', 'not_changed', 'removed', 'bytes_path' - ] - - def __init__(self, path): - """ - :param path: relative path to the node - """ - super().__init__(path=path) - - def __getattribute__(self, attr): - if attr.startswith('_') or attr in RemovedFileNode.ALLOWED_ATTRIBUTES: - return super().__getattribute__(attr) - raise RemovedFileNodeError(f"Cannot access attribute {attr} on RemovedFileNode. Not in allowed attributes") - - @LazyProperty - def state(self): - return NodeState.REMOVED - - class DirNode(Node): """ DirNode stores list of files and directories within this node. @@ -752,7 +617,7 @@ class DirNode(Node): super().__init__(path, NodeKind.DIR) self.commit = commit self._nodes = nodes - self.default_pre_load = default_pre_load or ['is_binary', 'size'] + self.default_pre_load = default_pre_load or ["is_binary", "size"] def __iter__(self): yield from self.nodes @@ -782,10 +647,9 @@ class DirNode(Node): @LazyProperty def nodes(self): if self.commit: - nodes = self.commit.get_nodes(self.path, pre_load=self.default_pre_load) + nodes = self.commit.get_nodes(self.bytes_path, pre_load=self.default_pre_load) else: nodes = self._nodes - self._nodes_dict = {node.path: node for node in nodes} return sorted(nodes) @LazyProperty @@ -796,47 +660,6 @@ class DirNode(Node): def dirs(self): return sorted(node for node in self.nodes if node.is_dir()) - def get_node(self, path): - """ - Returns node from within this particular ``DirNode``, so it is now - allowed to fetch, i.e. node located at 'docs/api/index.rst' from node - 'docs'. In order to access deeper nodes one must fetch nodes between - them first - this would work:: - - docs = root.get_node('docs') - docs.get_node('api').get_node('index.rst') - - :param: path - relative to the current node - - .. note:: - To access lazily (as in example above) node have to be initialized - with related commit object - without it node is out of - context and may know nothing about anything else than nearest - (located at same level) nodes. - """ - try: - path = path.rstrip('/') - if path == '': - raise NodeError("Cannot retrieve node without path") - self.nodes # access nodes first in order to set _nodes_dict - paths = path.split('/') - if len(paths) == 1: - if not self.is_root(): - path = '/'.join((self.path, paths[0])) - else: - path = paths[0] - return self._nodes_dict[path] - elif len(paths) > 1: - if self.commit is None: - raise NodeError("Cannot access deeper nodes without commit") - else: - path1, path2 = paths[0], '/'.join(paths[1:]) - return self.get_node(path1).get_node(path2) - else: - raise KeyError - except KeyError: - raise NodeError(f"Node does not exist at {path}") - @LazyProperty def state(self): raise NodeError("Cannot access state of DirNode") @@ -844,7 +667,7 @@ class DirNode(Node): @LazyProperty def size(self): size = 0 - for root, dirs, files in self.commit.walk(self.path): + for root, dirs, files in self.commit.walk(self.bytes_path): for f in files: size += f.size @@ -854,14 +677,12 @@ class DirNode(Node): def last_commit(self): if self.commit: pre_load = ["author", "date", "message", "parents"] - return self.commit.get_path_commit(self.path, pre_load=pre_load) - raise NodeError( - "Cannot retrieve last commit of the file without " - "related commit attribute") + return self.commit.get_path_commit(self.bytes_path, pre_load=pre_load) + raise NodeError("Cannot retrieve last commit of the file without related commit attribute") def __repr__(self): - short_id = getattr(self.commit, 'short_id', '') - return f'<{self.__class__.__name__} {self.path!r} @ {short_id}>' + short_id = getattr(self.commit, "short_id", "") + return f"<{self.__class__.__name__} path={self.str_path!r}, short_id={short_id}>" class RootNode(DirNode): @@ -870,21 +691,24 @@ class RootNode(DirNode): """ def __init__(self, nodes=(), commit=None): - super().__init__(path=b'', nodes=nodes, commit=commit) + super().__init__(path=b"", nodes=nodes, commit=commit) def __repr__(self): - return f'<{self.__class__.__name__}>' + short_id = getattr(self.commit, "short_id", "") + return f"<{self.__class__.__name__} path={self.str_path!r}, short_id={short_id}>" class SubModuleNode(Node): """ represents a SubModule of Git or SubRepo of Mercurial """ + is_binary = False size = 0 def __init__(self, name, url=None, commit=None, alias=None): self.path = name + self.str_path: str = safe_str(self.path) # we store paths as str self.kind = NodeKind.SUBMODULE self.alias = alias @@ -894,8 +718,8 @@ class SubModuleNode(Node): self.url = url or self._extract_submodule_url() def __repr__(self): - short_id = getattr(self.commit, 'short_id', '') - return f'<{self.__class__.__name__} {self.path!r} @ {short_id}>' + short_id = getattr(self.commit, "short_id", "") + return f"<{self.__class__.__name__} {self.str_path!r} @ {short_id}>" def _extract_submodule_url(self): # TODO: find a way to parse gits submodule file and extract the @@ -908,22 +732,22 @@ class SubModuleNode(Node): Returns name of the node so if its path then only last part is returned. """ - org = safe_str(self.path.rstrip('/').split('/')[-1]) - return f'{org} @ {self.commit.short_id}' + org = self.str_path.rstrip("/").split("/")[-1] + return f"{org} @ {self.commit.short_id}" class LargeFileNode(FileNode): - def __init__(self, path, url=None, commit=None, alias=None, org_path=None): self._validate_path(path) # can throw exception if path is invalid self.org_path = org_path # as stored in VCS as LF pointer - self.bytes_path = path.rstrip(b'/') # store for __repr__ - self.path = safe_str(self.bytes_path) # we store paths as str + self.bytes_path = path.rstrip(b"/") # store for __repr__ + self.str_path = safe_str(self.bytes_path) + self.path = self.str_path - self.kind = NodeKind.LARGEFILE + self.kind = NodeKind.LARGE_FILE self.alias = alias - self._content = b'' + self._content = b"" def _validate_path(self, path: bytes): """ @@ -932,7 +756,7 @@ class LargeFileNode(FileNode): self._assert_bytes(path) def __repr__(self): - return f'<{self.__class__.__name__} {self.org_path} -> {self.path!r}>' + return f"<{self.__class__.__name__} {self.org_path} -> {self.str_path!r}>" @LazyProperty def size(self): @@ -940,7 +764,7 @@ class LargeFileNode(FileNode): @LazyProperty def raw_bytes(self): - with open(self.path, 'rb') as f: + with open(self.path, "rb") as f: content = f.read() return content @@ -952,7 +776,7 @@ class LargeFileNode(FileNode): return self.org_path def stream_bytes(self): - with open(self.path, 'rb') as stream: + with open(self.path, "rb") as stream: while True: data = stream.read(16 * 1024) if not data: diff --git a/rhodecode/lib/vcs_common.py b/rhodecode/lib/vcs_common.py new file mode 100644 index 00000000..6ae5a26c --- /dev/null +++ b/rhodecode/lib/vcs_common.py @@ -0,0 +1,46 @@ +# Copyright (C) 2014-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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/ + + +""" +Common VCS module for rhodecode and vcsserver +""" + + +import enum + +FILEMODE_DEFAULT = 0o100644 +FILEMODE_EXECUTABLE = 0o100755 +FILEMODE_LINK = 0o120000 + + +class NodeKind(int, enum.Enum): + SUBMODULE = -1 + DIR = 1 + FILE = 2 + LARGE_FILE = 3 + + +def map_git_obj_type(obj_type): + if obj_type == "blob": + return NodeKind.FILE + elif obj_type == "tree": + return NodeKind.DIR + elif obj_type == "link": + return NodeKind.SUBMODULE + return None diff --git a/rhodecode/model/gist.py b/rhodecode/model/gist.py index ce150fa1..8127d205 100644 --- a/rhodecode/model/gist.py +++ b/rhodecode/model/gist.py @@ -103,7 +103,7 @@ class GistModel(BaseModel): raise VCSError(f'Failed to load gist repository for {repo}') commit = vcs_repo.get_commit(commit_id=revision) - return commit, [n for n in commit.get_node('/')] + return commit, [n for n in commit.get_node(b'/')] def create(self, description, owner, gist_mapping, gist_type=Gist.GIST_PUBLIC, lifetime=-1, gist_id=None, diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py index 22be808e..7d86790b 100644 --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -178,6 +178,7 @@ def get_diff_info( log.debug('Calculating authors of changed files') target_commit = source_repo.get_commit(ancestor_id) + # TODO: change to operate in bytes.. for fname, lines in changed_lines.items(): try: @@ -2223,8 +2224,7 @@ class MergeCheck(object): ) @classmethod - def validate(cls, pull_request, auth_user, translator, fail_early=False, - force_shadow_repo_refresh=False): + def validate(cls, pull_request, auth_user, translator, fail_early=False, force_shadow_repo_refresh=False): _ = translator merge_check = cls() @@ -2285,12 +2285,10 @@ class MergeCheck(object): # left over TODOs todos = CommentsModel().get_pull_request_unresolved_todos(pull_request) if todos: - log.debug("MergeCheck: cannot merge, {} " - "unresolved TODOs left.".format(len(todos))) + log.debug("MergeCheck: cannot merge, %s unresolved TODOs left.", len(todos)) if len(todos) == 1: - msg = _('Cannot merge, {} TODO still not resolved.').format( - len(todos)) + msg = _('Cannot merge, {} TODO still not resolved.').format(len(todos)) else: msg = _('Cannot merge, {} TODOs still not resolved.').format( len(todos)) diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py index b7b72d40..059c8795 100644 --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -33,6 +33,7 @@ from rhodecode.lib.auth import HasUserGroupPermissionAny from rhodecode.lib.caching_query import FromCache from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError, AttachedArtifactsError from rhodecode.lib import hooks_base +from rhodecode.lib.str_utils import safe_bytes from rhodecode.lib.user_log_filter import user_log_filter from rhodecode.lib.utils import make_db_config from rhodecode.lib.utils2 import ( @@ -1109,47 +1110,47 @@ class ReadmeFinder: different. """ - readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE) - path_re = re.compile(r'^docs?', re.IGNORECASE) + readme_re = re.compile(br'^readme(\.[^.]+)?$', re.IGNORECASE) + path_re = re.compile(br'^docs?', re.IGNORECASE) default_priorities = { None: 0, - '.rst': 1, - '.md': 1, - '.rest': 2, - '.mkdn': 2, - '.text': 2, - '.txt': 3, - '.mdown': 3, - '.markdown': 4, + b'.rst': 1, + b'.md': 1, + b'.rest': 2, + b'.mkdn': 2, + b'.text': 2, + b'.txt': 3, + b'.mdown': 3, + b'.markdown': 4, } path_priority = { - 'doc': 0, - 'docs': 1, + b'doc': 0, + b'docs': 1, } FALLBACK_PRIORITY = 99 RENDERER_TO_EXTENSION = { - 'rst': ['.rst', '.rest'], - 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'], + 'rst': [b'.rst', b'.rest'], + 'markdown': [b'.md', b'mkdn', b'.mdown', b'.markdown'], } def __init__(self, default_renderer=None): self._default_renderer = default_renderer - self._renderer_extensions = self.RENDERER_TO_EXTENSION.get( - default_renderer, []) + self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(default_renderer, []) - def search(self, commit, path='/'): + def search(self, commit, path=b'/'): """ Find a readme in the given `commit`. """ # firstly, check the PATH type if it is actually a DIR - if commit.get_node(path).kind != NodeKind.DIR: + bytes_path = safe_bytes(path) + if commit.get_node(bytes_path).kind != NodeKind.DIR: return None - nodes = commit.get_nodes(path) + nodes = commit.get_nodes(bytes_path) matches = self._match_readmes(nodes) matches = self._sort_according_to_priority(matches) if matches: @@ -1157,8 +1158,8 @@ class ReadmeFinder: paths = self._match_paths(nodes) paths = self._sort_paths_according_to_priority(paths) - for path in paths: - match = self.search(commit, path=path) + for bytes_path in paths: + match = self.search(commit, path=bytes_path) if match: return match @@ -1168,7 +1169,7 @@ class ReadmeFinder: for node in nodes: if not node.is_file(): continue - path = node.path.rsplit('/', 1)[-1] + path = node.bytes_path.rsplit(b'/', 1)[-1] match = self.readme_re.match(path) if match: extension = match.group(1) @@ -1178,28 +1179,26 @@ class ReadmeFinder: for node in nodes: if not node.is_dir(): continue - match = self.path_re.match(node.path) + match = self.path_re.match(node.bytes_path) if match: - yield node.path + yield node.bytes_path def _priority(self, extension): - renderer_priority = ( - 0 if extension in self._renderer_extensions else 1) - extension_priority = self.default_priorities.get( - extension, self.FALLBACK_PRIORITY) - return (renderer_priority, extension_priority) + renderer_priority = 0 if extension in self._renderer_extensions else 1 + extension_priority = self.default_priorities.get(extension, self.FALLBACK_PRIORITY) + return renderer_priority, extension_priority def _sort_according_to_priority(self, matches): def priority_and_path(match): - return (match.priority, match.path) + return match.priority, match.path return sorted(matches, key=priority_and_path) def _sort_paths_according_to_priority(self, paths): def priority_and_path(path): - return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path) + return self.path_priority.get(path, self.FALLBACK_PRIORITY), path return sorted(paths, key=priority_and_path) diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py index c53c729f..c2f37890 100644 --- a/rhodecode/model/scm.py +++ b/rhodecode/model/scm.py @@ -543,7 +543,7 @@ class ScmModel(BaseModel): root_path = root_path.lstrip('/') # get RootNode, inject pre-load options before walking - top_node = commit.get_node(root_path) + top_node = commit.get_node(safe_bytes(root_path)) extended_info_pre_load = [] if extended_info: extended_info_pre_load += ['md5'] @@ -614,12 +614,13 @@ class ScmModel(BaseModel): _files = list() _dirs = list() + bytes_path = safe_bytes(root_path) try: _repo = self._get_repo(repo_name) commit = _repo.scm_instance().get_commit(commit_id=commit_id) - root_path = root_path.lstrip('/') + root_path = bytes_path.lstrip(b'/') - top_node = commit.get_node(root_path) + top_node = commit.get_node(safe_bytes(root_path)) top_node.default_pre_load = [] for __, dirs, files in commit.walk(top_node): @@ -736,7 +737,7 @@ class ScmModel(BaseModel): _repo = self._get_repo(repo_name) commit = _repo.scm_instance().get_commit(commit_id=commit_id) root_path = root_path.lstrip('/') - top_node = commit.get_node(root_path) + top_node = commit.get_node(safe_bytes(root_path)) top_node.default_pre_load = [] for __, dirs, files in commit.walk(top_node): @@ -774,7 +775,7 @@ class ScmModel(BaseModel): only for git :param trigger_push_hook: trigger push hooks - :returns: new committed commit + :returns: new commit """ user, scm_instance, message, commiter, author, imc = self.initialize_inmemory_vars( user, repo, message, author) diff --git a/rhodecode/tests/config/test_routing_links.py b/rhodecode/tests/config/test_routing_links.py index aec9964b..3b6a8588 100644 --- a/rhodecode/tests/config/test_routing_links.py +++ b/rhodecode/tests/config/test_routing_links.py @@ -36,8 +36,8 @@ connection_available = pytest.mark.skipif( import requests +from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter -from requests.packages.urllib3.util.retry import Retry def requests_retry_session( diff --git a/rhodecode/tests/models/test_repos.py b/rhodecode/tests/models/test_repos.py index 929ac100..e8ce4223 100644 --- a/rhodecode/tests/models/test_repos.py +++ b/rhodecode/tests/models/test_repos.py @@ -92,8 +92,8 @@ class TestRepoModel(object): is not None) @pytest.mark.parametrize("filename, expected", [ - ("README", True), - ("README.rst", False), + (b"README", True), + (b"README.rst", False), ]) def test_filenode_is_link(self, vcsbackend, filename, expected): repo = vcsbackend.repo diff --git a/rhodecode/tests/vcs/test_archives.py b/rhodecode/tests/vcs/test_archives.py index de0625b3..0650b336 100644 --- a/rhodecode/tests/vcs/test_archives.py +++ b/rhodecode/tests/vcs/test_archives.py @@ -28,7 +28,7 @@ import pytest import rhodecode from rhodecode.lib.archive_cache import get_archival_config -from rhodecode.lib.str_utils import ascii_bytes +from rhodecode.lib.str_utils import ascii_bytes, safe_bytes, safe_str from rhodecode.lib.vcs.backends import base from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError, VCSError from rhodecode.lib.vcs.nodes import FileNode @@ -80,8 +80,8 @@ class TestArchives(BackendTestMixin): out_file.close() for x in range(5): - node_path = "%d/file_%d.txt" % (x, x) - with open(os.path.join(out_dir, "repo/" + node_path), "rb") as f: + node_path = b"%d/file_%d.txt" % (x, x) + with open(os.path.join(safe_bytes(str(out_dir)), b"repo/" + node_path), "rb") as f: file_content = f.read() assert file_content == self.tip.get_node(node_path).content @@ -120,8 +120,9 @@ class TestArchives(BackendTestMixin): zip_file = zipfile.ZipFile(str(archive_lnk)) for x in range(5): - node_path = "%d/file_%d.txt" % (x, x) - data = zip_file.read(f"repo/{node_path}") + node_path = b"%d/file_%d.txt" % (x, x) + # NOTE: zipfile operates only on strings inside the archive + data = zip_file.read(safe_str(b"repo/%s" % node_path)) decompressed = io.BytesIO() decompressed.write(data) @@ -143,8 +144,9 @@ class TestArchives(BackendTestMixin): assert b"commit_id:%b" % raw_id in metafile for x in range(5): - node_path = "%d/file_%d.txt" % (x, x) - data = zip_file.read(f"repo/{node_path}") + node_path = b"%d/file_%d.txt" % (x, x) + # NOTE: zipfile operates only on strings inside the archive + data = zip_file.read(safe_str(b"repo/%s" % node_path)) decompressed = io.BytesIO() decompressed.write(data) assert decompressed.getvalue() == self.tip.get_node(node_path).content diff --git a/rhodecode/tests/vcs/test_commits.py b/rhodecode/tests/vcs/test_commits.py index 2be37a6a..93a5b823 100644 --- a/rhodecode/tests/vcs/test_commits.py +++ b/rhodecode/tests/vcs/test_commits.py @@ -22,21 +22,17 @@ import time import pytest from rhodecode.lib.str_utils import safe_bytes -from rhodecode.lib.vcs.backends.base import CollectionGenerator, FILEMODE_DEFAULT, EmptyCommit +from rhodecode.lib.vcs.backends.base import CollectionGenerator, EmptyCommit from rhodecode.lib.vcs.exceptions import ( BranchDoesNotExistError, CommitDoesNotExistError, RepositoryError, EmptyRepositoryError, ) -from rhodecode.lib.vcs.nodes import ( - FileNode, - AddedFileNodesGenerator, - ChangedFileNodesGenerator, - RemovedFileNodesGenerator, -) +from rhodecode.lib.vcs.nodes import FileNode from rhodecode.tests import get_new_dir from rhodecode.tests.vcs.conftest import BackendTestMixin +from rhodecode.lib.vcs_common import NodeKind, FILEMODE_EXECUTABLE, FILEMODE_DEFAULT, FILEMODE_LINK class TestBaseChangeset(object): @@ -70,7 +66,7 @@ class TestCommitsInNonEmptyRepo(BackendTestMixin): } def test_walk_returns_empty_list_in_case_of_file(self): - result = list(self.tip.walk("file_0.txt")) + result = list(self.tip.walk(b"file_0.txt")) assert result == [] @pytest.mark.backends("git", "hg") @@ -319,7 +315,7 @@ class TestCommits(BackendTestMixin): def test_get_path_commit(self): commit = self.repo.get_commit() - commit.get_path_commit("file_4.txt") + commit.get_path_commit(b"file_4.txt") assert commit.message == "Commit 4" def test_get_filenodes_generator(self): @@ -500,8 +496,8 @@ class TestCommits(BackendTestMixin): @pytest.mark.parametrize( "filename, expected", [ - ("README.rst", False), - ("README", True), + (b"README.rst", False), + (b"README", True), ], ) def test_commit_is_link(vcsbackend, filename, expected): @@ -543,49 +539,41 @@ class TestCommitsChanges(BackendTestMixin): def test_initial_commit(self, local_dt_to_utc): commit = self.repo.get_commit(commit_idx=0) - assert set(commit.added) == { - commit.get_node("foo/bar"), - commit.get_node("foo/bał"), - commit.get_node("foobar"), - commit.get_node("qwe"), - } - assert set(commit.changed) == set() - assert set(commit.removed) == set() - assert set(commit.affected_files) == {"foo/bar", "foo/bał", "foobar", "qwe"} + assert sorted(commit.added_paths) == sorted([b"foo/bar", b"foo/ba\xc5\x82", b"foobar", b"qwe"]) + assert commit.changed_paths == [] + assert commit.removed_paths == [] + assert sorted(commit.affected_files) == sorted([b"foo/bar", b"foo/ba\xc5\x82", b"foobar", b"qwe"]) assert commit.date == local_dt_to_utc(datetime.datetime(2010, 1, 1, 20, 0)) def test_head_added(self): commit = self.repo.get_commit() - assert isinstance(commit.added, AddedFileNodesGenerator) - assert set(commit.added) == {commit.get_node("fallout")} - assert isinstance(commit.changed, ChangedFileNodesGenerator) - assert set(commit.changed) == {commit.get_node("foo/bar"), commit.get_node("foobar")} - assert isinstance(commit.removed, RemovedFileNodesGenerator) - assert len(commit.removed) == 1 - assert list(commit.removed)[0].path == "qwe" + + assert commit.added_paths == [b"fallout"] + assert commit.changed_paths == [b"foo/bar", b"foobar"] + assert commit.removed_paths == [b"qwe"] def test_get_filemode(self): commit = self.repo.get_commit() - assert FILEMODE_DEFAULT == commit.get_file_mode("foo/bar") + assert FILEMODE_DEFAULT == commit.get_file_mode(b"foo/bar") def test_get_filemode_non_ascii(self): commit = self.repo.get_commit() - assert FILEMODE_DEFAULT == commit.get_file_mode("foo/bał") - assert FILEMODE_DEFAULT == commit.get_file_mode("foo/bał") + assert FILEMODE_DEFAULT == commit.get_file_mode(b"foo/ba\xc5\x82") + assert FILEMODE_DEFAULT == commit.get_file_mode(b"foo/ba\xc5\x82") def test_get_path_history(self): commit = self.repo.get_commit() - history = commit.get_path_history("foo/bar") + history = commit.get_path_history(b"foo/bar") assert len(history) == 2 def test_get_path_history_with_limit(self): commit = self.repo.get_commit() - history = commit.get_path_history("foo/bar", limit=1) + history = commit.get_path_history(b"foo/bar", limit=1) assert len(history) == 1 def test_get_path_history_first_commit(self): commit = self.repo[0] - history = commit.get_path_history("foo/bar") + history = commit.get_path_history(b"foo/bar") assert len(history) == 1 diff --git a/rhodecode/tests/vcs/test_filenodes_listing_and_caches.py b/rhodecode/tests/vcs/test_filenodes_listing_and_caches.py new file mode 100644 index 00000000..a826ecc9 --- /dev/null +++ b/rhodecode/tests/vcs/test_filenodes_listing_and_caches.py @@ -0,0 +1,227 @@ +# Copyright (C) 2010-2024 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# 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 pytest + +from rhodecode.lib.str_utils import safe_bytes +from rhodecode.lib.vcs.nodes import FileNode +from rhodecode.lib.vcs_common import NodeKind +from rhodecode.tests.vcs.conftest import BackendTestMixin + + +class TestFileNodesListingAndCaches: + def test_filenode_get_root_node(self, vcsbackend): + repo = vcsbackend.repo + commit = repo.get_commit() + + # check if we start with empty nodes cache + assert commit.nodes == {} + assert commit._path_mode_cache == {} + assert commit._path_type_cache == {} + + top_dir = commit.get_node(b"") + + assert top_dir.is_dir() + + assert list(commit.nodes.keys()) == [b""] + assert list(commit._path_type_cache.keys()) == [b""] + assert commit._path_mode_cache == {} + + def test_filenode_get_file_node(self, vcsbackend): + repo = vcsbackend.repo + commit = repo.get_commit() + + # check if we start with empty nodes cache + assert commit.nodes == {} + assert commit._path_mode_cache == {} + assert commit._path_type_cache == {} + + file_node = commit.get_node(b"README.rst") + + assert file_node.is_file() + assert file_node.last_commit + + assert list(commit.nodes.keys()) == [b"README.rst"] + if repo.alias == "hg": + assert commit._path_type_cache == {b"README.rst": NodeKind.FILE} + assert commit._path_mode_cache == {b"README.rst": 33188} + + if repo.alias == "git": + assert commit._path_type_cache == { + b"README.rst": ["8f111ab5152b9fd34f52b0fb288e1216b5d2f55e", NodeKind.FILE] + } + assert commit._path_mode_cache == {b"README.rst": 33188} + + if repo.alias == "svn": + assert commit._path_type_cache == {b"README.rst": NodeKind.FILE} + assert commit._path_mode_cache == {b"README.rst": 33188} + + def test_filenode_get_nodes_from_top_dir(self, vcsbackend): + repo = vcsbackend.repo + commit = repo.get_commit() + + # check if we start with empty nodes cache + assert commit.nodes == {} + assert commit._path_mode_cache == {} + assert commit._path_type_cache == {} + + node_list = commit.get_nodes(b"") + + for node in node_list: + assert node + + if repo.alias == "svn": + assert list(commit.nodes.keys()) == [ + b"README", + b".hgignore", + b"MANIFEST.in", + b".travis.yml", + b"vcs", + b"tox.ini", + b"setup.py", + b"setup.cfg", + b"docs", + b"fakefile", + b"examples", + b"test_and_report.sh", + b"bin", + b".gitignore", + b".hgtags", + b"README.rst", + ] + assert commit._path_type_cache == { + b"": NodeKind.DIR, + b"README": 2, + b".hgignore": 2, + b"MANIFEST.in": 2, + b".travis.yml": 2, + b"vcs": 1, + b"tox.ini": 2, + b"setup.py": 2, + b"setup.cfg": 2, + b"docs": 1, + b"fakefile": 2, + b"examples": 1, + b"test_and_report.sh": 2, + b"bin": 1, + b".gitignore": 2, + b".hgtags": 2, + b"README.rst": 2, + } + assert commit._path_mode_cache == {} + + if repo.alias == "hg": + assert list(commit.nodes.keys()) == [ + b".gitignore", + b".hgignore", + b".hgtags", + b".travis.yml", + b"MANIFEST.in", + b"README", + b"README.rst", + b"docs", + b"run_test_and_report.sh", + b"setup.cfg", + b"setup.py", + b"test_and_report.sh", + b"tox.ini", + b"vcs", + ] + + assert commit._path_type_cache == { + b"": NodeKind.DIR, + b".gitignore": 2, + b".hgignore": 2, + b".hgtags": 2, + b".travis.yml": 2, + b"MANIFEST.in": 2, + b"README": 2, + b"README.rst": 2, + b"docs": 1, + b"run_test_and_report.sh": 2, + b"setup.cfg": 2, + b"setup.py": 2, + b"test_and_report.sh": 2, + b"tox.ini": 2, + b"vcs": 1, + } + assert commit._path_mode_cache == {} + + if repo.alias == "git": + assert list(commit.nodes.keys()) == [ + b".gitignore", + b".hgignore", + b".hgtags", + b"MANIFEST.in", + b"README", + b"README.rst", + b"docs", + b"run_test_and_report.sh", + b"setup.cfg", + b"setup.py", + b"test_and_report.sh", + b"tox.ini", + b"vcs", + ] + assert commit._path_type_cache == { + b"": ["2dab7f7377e36948b5633ae0877310380fdf64ba", NodeKind.DIR], + b".gitignore": ["7f95b0917e31b0cce1ad96a033b1a814c420cde3", 2], + b".hgignore": ["9bdee5ed67ceed7c4b5589ba26dada65df151aed", 2], + b".hgtags": ["63aa2e892deebdfdef1845fe52e2418ade03557b", 2], + b"MANIFEST.in": ["6d65aca5321c26589ae197b81511445cfb353c95", 2], + b"README": ["92cacd285355271487b7e379dba6ca60f9a554a4", 2], + b"README.rst": ["8f111ab5152b9fd34f52b0fb288e1216b5d2f55e", 2], + b"docs": ["eaa8cef76ec4e1baf06f515ab03bfc01719a913d", 1], + b"run_test_and_report.sh": ["e17011bdddc8812fda96e891a1087c73054f1f25", 2], + b"setup.cfg": ["2e76b65bc9106ed466d71fb2cfe710352eadfb49", 2], + b"setup.py": ["ccda2bd076eca961059deba6e39fb591d014052a", 2], + b"test_and_report.sh": ["0b14056dd7dc759a9719adef3d7bb529141a740e", 2], + b"tox.ini": ["c1735231e7af32f089b95455162db092824715a4", 2], + b"vcs": ["5868c69a8d74ccf3fdc655af8f3f185bc0ff2ef6", 1], + } + assert commit._path_mode_cache == { + b".gitignore": 33188, + b".hgignore": 33188, + b".hgtags": 33188, + b"MANIFEST.in": 33188, + b"README": 40960, + b"README.rst": 33188, + b"docs": 16384, + b"run_test_and_report.sh": 33261, + b"setup.cfg": 33188, + b"setup.py": 33188, + b"test_and_report.sh": 33261, + b"tox.ini": 33188, + b"vcs": 16384, + } + + def test_filenode_get_nodes_from_docs_dir(self, vcsbackend): + repo = vcsbackend.repo + commit = repo.get_commit() + + # check if we start with empty nodes cache + assert commit.nodes == {} + assert commit._path_mode_cache == {} + assert commit._path_type_cache == {} + + node_list = commit.get_nodes(b"docs") + + for node in node_list: + assert node diff --git a/rhodecode/tests/vcs/test_git.py b/rhodecode/tests/vcs/test_git.py index 684f7e8b..d9d40405 100644 --- a/rhodecode/tests/vcs/test_git.py +++ b/rhodecode/tests/vcs/test_git.py @@ -28,7 +28,7 @@ from rhodecode.lib.utils import make_db_config from rhodecode.lib.vcs.backends.base import Reference from rhodecode.lib.vcs.backends.git import GitRepository, GitCommit, discover_git_version from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError -from rhodecode.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState, SubModuleNode +from rhodecode.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState, SubModuleNode, RootNode from rhodecode.tests import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir from rhodecode.tests.vcs.conftest import BackendTestMixin @@ -219,23 +219,38 @@ class TestGitRepository(object): assert init_commit.message == "initial import\n" assert init_author == "Marcin Kuzminski " assert init_author == init_commit.committer - for path in ("vcs/__init__.py", "vcs/backends/BaseRepository.py", "vcs/backends/__init__.py"): + assert sorted(init_commit.added_paths) == sorted( + [ + b"vcs/__init__.py", + b"vcs/backends/BaseRepository.py", + b"vcs/backends/__init__.py", + ] + ) + assert sorted(init_commit.affected_files) == sorted( + [ + b"vcs/__init__.py", + b"vcs/backends/BaseRepository.py", + b"vcs/backends/__init__.py", + ] + ) + + for path in (b"vcs/__init__.py", b"vcs/backends/BaseRepository.py", b"vcs/backends/__init__.py"): assert isinstance(init_commit.get_node(path), FileNode) - for path in ("", "vcs", "vcs/backends"): + for path in (b"", b"vcs", b"vcs/backends"): assert isinstance(init_commit.get_node(path), DirNode) with pytest.raises(NodeDoesNotExistError): - init_commit.get_node(path="foobar") + init_commit.get_node(path=b"foobar") - node = init_commit.get_node("vcs/") + node = init_commit.get_node(b"vcs/") assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node("vcs") + node = init_commit.get_node(b"vcs") assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node("vcs/__init__.py") + node = init_commit.get_node(b"vcs/__init__.py") assert hasattr(node, "kind") assert node.kind == NodeKind.FILE @@ -257,7 +272,7 @@ Introduction TODO: To be written... """ - node = commit10.get_node("README.rst") + node = commit10.get_node(b"README.rst") assert node.kind == NodeKind.FILE assert node.str_content == README @@ -615,7 +630,7 @@ class TestGitCommit(object): def test_root_node(self): tip = self.repo.get_commit() - assert tip.root is tip.get_node("") + assert tip.root is tip.get_node(b"") def test_lazy_fetch(self): """ @@ -633,29 +648,29 @@ class TestGitCommit(object): # accessing root.nodes updates commit.nodes assert len(commit.nodes) == 9 - docs = root.get_node("docs") + docs = commit.get_node(b"docs") # we haven't yet accessed anything new as docs dir was already cached assert len(commit.nodes) == 9 assert len(docs.nodes) == 8 # accessing docs.nodes updates commit.nodes assert len(commit.nodes) == 17 - assert docs is commit.get_node("docs") + assert docs is commit.get_node(b"docs") assert docs is root.nodes[0] assert docs is root.dirs[0] - assert docs is commit.get_node("docs") + assert docs is commit.get_node(b"docs") def test_nodes_with_commit(self): commit_id = "2a13f185e4525f9d4b59882791a2d397b90d5ddc" commit = self.repo.get_commit(commit_id) root = commit.root - docs = root.get_node("docs") - assert docs is commit.get_node("docs") - api = docs.get_node("api") - assert api is commit.get_node("docs/api") - index = api.get_node("index.rst") - assert index is commit.get_node("docs/api/index.rst") - assert index is commit.get_node("docs").get_node("api").get_node("index.rst") + assert isinstance(root, RootNode) + docs = commit.get_node(b"docs") + assert docs is commit.get_node(b"docs") + api = commit.get_node(b"docs/api") + assert api is commit.get_node(b"docs/api") + index = commit.get_node(b"docs/api/index.rst") + assert index is commit.get_node(b"docs/api/index.rst") def test_branch_and_tags(self): """ @@ -682,12 +697,12 @@ class TestGitCommit(object): def test_file_size(self): to_check = ( - ("c1214f7e79e02fc37156ff215cd71275450cffc3", "vcs/backends/BaseRepository.py", 502), - ("d7e0d30fbcae12c90680eb095a4f5f02505ce501", "vcs/backends/hg.py", 854), - ("6e125e7c890379446e98980d8ed60fba87d0f6d1", "setup.py", 1068), - ("d955cd312c17b02143c04fa1099a352b04368118", "vcs/backends/base.py", 2921), - ("ca1eb7957a54bce53b12d1a51b13452f95bc7c7e", "vcs/backends/base.py", 3936), - ("f50f42baeed5af6518ef4b0cb2f1423f3851a941", "vcs/backends/base.py", 6189), + ("c1214f7e79e02fc37156ff215cd71275450cffc3", b"vcs/backends/BaseRepository.py", 502), + ("d7e0d30fbcae12c90680eb095a4f5f02505ce501", b"vcs/backends/hg.py", 854), + ("6e125e7c890379446e98980d8ed60fba87d0f6d1", b"setup.py", 1068), + ("d955cd312c17b02143c04fa1099a352b04368118", b"vcs/backends/base.py", 2921), + ("ca1eb7957a54bce53b12d1a51b13452f95bc7c7e", b"vcs/backends/base.py", 3936), + ("f50f42baeed5af6518ef4b0cb2f1423f3851a941", b"vcs/backends/base.py", 6189), ) for commit_id, path, size in to_check: node = self.repo.get_commit(commit_id).get_node(path) @@ -695,17 +710,17 @@ class TestGitCommit(object): assert node.size == size def test_file_history_from_commits(self): - node = self.repo[10].get_node("setup.py") + node = self.repo[10].get_node(b"setup.py") commit_ids = [commit.raw_id for commit in node.history] assert ["ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == commit_ids - node = self.repo[20].get_node("setup.py") + node = self.repo[20].get_node(b"setup.py") node_ids = [commit.raw_id for commit in node.history] assert ["191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == node_ids # special case we check history from commit that has this particular # file changed this means we check if it's included as well - node = self.repo.get_commit("191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e").get_node("setup.py") + node = self.repo.get_commit("191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e").get_node(b"setup.py") node_ids = [commit.raw_id for commit in node.history] assert ["191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == node_ids @@ -713,7 +728,7 @@ class TestGitCommit(object): # we can only check if those commits are present in the history # as we cannot update this test every time file is changed files = { - "setup.py": [ + b"setup.py": [ "54386793436c938cff89326944d4c2702340037d", "51d254f0ecf5df2ce50c0b115741f4cf13985dab", "998ed409c795fec2012b1c0ca054d99888b22090", @@ -724,7 +739,7 @@ class TestGitCommit(object): "191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", "ff7ca51e58c505fec0dd2491de52c622bb7a806b", ], - "vcs/nodes.py": [ + b"vcs/nodes.py": [ "33fa3223355104431402a888fa77a4e9956feb3e", "fa014c12c26d10ba682fadb78f2a11c24c8118e1", "e686b958768ee96af8029fe19c6050b1a8dd3b2b", @@ -757,7 +772,7 @@ class TestGitCommit(object): "dd80b0f6cf5052f17cc738c2951c4f2070200d7f", "ff7ca51e58c505fec0dd2491de52c622bb7a806b", ], - "vcs/backends/git.py": [ + b"vcs/backends/git.py": [ "4cf116ad5a457530381135e2f4c453e68a1b0105", "9a751d84d8e9408e736329767387f41b36935153", "cb681fb539c3faaedbcdf5ca71ca413425c18f01", @@ -778,7 +793,7 @@ class TestGitCommit(object): def test_file_annotate(self): files = { - "vcs/backends/__init__.py": { + b"vcs/backends/__init__.py": { "c1214f7e79e02fc37156ff215cd71275450cffc3": { "lines_no": 1, "commits": [ @@ -870,39 +885,31 @@ class TestGitCommit(object): """ Tests state of FileNodes. """ - node = self.repo.get_commit("e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0").get_node("vcs/utils/diffs.py") - assert node.state, NodeState.ADDED - assert node.added - assert not node.changed - assert not node.not_changed - assert not node.removed + commit = self.repo.get_commit("e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0") + node = commit.get_node(b"vcs/utils/diffs.py") + assert node.bytes_path in commit.added_paths - node = self.repo.get_commit("33fa3223355104431402a888fa77a4e9956feb3e").get_node(".hgignore") - assert node.state, NodeState.CHANGED - assert not node.added - assert node.changed - assert not node.not_changed - assert not node.removed + commit = self.repo.get_commit("33fa3223355104431402a888fa77a4e9956feb3e") + node = commit.get_node(b".hgignore") + assert node.bytes_path in commit.changed_paths - node = self.repo.get_commit("e29b67bd158580fc90fc5e9111240b90e6e86064").get_node("setup.py") - assert node.state, NodeState.NOT_CHANGED - assert not node.added - assert not node.changed - assert node.not_changed - assert not node.removed + commit = self.repo.get_commit("e29b67bd158580fc90fc5e9111240b90e6e86064") + node = commit.get_node(b"setup.py") + assert node.bytes_path not in commit.affected_files # If node has REMOVED state then trying to fetch it would raise # CommitError exception commit = self.repo.get_commit("fa6600f6848800641328adbf7811fd2372c02ab2") - path = "vcs/backends/BaseRepository.py" + path = b"vcs/backends/BaseRepository.py" with pytest.raises(NodeDoesNotExistError): commit.get_node(path) + # but it would be one of ``removed`` (commit's attribute) - assert path in [rf.path for rf in commit.removed] + assert path in [rf for rf in commit.removed_paths] commit = self.repo.get_commit("54386793436c938cff89326944d4c2702340037d") - changed = ["setup.py", "tests/test_nodes.py", "vcs/backends/hg.py", "vcs/nodes.py"] - assert set(changed) == set([f.path for f in commit.changed]) + changed = [b"setup.py", b"tests/test_nodes.py", b"vcs/backends/hg.py", b"vcs/nodes.py"] + assert set(changed) == set([f for f in commit.changed_paths]) def test_unicode_branch_refs(self): unicode_branches = { @@ -936,14 +943,14 @@ class TestGitCommit(object): def test_repo_files_content_types(self): commit = self.repo.get_commit() - for node in commit.get_node("/"): + for node in commit.get_node(b"/"): if node.is_file(): assert type(node.content) == bytes assert type(node.str_content) == str def test_wrong_path(self): # There is 'setup.py' in the root dir but not there: - path = "foo/bar/setup.py" + path = b"foo/bar/setup.py" tip = self.repo.get_commit() with pytest.raises(VCSError): tip.get_node(path) @@ -981,8 +988,7 @@ class TestLargeFileRepo(object): repo = backend_git.create_test_repo("largefiles", conf) tip = repo.scm_instance().get_commit() - node = tip.get_node("1MB.zip") - + node = tip.get_node(b"1MB.zip") # extract stored LF node into the origin cache repo_lfs_store: str = os.path.join(repo.repo_path, repo.repo_name, "lfs_store") @@ -1002,7 +1008,7 @@ class TestLargeFileRepo(object): assert lf_node.is_largefile() is True assert lf_node.size == 1024000 - assert lf_node.name == "1MB.zip" + assert lf_node.name == b"1MB.zip" @pytest.mark.usefixtures("vcs_repository_support") @@ -1032,14 +1038,11 @@ class TestGitSpecificWithRepo(BackendTestMixin): def test_paths_slow_traversing(self): commit = self.repo.get_commit() - assert ( - commit.get_node("foobar").get_node("static").get_node("js").get_node("admin").get_node("base.js").content - == b"base" - ) + assert commit.get_node(b"foobar/static/js/admin/base.js").content == b"base" def test_paths_fast_traversing(self): commit = self.repo.get_commit() - assert commit.get_node("foobar/static/js/admin/base.js").content == b"base" + assert commit.get_node(b"foobar/static/js/admin/base.js").content == b"base" def test_get_diff_runs_git_command_with_hashes(self): comm1 = self.repo[0] @@ -1110,12 +1113,15 @@ class TestGitRegression(BackendTestMixin): @pytest.mark.parametrize( "path, expected_paths", [ - ("bot", ["bot/build", "bot/templates", "bot/__init__.py"]), - ("bot/build", ["bot/build/migrations", "bot/build/static", "bot/build/templates"]), - ("bot/build/static", ["bot/build/static/templates"]), - ("bot/build/static/templates", ["bot/build/static/templates/f.html", "bot/build/static/templates/f1.html"]), - ("bot/build/templates", ["bot/build/templates/err.html", "bot/build/templates/err2.html"]), - ("bot/templates/", ["bot/templates/404.html", "bot/templates/500.html"]), + (b"bot", ["bot/build", "bot/templates", "bot/__init__.py"]), + (b"bot/build", ["bot/build/migrations", "bot/build/static", "bot/build/templates"]), + (b"bot/build/static", ["bot/build/static/templates"]), + ( + b"bot/build/static/templates", + ["bot/build/static/templates/f.html", "bot/build/static/templates/f1.html"], + ), + (b"bot/build/templates", ["bot/build/templates/err.html", "bot/build/templates/err2.html"]), + (b"bot/templates/", ["bot/templates/404.html", "bot/templates/500.html"]), ], ) def test_similar_paths(self, path, expected_paths): @@ -1146,8 +1152,8 @@ class TestGetSubmoduleUrl(object): node.str_content = ( '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n" ) - result = commit._get_submodule_url("subrepo1") - get_node_mock.assert_called_once_with(".gitmodules") + result = commit._get_submodule_url(b"subrepo1") + get_node_mock.assert_called_once_with(b".gitmodules") assert result == "https://code.rhodecode.com/dulwich" def test_complex_submodule_path(self): @@ -1160,14 +1166,14 @@ class TestGetSubmoduleUrl(object): "\tpath = complex/subrepo/path\n" "\turl = https://code.rhodecode.com/dulwich\n" ) - result = commit._get_submodule_url("complex/subrepo/path") - get_node_mock.assert_called_once_with(".gitmodules") + result = commit._get_submodule_url(b"complex/subrepo/path") + get_node_mock.assert_called_once_with(b".gitmodules") assert result == "https://code.rhodecode.com/dulwich" def test_submodules_file_not_found(self): commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1) with mock.patch.object(commit, "get_node", side_effect=NodeDoesNotExistError): - result = commit._get_submodule_url("complex/subrepo/path") + result = commit._get_submodule_url(b"complex/subrepo/path") assert result is None def test_path_not_found(self): @@ -1178,8 +1184,8 @@ class TestGetSubmoduleUrl(object): node.str_content = ( '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n" ) - result = commit._get_submodule_url("subrepo2") - get_node_mock.assert_called_once_with(".gitmodules") + result = commit._get_submodule_url(b"subrepo2") + get_node_mock.assert_called_once_with(b".gitmodules") assert result is None def test_returns_cached_values(self): @@ -1191,44 +1197,43 @@ class TestGetSubmoduleUrl(object): '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n" ) for _ in range(3): - commit._get_submodule_url("subrepo1") - get_node_mock.assert_called_once_with(".gitmodules") + commit._get_submodule_url(b"subrepo1") + get_node_mock.assert_called_once_with(b".gitmodules") def test_get_node_returns_a_link(self): repository = mock.Mock() repository.alias = "git" commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1) submodule_url = "https://code.rhodecode.com/dulwich" - get_id_patch = mock.patch.object(commit, "_get_tree_id_for_path", return_value=(1, "link")) + get_id_patch = mock.patch.object(commit, "_get_path_tree_id_and_type", return_value=(1, NodeKind.SUBMODULE)) get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url) with get_id_patch, get_submodule_patch as submodule_mock: - node = commit.get_node("/abcde") + node = commit.get_node(b"/abcde") - submodule_mock.assert_called_once_with("/abcde") + submodule_mock.assert_called_once_with(b"/abcde") assert type(node) == SubModuleNode assert node.url == submodule_url def test_get_nodes_returns_links(self): repository = mock.MagicMock() repository.alias = "git" - repository._remote.tree_items.return_value = [("subrepo", "stat", 1, "link")] + repository._remote.tree_items.return_value = [(b"subrepo", "stat", 1, NodeKind.SUBMODULE)] commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1) submodule_url = "https://code.rhodecode.com/dulwich" - get_id_patch = mock.patch.object(commit, "_get_tree_id_for_path", return_value=(1, "tree")) + get_id_patch = mock.patch.object(commit, "_get_path_tree_id_and_type", return_value=(1, NodeKind.DIR)) get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url) with get_id_patch, get_submodule_patch as submodule_mock: - nodes = commit.get_nodes("/abcde") + nodes = commit.get_nodes(b"/abcde") - submodule_mock.assert_called_once_with("/abcde/subrepo") + submodule_mock.assert_called_once_with(b"/abcde/subrepo") assert len(nodes) == 1 assert type(nodes[0]) == SubModuleNode assert nodes[0].url == submodule_url class TestGetShadowInstance(object): - @pytest.fixture() def repo(self, vcsbackend_git): _git_repo = vcsbackend_git.repo diff --git a/rhodecode/tests/vcs/test_hg.py b/rhodecode/tests/vcs/test_hg.py index 49f90868..a768e55d 100644 --- a/rhodecode/tests/vcs/test_hg.py +++ b/rhodecode/tests/vcs/test_hg.py @@ -27,7 +27,7 @@ from rhodecode.lib.vcs import backends from rhodecode.lib.vcs.backends.base import Reference, MergeResponse, MergeFailureReason from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialCommit from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError, CommitDoesNotExistError -from rhodecode.lib.vcs.nodes import FileNode, NodeKind, NodeState +from rhodecode.lib.vcs.nodes import FileNode, NodeKind, DirNode, RootNode from rhodecode.tests import TEST_HG_REPO, TEST_HG_REPO_CLONE, repo_id_generator @@ -41,22 +41,17 @@ def repo_path_generator(): i = 0 while True: i += 1 - yield "%s-%d" % (TEST_HG_REPO_CLONE, i) + yield f"{TEST_HG_REPO_CLONE}-{i:d}" REPO_PATH_GENERATOR = repo_path_generator() -@pytest.fixture(scope="class", autouse=True) -def repo(request, baseapp): - repo = MercurialRepository(TEST_HG_REPO) - if request.cls: - request.cls.repo = repo - return repo - - -class TestMercurialRepository(object): +class TestMercurialRepository: # pylint: disable=protected-access + @pytest.fixture(autouse=True) + def prepare(self): + self.repo = MercurialRepository(TEST_HG_REPO) def get_clone_repo(self): """ @@ -100,9 +95,8 @@ class TestMercurialRepository(object): def test_repo_clone(self): if os.path.exists(TEST_HG_REPO_CLONE): - self.fail( - "Cannot test mercurial clone repo as location %s already " - "exists. You should manually remove it first." % TEST_HG_REPO_CLONE + pytest.fail( + f"Cannot test mercurial clone repo as location {TEST_HG_REPO_CLONE} already exists. You should manually remove it first." ) repo = MercurialRepository(TEST_HG_REPO) @@ -217,8 +211,8 @@ class TestMercurialRepository(object): assert "git" in self.repo._get_branches(closed=True) assert "web" in self.repo._get_branches(closed=True) - for name, id in self.repo.branches.items(): - assert isinstance(self.repo.get_commit(id), MercurialCommit) + for name, commit_id in self.repo.branches.items(): + assert isinstance(self.repo.get_commit(commit_id), MercurialCommit) def test_tip_in_tags(self): # tip is always a tag @@ -235,29 +229,38 @@ class TestMercurialRepository(object): assert init_commit.message == "initial import" assert init_author == "Marcin Kuzminski " assert init_author == init_commit.committer - assert sorted(init_commit._file_paths) == sorted( + assert sorted(init_commit.added_paths) == sorted( [ - "vcs/__init__.py", - "vcs/backends/BaseRepository.py", - "vcs/backends/__init__.py", + b"vcs/__init__.py", + b"vcs/backends/BaseRepository.py", + b"vcs/backends/__init__.py", + ] + ) + assert sorted(init_commit.affected_files) == sorted( + [ + b"vcs/__init__.py", + b"vcs/backends/BaseRepository.py", + b"vcs/backends/__init__.py", ] ) - assert sorted(init_commit._dir_paths) == sorted(["", "vcs", "vcs/backends"]) - assert init_commit._dir_paths + init_commit._file_paths == init_commit._paths + for path in (b"vcs/__init__.py", b"vcs/backends/BaseRepository.py", b"vcs/backends/__init__.py"): + assert isinstance(init_commit.get_node(path), FileNode) + for path in (b"", b"vcs", b"vcs/backends"): + assert isinstance(init_commit.get_node(path), DirNode) with pytest.raises(NodeDoesNotExistError): - init_commit.get_node(path="foobar") + init_commit.get_node(path=b"foobar") - node = init_commit.get_node("vcs/") + node = init_commit.get_node(b"vcs/") assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node("vcs") + node = init_commit.get_node(b"vcs") assert hasattr(node, "kind") assert node.kind == NodeKind.DIR - node = init_commit.get_node("vcs/__init__.py") + node = init_commit.get_node(b"vcs/__init__.py") assert hasattr(node, "kind") assert node.kind == NodeKind.FILE @@ -279,7 +282,7 @@ class TestMercurialRepository(object): def test_commit10(self): commit10 = self.repo.get_commit(commit_idx=10) - README = """=== + readme = """=== VCS === @@ -291,9 +294,9 @@ Introduction TODO: To be written... """ - node = commit10.get_node("README.rst") + node = commit10.get_node(b"README.rst") assert node.kind == NodeKind.FILE - assert node.str_content == README + assert node.str_content == readme def test_local_clone(self): clone_path = next(REPO_PATH_GENERATOR) @@ -370,7 +373,7 @@ TODO: To be written... assert target_repo.branches["default"] == commit_id def test_local_pull_from_same_repo(self): - reference = Reference("branch", "default", None) + reference = Reference("branch", "default", "") with pytest.raises(ValueError): self.repo._local_pull(self.repo.path, reference) @@ -503,7 +506,7 @@ TODO: To be written... # Check we are not left in an intermediate merge state assert not os.path.exists(os.path.join(target_repo.path, ".hg", "merge", "state")) - def test_local_merge_of_two_branches_of_the_same_repo(self, backend_hg): + def test_local_merge_of_two_branches_of_the_same_repo(self, backend_hg, vcs_repo): commits = [ {"message": "a"}, {"message": "b", "branch": "b"}, @@ -639,7 +642,7 @@ TODO: To be written... # add an extra head to the target repo imc = target_repo.in_memory_commit - imc.add(FileNode(b"file_x", content="foo")) + imc.add(FileNode(b"file_x", content=b"foo")) commits = list(target_repo.get_commits()) imc.commit( message="Automatic commit from repo merge test", @@ -728,8 +731,7 @@ TODO: To be written... assert len(target_repo.commit_ids) == 2 + 2 -class TestGetShadowInstance(object): - +class TestGetShadowInstance: @pytest.fixture() def repo(self, vcsbackend_hg): _hg_repo = vcsbackend_hg.repo @@ -742,17 +744,21 @@ class TestGetShadowInstance(object): assert shadow.config.serialize() == repo.config.serialize() def test_disables_hooks_section(self, repo): - repo.config.set('hooks', 'foo', 'val') + repo.config.set("hooks", "foo", "val") shadow = repo.get_shadow_instance(repo.path) - assert not shadow.config.items('hooks') + assert not shadow.config.items("hooks") def test_allows_to_keep_hooks(self, repo): - repo.config.set('hooks', 'foo', 'val') + repo.config.set("hooks", "foo", "val") shadow = repo.get_shadow_instance(repo.path, enable_hooks=True) - assert shadow.config.items('hooks') + assert shadow.config.items("hooks") -class TestMercurialCommit(object): +class TestMercurialCommit: + @pytest.fixture(autouse=True) + def prepare(self): + self.repo = MercurialRepository(TEST_HG_REPO) + def _test_equality(self, commit): idx = commit.idx assert commit == self.repo.get_commit(commit_idx=idx) @@ -772,7 +778,7 @@ class TestMercurialCommit(object): def test_root_node(self): tip = self.repo.get_commit("tip") - assert tip.root is tip.get_node("") + assert tip.root is tip.get_node(b"") def test_lazy_fetch(self): """ @@ -788,28 +794,28 @@ class TestMercurialCommit(object): # accessing root.nodes updates commit.nodes assert len(commit.nodes) == 9 - docs = root.get_node("docs") + docs = commit.get_node(b"docs") # we haven't yet accessed anything new as docs dir was already cached assert len(commit.nodes) == 9 assert len(docs.nodes) == 8 # accessing docs.nodes updates commit.nodes assert len(commit.nodes) == 17 - assert docs is commit.get_node("docs") + assert docs is commit.get_node(b"docs") assert docs is root.nodes[0] assert docs is root.dirs[0] - assert docs is commit.get_node("docs") + assert docs is commit.get_node(b"docs") def test_nodes_with_commit(self): commit = self.repo.get_commit(commit_idx=45) root = commit.root - docs = root.get_node("docs") - assert docs is commit.get_node("docs") - api = docs.get_node("api") - assert api is commit.get_node("docs/api") - index = api.get_node("index.rst") - assert index is commit.get_node("docs/api/index.rst") - assert index is commit.get_node("docs").get_node("api").get_node("index.rst") + assert isinstance(root, RootNode) + docs = commit.get_node(b"docs") + assert docs is commit.get_node(b"docs") + api = commit.get_node(b"docs/api") + assert api is commit.get_node(b"docs/api") + index = commit.get_node(b"docs/api/index.rst") + assert index is commit.get_node(b"docs/api/index.rst") def test_branch_and_tags(self): commit0 = self.repo.get_commit(commit_idx=0) @@ -837,28 +843,28 @@ class TestMercurialCommit(object): def test_file_size(self): to_check = ( - (10, "setup.py", 1068), - (20, "setup.py", 1106), - (60, "setup.py", 1074), - (10, "vcs/backends/base.py", 2921), - (20, "vcs/backends/base.py", 3936), - (60, "vcs/backends/base.py", 6189), + (10, b"setup.py", 1068), + (20, b"setup.py", 1106), + (60, b"setup.py", 1074), + (10, b"vcs/backends/base.py", 2921), + (20, b"vcs/backends/base.py", 3936), + (60, b"vcs/backends/base.py", 6189), ) for idx, path, size in to_check: self._test_file_size(idx, path, size) def test_file_history_from_commits(self): - node = self.repo[10].get_node("setup.py") + node = self.repo[10].get_node(b"setup.py") commit_ids = [commit.raw_id for commit in node.history] assert ["3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"] == commit_ids - node = self.repo[20].get_node("setup.py") + node = self.repo[20].get_node(b"setup.py") node_ids = [commit.raw_id for commit in node.history] assert ["eada5a770da98ab0dd7325e29d00e0714f228d09", "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"] == node_ids # special case we check history from commit that has this particular # file changed this means we check if it's included as well - node = self.repo.get_commit("eada5a770da98ab0dd7325e29d00e0714f228d09").get_node("setup.py") + node = self.repo.get_commit("eada5a770da98ab0dd7325e29d00e0714f228d09").get_node(b"setup.py") node_ids = [commit.raw_id for commit in node.history] assert ["eada5a770da98ab0dd7325e29d00e0714f228d09", "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"] == node_ids @@ -866,9 +872,9 @@ class TestMercurialCommit(object): # we can only check if those commits are present in the history # as we cannot update this test every time file is changed files = { - "setup.py": [7, 18, 45, 46, 47, 69, 77], - "vcs/nodes.py": [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60, 61, 73, 76], - "vcs/backends/hg.py": [ + b"setup.py": [7, 18, 45, 46, 47, 69, 77], + b"vcs/nodes.py": [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60, 61, 73, 76], + b"vcs/backends/hg.py": [ 4, 5, 6, @@ -927,7 +933,7 @@ class TestMercurialCommit(object): def test_file_annotate(self): files = { - "vcs/backends/__init__.py": { + b"vcs/backends/__init__.py": { 89: { "lines_no": 31, "commits": [ @@ -1002,7 +1008,7 @@ class TestMercurialCommit(object): ], }, }, - "vcs/exceptions.py": { + b"vcs/exceptions.py": { 89: { "lines_no": 18, "commits": [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 18, 18, 18], @@ -1016,25 +1022,25 @@ class TestMercurialCommit(object): "commits": [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 18, 18, 18], }, }, - "MANIFEST.in": { + b"MANIFEST.in": { 89: {"lines_no": 5, "commits": [7, 7, 7, 71, 71]}, 20: {"lines_no": 3, "commits": [7, 7, 7]}, 55: {"lines_no": 3, "commits": [7, 7, 7]}, }, } - for fname, commit_dict in files.items(): + for file_name, commit_dict in files.items(): for idx, __ in commit_dict.items(): commit = self.repo.get_commit(commit_idx=idx) - l1_1 = [x[1] for x in commit.get_file_annotate(fname)] - l1_2 = [x[2]().raw_id for x in commit.get_file_annotate(fname)] + l1_1 = [x[1] for x in commit.get_file_annotate(file_name)] + l1_2 = [x[2]().raw_id for x in commit.get_file_annotate(file_name)] assert l1_1 == l1_2 - l1 = l1_2 = [x[2]().idx for x in commit.get_file_annotate(fname)] - l2 = files[fname][idx]["commits"] + l1 = l1_2 = [x[2]().idx for x in commit.get_file_annotate(file_name)] + l2 = files[file_name][idx]["commits"] assert l1 == l2, ( "The lists of commit for %s@commit_id%s" "from annotation list should match each other," - "got \n%s \nvs \n%s " % (fname, idx, l1, l2) + "got \n%s \nvs \n%s " % (file_name, idx, l1, l2) ) def test_commit_state(self): @@ -1047,55 +1053,51 @@ class TestMercurialCommit(object): # changed: 13 # added: 20 # removed: 1 - changed = set( - [ - ".hgignore", - "README.rst", - "docs/conf.py", - "docs/index.rst", - "setup.py", - "tests/test_hg.py", - "tests/test_nodes.py", - "vcs/__init__.py", - "vcs/backends/__init__.py", - "vcs/backends/base.py", - "vcs/backends/hg.py", - "vcs/nodes.py", - "vcs/utils/__init__.py", - ] - ) + changed = { + b".hgignore", + b"README.rst", + b"docs/conf.py", + b"docs/index.rst", + b"setup.py", + b"tests/test_hg.py", + b"tests/test_nodes.py", + b"vcs/__init__.py", + b"vcs/backends/__init__.py", + b"vcs/backends/base.py", + b"vcs/backends/hg.py", + b"vcs/nodes.py", + b"vcs/utils/__init__.py", + } - added = set( - [ - "docs/api/backends/hg.rst", - "docs/api/backends/index.rst", - "docs/api/index.rst", - "docs/api/nodes.rst", - "docs/api/web/index.rst", - "docs/api/web/simplevcs.rst", - "docs/installation.rst", - "docs/quickstart.rst", - "setup.cfg", - "vcs/utils/baseui_config.py", - "vcs/utils/web.py", - "vcs/web/__init__.py", - "vcs/web/exceptions.py", - "vcs/web/simplevcs/__init__.py", - "vcs/web/simplevcs/exceptions.py", - "vcs/web/simplevcs/middleware.py", - "vcs/web/simplevcs/models.py", - "vcs/web/simplevcs/settings.py", - "vcs/web/simplevcs/utils.py", - "vcs/web/simplevcs/views.py", - ] - ) + added = { + b"docs/api/backends/hg.rst", + b"docs/api/backends/index.rst", + b"docs/api/index.rst", + b"docs/api/nodes.rst", + b"docs/api/web/index.rst", + b"docs/api/web/simplevcs.rst", + b"docs/installation.rst", + b"docs/quickstart.rst", + b"setup.cfg", + b"vcs/utils/baseui_config.py", + b"vcs/utils/web.py", + b"vcs/web/__init__.py", + b"vcs/web/exceptions.py", + b"vcs/web/simplevcs/__init__.py", + b"vcs/web/simplevcs/exceptions.py", + b"vcs/web/simplevcs/middleware.py", + b"vcs/web/simplevcs/models.py", + b"vcs/web/simplevcs/settings.py", + b"vcs/web/simplevcs/utils.py", + b"vcs/web/simplevcs/views.py", + } - removed = set(["docs/api.rst"]) + removed = {b"docs/api.rst"} commit64 = self.repo.get_commit("46ad32a4f974") - assert set((node.path for node in commit64.added)) == added - assert set((node.path for node in commit64.changed)) == changed - assert set((node.path for node in commit64.removed)) == removed + assert set((node for node in commit64.added_paths)) == added + assert set((node for node in commit64.changed_paths)) == changed + assert set((node for node in commit64.removed_paths)) == removed # commit_id b090f22d27d6: # hg st --rev b090f22d27d6 @@ -1103,9 +1105,9 @@ class TestMercurialCommit(object): # added: 20 # removed: 1 commit88 = self.repo.get_commit("b090f22d27d6") - assert set((node.path for node in commit88.added)) == set() - assert set((node.path for node in commit88.changed)) == set([".hgignore"]) - assert set((node.path for node in commit88.removed)) == set() + assert set((node for node in commit88.added_paths)) == set() + assert set((node for node in commit88.changed_paths)) == {b".hgignore"} + assert set((node for node in commit88.removed_paths)) == set() # # 85: @@ -1114,55 +1116,40 @@ class TestMercurialCommit(object): # changed: 4 ['vcs/web/simplevcs/models.py', ...] # removed: 1 ['vcs/utils/web.py'] commit85 = self.repo.get_commit(commit_idx=85) - assert set((node.path for node in commit85.added)) == set( - ["vcs/utils/diffs.py", "vcs/web/simplevcs/views/diffs.py"] - ) - assert set((node.path for node in commit85.changed)) == set( - [ - "vcs/web/simplevcs/models.py", - "vcs/web/simplevcs/utils.py", - "vcs/web/simplevcs/views/__init__.py", - "vcs/web/simplevcs/views/repository.py", - ] - ) - assert set((node.path for node in commit85.removed)) == set(["vcs/utils/web.py"]) + assert set((node for node in commit85.added_paths)) == {b"vcs/utils/diffs.py", b"vcs/web/simplevcs/views/diffs.py"} + assert set((node for node in commit85.changed_paths)) == { + b"vcs/web/simplevcs/models.py", + b"vcs/web/simplevcs/utils.py", + b"vcs/web/simplevcs/views/__init__.py", + b"vcs/web/simplevcs/views/repository.py", + } + assert set((node for node in commit85.removed_paths)) == {b"vcs/utils/web.py"} def test_files_state(self): """ Tests state of FileNodes. """ commit = self.repo.get_commit(commit_idx=85) - node = commit.get_node("vcs/utils/diffs.py") - assert node.state, NodeState.ADDED - assert node.added - assert not node.changed - assert not node.not_changed - assert not node.removed + node = commit.get_node(b"vcs/utils/diffs.py") + assert node.bytes_path in commit.added_paths commit = self.repo.get_commit(commit_idx=88) - node = commit.get_node(".hgignore") - assert node.state, NodeState.CHANGED - assert not node.added - assert node.changed - assert not node.not_changed - assert not node.removed + node = commit.get_node(b".hgignore") + assert node.bytes_path in commit.changed_paths commit = self.repo.get_commit(commit_idx=85) - node = commit.get_node("setup.py") - assert node.state, NodeState.NOT_CHANGED - assert not node.added - assert not node.changed - assert node.not_changed - assert not node.removed + node = commit.get_node(b"setup.py") + assert node.bytes_path not in commit.affected_files # If node has REMOVED state then trying to fetch it would raise # CommitError exception commit = self.repo.get_commit(commit_idx=2) - path = "vcs/backends/BaseRepository.py" + path = b"vcs/backends/BaseRepository.py" with pytest.raises(NodeDoesNotExistError): commit.get_node(path) + # but it would be one of ``removed`` (commit's attribute) - assert path in [rf.path for rf in commit.removed] + assert path in [rf for rf in commit.removed_paths] def test_commit_message_is_unicode(self): for cm in self.repo: @@ -1174,14 +1161,14 @@ class TestMercurialCommit(object): def test_repo_files_content_type(self): test_commit = self.repo.get_commit(commit_idx=100) - for node in test_commit.get_node("/"): + for node in test_commit.get_node(b"/"): if node.is_file(): assert type(node.content) == bytes assert type(node.str_content) == str def test_wrong_path(self): # There is 'setup.py' in the root dir but not there: - path = "foo/bar/setup.py" + path = b"foo/bar/setup.py" with pytest.raises(VCSError): self.repo.get_commit().get_node(path) @@ -1196,23 +1183,27 @@ class TestMercurialCommit(object): assert "marcink" == self.repo.get_commit("84478366594b").author_name -class TestLargeFileRepo(object): +class TestLargeFileRepo: def test_large_file(self, backend_hg): conf = make_db_config() hg_largefiles_store = conf.get("largefiles", "usercache") repo = backend_hg.create_test_repo("largefiles", conf) tip = repo.scm_instance().get_commit() - node = tip.get_node(".hglf/thisfileislarge") + node = tip.get_node(b".hglf/thisfileislarge") lf_node = node.get_largefile_node() assert lf_node.is_largefile() is True assert lf_node.size == 1024000 - assert lf_node.name == ".hglf/thisfileislarge" + assert lf_node.name == b".hglf/thisfileislarge" -class TestGetBranchName(object): +class TestGetBranchName: + @pytest.fixture(autouse=True) + def prepare(self): + self.repo = MercurialRepository(TEST_HG_REPO) + def test_returns_ref_name_when_type_is_branch(self): ref = self._create_ref("branch", "fake-name") result = self.repo._get_branch_name(ref) @@ -1235,7 +1226,11 @@ class TestGetBranchName(object): return ref -class TestIsTheSameBranch(object): +class TestIsTheSameBranch: + @pytest.fixture(autouse=True) + def prepare(self): + self.repo = MercurialRepository(TEST_HG_REPO) + def test_returns_true_when_branches_are_equal(self): source_ref = mock.Mock(name="source-ref") target_ref = mock.Mock(name="target-ref") diff --git a/rhodecode/tests/vcs/test_inmemory.py b/rhodecode/tests/vcs/test_inmemory.py index 2d7d49f1..8d52e43b 100644 --- a/rhodecode/tests/vcs/test_inmemory.py +++ b/rhodecode/tests/vcs/test_inmemory.py @@ -128,8 +128,8 @@ class TestInMemoryCommit(BackendTestMixin): ] self.imc.add(*to_add) commit = self.imc.commit("Initial", "joe doe ") - assert isinstance(commit.get_node("foo"), DirNode) - assert isinstance(commit.get_node("foo/bar"), DirNode) + assert isinstance(commit.get_node(b"foo"), DirNode) + assert isinstance(commit.get_node(b"foo/bar"), DirNode) self.assert_nodes_in_commit(commit, to_add) # commit some more files again @@ -244,7 +244,7 @@ class TestInMemoryCommit(BackendTestMixin): tip = self.repo.get_commit() node = nodes[0] - assert node.content == tip.get_node(node.path).content + assert node.content == tip.get_node(node.bytes_path).content self.imc.remove(node) self.imc.commit(message=f"Removed {node.path}", author="Some Name ") @@ -252,7 +252,7 @@ class TestInMemoryCommit(BackendTestMixin): assert tip != newtip assert tip.id != newtip.id with pytest.raises(NodeDoesNotExistError): - newtip.get_node(node.path) + newtip.get_node(node.bytes_path) def test_remove_last_file_from_directory(self): node = FileNode(b"omg/qwe/foo/bar", content=b"foobar") @@ -262,7 +262,7 @@ class TestInMemoryCommit(BackendTestMixin): self.imc.remove(node) tip = self.imc.commit("removed", "joe doe ") with pytest.raises(NodeDoesNotExistError): - tip.get_node("omg/qwe/foo/bar") + tip.get_node(b"omg/qwe/foo/bar") def test_remove_raise_node_does_not_exist(self, nodes): self.imc.remove(nodes[0]) @@ -338,5 +338,5 @@ class TestInMemoryCommit(BackendTestMixin): def assert_nodes_in_commit(self, commit, nodes): for node in nodes: - assert commit.get_node(node.path).path == node.path - assert commit.get_node(node.path).content == node.content + assert commit.get_node(node.bytes_path).path == node.path + assert commit.get_node(node.bytes_path).content == node.content diff --git a/rhodecode/tests/vcs/test_nodes.py b/rhodecode/tests/vcs/test_nodes.py index 77e23da5..0efe1821 100644 --- a/rhodecode/tests/vcs/test_nodes.py +++ b/rhodecode/tests/vcs/test_nodes.py @@ -155,30 +155,6 @@ class TestNodeBasics: with pytest.raises(NodeError): node.content # noqa - def test_dir_node_iter(self): - nodes = [ - DirNode(b"docs"), - DirNode(b"tests"), - FileNode(b"bar"), - FileNode(b"foo"), - FileNode(b"readme.txt"), - FileNode(b"setup.py"), - ] - dirnode = DirNode(b"", nodes=nodes) - for node in dirnode: - assert node == dirnode.get_node(node.path) - - def test_node_state(self): - """ - Without link to commit nodes should raise NodeError. - """ - node = FileNode(b"anything") - with pytest.raises(NodeError): - node.state # noqa - node = DirNode(b"anything") - with pytest.raises(NodeError): - node.state # noqa - def test_file_node_stat(self): node = FileNode(b"foobar", b"empty... almost") mode = node.mode # default should be 0100644 @@ -272,5 +248,5 @@ class TestNodesCommits(BackendTestMixin): last_commit = repo.get_commit() for x in range(3): - node = last_commit.get_node(f"file_{x}.txt") + node = last_commit.get_node(b"file_%d.txt" % x) assert node.last_commit == repo[x] diff --git a/rhodecode/tests/vcs/test_svn.py b/rhodecode/tests/vcs/test_svn.py index 9eb64898..bb333593 100644 --- a/rhodecode/tests/vcs/test_svn.py +++ b/rhodecode/tests/vcs/test_svn.py @@ -128,7 +128,7 @@ def test_read_full_file_tree(head): def test_topnode_files_attribute(head): - topnode = head.get_node("") + topnode = head.get_node(b"") topnode.files @@ -173,23 +173,23 @@ class TestSVNCommit(object): self.repo = repo def test_file_history_from_commits(self): - node = self.repo[10].get_node("setup.py") + node = self.repo[10].get_node(b"setup.py") commit_ids = [commit.raw_id for commit in node.history] assert ["8"] == commit_ids - node = self.repo[20].get_node("setup.py") + node = self.repo[20].get_node(b"setup.py") node_ids = [commit.raw_id for commit in node.history] assert ["18", "8"] == node_ids # special case we check history from commit that has this particular # file changed this means we check if it's included as well - node = self.repo.get_commit("18").get_node("setup.py") + node = self.repo.get_commit("18").get_node(b"setup.py") node_ids = [commit.raw_id for commit in node.history] assert ["18", "8"] == node_ids def test_repo_files_content_type(self): test_commit = self.repo.get_commit(commit_idx=100) - for node in test_commit.get_node("/"): + for node in test_commit.get_node(b"/"): if node.is_file(): assert type(node.content) == bytes assert type(node.str_content) == str diff --git a/rhodecode/tests/vcs/test_tags.py b/rhodecode/tests/vcs/test_tags.py index 306f0aca..d0504f53 100644 --- a/rhodecode/tests/vcs/test_tags.py +++ b/rhodecode/tests/vcs/test_tags.py @@ -30,11 +30,11 @@ class TestTags(BackendTestMixin): def test_new_tag(self): tip = self.repo.get_commit() tagsize = len(self.repo.tags) - tag = self.repo.tag("last-commit", "joe", tip.raw_id) + tag_commit = self.repo.tag("last-commit", "joe", tip.raw_id) assert len(self.repo.tags) == tagsize + 1 for top, __, __ in tip.walk(): - assert top == tag.get_node(top.path) + assert top == tag_commit.get_node(top.bytes_path) def test_tag_already_exist(self): tip = self.repo.get_commit() diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py index 6e5159fb..960b2c9f 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py @@ -65,7 +65,7 @@ class TestVCSOperationsOnUsingBadClient(object): # push fails repo is locked by other user ! push_url = rcstack.repo_clone_url(HG_REPO) stdout, stderr = _add_files_and_push("hg", tmpdir.strpath, clone_url=push_url) - msg = "Your hg client (ver=mercurial/proto-1.0 (Mercurial 6.7.4)) is forbidden by security rules" + msg = "Your hg client (version=mercurial/proto-1.0 (Mercurial 6.7.4)) is forbidden by security rules" assert msg in stderr def test_push_with_bad_client_repo_by_other_user_git(self, rcstack, tmpdir): @@ -81,7 +81,7 @@ class TestVCSOperationsOnUsingBadClient(object): push_url = rcstack.repo_clone_url(GIT_REPO) stdout, stderr = _add_files_and_push("git", tmpdir.strpath, clone_url=push_url) - err = "Your git client (ver=git/2.45.2) is forbidden by security rules" + err = "Your git client (version=git/2.45.2) is forbidden by security rules" assert err in stderr @pytest.mark.xfail(reason="Lack of proper SVN support of cloning") diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py b/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py index 6f4b8a9b..84ea96fa 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_svn.py @@ -124,7 +124,6 @@ class TestVCSOperationsSVN(object): assert 'not found' in stderr - @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') def test_clone_existing_path_svn_not_in_database( self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-git', repo_type='git') @@ -136,7 +135,6 @@ class TestVCSOperationsSVN(object): f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) assert 'not found' in stderr - @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') def test_clone_existing_path_svn_not_in_database_different_scm( self, rcstack, tmpdir, fs_repo_only): db_name = fs_repo_only('not-in-db-hg', repo_type='hg') @@ -149,7 +147,6 @@ class TestVCSOperationsSVN(object): f'svn checkout {flags} {auth}', clone_url, tmpdir.strpath) assert 'not found' in stderr - @pytest.mark.xfail(reason='Lack of proper SVN support of cloning') def test_clone_non_existing_store_path_svn(self, rcstack, tmpdir, user_util): repo = user_util.create_repo(repo_type='git') clone_url = rcstack.repo_clone_url(repo.repo_name) From 200dfdcf24d644e2aa3789268c8027cf498a072e Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Thu, 28 Nov 2024 15:08:42 +0100 Subject: [PATCH 86/99] docs: updated changelog --- docs/release-notes/release-notes-5.4.0.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/release-notes-5.4.0.rst b/docs/release-notes/release-notes-5.4.0.rst index 6d5634b2..846f3a4e 100644 --- a/docs/release-notes/release-notes-5.4.0.rst +++ b/docs/release-notes/release-notes-5.4.0.rst @@ -10,30 +10,34 @@ Release Date New Features ^^^^^^^^^^^^ - +- RhodeCode and VcsServer operate now on bytestrings instead of strings for diffs and files. This allows a mixed or non-utf8 + encoded files to be used. +- Remap and rescan: added more resilient remap and removal option, and also split the logic to either add or cleanup. General ^^^^^^^ +- Ported build system to use pyproject.toml. Security ^^^^^^^^ +- Multiple library bumps to fix security issues found in those python packages. Performance ^^^^^^^^^^^ - - +- SVN: added same caches for stored file nodes as git and mercurial have for improved performance. +- GIT: moved certain operations into vcsserver Fixes ^^^^^ - Fixed UI visualization issues with long commit messages - Fixed masking for some fields in admin settings - +- Fixed issues with caches calculations when passed search fields were empty Upgrade notes ^^^^^^^^^^^^^ From ea05521e76724dfdf59aa8aef4feeaa4db99b249 Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Mon, 2 Dec 2024 09:24:32 +0100 Subject: [PATCH 87/99] fix(hg-items): fixed missing submodules code and add forgotten stats item --- .../apps/repository/tests/test_repo_files.py | 1040 ++++++++--------- rhodecode/lib/vcs/backends/hg/commit.py | 20 +- rhodecode/lib/vcs/nodes.py | 11 +- rhodecode/tests/fixtures/fixture_utils.py | 34 +- rhodecode/tests/utils.py | 6 +- .../vcs/test_filenodes_listing_and_caches.py | 17 +- 6 files changed, 531 insertions(+), 597 deletions(-) diff --git a/rhodecode/apps/repository/tests/test_repo_files.py b/rhodecode/apps/repository/tests/test_repo_files.py index 20de791f..945b68aa 100644 --- a/rhodecode/apps/repository/tests/test_repo_files.py +++ b/rhodecode/apps/repository/tests/test_repo_files.py @@ -41,207 +41,171 @@ fixture = Fixture() def get_node_history(backend_type): return { - 'hg': json.loads(fixture.load_resource('hg_node_history_response.json')), - 'git': json.loads(fixture.load_resource('git_node_history_response.json')), - 'svn': json.loads(fixture.load_resource('svn_node_history_response.json')), + "hg": json.loads(fixture.load_resource("hg_node_history_response.json")), + "git": json.loads(fixture.load_resource("git_node_history_response.json")), + "svn": json.loads(fixture.load_resource("svn_node_history_response.json")), }[backend_type] def assert_files_in_response(response, files, params): - template = ( - 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"') + template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"' _assert_items_in_response(response, files, template, params) def assert_dirs_in_response(response, dirs, params): - template = ( - 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"') + template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"' _assert_items_in_response(response, dirs, template, params) def _assert_items_in_response(response, items, template, params): for item in items: - item_params = {'name': item} + item_params = {"name": item} item_params.update(params) response.mustcontain(template % item_params) def assert_timeago_in_response(response, items, params): for item in items: - response.mustcontain(h.age_component(params['date'])) + response.mustcontain(h.age_component(params["date"])) @pytest.mark.usefixtures("app") class TestFilesViews(object): - def test_show_files(self, backend): - response = self.app.get( - route_path('repo_files', - repo_name=backend.repo_name, - commit_id='tip', f_path='/')) + response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path="")) commit = backend.repo.get_commit() - params = { - 'repo_name': backend.repo_name, - 'commit_id': commit.raw_id, - 'date': commit.date - } - assert_dirs_in_response(response, ['docs', 'vcs'], params) + params = {"repo_name": backend.repo_name, "commit_id": commit.raw_id, "date": commit.date} + assert_dirs_in_response(response, ["docs", "vcs"], params) files = [ - '.gitignore', - '.hgignore', - '.hgtags', + ".gitignore", + ".hgignore", + ".hgtags", # TODO: missing in Git # '.travis.yml', - 'MANIFEST.in', - 'README.rst', + "MANIFEST.in", + "README.rst", # TODO: File is missing in svn repository # 'run_test_and_report.sh', - 'setup.cfg', - 'setup.py', - 'test_and_report.sh', - 'tox.ini', + "setup.cfg", + "setup.py", + "test_and_report.sh", + "tox.ini", ] assert_files_in_response(response, files, params) assert_timeago_in_response(response, files, params) def test_show_files_links_submodules_with_absolute_url(self, backend_hg): - repo = backend_hg['subrepos'] - response = self.app.get( - route_path('repo_files', - repo_name=repo.repo_name, - commit_id='tip', f_path='/')) + repo = backend_hg["subrepos"] + response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) assert_response = response.assert_response() - assert_response.contains_one_link( - 'absolute-path @ 000000000000', 'http://example.com/absolute-path') + assert_response.contains_one_link("absolute-path @ 000000000000", "http://example.com/absolute-path") - def test_show_files_links_submodules_with_absolute_url_subpaths( - self, backend_hg): - repo = backend_hg['subrepos'] - response = self.app.get( - route_path('repo_files', - repo_name=repo.repo_name, - commit_id='tip', f_path='/')) + def test_show_files_links_submodules_with_absolute_url_subpaths(self, backend_hg): + repo = backend_hg["subrepos"] + response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) assert_response = response.assert_response() - assert_response.contains_one_link( - 'subpaths-path @ 000000000000', - 'http://sub-base.example.com/subpaths-path') + assert_response.contains_one_link("subpaths-path @ 000000000000", "http://sub-base.example.com/subpaths-path") @pytest.mark.xfail_backends("svn", reason="Depends on branch support") def test_files_menu(self, backend): new_branch = "temp_branch_name" - commits = [ - {'message': 'a'}, - {'message': 'b', 'branch': new_branch} - ] + commits = [{"message": "a"}, {"message": "b", "branch": new_branch}] backend.create_repo(commits) backend.repo.landing_rev = f"branch:{new_branch}" Session().commit() # get response based on tip and not new commit - response = self.app.get( - route_path('repo_files', - repo_name=backend.repo_name, - commit_id='tip', f_path='/')) + response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path="")) # make sure Files menu url is not tip but new commit landing_rev = backend.repo.landing_ref_name - files_url = route_path('repo_files:default_path', - repo_name=backend.repo_name, - commit_id=landing_rev, params={'at': landing_rev}) + files_url = route_path( + "repo_files:default_path", repo_name=backend.repo_name, commit_id=landing_rev, params={"at": landing_rev} + ) - assert landing_rev != 'tip' + assert landing_rev != "tip" response.mustcontain(f'
  • ') def test_show_files_commit(self, backend): commit = backend.repo.get_commit(commit_idx=32) response = self.app.get( - route_path('repo_files', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='/')) + route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="") + ) - dirs = ['docs', 'tests'] - files = ['README.rst'] + dirs = ["docs", "tests"] + files = ["README.rst"] params = { - 'repo_name': backend.repo_name, - 'commit_id': commit.raw_id, + "repo_name": backend.repo_name, + "commit_id": commit.raw_id, } assert_dirs_in_response(response, dirs, params) assert_files_in_response(response, files, params) def test_show_files_different_branch(self, backend): branches = dict( - hg=(150, ['git']), + hg=(150, ["git"]), # TODO: Git test repository does not contain other branches - git=(633, ['master']), + git=(633, ["master"]), # TODO: Branch support in Subversion - svn=(150, []) + svn=(150, []), ) idx, branches = branches[backend.alias] commit = backend.repo.get_commit(commit_idx=idx) response = self.app.get( - route_path('repo_files', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='/')) + route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="") + ) assert_response = response.assert_response() for branch in branches: - assert_response.element_contains('.tags .branchtag', branch) + assert_response.element_contains(".tags .branchtag", branch) def test_show_files_paging(self, backend): repo = backend.repo indexes = [73, 92, 109, 1, 0] - idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id) - for rev in indexes] + idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id) for rev in indexes] for idx in idx_map: - response = self.app.get( - route_path('repo_files', - repo_name=backend.repo_name, - commit_id=idx[1], f_path='/')) + response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id=idx[1], f_path="")) response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8])) def test_file_source(self, backend): commit = backend.repo.get_commit(commit_idx=167) response = self.app.get( - route_path('repo_files', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='vcs/nodes.py')) + route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py") + ) msgbox = """
    %s
    """ - response.mustcontain(msgbox % (commit.message, )) + response.mustcontain(msgbox % (commit.message,)) assert_response = response.assert_response() if commit.branch: - assert_response.element_contains( - '.tags.tags-main .branchtag', commit.branch) + assert_response.element_contains(".tags.tags-main .branchtag", commit.branch) if commit.tags: for tag in commit.tags: - assert_response.element_contains('.tags.tags-main .tagtag', tag) + assert_response.element_contains(".tags.tags-main .tagtag", tag) def test_file_source_annotated(self, backend): response = self.app.get( - route_path('repo_files:annotated', - repo_name=backend.repo_name, - commit_id='tip', f_path='vcs/nodes.py')) + route_path("repo_files:annotated", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py") + ) expected_commits = { - 'hg': 'r356', - 'git': 'r345', - 'svn': 'r208', + "hg": "r356", + "git": "r345", + "svn": "r208", } response.mustcontain(expected_commits[backend.alias]) def test_file_source_authors(self, backend): response = self.app.get( - route_path('repo_file_authors', - repo_name=backend.repo_name, - commit_id='tip', f_path='vcs/nodes.py')) + route_path("repo_file_authors", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py") + ) expected_authors = { - 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'), - 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'), - 'svn': ('marcin', 'lukasz'), + "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"), + "git": ("Marcin Kuzminski", "Lukasz Balcerzak"), + "svn": ("marcin", "lukasz"), } for author in expected_authors[backend.alias]: @@ -249,14 +213,18 @@ class TestFilesViews(object): def test_file_source_authors_with_annotation(self, backend): response = self.app.get( - route_path('repo_file_authors', - repo_name=backend.repo_name, - commit_id='tip', f_path='vcs/nodes.py', - params=dict(annotate=1))) + route_path( + "repo_file_authors", + repo_name=backend.repo_name, + commit_id="tip", + f_path="vcs/nodes.py", + params=dict(annotate=1), + ) + ) expected_authors = { - 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'), - 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'), - 'svn': ('marcin', 'lukasz'), + "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"), + "git": ("Marcin Kuzminski", "Lukasz Balcerzak"), + "svn": ("marcin", "lukasz"), } for author in expected_authors[backend.alias]: @@ -264,94 +232,88 @@ class TestFilesViews(object): def test_file_source_history(self, backend, xhr_header): response = self.app.get( - route_path('repo_file_history', - repo_name=backend.repo_name, - commit_id='tip', f_path='vcs/nodes.py'), - extra_environ=xhr_header) + route_path("repo_file_history", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py"), + extra_environ=xhr_header, + ) assert get_node_history(backend.alias) == json.loads(response.body) def test_file_source_history_svn(self, backend_svn, xhr_header): - simple_repo = backend_svn['svn-simple-layout'] + simple_repo = backend_svn["svn-simple-layout"] response = self.app.get( - route_path('repo_file_history', - repo_name=simple_repo.repo_name, - commit_id='tip', f_path='trunk/example.py'), - extra_environ=xhr_header) + route_path( + "repo_file_history", repo_name=simple_repo.repo_name, commit_id="tip", f_path="trunk/example.py" + ), + extra_environ=xhr_header, + ) - expected_data = json.loads( - fixture.load_resource('svn_node_history_branches.json')) + expected_data = json.loads(fixture.load_resource("svn_node_history_branches.json")) assert expected_data == response.json def test_file_source_history_with_annotation(self, backend, xhr_header): response = self.app.get( - route_path('repo_file_history', - repo_name=backend.repo_name, - commit_id='tip', f_path='vcs/nodes.py', - params=dict(annotate=1)), - - extra_environ=xhr_header) + route_path( + "repo_file_history", + repo_name=backend.repo_name, + commit_id="tip", + f_path="vcs/nodes.py", + params=dict(annotate=1), + ), + extra_environ=xhr_header, + ) assert get_node_history(backend.alias) == json.loads(response.body) def test_tree_search_top_level(self, backend, xhr_header): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_files_nodelist', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='/'), - extra_environ=xhr_header) - assert 'nodes' in response.json - assert {'name': 'docs', 'type': 'dir'} in response.json['nodes'] + route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""), + extra_environ=xhr_header, + ) + assert "nodes" in response.json + assert {"name": "docs", "type": "dir"} in response.json["nodes"] def test_tree_search_missing_xhr(self, backend): self.app.get( - route_path('repo_files_nodelist', - repo_name=backend.repo_name, - commit_id='tip', f_path='/'), - status=404) + route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404 + ) def test_tree_search_at_path(self, backend, xhr_header): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_files_nodelist', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='/docs'), - extra_environ=xhr_header) - assert 'nodes' in response.json - nodes = response.json['nodes'] - assert {'name': 'docs/api', 'type': 'dir'} in nodes - assert {'name': 'docs/index.rst', 'type': 'file'} in nodes + route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs"), + extra_environ=xhr_header, + ) + assert "nodes" in response.json + nodes = response.json["nodes"] + assert {"name": "docs/api", "type": "dir"} in nodes + assert {"name": "docs/index.rst", "type": "file"} in nodes def test_tree_search_at_path_2nd_level(self, backend, xhr_header): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_files_nodelist', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='/docs/api'), - extra_environ=xhr_header) - assert 'nodes' in response.json - nodes = response.json['nodes'] - assert {'name': 'docs/api/index.rst', 'type': 'file'} in nodes + route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs/api"), + extra_environ=xhr_header, + ) + assert "nodes" in response.json + nodes = response.json["nodes"] + assert {"name": "docs/api/index.rst", "type": "file"} in nodes def test_tree_search_at_path_missing_xhr(self, backend): self.app.get( - route_path('repo_files_nodelist', - repo_name=backend.repo_name, - commit_id='tip', f_path='/docs'), - status=404) + route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path="/docs"), status=404 + ) def test_nodetree(self, backend, xhr_header): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_nodetree_full', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='/'), - extra_environ=xhr_header) + route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""), + extra_environ=xhr_header, + ) assert_response = response.assert_response() - for attr in ['data-commit-id', 'data-date', 'data-author']: - elements = assert_response.get_elements('[{}]'.format(attr)) + for attr in ["data-commit-id", "data-date", "data-author"]: + elements = assert_response.get_elements("[{}]".format(attr)) assert len(elements) > 1 for element in elements: @@ -360,124 +322,113 @@ class TestFilesViews(object): def test_nodetree_if_file(self, backend, xhr_header): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_nodetree_full', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='README.rst'), - extra_environ=xhr_header) - assert response.text == '' + route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="README.rst"), + extra_environ=xhr_header, + ) + assert response.text == "" def test_nodetree_wrong_path(self, backend, xhr_header): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_nodetree_full', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='/dont-exist'), - extra_environ=xhr_header) + route_path( + "repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/dont-exist" + ), + extra_environ=xhr_header, + ) - err = 'error: There is no file nor ' \ - 'directory at the given path' + err = "error: There is no file nor " "directory at the given path" assert err in response.text def test_nodetree_missing_xhr(self, backend): self.app.get( - route_path('repo_nodetree_full', - repo_name=backend.repo_name, - commit_id='tip', f_path='/'), - status=404) + route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404 + ) @pytest.mark.usefixtures("app", "autologin_user") class TestRawFileHandling(object): - def test_download_file(self, backend): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_file_download', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='vcs/nodes.py'),) + route_path( + "repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py" + ), + ) - assert response.content_disposition == 'attachment; filename="nodes.py"; filename*=UTF-8\'\'nodes.py' + assert response.content_disposition == "attachment; filename=\"nodes.py\"; filename*=UTF-8''nodes.py" assert response.content_type == "text/x-python" def test_download_file_wrong_cs(self, backend): - raw_id = u'ERRORce30c96924232dffcd24178a07ffeb5dfc' + raw_id = "ERRORce30c96924232dffcd24178a07ffeb5dfc" response = self.app.get( - route_path('repo_file_download', - repo_name=backend.repo_name, - commit_id=raw_id, f_path='vcs/nodes.svg'), - status=404) + route_path("repo_file_download", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"), + status=404, + ) msg = """No such commit exists for this repository""" response.mustcontain(msg) def test_download_file_wrong_f_path(self, backend): commit = backend.repo.get_commit(commit_idx=173) - f_path = 'vcs/ERRORnodes.py' + f_path = "vcs/ERRORnodes.py" response = self.app.get( - route_path('repo_file_download', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path=f_path), - status=404) + route_path("repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path), + status=404, + ) - msg = ( - "There is no file nor directory at the given path: " - "`%s` at commit %s" % (f_path, commit.short_id)) + msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id) response.mustcontain(msg) def test_file_raw(self, backend): commit = backend.repo.get_commit(commit_idx=173) response = self.app.get( - route_path('repo_file_raw', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path='vcs/nodes.py'),) + route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py"), + ) assert response.content_type == "text/plain" def test_file_raw_binary(self, backend): commit = backend.repo.get_commit() response = self.app.get( - route_path('repo_file_raw', - repo_name=backend.repo_name, - commit_id=commit.raw_id, - f_path='docs/theme/ADC/static/breadcrumb_background.png'),) + route_path( + "repo_file_raw", + repo_name=backend.repo_name, + commit_id=commit.raw_id, + f_path="docs/theme/ADC/static/breadcrumb_background.png", + ), + ) - assert response.content_disposition == 'inline' + assert response.content_disposition == "inline" def test_raw_file_wrong_cs(self, backend): - raw_id = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc' + raw_id = "ERRORcce30c96924232dffcd24178a07ffeb5dfc" response = self.app.get( - route_path('repo_file_raw', - repo_name=backend.repo_name, - commit_id=raw_id, f_path='vcs/nodes.svg'), - status=404) + route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"), + status=404, + ) msg = """No such commit exists for this repository""" response.mustcontain(msg) def test_raw_wrong_f_path(self, backend): commit = backend.repo.get_commit(commit_idx=173) - f_path = 'vcs/ERRORnodes.py' + f_path = "vcs/ERRORnodes.py" response = self.app.get( - route_path('repo_file_raw', - repo_name=backend.repo_name, - commit_id=commit.raw_id, f_path=f_path), - status=404) + route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path), status=404 + ) - msg = ( - "There is no file nor directory at the given path: " - "`%s` at commit %s" % (f_path, commit.short_id)) + msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id) response.mustcontain(msg) def test_raw_svg_should_not_be_rendered(self, backend): backend.create_repo() backend.ensure_file(b"xss.svg") response = self.app.get( - route_path('repo_file_raw', - repo_name=backend.repo_name, - commit_id='tip', f_path='xss.svg'),) + route_path("repo_file_raw", repo_name=backend.repo_name, commit_id="tip", f_path="xss.svg"), + ) # If the content type is image/svg+xml then it allows to render HTML # and malicious SVG. assert response.content_type == "text/plain" @@ -485,25 +436,23 @@ class TestRawFileHandling(object): @pytest.mark.usefixtures("app") class TestRepositoryArchival(object): - def test_archival(self, backend): backend.enable_downloads() commit = backend.repo.get_commit(commit_idx=173) for a_type, content_type, extension in settings.ARCHIVE_SPECS: - path_sha = get_path_sha('/') - filename = get_archive_name(backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha) + path_sha = get_path_sha("/") + filename = get_archive_name( + backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha + ) fname = commit.raw_id + extension - response = self.app.get( - route_path('repo_archivefile', - repo_name=backend.repo_name, - fname=fname)) + response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname)) - assert response.status == '200 OK' + assert response.status == "200 OK" headers = [ - ('Content-Disposition', f'attachment; filename={filename}'), - ('Content-Type', content_type), + ("Content-Disposition", f"attachment; filename={filename}"), + ("Content-Type", content_type), ] for header in headers: @@ -513,19 +462,25 @@ class TestRepositoryArchival(object): backend.enable_downloads() commit = backend.repo.get_commit(commit_idx=173) for a_type, content_type, extension in settings.ARCHIVE_SPECS: - path_sha = get_path_sha('/') - filename = get_archive_name(backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha, with_hash=False) + path_sha = get_path_sha("/") + filename = get_archive_name( + backend.repo_id, + backend.repo_name, + commit_sha=commit.short_id, + ext=extension, + path_sha=path_sha, + with_hash=False, + ) fname = commit.raw_id + extension response = self.app.get( - route_path('repo_archivefile', - repo_name=backend.repo_name, - fname=fname, params={'with_hash': 0})) + route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"with_hash": 0}) + ) - assert response.status == '200 OK' + assert response.status == "200 OK" headers = [ - ('Content-Disposition', f'attachment; filename={filename}'), - ('Content-Type', content_type), + ("Content-Disposition", f"attachment; filename={filename}"), + ("Content-Type", content_type), ] for header in headers: @@ -534,359 +489,332 @@ class TestRepositoryArchival(object): def test_archival_at_path(self, backend): backend.enable_downloads() commit = backend.repo.get_commit(commit_idx=190) - at_path = 'vcs' + at_path = "vcs" for a_type, content_type, extension in settings.ARCHIVE_SPECS: path_sha = get_path_sha(at_path) - filename = get_archive_name(backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha) + filename = get_archive_name( + backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha + ) fname = commit.raw_id + extension response = self.app.get( - route_path('repo_archivefile', - repo_name=backend.repo_name, - fname=fname, params={'at_path': at_path})) + route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"at_path": at_path}) + ) - assert response.status == '200 OK' + assert response.status == "200 OK" headers = [ - ('Content-Disposition', f'attachment; filename={filename}'), - ('Content-Type', content_type), + ("Content-Disposition", f"attachment; filename={filename}"), + ("Content-Type", content_type), ] for header in headers: assert header in list(response.headers.items()) - @pytest.mark.parametrize('arch_ext',[ - 'tar', 'rar', 'x', '..ax', '.zipz', 'tar.gz.tar']) + @pytest.mark.parametrize("arch_ext", ["tar", "rar", "x", "..ax", ".zipz", "tar.gz.tar"]) def test_archival_wrong_ext(self, backend, arch_ext): backend.enable_downloads() commit = backend.repo.get_commit(commit_idx=173) - fname = commit.raw_id + '.' + arch_ext + fname = commit.raw_id + "." + arch_ext - response = self.app.get( - route_path('repo_archivefile', - repo_name=backend.repo_name, - fname=fname)) - response.mustcontain( - 'Unknown archive type for: `{}`'.format(fname)) + response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname)) + response.mustcontain("Unknown archive type for: `{}`".format(fname)) - @pytest.mark.parametrize('commit_id', [ - '00x000000', 'tar', 'wrong', '@$@$42413232', '232dffcd']) + @pytest.mark.parametrize("commit_id", ["00x000000", "tar", "wrong", "@$@$42413232", "232dffcd"]) def test_archival_wrong_commit_id(self, backend, commit_id): backend.enable_downloads() - fname = f'{commit_id}.zip' + fname = f"{commit_id}.zip" - response = self.app.get( - route_path('repo_archivefile', - repo_name=backend.repo_name, - fname=fname)) - response.mustcontain('Unknown commit_id') + response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname)) + response.mustcontain("Unknown commit_id") @pytest.mark.usefixtures("app") class TestFilesDiff(object): - - @pytest.mark.parametrize("diff", ['diff', 'download', 'raw']) + @pytest.mark.parametrize("diff", ["diff", "download", "raw"]) def test_file_full_diff(self, backend, diff): commit1 = backend.repo.get_commit(commit_idx=-1) commit2 = backend.repo.get_commit(commit_idx=-2) response = self.app.get( - route_path('repo_files_diff', - repo_name=backend.repo_name, - f_path='README'), + route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README"), params={ - 'diff1': commit2.raw_id, - 'diff2': commit1.raw_id, - 'fulldiff': '1', - 'diff': diff, - }) + "diff1": commit2.raw_id, + "diff2": commit1.raw_id, + "fulldiff": "1", + "diff": diff, + }, + ) - if diff == 'diff': + if diff == "diff": # use redirect since this is OLD view redirecting to compare page response = response.follow() # It's a symlink to README.rst - response.mustcontain('README.rst') - response.mustcontain('No newline at end of file') + response.mustcontain("README.rst") + response.mustcontain("No newline at end of file") def test_file_binary_diff(self, backend): commits = [ - {'message': 'First commit'}, - {'message': 'Commit with binary', - 'added': [nodes.FileNode(b'file.bin', content='\0BINARY\0')]}, + {"message": "First commit"}, + {"message": "Commit with binary", "added": [nodes.FileNode(b"file.bin", content="\0BINARY\0")]}, ] repo = backend.create_repo(commits=commits) response = self.app.get( - route_path('repo_files_diff', - repo_name=backend.repo_name, - f_path='file.bin'), + route_path("repo_files_diff", repo_name=backend.repo_name, f_path="file.bin"), params={ - 'diff1': repo.get_commit(commit_idx=0).raw_id, - 'diff2': repo.get_commit(commit_idx=1).raw_id, - 'fulldiff': '1', - 'diff': 'diff', - }) + "diff1": repo.get_commit(commit_idx=0).raw_id, + "diff2": repo.get_commit(commit_idx=1).raw_id, + "fulldiff": "1", + "diff": "diff", + }, + ) # use redirect since this is OLD view redirecting to compare page response = response.follow() - response.mustcontain('Collapse 1 commit') + response.mustcontain("Collapse 1 commit") file_changes = (1, 0, 0) compare_page = ComparePage(response) compare_page.contains_change_summary(*file_changes) - if backend.alias == 'svn': - response.mustcontain('new file 10644') + if backend.alias == "svn": + response.mustcontain("new file 10644") # TODO(marcink): SVN doesn't yet detect binary changes else: - response.mustcontain('new file 100644') - response.mustcontain('binary diff hidden') + response.mustcontain("new file 100644") + response.mustcontain("binary diff hidden") def test_diff_2way(self, backend): commit1 = backend.repo.get_commit(commit_idx=-1) commit2 = backend.repo.get_commit(commit_idx=-2) response = self.app.get( - route_path('repo_files_diff_2way_redirect', - repo_name=backend.repo_name, - f_path='README'), + route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"), params={ - 'diff1': commit2.raw_id, - 'diff2': commit1.raw_id, - }) + "diff1": commit2.raw_id, + "diff2": commit1.raw_id, + }, + ) # use redirect since this is OLD view redirecting to compare page response = response.follow() # It's a symlink to README.rst - response.mustcontain('README.rst') - response.mustcontain('No newline at end of file') + response.mustcontain("README.rst") + response.mustcontain("No newline at end of file") def test_requires_one_commit_id(self, backend, autologin_user): response = self.app.get( - route_path('repo_files_diff', - repo_name=backend.repo_name, - f_path='README.rst'), - status=400) - response.mustcontain( - 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.') + route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README.rst"), status=400 + ) + response.mustcontain("Need query parameter", "diff1", "diff2", "to generate a diff.") def test_returns_no_files_if_file_does_not_exist(self, vcsbackend): repo = vcsbackend.repo response = self.app.get( - route_path('repo_files_diff', - repo_name=repo.name, - f_path='does-not-exist-in-any-commit'), - params={ - 'diff1': repo[0].raw_id, - 'diff2': repo[1].raw_id - }) + route_path("repo_files_diff", repo_name=repo.name, f_path="does-not-exist-in-any-commit"), + params={"diff1": repo[0].raw_id, "diff2": repo[1].raw_id}, + ) response = response.follow() - response.mustcontain('No files') + response.mustcontain("No files") def test_returns_redirect_if_file_not_changed(self, backend): commit = backend.repo.get_commit(commit_idx=-1) response = self.app.get( - route_path('repo_files_diff_2way_redirect', - repo_name=backend.repo_name, - f_path='README'), + route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"), params={ - 'diff1': commit.raw_id, - 'diff2': commit.raw_id, - }) + "diff1": commit.raw_id, + "diff2": commit.raw_id, + }, + ) response = response.follow() - response.mustcontain('No files') - response.mustcontain('No commits in this compare') + response.mustcontain("No files") + response.mustcontain("No commits in this compare") def test_supports_diff_to_different_path_svn(self, backend_svn): - #TODO: check this case + # TODO: check this case return - repo = backend_svn['svn-simple-layout'].scm_instance() - commit_id_1 = '24' - commit_id_2 = '26' + repo = backend_svn["svn-simple-layout"].scm_instance() + commit_id_1 = "24" + commit_id_2 = "26" response = self.app.get( - route_path('repo_files_diff', - repo_name=backend_svn.repo_name, - f_path='trunk/example.py'), + route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"), params={ - 'diff1': 'tags/v0.2/example.py@' + commit_id_1, - 'diff2': commit_id_2, - }) + "diff1": "tags/v0.2/example.py@" + commit_id_1, + "diff2": commit_id_2, + }, + ) response = response.follow() response.mustcontain( # diff contains this - "Will print out a useful message on invocation.") + "Will print out a useful message on invocation." + ) # Note: Expecting that we indicate the user what's being compared response.mustcontain("trunk/example.py") response.mustcontain("tags/v0.2/example.py") def test_show_rev_redirects_to_svn_path(self, backend_svn): - #TODO: check this case + # TODO: check this case return - repo = backend_svn['svn-simple-layout'].scm_instance() + repo = backend_svn["svn-simple-layout"].scm_instance() commit_id = repo[-1].raw_id response = self.app.get( - route_path('repo_files_diff', - repo_name=backend_svn.repo_name, - f_path='trunk/example.py'), + route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"), params={ - 'diff1': 'branches/argparse/example.py@' + commit_id, - 'diff2': commit_id, + "diff1": "branches/argparse/example.py@" + commit_id, + "diff2": commit_id, }, - status=302) + status=302, + ) response = response.follow() - assert response.headers['Location'].endswith( - 'svn-svn-simple-layout/files/26/branches/argparse/example.py') + assert response.headers["Location"].endswith("svn-svn-simple-layout/files/26/branches/argparse/example.py") def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn): - #TODO: check this case + # TODO: check this case return - repo = backend_svn['svn-simple-layout'].scm_instance() + repo = backend_svn["svn-simple-layout"].scm_instance() commit_id = repo[-1].raw_id response = self.app.get( - route_path('repo_files_diff', - repo_name=backend_svn.repo_name, - f_path='trunk/example.py'), + route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"), params={ - 'diff1': 'branches/argparse/example.py@' + commit_id, - 'diff2': commit_id, - 'show_rev': 'Show at Revision', - 'annotate': 'true', + "diff1": "branches/argparse/example.py@" + commit_id, + "diff2": commit_id, + "show_rev": "Show at Revision", + "annotate": "true", }, - status=302) + status=302, + ) response = response.follow() - assert response.headers['Location'].endswith( - 'svn-svn-simple-layout/annotate/26/branches/argparse/example.py') + assert response.headers["Location"].endswith("svn-svn-simple-layout/annotate/26/branches/argparse/example.py") @pytest.mark.usefixtures("app", "autologin_user") class TestModifyFilesWithWebInterface(object): - def test_add_file_view(self, backend): - self.app.get( - route_path('repo_files_add_file', - repo_name=backend.repo_name, - commit_id='tip', f_path='/') - ) + self.app.get(route_path("repo_files_add_file", repo_name=backend.repo_name, commit_id="tip", f_path="")) @pytest.mark.xfail_backends("svn", reason="Depends on online editing") def test_add_file_into_repo_missing_content(self, backend, csrf_token): backend.create_repo() - filename = 'init.py' + filename = "init.py" response = self.app.post( - route_path('repo_files_create_file', - repo_name=backend.repo_name, - commit_id='tip', f_path='/'), + route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id="tip", f_path=""), params={ - 'content': "", - 'filename': filename, - 'csrf_token': csrf_token, + "content": "", + "filename": filename, + "csrf_token": csrf_token, }, - status=302) - expected_msg = 'Successfully committed new file `{}`'.format(os.path.join(filename)) + status=302, + ) + expected_msg = "Successfully committed new file `{}`".format(os.path.join(filename)) assert_session_flash(response, expected_msg) def test_add_file_into_repo_missing_filename(self, backend, csrf_token): commit_id = backend.repo.get_commit().raw_id response = self.app.post( - route_path('repo_files_create_file', - repo_name=backend.repo_name, - commit_id=commit_id, f_path='/'), + route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id=commit_id, f_path=""), params={ - 'content': "foo", - 'csrf_token': csrf_token, + "content": "foo", + "csrf_token": csrf_token, }, - status=302) + status=302, + ) - assert_session_flash(response, 'No filename specified') + assert_session_flash(response, "No filename specified") - def test_add_file_into_repo_errors_and_no_commits( - self, backend, csrf_token): + def test_add_file_into_repo_errors_and_no_commits(self, backend, csrf_token): repo = backend.create_repo() # Create a file with no filename, it will display an error but # the repo has no commits yet response = self.app.post( - route_path('repo_files_create_file', - repo_name=repo.repo_name, - commit_id='tip', f_path='/'), + route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id="tip", f_path=""), params={ - 'content': "foo", - 'csrf_token': csrf_token, + "content": "foo", + "csrf_token": csrf_token, }, - status=302) + status=302, + ) - assert_session_flash(response, 'No filename specified') + assert_session_flash(response, "No filename specified") # Not allowed, redirect to the summary redirected = response.follow() - summary_url = h.route_path('repo_summary', repo_name=repo.repo_name) + summary_url = h.route_path("repo_summary", repo_name=repo.repo_name) # As there are no commits, displays the summary page with the error of # creating a file with no filename assert redirected.request.path == summary_url - @pytest.mark.parametrize("filename, clean_filename", [ - ('/abs/foo', 'abs/foo'), - ('../rel/foo', 'rel/foo'), - ('file/../foo/foo', 'file/foo/foo'), - ]) + @pytest.mark.parametrize( + "filename, clean_filename", + [ + ("/abs/foo", "abs/foo"), + ("../rel/foo", "rel/foo"), + ("file/../foo/foo", "file/foo/foo"), + ], + ) def test_add_file_into_repo_bad_filenames(self, filename, clean_filename, backend, csrf_token): repo = backend.create_repo() commit_id = repo.get_commit().raw_id response = self.app.post( - route_path('repo_files_create_file', - repo_name=repo.repo_name, - commit_id=commit_id, f_path='/'), + route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""), params={ - 'content': "foo", - 'filename': filename, - 'csrf_token': csrf_token, + "content": "foo", + "filename": filename, + "csrf_token": csrf_token, }, - status=302) + status=302, + ) - expected_msg = 'Successfully committed new file `{}`'.format(clean_filename) + expected_msg = "Successfully committed new file `{}`".format(clean_filename) assert_session_flash(response, expected_msg) - @pytest.mark.parametrize("cnt, filename, content", [ - (1, 'foo.txt', "Content"), - (2, 'dir/foo.rst', "Content"), - (3, 'dir/foo-second.rst', "Content"), - (4, 'rel/dir/foo.bar', "Content"), - ]) + @pytest.mark.parametrize( + "cnt, filename, content", + [ + (1, "foo.txt", "Content"), + (2, "dir/foo.rst", "Content"), + (3, "dir/foo-second.rst", "Content"), + (4, "rel/dir/foo.bar", "Content"), + ], + ) def test_add_file_into_empty_repo(self, cnt, filename, content, backend, csrf_token): repo = backend.create_repo() commit_id = repo.get_commit().raw_id response = self.app.post( - route_path('repo_files_create_file', - repo_name=repo.repo_name, - commit_id=commit_id, f_path='/'), + route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""), params={ - 'content': content, - 'filename': filename, - 'csrf_token': csrf_token, + "content": content, + "filename": filename, + "csrf_token": csrf_token, }, - status=302) + status=302, + ) - expected_msg = 'Successfully committed new file `{}`'.format(filename) + expected_msg = "Successfully committed new file `{}`".format(filename) assert_session_flash(response, expected_msg) def test_edit_file_view(self, backend): response = self.app.get( - route_path('repo_files_edit_file', - repo_name=backend.repo_name, - commit_id=backend.default_head_id, - f_path='vcs/nodes.py'), - status=200) + route_path( + "repo_files_edit_file", + repo_name=backend.repo_name, + commit_id=backend.default_head_id, + f_path="vcs/nodes.py", + ), + status=200, + ) response.mustcontain("Module holding everything related to vcs nodes.") def test_edit_file_view_not_on_branch(self, backend): @@ -894,229 +822,221 @@ class TestModifyFilesWithWebInterface(object): backend.ensure_file(b"vcs/nodes.py") response = self.app.get( - route_path('repo_files_edit_file', - repo_name=repo.repo_name, - commit_id='tip', - f_path='vcs/nodes.py'), - status=302) - assert_session_flash( - response, 'Cannot modify file. Given commit `tip` is not head of a branch.') + route_path("repo_files_edit_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"), + status=302, + ) + assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.") def test_edit_file_view_commit_changes(self, backend, csrf_token): repo = backend.create_repo() backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'") response = self.app.post( - route_path('repo_files_update_file', - repo_name=repo.repo_name, - commit_id=backend.default_head_id, - f_path='vcs/nodes.py'), + route_path( + "repo_files_update_file", + repo_name=repo.repo_name, + commit_id=backend.default_head_id, + f_path="vcs/nodes.py", + ), params={ - 'content': "print 'hello world'", - 'message': 'I committed', - 'filename': "vcs/nodes.py", - 'csrf_token': csrf_token, + "content": "print 'hello world'", + "message": "I committed", + "filename": "vcs/nodes.py", + "csrf_token": csrf_token, }, - status=302) - assert_session_flash( - response, 'Successfully committed changes to file `vcs/nodes.py`') + status=302, + ) + assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`") tip = repo.get_commit(commit_idx=-1) - assert tip.message == 'I committed' + assert tip.message == "I committed" def test_replace_binary_file_view_commit_changes(self, backend, csrf_token): repo = backend.create_repo() backend.ensure_file(b"vcs/nodes.docx", content=b"PREVIOUS CONTENT'") response = self.app.post( - route_path('repo_files_replace_binary', - repo_name=repo.repo_name, - commit_id=backend.default_head_id, - f_path='vcs/nodes.docx'), + route_path( + "repo_files_replace_binary", + repo_name=repo.repo_name, + commit_id=backend.default_head_id, + f_path="vcs/nodes.docx", + ), params={ - 'message': 'I committed', - 'csrf_token': csrf_token, + "message": "I committed", + "csrf_token": csrf_token, }, - upload_files=[('files_upload', 'vcs/nodes.docx', b'SOME CONTENT')], - status=200) - assert_session_flash( - response, 'Successfully committed 1 new file') + upload_files=[("files_upload", "vcs/nodes.docx", b"SOME CONTENT")], + status=200, + ) + assert_session_flash(response, "Successfully committed 1 new file") tip = repo.get_commit(commit_idx=-1) - assert tip.message == 'I committed' + assert tip.message == "I committed" - def test_edit_file_view_commit_changes_default_message(self, backend, - csrf_token): + def test_edit_file_view_commit_changes_default_message(self, backend, csrf_token): repo = backend.create_repo() backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'") - commit_id = ( - backend.default_branch_name or - backend.repo.scm_instance().commit_ids[-1]) + commit_id = backend.default_branch_name or backend.repo.scm_instance().commit_ids[-1] response = self.app.post( - route_path('repo_files_update_file', - repo_name=repo.repo_name, - commit_id=commit_id, - f_path='vcs/nodes.py'), + route_path("repo_files_update_file", repo_name=repo.repo_name, commit_id=commit_id, f_path="vcs/nodes.py"), params={ - 'content': "print 'hello world'", - 'message': '', - 'filename': "vcs/nodes.py", - 'csrf_token': csrf_token, + "content": "print 'hello world'", + "message": "", + "filename": "vcs/nodes.py", + "csrf_token": csrf_token, }, - status=302) - assert_session_flash( - response, 'Successfully committed changes to file `vcs/nodes.py`') + status=302, + ) + assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`") tip = repo.get_commit(commit_idx=-1) - assert tip.message == 'Edited file vcs/nodes.py via RhodeCode Enterprise' + assert tip.message == "Edited file vcs/nodes.py via RhodeCode Enterprise" def test_replace_binary_file_content_with_content_that_not_belong_to_original_type(self, backend, csrf_token): repo = backend.create_repo() backend.ensure_file(b"vcs/sheet.xlsx", content=b"PREVIOUS CONTENT'") response = self.app.post( - route_path('repo_files_replace_binary', - repo_name=repo.repo_name, - commit_id=backend.default_head_id, - f_path='vcs/sheet.xlsx'), + route_path( + "repo_files_replace_binary", + repo_name=repo.repo_name, + commit_id=backend.default_head_id, + f_path="vcs/sheet.xlsx", + ), params={ - 'message': 'I committed', - 'csrf_token': csrf_token, + "message": "I committed", + "csrf_token": csrf_token, }, - upload_files=[('files_upload', 'vcs/sheet.docx', b'SOME CONTENT')], - status=200) - assert response.json['error'] == "file extension of uploaded file doesn't match an original file's extension" + upload_files=[("files_upload", "vcs/sheet.docx", b"SOME CONTENT")], + status=200, + ) + assert response.json["error"] == "file extension of uploaded file doesn't match an original file's extension" - @pytest.mark.parametrize("replacement_files, expected_error", [ - ([], 'missing files'), - ( - [('files_upload', 'vcs/node1.docx', b'SOME CONTENT'), - ('files_upload', 'vcs/node2.docx', b'SOME CONTENT')], - 'too many files for replacement'), - ]) - def test_replace_binary_with_wrong_amount_of_content_sources(self, replacement_files, expected_error, backend, - csrf_token): + @pytest.mark.parametrize( + "replacement_files, expected_error", + [ + ([], "missing files"), + ( + [ + ("files_upload", "vcs/node1.docx", b"SOME CONTENT"), + ("files_upload", "vcs/node2.docx", b"SOME CONTENT"), + ], + "too many files for replacement", + ), + ], + ) + def test_replace_binary_with_wrong_amount_of_content_sources( + self, replacement_files, expected_error, backend, csrf_token + ): repo = backend.create_repo() backend.ensure_file(b"vcs/node.docx", content=b"PREVIOUS CONTENT'") response = self.app.post( - route_path('repo_files_replace_binary', - repo_name=repo.repo_name, - commit_id=backend.default_head_id, - f_path='vcs/node.docx'), + route_path( + "repo_files_replace_binary", + repo_name=repo.repo_name, + commit_id=backend.default_head_id, + f_path="vcs/node.docx", + ), params={ - 'message': 'I committed', - 'csrf_token': csrf_token, + "message": "I committed", + "csrf_token": csrf_token, }, upload_files=replacement_files, - status=200) - assert response.json['error'] == expected_error + status=200, + ) + assert response.json["error"] == expected_error def test_delete_file_view(self, backend): self.app.get( - route_path('repo_files_remove_file', - repo_name=backend.repo_name, - commit_id=backend.default_head_id, - f_path='vcs/nodes.py'), - status=200) + route_path( + "repo_files_remove_file", + repo_name=backend.repo_name, + commit_id=backend.default_head_id, + f_path="vcs/nodes.py", + ), + status=200, + ) def test_delete_file_view_not_on_branch(self, backend): repo = backend.create_repo() - backend.ensure_file(b'vcs/nodes.py') + backend.ensure_file(b"vcs/nodes.py") response = self.app.get( - route_path('repo_files_remove_file', - repo_name=repo.repo_name, - commit_id='tip', - f_path='vcs/nodes.py'), - status=302) - assert_session_flash( - response, 'Cannot modify file. Given commit `tip` is not head of a branch.') + route_path("repo_files_remove_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"), + status=302, + ) + assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.") def test_delete_file_view_commit_changes(self, backend, csrf_token): repo = backend.create_repo() backend.ensure_file(b"vcs/nodes.py") response = self.app.post( - route_path('repo_files_delete_file', - repo_name=repo.repo_name, - commit_id=backend.default_head_id, - f_path='vcs/nodes.py'), + route_path( + "repo_files_delete_file", + repo_name=repo.repo_name, + commit_id=backend.default_head_id, + f_path="vcs/nodes.py", + ), params={ - 'message': 'i committed', - 'csrf_token': csrf_token, + "message": "i committed", + "csrf_token": csrf_token, }, - status=302) - assert_session_flash( - response, 'Successfully deleted file `vcs/nodes.py`') + status=302, + ) + assert_session_flash(response, "Successfully deleted file `vcs/nodes.py`") @pytest.mark.usefixtures("app") class TestFilesViewOtherCases(object): - def test_access_empty_repo_redirect_to_summary_with_alert_write_perms( - self, backend_stub, autologin_regular_user, user_regular, - user_util): - + self, backend_stub, autologin_regular_user, user_regular, user_util + ): repo = backend_stub.create_repo() - user_util.grant_user_permission_to_repo( - repo, user_regular, 'repository.write') - response = self.app.get( - route_path('repo_files', - repo_name=repo.repo_name, - commit_id='tip', f_path='/')) + user_util.grant_user_permission_to_repo(repo, user_regular, "repository.write") + response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) - repo_file_add_url = route_path( - 'repo_files_add_file', - repo_name=repo.repo_name, - commit_id=0, f_path='') + repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="") add_new = f'
    add a new file' - repo_file_upload_url = route_path( - 'repo_files_upload_file', - repo_name=repo.repo_name, - commit_id=0, f_path='') + repo_file_upload_url = route_path("repo_files_upload_file", repo_name=repo.repo_name, commit_id=0, f_path="") upload_new = f'upload a new file' - assert_session_flash( - response, - 'There are no files yet. Click here to %s or %s.' % (add_new, upload_new) - ) + assert_session_flash(response, "There are no files yet. Click here to %s or %s." % (add_new, upload_new)) def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms( - self, backend_stub, autologin_regular_user): + self, backend_stub, autologin_regular_user + ): repo = backend_stub.create_repo() # init session for anon user - route_path('repo_summary', repo_name=repo.repo_name) + route_path("repo_summary", repo_name=repo.repo_name) - repo_file_add_url = route_path( - 'repo_files_add_file', - repo_name=repo.repo_name, - commit_id=0, f_path='') + repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="") - response = self.app.get( - route_path('repo_files', - repo_name=repo.repo_name, - commit_id='tip', f_path='/')) + response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) assert_session_flash(response, no_=repo_file_add_url) - @pytest.mark.parametrize('file_node', [ - b'archive/file.zip', - b'diff/my-file.txt', - b'render.py', - b'render', - b'remove_file', - b'remove_file/to-delete.txt', - ]) + @pytest.mark.parametrize( + "file_node", + [ + b"archive/file.zip", + b"diff/my-file.txt", + b"render.py", + b"render", + b"remove_file", + b"remove_file/to-delete.txt", + ], + ) def test_file_names_equal_to_routes_parts(self, backend, file_node): backend.create_repo() backend.ensure_file(file_node) self.app.get( - route_path('repo_files', - repo_name=backend.repo_name, - commit_id='tip', f_path=safe_str(file_node)), - status=200) + route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=safe_str(file_node)), + status=200, + ) class TestAdjustFilePathForSvn(object): @@ -1125,20 +1045,20 @@ class TestAdjustFilePathForSvn(object): """ def test_returns_path_relative_to_matched_reference(self): - repo = self._repo(branches=['trunk']) - self.assert_file_adjustment('trunk/file', 'file', repo) + repo = self._repo(branches=["trunk"]) + self.assert_file_adjustment("trunk/file", "file", repo) def test_does_not_modify_file_if_no_reference_matches(self): - repo = self._repo(branches=['trunk']) - self.assert_file_adjustment('notes/file', 'notes/file', repo) + repo = self._repo(branches=["trunk"]) + self.assert_file_adjustment("notes/file", "notes/file", repo) def test_does_not_adjust_partial_directory_names(self): - repo = self._repo(branches=['trun']) - self.assert_file_adjustment('trunk/file', 'trunk/file', repo) + repo = self._repo(branches=["trun"]) + self.assert_file_adjustment("trunk/file", "trunk/file", repo) def test_is_robust_to_patterns_which_prefix_other_patterns(self): - repo = self._repo(branches=['trunk', 'trunk/new', 'trunk/old']) - self.assert_file_adjustment('trunk/new/file', 'file', repo) + repo = self._repo(branches=["trunk", "trunk/new", "trunk/old"]) + self.assert_file_adjustment("trunk/new/file", "file", repo) def assert_file_adjustment(self, f_path, expected, repo): result = RepoFilesView.adjust_file_path_for_svn(f_path, repo) @@ -1146,6 +1066,6 @@ class TestAdjustFilePathForSvn(object): def _repo(self, branches=None): repo = mock.Mock() - repo.branches = OrderedDict((name, '0') for name in branches or []) + repo.branches = OrderedDict((name, "0") for name in branches or []) repo.tags = {} return repo diff --git a/rhodecode/lib/vcs/backends/hg/commit.py b/rhodecode/lib/vcs/backends/hg/commit.py index 03b5bfd9..57b206ff 100644 --- a/rhodecode/lib/vcs/backends/hg/commit.py +++ b/rhodecode/lib/vcs/backends/hg/commit.py @@ -19,7 +19,7 @@ """ HG commit module """ - +import os import logging from zope.cachedescriptors.property import Lazy as LazyProperty @@ -274,16 +274,15 @@ class MercurialCommit(base.BaseCommit): path_nodes = [] - for obj_path, node_kind in self._remote.dir_items(self.raw_id, path): + for obj_path, (node_kind, flags) in self._remote.dir_items(self.raw_id, path): if node_kind is None: raise CommitError(f"Requested object type={node_kind} cannot be mapped to a proper type") - # TODO: implement it ?? - stat_ = None - # # cache file mode - # if obj_path not in self._path_mode_cache: - # self._path_mode_cache[obj_path] = stat_ + stat_ = flags + # cache file mode + if obj_path not in self._path_mode_cache: + self._path_mode_cache[obj_path] = stat_ # cache type if node_kind not in self._path_type_cache: @@ -301,6 +300,13 @@ class MercurialCommit(base.BaseCommit): self.nodes[obj_path] = entry path_nodes.append(entry) + for obj_path, (location, commit, scm_type) in self._submodules.items(): + + if os.path.dirname(obj_path) == path: + entry = SubModuleNode(obj_path, url=location, commit=commit, alias=scm_type) + self.nodes[obj_path] = entry + path_nodes.append(entry) + path_nodes.sort() return path_nodes diff --git a/rhodecode/lib/vcs/nodes.py b/rhodecode/lib/vcs/nodes.py index be70067c..b5b0139d 100644 --- a/rhodecode/lib/vcs/nodes.py +++ b/rhodecode/lib/vcs/nodes.py @@ -707,24 +707,23 @@ class SubModuleNode(Node): size = 0 def __init__(self, name, url=None, commit=None, alias=None): - self.path = name + self.path: bytes = name self.str_path: str = safe_str(self.path) # we store paths as str self.kind = NodeKind.SUBMODULE self.alias = alias # we have to use EmptyCommit here since this can point to svn/git/hg # submodules we cannot get from repository - self.commit = EmptyCommit(str(commit), alias=alias) - self.url = url or self._extract_submodule_url() + self.commit = EmptyCommit(safe_str(commit), alias=alias) + self.url = safe_str(url) or self._extract_submodule_url() def __repr__(self): short_id = getattr(self.commit, "short_id", "") return f"<{self.__class__.__name__} {self.str_path!r} @ {short_id}>" def _extract_submodule_url(self): - # TODO: find a way to parse gits submodule file and extract the - # linking URL - return self.path + # TODO: find a way to parse gits submodule file and extract the linking URL + return safe_str(self.path) @LazyProperty def name(self): diff --git a/rhodecode/tests/fixtures/fixture_utils.py b/rhodecode/tests/fixtures/fixture_utils.py index 8b64efc8..9599e549 100644 --- a/rhodecode/tests/fixtures/fixture_utils.py +++ b/rhodecode/tests/fixtures/fixture_utils.py @@ -185,18 +185,13 @@ def baseapp(request, ini_config, http_environ_session, available_port_factory, v # start vcsserver _vcsserver_port = available_port_factory() - vcsserver_instance = vcsserver_factory( - request, - store_dir=store_dir, - port=_vcsserver_port, - info_prefix="base-app-" - ) + vcsserver_instance = vcsserver_factory(request, store_dir=store_dir, port=_vcsserver_port, info_prefix="base-app-") settings["vcs.server"] = vcsserver_instance.bind_addr # we skip setting store_dir for baseapp, it's internally set via testing rhodecode.ini # settings['repo_store.path'] = str(store_dir) - console_printer(f' :warning: [green]pytest-setup[/green] Starting base pyramid-app: {ini_config}') + console_printer(f" :warning: [green]pytest-setup[/green] Starting base pyramid-app: {ini_config}") pyramid_baseapp = make_pyramid_app({"__file__": ini_config}, **settings) # start celery @@ -206,10 +201,10 @@ def baseapp(request, ini_config, http_environ_session, available_port_factory, v port=None, info_prefix="base-app-", overrides=( - {'handler_console': {'level': 'DEBUG'}}, - {'app:main': {'vcs.server': vcsserver_instance.bind_addr}}, - {'app:main': {'repo_store.path': store_dir}} - ) + {"handler_console": {"level": "DEBUG"}}, + {"app:main": {"vcs.server": vcsserver_instance.bind_addr}}, + {"app:main": {"repo_store.path": store_dir}}, + ), ) return pyramid_baseapp @@ -401,7 +396,7 @@ def backend_base(request, backend_alias, test_repo): utils.check_xfail_backends(request.node, backend_alias) utils.check_skip_backends(request.node, backend_alias) - repo_name = "vcs_test_%s" % (backend_alias,) + repo_name = f"vcs_test_{backend_alias}" backend = Backend( alias=backend_alias, repo_name=repo_name, test_name=request.node.name, test_repo_container=test_repo ) @@ -698,7 +693,7 @@ class VcsBackend(object): repo = repo_class(self._repo_path, create=True, src_url=src_url, bare=bare) self._cleanup_repos.append(repo) - commits = commits or [{"message": "Commit %s of %s" % (x, repo_name)} for x in range(number_of_commits)] + commits = commits or [{"message": f"Commit {x} of {repo_name}"} for x in range(number_of_commits)] _add_commits_to_repo(repo, commits) return repo @@ -729,7 +724,7 @@ class VcsBackend(object): def vcsbackend_base(request, backend_alias, tests_tmp_path, baseapp, test_repo) -> VcsBackend: if backend_alias not in request.config.getoption("--backends"): - pytest.skip("Backend %s not selected." % (backend_alias,)) + pytest.skip(f"Backend {backend_alias} not selected.") utils.check_xfail_backends(request.node, backend_alias) utils.check_skip_backends(request.node, backend_alias) @@ -843,7 +838,7 @@ class RepoServer(object): def serve(self, vcsrepo): if vcsrepo.alias != "svn": - raise TypeError("Backend %s not supported" % vcsrepo.alias) + raise TypeError(f"Backend {vcsrepo.alias} not supported") proc = subprocess.Popen( ["svnserve", "-d", "--foreground", "--listen-host", "localhost", "--root", vcsrepo.path] @@ -1125,14 +1120,14 @@ class UserUtility(object): return name def create_repo_group(self, owner=TEST_USER_ADMIN_LOGIN, auto_cleanup=True): - group_name = "{prefix}_repogroup_{count}".format(prefix=self._test_name, count=len(self.repo_group_ids)) + group_name = f"{self._test_name}_repogroup_{len(self.repo_group_ids)}" repo_group = self.fixture.create_repo_group(group_name, cur_user=owner) if auto_cleanup: self.repo_group_ids.append(repo_group.group_id) return repo_group def create_repo(self, owner=TEST_USER_ADMIN_LOGIN, parent=None, auto_cleanup=True, repo_type="hg", bare=False): - repo_name = "{prefix}_repository_{count}".format(prefix=self._test_name, count=len(self.repos_ids)) + repo_name = f"{self._test_name}_repository_{len(self.repos_ids)}" repository = self.fixture.create_repo( repo_name, cur_user=owner, repo_group=parent, repo_type=repo_type, bare=bare @@ -1142,7 +1137,7 @@ class UserUtility(object): return repository def create_user(self, auto_cleanup=True, **kwargs): - user_name = "{prefix}_user_{count}".format(prefix=self._test_name, count=len(self.user_ids)) + user_name = f"{self._test_name}_user_{len(self.user_ids)}" user = self.fixture.create_user(user_name, **kwargs) if auto_cleanup: self.user_ids.append(user.user_id) @@ -1158,7 +1153,7 @@ class UserUtility(object): return user, user_group def create_user_group(self, owner=TEST_USER_ADMIN_LOGIN, members=None, auto_cleanup=True, **kwargs): - group_name = "{prefix}_usergroup_{count}".format(prefix=self._test_name, count=len(self.user_group_ids)) + group_name = f"{self._test_name}_usergroup_{len(self.user_group_ids)}" user_group = self.fixture.create_user_group(group_name, cur_user=owner, **kwargs) if auto_cleanup: @@ -1694,4 +1689,3 @@ def repo_groups(request): fixture.destroy_repo_group(parent_group) return zombie_group, parent_group, child_group - diff --git a/rhodecode/tests/utils.py b/rhodecode/tests/utils.py index ee280523..6d7bac1e 100644 --- a/rhodecode/tests/utils.py +++ b/rhodecode/tests/utils.py @@ -301,7 +301,7 @@ class AssertResponse(object): sel = CSSSelector('a[href]') elements = [ e for e in sel(doc) if e.text_content().strip() == link_text] - assert len(elements) == 1, "Did not find link or found multiple links" + assert len(elements) == 1, f"Did not find link or found multiple links, found={len(elements)}" self._ensure_url_equal(elements[0].attrib.get('href'), href) def contains_one_anchor(self, anchor_id): @@ -309,14 +309,14 @@ class AssertResponse(object): doc = fromstring(self.response.body) sel = CSSSelector('#' + anchor_id) elements = sel(doc) - assert len(elements) == 1, 'cannot find 1 element {}'.format(anchor_id) + assert len(elements) == 1, f'cannot find 1 element {anchor_id}' def _ensure_url_equal(self, found, expected): assert _Url(found) == _Url(expected) def get_element(self, css_selector): elements = self._get_elements(css_selector) - assert len(elements) == 1, 'cannot find 1 element {}'.format(css_selector) + assert len(elements) == 1, f'cannot find 1 element {css_selector}' return elements[0] def get_elements(self, css_selector): diff --git a/rhodecode/tests/vcs/test_filenodes_listing_and_caches.py b/rhodecode/tests/vcs/test_filenodes_listing_and_caches.py index a826ecc9..e96c35e7 100644 --- a/rhodecode/tests/vcs/test_filenodes_listing_and_caches.py +++ b/rhodecode/tests/vcs/test_filenodes_listing_and_caches.py @@ -162,7 +162,22 @@ class TestFileNodesListingAndCaches: b"tox.ini": 2, b"vcs": 1, } - assert commit._path_mode_cache == {} + assert commit._path_mode_cache == { + b".gitignore": 33188, + b".hgignore": 33188, + b".hgtags": 33188, + b".travis.yml": 33188, + b"MANIFEST.in": 33188, + b"README": 40960, + b"README.rst": 33188, + b"docs": 33188, + b"run_test_and_report.sh": 33261, + b"setup.cfg": 33188, + b"setup.py": 33188, + b"test_and_report.sh": 33261, + b"tox.ini": 33188, + b"vcs": 33188, + } if repo.alias == "git": assert list(commit.nodes.keys()) == [ From 7c321dd71f802c80a501b732e0446cd6387a712a Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Tue, 3 Dec 2024 13:00:13 +0200 Subject: [PATCH 88/99] updated latest release notes. --- docs/release-notes/release-notes-5.4.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/release-notes-5.4.0.rst b/docs/release-notes/release-notes-5.4.0.rst index 846f3a4e..d4766e3c 100644 --- a/docs/release-notes/release-notes-5.4.0.rst +++ b/docs/release-notes/release-notes-5.4.0.rst @@ -18,6 +18,7 @@ General ^^^^^^^ - Ported build system to use pyproject.toml. +- Bumped version of mercurial and hg-evolve. Security From 3a41a8b91556ce2d6b6a95c4f52c8991963c748b Mon Sep 17 00:00:00 2001 From: RhodeCode Admin Date: Wed, 4 Dec 2024 22:33:45 +0100 Subject: [PATCH 89/99] fix(file-caching): fixed cases when old cache was used before changes to operate on bytestrings --- rhodecode/apps/_base/__init__.py | 20 +- rhodecode/apps/repository/views/repo_files.py | 997 ++++++++---------- rhodecode/lib/rc_cache/__init__.py | 2 +- rhodecode/lib/utils2.py | 1 + rhodecode/lib/vcs/backends/git/commit.py | 17 +- rhodecode/lib/vcs/backends/hg/commit.py | 4 +- rhodecode/lib/vcs/backends/svn/commit.py | 6 +- rhodecode/lib/vcs/nodes.py | 13 +- rhodecode/model/repo.py | 6 +- .../templates/files/files_browser_tree.mako | 4 +- rhodecode/tests/vcs/test_git.py | 6 +- 11 files changed, 490 insertions(+), 586 deletions(-) diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py index 4ea62e78..be7428bb 100644 --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -395,7 +395,7 @@ class RepoAppView(BaseAppView): settings = settings_model.get_repo_settings_inherited() return settings.get(settings_key, default) - def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path="/"): + def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path="/", nodes=None): log.debug("Looking for README file at path %s", path) if commit_id: landing_commit_id = commit_id @@ -413,16 +413,14 @@ class RepoAppView(BaseAppView): @region.conditional_cache_on_arguments(namespace=cache_namespace_uid) def generate_repo_readme( - repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type + _repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type ): - readme_data = None - readme_filename = None + _readme_data = None + _readme_filename = None commit = db_repo.get_commit(_commit_id) log.debug("Searching for a README file at commit %s.", _commit_id) - readme_node = ReadmeFinder(_renderer_type).search( - commit, path=_readme_search_path - ) + readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path, nodes=nodes) if readme_node: log.debug("Found README node: %s", readme_node) @@ -442,19 +440,19 @@ class RepoAppView(BaseAppView): ), } - readme_data = self._render_readme_or_none( + _readme_data = self._render_readme_or_none( commit, readme_node, relative_urls ) - readme_filename = readme_node.str_path + _readme_filename = readme_node.str_path - return readme_data, readme_filename + return _readme_data, _readme_filename readme_data, readme_filename = generate_repo_readme( db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, - renderer_type, + renderer_type ) compute_time = time.time() - start diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py index e1ba3261..0e3f0878 100644 --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -40,25 +40,34 @@ from rhodecode.lib import diffs, helpers as h, rc_cache from rhodecode.lib import audit_logger from rhodecode.lib.hash_utils import sha1_safe from rhodecode.lib.archive_cache import ( - get_archival_cache_store, get_archival_config, ArchiveCacheGenerationLock, archive_iterator) + get_archival_cache_store, + get_archival_config, + ArchiveCacheGenerationLock, + archive_iterator, +) from rhodecode.lib.str_utils import safe_bytes, convert_special_chars from rhodecode.lib.view_utils import parse_path_ref from rhodecode.lib.exceptions import NonRelativePathError -from rhodecode.lib.codeblocks import ( - filenode_as_lines_tokens, filenode_as_annotated_lines_tokens) +from rhodecode.lib.codeblocks import filenode_as_lines_tokens, filenode_as_annotated_lines_tokens from rhodecode.lib.utils2 import convert_line_endings, detect_mode from rhodecode.lib.type_utils import str2bool from rhodecode.lib.str_utils import safe_str, safe_int, header_safe_str -from rhodecode.lib.auth import ( - LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) +from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired from rhodecode.lib.vcs import path as vcspath from rhodecode.lib.vcs.backends.base import EmptyCommit from rhodecode.lib.vcs.conf import settings from rhodecode.lib.vcs.nodes import FileNode from rhodecode.lib.vcs.exceptions import ( - RepositoryError, CommitDoesNotExistError, EmptyRepositoryError, - ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError, - NodeDoesNotExistError, CommitError, NodeError) + RepositoryError, + CommitDoesNotExistError, + EmptyRepositoryError, + ImproperArchiveTypeError, + VCSError, + NodeAlreadyExistsError, + NodeDoesNotExistError, + CommitError, + NodeError, +) from rhodecode.model.scm import ScmModel from rhodecode.model.db import Repository @@ -66,17 +75,17 @@ from rhodecode.model.db import Repository log = logging.getLogger(__name__) -def get_archive_name(db_repo_id, db_repo_name, commit_sha, ext, subrepos=False, path_sha='', with_hash=True): +def get_archive_name(db_repo_id, db_repo_name, commit_sha, ext, subrepos=False, path_sha="", with_hash=True): # original backward compat name of archive - clean_name = safe_str(convert_special_chars(db_repo_name).replace('/', '_')) + clean_name = safe_str(convert_special_chars(db_repo_name).replace("/", "_")) # e.g vcsserver-id-abcd-sub-1-abcfdef-archive-all.zip # vcsserver-id-abcd-sub-0-abcfdef-COMMIT_SHA-PATH_SHA.zip id_sha = sha1_safe(str(db_repo_id))[:4] - sub_repo = 'sub-1' if subrepos else 'sub-0' - commit = commit_sha if with_hash else 'archive' - path_marker = (path_sha if with_hash else '') or 'all' - archive_name = f'{clean_name}-id-{id_sha}-{sub_repo}-{commit}-{path_marker}{ext}' + sub_repo = "sub-1" if subrepos else "sub-0" + commit = commit_sha if with_hash else "archive" + path_marker = (path_sha if with_hash else "") or "all" + archive_name = f"{clean_name}-id-{id_sha}-{sub_repo}-{commit}-{path_marker}{ext}" return archive_name @@ -86,16 +95,15 @@ def get_path_sha(at_path): def _get_archive_spec(fname): - log.debug('Detecting archive spec for: `%s`', fname) + log.debug("Detecting archive spec for: `%s`", fname) fileformat = None ext = None content_type = None for a_type, content_type, extension in settings.ARCHIVE_SPECS: - if fname.endswith(extension): fileformat = a_type - log.debug('archive is of type: %s', fileformat) + log.debug("archive is of type: %s", fileformat) ext = extension break @@ -103,13 +111,12 @@ def _get_archive_spec(fname): raise ValueError() # left over part of whole fname is the commit - commit_id = fname[:-len(ext)] + commit_id = fname[: -len(ext)] return commit_id, ext, fileformat, content_type class RepoFilesView(RepoAppView): - @staticmethod def adjust_file_path_for_svn(f_path, repo): """ @@ -118,13 +125,11 @@ class RepoFilesView(RepoAppView): This is mainly based on prefix matching of the recognized tags and branches in the underlying repository. """ - tags_and_branches = itertools.chain( - repo.branches.keys(), - repo.tags.keys()) + tags_and_branches = itertools.chain(repo.branches.keys(), repo.tags.keys()) tags_and_branches = sorted(tags_and_branches, key=len, reverse=True) for name in tags_and_branches: - if f_path.startswith(f'{name}/'): + if f_path.startswith(f"{name}/"): f_path = vcspath.relpath(f_path, name) break return f_path @@ -135,59 +140,52 @@ class RepoFilesView(RepoAppView): c.enable_downloads = self.db_repo.enable_downloads return c - def _ensure_not_locked(self, commit_id='tip'): + def _ensure_not_locked(self, commit_id="tip"): _ = self.request.translate repo = self.db_repo if repo.enable_locking and repo.locked[0]: - h.flash(_('This repository has been locked by %s on %s') - % (h.person_by_id(repo.locked[0]), - h.format_date(h.time_to_datetime(repo.locked[1]))), - 'warning') - files_url = h.route_path( - 'repo_files:default_path', - repo_name=self.db_repo_name, commit_id=commit_id) + h.flash( + _("This repository has been locked by %s on %s") + % (h.person_by_id(repo.locked[0]), h.format_date(h.time_to_datetime(repo.locked[1]))), + "warning", + ) + files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id) raise HTTPFound(files_url) - def forbid_non_head(self, is_head, f_path, commit_id='tip', json_mode=False): + def forbid_non_head(self, is_head, f_path, commit_id="tip", json_mode=False): _ = self.request.translate if not is_head: - message = _('Cannot modify file. ' - 'Given commit `{}` is not head of a branch.').format(commit_id) - h.flash(message, category='warning') + message = _("Cannot modify file. " "Given commit `{}` is not head of a branch.").format(commit_id) + h.flash(message, category="warning") if json_mode: return message - files_url = h.route_path( - 'repo_files', repo_name=self.db_repo_name, commit_id=commit_id, - f_path=f_path) + files_url = h.route_path("repo_files", repo_name=self.db_repo_name, commit_id=commit_id, f_path=f_path) raise HTTPFound(files_url) - def check_branch_permission(self, branch_name, commit_id='tip', json_mode=False): + def check_branch_permission(self, branch_name, commit_id="tip", json_mode=False): _ = self.request.translate - rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission( - self.db_repo_name, branch_name) - if branch_perm and branch_perm not in ['branch.push', 'branch.push_force']: - message = _('Branch `{}` changes forbidden by rule {}.').format( - h.escape(branch_name), h.escape(rule)) - h.flash(message, 'warning') + rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(self.db_repo_name, branch_name) + if branch_perm and branch_perm not in ["branch.push", "branch.push_force"]: + message = _("Branch `{}` changes forbidden by rule {}.").format(h.escape(branch_name), h.escape(rule)) + h.flash(message, "warning") if json_mode: return message - files_url = h.route_path( - 'repo_files:default_path', repo_name=self.db_repo_name, commit_id=commit_id) + files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id) raise HTTPFound(files_url) def _get_commit_and_path(self): default_commit_id = self.db_repo.landing_ref_name - default_f_path = '/' + default_f_path = "/" - commit_id = self.request.matchdict.get('commit_id', default_commit_id) + commit_id = self.request.matchdict.get("commit_id", default_commit_id) f_path = self._get_f_path(self.request.matchdict, default_f_path) bytes_path = safe_bytes(f_path) @@ -195,8 +193,8 @@ class RepoFilesView(RepoAppView): @classmethod def _get_default_encoding(cls, c): - enc_list = getattr(c, 'default_encodings', []) - return enc_list[0] if enc_list else 'UTF-8' + enc_list = getattr(c, "default_encodings", []) + return enc_list[0] if enc_list else "UTF-8" def _get_commit_or_redirect(self, commit_id, redirect_after=True): """ @@ -215,31 +213,25 @@ class RepoFilesView(RepoAppView): return None add_new = upload_new = "" - if h.HasRepoPermissionAny( - 'repository.write', 'repository.admin')(self.db_repo_name): - _url = h.route_path( - 'repo_files_add_file', - repo_name=self.db_repo_name, commit_id=0, f_path='') - add_new = h.link_to( - _('add a new file'), _url, class_="alert-link") + if h.HasRepoPermissionAny("repository.write", "repository.admin")(self.db_repo_name): + _url = h.route_path("repo_files_add_file", repo_name=self.db_repo_name, commit_id=0, f_path="") + add_new = h.link_to(_("add a new file"), _url, class_="alert-link") - _url_upld = h.route_path( - 'repo_files_upload_file', - repo_name=self.db_repo_name, commit_id=0, f_path='') - upload_new = h.link_to( - _('upload a new file'), _url_upld, class_="alert-link") + _url_upld = h.route_path("repo_files_upload_file", repo_name=self.db_repo_name, commit_id=0, f_path="") + upload_new = h.link_to(_("upload a new file"), _url_upld, class_="alert-link") - h.flash(h.literal( - _('There are no files yet. Click here to %s or %s.') % (add_new, upload_new)), category='warning') - raise HTTPFound( - h.route_path('repo_summary', repo_name=self.db_repo_name)) + h.flash( + h.literal(_("There are no files yet. Click here to %s or %s.") % (add_new, upload_new)), + category="warning", + ) + raise HTTPFound(h.route_path("repo_summary", repo_name=self.db_repo_name)) except (CommitDoesNotExistError, LookupError) as e: - msg = _('No such commit exists for this repository. Commit: {}').format(commit_id) - h.flash(msg, category='error') + msg = _("No such commit exists for this repository. Commit: {}").format(commit_id) + h.flash(msg, category="error") raise HTTPNotFound() except RepositoryError as e: - h.flash(h.escape(safe_str(e)), category='error') + h.flash(h.escape(safe_str(e)), category="error") raise HTTPNotFound() def _get_filenode_or_redirect(self, commit_obj, path, pre_load=None): @@ -252,22 +244,22 @@ class RepoFilesView(RepoAppView): try: file_node = commit_obj.get_node(path, pre_load=pre_load) if file_node.is_dir(): - raise RepositoryError('The given path is a directory') + raise RepositoryError("The given path is a directory") except CommitDoesNotExistError: - log.exception('No such commit exists for this repository') - h.flash(_('No such commit exists for this repository'), category='error') + log.exception("No such commit exists for this repository") + h.flash(_("No such commit exists for this repository"), category="error") raise HTTPNotFound() except RepositoryError as e: - log.warning('Repository error while fetching filenode `%s`. Err:%s', path, e) - h.flash(h.escape(safe_str(e)), category='error') + log.warning("Repository error while fetching filenode `%s`. Err:%s", path, e) + h.flash(h.escape(safe_str(e)), category="error") raise HTTPNotFound() return file_node def _is_valid_head(self, commit_id, repo, landing_ref): - branch_name = sha_commit_id = '' + branch_name = sha_commit_id = "" is_head = False - log.debug('Checking if commit_id `%s` is a head for %s.', commit_id, repo) + log.debug("Checking if commit_id `%s` is a head for %s.", commit_id, repo) for _branch_name, branch_commit_id in repo.branches.items(): # simple case we pass in branch name, it's a HEAD @@ -303,39 +295,39 @@ class RepoFilesView(RepoAppView): return branch_name, sha_commit_id, is_head def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None): - repo_id = self.db_repo.repo_id force_recache = self.get_recache_flag() - cache_seconds = safe_int( - rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time')) + cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time") cache_on = not force_recache and cache_seconds > 0 - log.debug( - 'Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`' - 'with caching: %s[TTL: %ss]' % ( - repo_id, commit_id, f_path, cache_on, cache_seconds or 0)) - cache_namespace_uid = f'repo.{rc_cache.FILE_TREE_CACHE_VER}.{repo_id}' - region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid) + log.debug( + "Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`" + "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0) + ) + + cache_namespace_uid = f"repo.{rc_cache.FILE_TREE_CACHE_VER}.{repo_id}" + region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid) @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on) def compute_file_tree(_name_hash, _repo_id, _commit_id, _f_path, _full_load, _at_rev): - log.debug('Generating cached file tree at for repo_id: %s, %s, %s', - _repo_id, _commit_id, _f_path) + log.debug("Generating cached file tree at for repo_id: %s, %s, %s", _repo_id, _commit_id, _f_path) c.full_load = _full_load return render( - 'rhodecode:templates/files/files_browser_tree.mako', - self._get_template_context(c), self.request, _at_rev) + "rhodecode:templates/files/files_browser_tree.mako", + self._get_template_context(c), + self.request, + _at_rev, + ) return compute_file_tree( - self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path, full_load, at_rev) + self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path, full_load, at_rev + ) def create_pure_path(self, *parts): # Split paths and sanitize them, removing any ../ etc - sanitized_path = [ - x for x in pathlib.PurePath(*parts).parts - if x not in ['.', '..']] + sanitized_path = [x for x in pathlib.PurePath(*parts).parts if x not in [".", ".."]] pure_path = pathlib.PurePath(*sanitized_path) return pure_path @@ -343,10 +335,7 @@ class RepoFilesView(RepoAppView): def _is_lf_enabled(self, target_repo): lf_enabled = False - lf_key_for_vcs_map = { - 'hg': 'extensions_largefiles', - 'git': 'vcs_git_lfs_enabled' - } + lf_key_for_vcs_map = {"hg": "extensions_largefiles", "git": "vcs_git_lfs_enabled"} lf_key_for_vcs = lf_key_for_vcs_map.get(target_repo.repo_type) @@ -356,66 +345,77 @@ class RepoFilesView(RepoAppView): return lf_enabled @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_archivefile(self): # archive cache config from rhodecode import CONFIG + _ = self.request.translate self.load_default_context() - subrepos = self.request.GET.get('subrepos') == 'true' - with_hash = str2bool(self.request.GET.get('with_hash', '1')) + subrepos = self.request.GET.get("subrepos") == "true" + with_hash = str2bool(self.request.GET.get("with_hash", "1")) - default_at_path = '/' - fname = self.request.matchdict['fname'] - at_path = self.request.GET.get('at_path') or default_at_path + default_at_path = "/" + fname = self.request.matchdict["fname"] + at_path = self.request.GET.get("at_path") or default_at_path if not self.db_repo.enable_downloads: - return Response(_('Downloads disabled')) + return Response(_("Downloads disabled")) try: commit_id, ext, file_format, content_type = _get_archive_spec(fname) except ValueError: - return Response(_('Unknown archive type for: `{}`').format(h.escape(fname))) + return Response(_("Unknown archive type for: `{}`").format(h.escape(fname))) try: commit = self.rhodecode_vcs_repo.get_commit(commit_id) except CommitDoesNotExistError: - return Response(_('Unknown commit_id {}').format( - h.escape(commit_id))) + return Response(_("Unknown commit_id {}").format(h.escape(commit_id))) except EmptyRepositoryError: - return Response(_('Empty repository')) + return Response(_("Empty repository")) # we used a ref, or a shorter version, lets redirect client ot use explicit hash if commit_id != commit.raw_id: - fname=f'{commit.raw_id}{ext}' + fname = f"{commit.raw_id}{ext}" raise HTTPFound(self.request.current_route_path(fname=fname)) try: at_path = commit.get_node(safe_bytes(at_path)).path or default_at_path except Exception: - return Response(_('No node at path {} for this repository').format(h.escape(at_path))) + return Response(_("No node at path {} for this repository").format(h.escape(at_path))) path_sha = get_path_sha(at_path) # used for cache etc, consistent unique archive name archive_name_key = get_archive_name( - self.db_repo.repo_id, self.db_repo_name, commit_sha=commit.short_id, ext=ext, subrepos=subrepos, - path_sha=path_sha, with_hash=True) + self.db_repo.repo_id, + self.db_repo_name, + commit_sha=commit.short_id, + ext=ext, + subrepos=subrepos, + path_sha=path_sha, + with_hash=True, + ) if not with_hash: - path_sha = '' + path_sha = "" # what end client gets served response_archive_name = get_archive_name( - self.db_repo.repo_id, self.db_repo_name, commit_sha=commit.short_id, ext=ext, subrepos=subrepos, - path_sha=path_sha, with_hash=with_hash) + self.db_repo.repo_id, + self.db_repo_name, + commit_sha=commit.short_id, + ext=ext, + subrepos=subrepos, + path_sha=path_sha, + with_hash=with_hash, + ) # remove extension from our archive directory name - archive_dir_name = response_archive_name[:-len(ext)] + archive_dir_name = response_archive_name[: -len(ext)] - archive_cache_disable = self.request.GET.get('no_cache') + archive_cache_disable = self.request.GET.get("no_cache") d_cache = get_archival_cache_store(config=CONFIG) @@ -423,29 +423,38 @@ class RepoFilesView(RepoAppView): d_cache_conf = get_archival_config(config=CONFIG) # This is also a cache key, and lock key - reentrant_lock_key = archive_name_key + '.lock' + reentrant_lock_key = archive_name_key + ".lock" use_cached_archive = False if not archive_cache_disable and archive_name_key in d_cache: reader, metadata = d_cache.fetch(archive_name_key) use_cached_archive = True - log.debug('Found cached archive as key=%s tag=%s, serving archive from cache reader=%s', - archive_name_key, metadata, reader.name) + log.debug( + "Found cached archive as key=%s tag=%s, serving archive from cache reader=%s", + archive_name_key, + metadata, + reader.name, + ) else: reader = None - log.debug('Archive with key=%s is not yet cached, creating one now...', archive_name_key) + log.debug("Archive with key=%s is not yet cached, creating one now...", archive_name_key) if not reader: # generate new archive, as previous was not found in the cache try: with d_cache.get_lock(reentrant_lock_key): try: - commit.archive_repo(archive_name_key, archive_dir_name=archive_dir_name, - kind=file_format, subrepos=subrepos, - archive_at_path=at_path, cache_config=d_cache_conf) + commit.archive_repo( + archive_name_key, + archive_dir_name=archive_dir_name, + kind=file_format, + subrepos=subrepos, + archive_at_path=at_path, + cache_config=d_cache_conf, + ) except ImproperArchiveTypeError: - return _('Unknown archive type') + return _("Unknown archive type") except ArchiveCacheGenerationLock: retry_after = round(random.uniform(0.3, 3.0), 1) @@ -464,7 +473,7 @@ class RepoFilesView(RepoAppView): reader, metadata = d_cache.fetch(archive_name_key, retry=True, retry_attempts=30) response = Response(app_iter=archive_iterator(reader)) - response.content_disposition = f'attachment; filename={response_archive_name}' + response.content_disposition = f"attachment; filename={response_archive_name}" response.content_type = str(content_type) try: @@ -472,23 +481,25 @@ class RepoFilesView(RepoAppView): finally: # store download action audit_logger.store_web( - 'repo.archive.download', action_data={ - 'user_agent': self.request.user_agent, - 'archive_name': archive_name_key, - 'archive_spec': fname, - 'archive_cached': use_cached_archive}, + "repo.archive.download", + action_data={ + "user_agent": self.request.user_agent, + "archive_name": archive_name_key, + "archive_spec": fname, + "archive_cached": use_cached_archive, + }, user=self._rhodecode_user, repo=self.db_repo, - commit=True + commit=True, ) def _get_file_node(self, commit_id, f_path): - if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]: + if commit_id not in ["", None, "None", "0" * 12, "0" * 40]: commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id) try: node = commit.get_node(safe_bytes(f_path)) if node.is_dir(): - raise NodeError(f'{node} path is a {type(node)} not a file') + raise NodeError(f"{node} path is a {type(node)} not a file") except NodeDoesNotExistError: commit = EmptyCommit( commit_id=commit_id, @@ -497,46 +508,43 @@ class RepoFilesView(RepoAppView): alias=commit.repository.alias, message=commit.message, author=commit.author, - date=commit.date) - node = FileNode(safe_bytes(f_path), b'', commit=commit) + date=commit.date, + ) + node = FileNode(safe_bytes(f_path), b"", commit=commit) else: - commit = EmptyCommit( - repo=self.rhodecode_vcs_repo, - alias=self.rhodecode_vcs_repo.alias) - node = FileNode(safe_bytes(f_path), b'', commit=commit) + commit = EmptyCommit(repo=self.rhodecode_vcs_repo, alias=self.rhodecode_vcs_repo.alias) + node = FileNode(safe_bytes(f_path), b"", commit=commit) return node @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_files_diff(self): c = self.load_default_context() f_path = self._get_f_path(self.request.matchdict) - diff1 = self.request.GET.get('diff1', '') - diff2 = self.request.GET.get('diff2', '') + diff1 = self.request.GET.get("diff1", "") + diff2 = self.request.GET.get("diff2", "") path1, diff1 = parse_path_ref(diff1, default_path=f_path) - ignore_whitespace = str2bool(self.request.GET.get('ignorews')) - line_context = self.request.GET.get('context', 3) + ignore_whitespace = str2bool(self.request.GET.get("ignorews")) + line_context = self.request.GET.get("context", 3) if not any((diff1, diff2)): - h.flash( - 'Need query parameter "diff1" or "diff2" to generate a diff.', - category='error') + h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error") raise HTTPBadRequest() - c.action = self.request.GET.get('diff') - if c.action not in ['download', 'raw']: + c.action = self.request.GET.get("diff") + if c.action not in ["download", "raw"]: compare_url = h.route_path( - 'repo_compare', + "repo_compare", repo_name=self.db_repo_name, - source_ref_type='rev', + source_ref_type="rev", source_ref=diff1, target_repo=self.db_repo_name, - target_ref_type='rev', + target_ref_type="rev", target_ref=diff2, - _query=dict(f_path=f_path)) + _query=dict(f_path=f_path), + ) # redirect to new view if we render diff raise HTTPFound(compare_url) @@ -545,43 +553,34 @@ class RepoFilesView(RepoAppView): node2 = self._get_file_node(diff2, f_path) except (RepositoryError, NodeError): log.exception("Exception while trying to get node from repository") - raise HTTPFound( - h.route_path('repo_files', repo_name=self.db_repo_name, - commit_id='tip', f_path=f_path)) + raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path=f_path)) - if all(isinstance(node.commit, EmptyCommit) - for node in (node1, node2)): + if all(isinstance(node.commit, EmptyCommit) for node in (node1, node2)): raise HTTPNotFound() c.commit_1 = node1.commit c.commit_2 = node2.commit - if c.action == 'download': - _diff = diffs.get_gitdiff(node1, node2, - ignore_whitespace=ignore_whitespace, - context=line_context) + if c.action == "download": + _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context) # NOTE: this was using diff_format='gitdiff' - diff = diffs.DiffProcessor(_diff, diff_format='newdiff') + diff = diffs.DiffProcessor(_diff, diff_format="newdiff") response = Response(self.path_filter.get_raw_patch(diff)) - response.content_type = 'text/plain' - response.content_disposition = ( - f'attachment; filename={f_path}_{diff1}_vs_{diff2}.diff' - ) + response.content_type = "text/plain" + response.content_disposition = f"attachment; filename={f_path}_{diff1}_vs_{diff2}.diff" charset = self._get_default_encoding(c) if charset: response.charset = charset return response - elif c.action == 'raw': - _diff = diffs.get_gitdiff(node1, node2, - ignore_whitespace=ignore_whitespace, - context=line_context) + elif c.action == "raw": + _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context) # NOTE: this was using diff_format='gitdiff' - diff = diffs.DiffProcessor(_diff, diff_format='newdiff') + diff = diffs.DiffProcessor(_diff, diff_format="newdiff") response = Response(self.path_filter.get_raw_patch(diff)) - response.content_type = 'text/plain' + response.content_type = "text/plain" charset = self._get_default_encoding(c) if charset: response.charset = charset @@ -591,31 +590,32 @@ class RepoFilesView(RepoAppView): raise HTTPNotFound() @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_files_diff_2way_redirect(self): """ Kept only to make OLD links work """ f_path = self._get_f_path_unchecked(self.request.matchdict) - diff1 = self.request.GET.get('diff1', '') - diff2 = self.request.GET.get('diff2', '') + diff1 = self.request.GET.get("diff1", "") + diff2 = self.request.GET.get("diff2", "") if not any((diff1, diff2)): - h.flash( - 'Need query parameter "diff1" or "diff2" to generate a diff.', - category='error') + h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error") raise HTTPBadRequest() compare_url = h.route_path( - 'repo_compare', + "repo_compare", repo_name=self.db_repo_name, - source_ref_type='rev', + source_ref_type="rev", source_ref=diff1, - target_ref_type='rev', + target_ref_type="rev", target_ref=diff2, - _query=dict(f_path=f_path, diffmode='sideside', - target_repo=self.db_repo_name,)) + _query=dict( + f_path=f_path, + diffmode="sideside", + target_repo=self.db_repo_name, + ), + ) raise HTTPFound(compare_url) @LoginRequired() @@ -629,138 +629,131 @@ class RepoFilesView(RepoAppView): landing_url = h.repo_files_by_ref_url( c.rhodecode_db_repo.repo_name, c.rhodecode_db_repo.repo_type, - f_path='', + f_path="", ref_name=ref_name, - commit_id='tip', - query=dict(at=ref_name) + commit_id="tip", + query=dict(at=ref_name), ) raise HTTPFound(landing_url) @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_files(self): c = self.load_default_context() - view_name = getattr(self.request.matched_route, 'name', None) + view_name = getattr(self.request.matched_route, "name", None) - c.annotate = view_name == 'repo_files:annotated' + c.annotate = view_name == "repo_files:annotated" # default is false, but .rst/.md files later are auto rendered, we can # overwrite auto rendering by setting this GET flag - c.renderer = view_name == 'repo_files:rendered' or not self.request.GET.get('no-render', False) + c.renderer = view_name == "repo_files:rendered" or not self.request.GET.get("no-render", False) commit_id, f_path, bytes_path = self._get_commit_and_path() c.commit = self._get_commit_or_redirect(commit_id) - c.branch = self.request.GET.get('branch', None) + c.branch = self.request.GET.get("branch", None) c.f_path = f_path - at_rev = self.request.GET.get('at') + at_rev = self.request.GET.get("at") # files or dirs try: - - c.file = c.commit.get_node(bytes_path, pre_load=['is_binary', 'size', 'data']) + c.file = c.commit.get_node(bytes_path, pre_load=["is_binary", "size", "data"]) c.file_author = True - c.file_tree = '' + c.file_tree = "" # prev link try: prev_commit = c.commit.prev(c.branch) c.prev_commit = prev_commit - c.url_prev = h.route_path('repo_files', repo_name=self.db_repo_name, commit_id=prev_commit.raw_id, f_path=f_path) + c.url_prev = h.route_path( + "repo_files", repo_name=self.db_repo_name, commit_id=prev_commit.raw_id, f_path=f_path + ) if c.branch: - c.url_prev += f'?branch={c.branch}' + c.url_prev += f"?branch={c.branch}" except (CommitDoesNotExistError, VCSError): - c.url_prev = '#' + c.url_prev = "#" c.prev_commit = EmptyCommit() # next link try: next_commit = c.commit.next(c.branch) c.next_commit = next_commit - c.url_next = h.route_path('repo_files', repo_name=self.db_repo_name, commit_id=next_commit.raw_id, f_path=f_path) + c.url_next = h.route_path( + "repo_files", repo_name=self.db_repo_name, commit_id=next_commit.raw_id, f_path=f_path + ) if c.branch: - c.url_next += f'?branch={c.branch}' + c.url_next += f"?branch={c.branch}" except (CommitDoesNotExistError, VCSError): - c.url_next = '#' + c.url_next = "#" c.next_commit = EmptyCommit() # load file content if c.file.is_file(): - c.lf_node = {} has_lf_enabled = self._is_lf_enabled(self.db_repo) if has_lf_enabled: c.lf_node = c.file.get_largefile_node() - c.file_source_page = 'true' + c.file_source_page = "true" c.file_last_commit = c.file.last_commit c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file if not (c.file_size_too_big or c.file.is_binary): if c.annotate: # annotation has precedence over renderer - c.annotated_lines = filenode_as_annotated_lines_tokens( - c.file - ) + c.annotated_lines = filenode_as_annotated_lines_tokens(c.file) else: - c.renderer = ( - c.renderer and h.renderer_from_filename(c.file.path) - ) + c.renderer = c.renderer and h.renderer_from_filename(c.file.path) if not c.renderer: c.lines = filenode_as_lines_tokens(c.file) - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) c.on_branch_head = is_head - branch = c.commit.branch if ( - c.commit.branch and '/' not in c.commit.branch) else None + branch = c.commit.branch if (c.commit.branch and "/" not in c.commit.branch) else None c.branch_or_raw_id = branch or c.commit.raw_id c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id) author = c.file_last_commit.author - c.authors = [[ - h.email(author), - h.person(author, 'username_or_name_or_email'), - 1 - ]] + c.authors = [[h.email(author), h.person(author, "username_or_name_or_email"), 1]] - else: # load tree content at path - c.file_source_page = 'false' + else: # load tree content (dir content) at path + c.file_source_page = "false" c.authors = [] + + dir_node = c.file + c.file_nodes = dir_node.commit.get_nodes(dir_node.bytes_path, pre_load=dir_node.default_pre_load) # this loads a simple tree without metadata to speed things up # later via ajax we call repo_nodetree_full and fetch whole c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev) - c.readme_data, c.readme_file = \ - self._get_readme_data(self.db_repo, c.visual.default_renderer, c.commit.raw_id, bytes_path) + c.readme_data, c.readme_file = self._get_readme_data( + self.db_repo, c.visual.default_renderer, c.commit.raw_id, bytes_path, nodes=c.file_nodes + ) except RepositoryError as e: - h.flash(h.escape(safe_str(e)), category='error') + h.flash(h.escape(safe_str(e)), category="error") raise HTTPNotFound() - if self.request.environ.get('HTTP_X_PJAX'): - html = render('rhodecode:templates/files/files_pjax.mako', - self._get_template_context(c), self.request) + if self.request.environ.get("HTTP_X_PJAX"): + html = render("rhodecode:templates/files/files_pjax.mako", self._get_template_context(c), self.request) else: - html = render('rhodecode:templates/files/files.mako', - self._get_template_context(c), self.request) + html = render("rhodecode:templates/files/files.mako", self._get_template_context(c), self.request) return Response(html) - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_files_annotated_previous(self): self.load_default_context() commit_id, bytes_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) prev_commit_id = commit.raw_id - line_anchor = self.request.GET.get('line_anchor') + line_anchor = self.request.GET.get("line_anchor") is_file = False try: _file = commit.get_node(bytes_path) @@ -770,18 +763,19 @@ class RepoFilesView(RepoAppView): if is_file: history = commit.get_path_history(bytes_path) - prev_commit_id = history[1].raw_id \ - if len(history) > 1 else prev_commit_id + prev_commit_id = history[1].raw_id if len(history) > 1 else prev_commit_id prev_url = h.route_path( - 'repo_files:annotated', repo_name=self.db_repo_name, - commit_id=prev_commit_id, f_path=bytes_path, - _anchor=f'L{line_anchor}') + "repo_files:annotated", + repo_name=self.db_repo_name, + commit_id=prev_commit_id, + f_path=bytes_path, + _anchor=f"L{line_anchor}", + ) raise HTTPFound(prev_url) @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_nodetree_full(self): """ Returns rendered html of file tree that contains commit date, @@ -795,17 +789,17 @@ class RepoFilesView(RepoAppView): try: dir_node = commit.get_node(bytes_path) except RepositoryError as e: - return Response(f'error: {h.escape(safe_str(e))}') + return Response(f"error: {h.escape(safe_str(e))}") if dir_node.is_file(): - return Response('') + return Response("") c.file = dir_node + c.file_nodes = dir_node.commit.get_nodes(dir_node.bytes_path, pre_load=dir_node.default_pre_load) c.commit = commit - at_rev = self.request.GET.get('at') + at_rev = self.request.GET.get("at") - html = self._get_tree_at_commit( - c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev) + html = self._get_tree_at_commit(c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev) return Response(html) @@ -814,15 +808,12 @@ class RepoFilesView(RepoAppView): safe_path = f_name.replace('"', '\\"') encoded_path = urllib.parse.quote(f_name) - headers = f"attachment; " \ - f"filename=\"{safe_path}\"; " \ - f"filename*=UTF-8\'\'{encoded_path}" + headers = f"attachment; " f'filename="{safe_path}"; ' f"filename*=UTF-8''{encoded_path}" return header_safe_str(headers) @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_file_raw(self): """ Action for show as raw, some mimetypes are "rendered", @@ -839,16 +830,15 @@ class RepoFilesView(RepoAppView): # you can also provide a content-disposition to override the # default "attachment" disposition. # orig_type: (new_type, new_dispo) - # show images inline: # Do not re-add SVG: it is unsafe and permits XSS attacks. One can # for example render an SVG with javascript inside or even render # HTML. - 'image/x-icon': ('image/x-icon', 'inline'), - 'image/png': ('image/png', 'inline'), - 'image/gif': ('image/gif', 'inline'), - 'image/jpeg': ('image/jpeg', 'inline'), - 'application/pdf': ('application/pdf', 'inline'), + "image/x-icon": ("image/x-icon", "inline"), + "image/png": ("image/png", "inline"), + "image/gif": ("image/gif", "inline"), + "image/jpeg": ("image/jpeg", "inline"), + "application/pdf": ("application/pdf", "inline"), } mimetype = file_node.mimetype @@ -858,7 +848,7 @@ class RepoFilesView(RepoAppView): # we don't know anything special about this, handle it safely if file_node.is_binary: # do same as download raw for binary files - mimetype, disposition = 'application/octet-stream', 'attachment' + mimetype, disposition = "application/octet-stream", "attachment" else: # do not just use the original mimetype, but force text/plain, # otherwise it would serve text/html and that might be unsafe. @@ -867,9 +857,9 @@ class RepoFilesView(RepoAppView): # binary.This might lead to erroneous text display in some # cases, but helps in other cases, like with text files # without extension. - mimetype, disposition = 'text/plain', 'inline' + mimetype, disposition = "text/plain", "inline" - if disposition == 'attachment': + if disposition == "attachment": disposition = self._get_attachement_headers(f_path) stream_content = file_node.stream_bytes() @@ -885,8 +875,7 @@ class RepoFilesView(RepoAppView): return response @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_file_download(self): c = self.load_default_context() @@ -894,7 +883,7 @@ class RepoFilesView(RepoAppView): commit = self._get_commit_or_redirect(commit_id) file_node = self._get_filenode_or_redirect(commit, bytes_path) - if self.request.GET.get('lf'): + if self.request.GET.get("lf"): # only if lf get flag is passed, we download this file # as LFS/Largefile lf_node = file_node.get_largefile_node() @@ -917,44 +906,41 @@ class RepoFilesView(RepoAppView): return response def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path): - - cache_seconds = rhodecode.ConfigGet().get_int('rc_cache.cache_repo.expiration_time') + cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time") cache_on = cache_seconds > 0 log.debug( - 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`' - 'with caching: %s[TTL: %ss]' % ( - repo_id, commit_id, f_path, cache_on, cache_seconds or 0)) + "Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`" + "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0) + ) - cache_namespace_uid = f'repo.{repo_id}' - region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid) + cache_namespace_uid = f"repo.{repo_id}" + region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid) @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on) def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path): - log.debug('Generating cached nodelist for repo_id:%s, %s, %s', _repo_id, commit_id, f_path) + log.debug("Generating cached nodelist for repo_id:%s, %s, %s", _repo_id, commit_id, f_path) try: _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path) except (RepositoryError, CommitDoesNotExistError, Exception) as e: log.exception(safe_str(e)) - h.flash(h.escape(safe_str(e)), category='error') - raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name, commit_id='tip', f_path='/')) + h.flash(h.escape(safe_str(e)), category="error") + raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path="/")) return _d + _f result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path) - return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result) + return filter(lambda n: self.path_filter.path_access_allowed(n["name"]), result) @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_nodelist(self): self.load_default_context() commit_id, f_path, bytes_path = self._get_commit_and_path() commit = self._get_commit_or_redirect(commit_id) - metadata = self._get_nodelist_at_commit( - self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path) - return {'nodes': [x for x in metadata]} + metadata = self._get_nodelist_at_commit(self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path) + return {"nodes": [x for x in metadata]} def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type): items = [] @@ -971,7 +957,7 @@ class RepoFilesView(RepoAppView): # NOTE(dan): old code we used in "diff" mode compare new_f_path = vcspath.join(name, f_path) - return f'{new_f_path}@{commit_id}' + return f"{new_f_path}@{commit_id}" def _get_node_history(self, commit_obj, f_path, commits=None): """ @@ -997,29 +983,26 @@ class RepoFilesView(RepoAppView): history = [] commits_group = ([], _("Changesets")) for commit in commits: - branch = ' (%s)' % commit.branch if commit.branch else '' - n_desc = f'r{commit.idx}:{commit.short_id}{branch}' - commits_group[0].append((commit.raw_id, n_desc, 'sha')) + branch = " (%s)" % commit.branch if commit.branch else "" + n_desc = f"r{commit.idx}:{commit.short_id}{branch}" + commits_group[0].append((commit.raw_id, n_desc, "sha")) history.append(commits_group) symbolic_reference = self._symbolic_reference - if self.rhodecode_vcs_repo.alias == 'svn': - adjusted_f_path = RepoFilesView.adjust_file_path_for_svn( - f_path, self.rhodecode_vcs_repo) + if self.rhodecode_vcs_repo.alias == "svn": + adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(f_path, self.rhodecode_vcs_repo) if adjusted_f_path != f_path: log.debug( - 'Recognized svn tag or branch in file "%s", using svn ' - 'specific symbolic references', f_path) + 'Recognized svn tag or branch in file "%s", using svn ' "specific symbolic references", f_path + ) f_path = adjusted_f_path symbolic_reference = self._symbolic_reference_svn - branches = self._create_references( - self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch') + branches = self._create_references(self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, "branch") branches_group = (branches, _("Branches")) - tags = self._create_references( - self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag') + tags = self._create_references(self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, "tag") tags_group = (tags, _("Tags")) history.append(branches_group) @@ -1028,8 +1011,7 @@ class RepoFilesView(RepoAppView): return history, commits @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_file_history(self): self.load_default_context() @@ -1044,35 +1026,23 @@ class RepoFilesView(RepoAppView): for section_items, section in file_history: items = [] for obj_id, obj_text, obj_type in section_items: - at_rev = '' - if obj_type in ['branch', 'bookmark', 'tag']: + at_rev = "" + if obj_type in ["branch", "bookmark", "tag"]: at_rev = obj_text - entry = { - 'id': obj_id, - 'text': obj_text, - 'type': obj_type, - 'at_rev': at_rev - } + entry = {"id": obj_id, "text": obj_text, "type": obj_type, "at_rev": at_rev} items.append(entry) - res.append({ - 'text': section, - 'children': items - }) + res.append({"text": section, "children": items}) - data = { - 'more': False, - 'results': res - } + data = {"more": False, "results": res} return data - log.warning('Cannot fetch history for directory') + log.warning("Cannot fetch history for directory") raise HTTPBadRequest() @LoginRequired() - @HasRepoPermissionAnyDecorator( - 'repository.read', 'repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin") def repo_file_authors(self): c = self.load_default_context() @@ -1084,12 +1054,10 @@ class RepoFilesView(RepoAppView): raise HTTPBadRequest() c.file_last_commit = file_node.last_commit - if self.request.GET.get('annotate') == '1': + if self.request.GET.get("annotate") == "1": # use _hist from annotation if annotation mode is on commit_ids = {x[1] for x in file_node.annotate} - _hist = ( - self.rhodecode_vcs_repo.get_commit(commit_id) - for commit_id in commit_ids) + _hist = (self.rhodecode_vcs_repo.get_commit(commit_id) for commit_id in commit_ids) else: _f_history, _hist = self._get_node_history(commit, f_path) c.file_author = False @@ -1100,8 +1068,8 @@ class RepoFilesView(RepoAppView): if author not in unique: unique[commit.author] = [ h.email(author), - h.person(author, 'username_or_name_or_email'), - 1 # counter + h.person(author, "username_or_name_or_email"), + 1, # counter ] else: @@ -1113,20 +1081,21 @@ class RepoFilesView(RepoAppView): return self._get_template_context(c) @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") def repo_files_check_head(self): self.load_default_context() commit_id, f_path, bytes_path = self._get_commit_and_path() - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) - new_path = self.request.POST.get('path') - operation = self.request.POST.get('operation') - path_exist = '' + new_path = self.request.POST.get("path") + operation = self.request.POST.get("operation") + path_exist = "" - if new_path and operation in ['create', 'upload']: - new_f_path = os.path.join(f_path.lstrip('/'), new_path) + if new_path and operation in ["create", "upload"]: + new_f_path = os.path.join(f_path.lstrip("/"), new_path) try: commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id) # NOTE(dan): construct whole path without leading / @@ -1136,24 +1105,19 @@ class RepoFilesView(RepoAppView): except (EmptyRepositoryError, NodeDoesNotExistError): pass - return { - 'branch': _branch_name, - 'sha': _sha_commit_id, - 'is_head': is_head, - 'path_exists': path_exist - } + return {"branch": _branch_name, "sha": _sha_commit_id, "is_head": is_head, "path_exists": path_exist} @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") def repo_files_remove_file(self): _ = self.request.translate c = self.load_default_context() commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) self.forbid_non_head(is_head, f_path) self.check_branch_permission(_branch_name) @@ -1161,14 +1125,13 @@ class RepoFilesView(RepoAppView): c.commit = self._get_commit_or_redirect(commit_id) c.file = self._get_filenode_or_redirect(c.commit, bytes_path) - c.default_message = _( - 'Deleted file {} via RhodeCode Enterprise').format(f_path) + c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path) c.f_path = f_path return self._get_template_context(c) @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") @CSRFRequired() def repo_files_delete_file(self): _ = self.request.translate @@ -1177,9 +1140,9 @@ class RepoFilesView(RepoAppView): commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) self.forbid_non_head(is_head, f_path) self.check_branch_permission(_branch_name) @@ -1187,46 +1150,39 @@ class RepoFilesView(RepoAppView): c.commit = self._get_commit_or_redirect(commit_id) c.file = self._get_filenode_or_redirect(c.commit, bytes_path) - c.default_message = _('Deleted file {} via RhodeCode Enterprise').format(f_path) + c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path) c.f_path = f_path node_path = f_path author = self._rhodecode_db_user.full_contact - message = self.request.POST.get('message') or c.default_message + message = self.request.POST.get("message") or c.default_message try: - nodes = { - safe_bytes(node_path): { - 'content': b'' - } - } + nodes = {safe_bytes(node_path): {"content": b""}} ScmModel().delete_nodes( - user=self._rhodecode_db_user.user_id, repo=self.db_repo, + user=self._rhodecode_db_user.user_id, + repo=self.db_repo, message=message, nodes=nodes, parent_commit=c.commit, author=author, ) - h.flash( - _('Successfully deleted file `{}`').format( - h.escape(f_path)), category='success') + h.flash(_("Successfully deleted file `{}`").format(h.escape(f_path)), category="success") except Exception: - log.exception('Error during commit operation') - h.flash(_('Error occurred during commit'), category='error') - raise HTTPFound( - h.route_path('repo_commit', repo_name=self.db_repo_name, - commit_id='tip')) + log.exception("Error during commit operation") + h.flash(_("Error occurred during commit"), category="error") + raise HTTPFound(h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")) @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") def repo_files_edit_file(self): _ = self.request.translate c = self.load_default_context() commit_id, f_path, bytes_path = self._get_commit_and_path() self._ensure_not_locked() - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) self.forbid_non_head(is_head, f_path, commit_id=commit_id) self.check_branch_permission(_branch_name, commit_id=commit_id) @@ -1236,18 +1192,17 @@ class RepoFilesView(RepoAppView): if c.file.is_binary: files_url = h.route_path( - 'repo_files', - repo_name=self.db_repo_name, - commit_id=c.commit.raw_id, f_path=f_path) + "repo_files", repo_name=self.db_repo_name, commit_id=c.commit.raw_id, f_path=f_path + ) raise HTTPFound(files_url) - c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path) + c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path) c.f_path = f_path return self._get_template_context(c) @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") @CSRFRequired() def repo_files_update_file(self): _ = self.request.translate @@ -1260,53 +1215,52 @@ class RepoFilesView(RepoAppView): c.file = self._get_filenode_or_redirect(c.commit, bytes_path) if c.file.is_binary: - raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name, - commit_id=c.commit.raw_id, f_path=f_path)) + raise HTTPFound( + h.route_path("repo_files", repo_name=self.db_repo_name, commit_id=c.commit.raw_id, f_path=f_path) + ) - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) self.forbid_non_head(is_head, f_path, commit_id=commit_id) self.check_branch_permission(_branch_name, commit_id=commit_id) - c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path) + c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path) c.f_path = f_path old_content = c.file.str_content sl = old_content.splitlines(1) - first_line = sl[0] if sl else '' + first_line = sl[0] if sl else "" r_post = self.request.POST # line endings: 0 - Unix, 1 - Mac, 2 - DOS line_ending_mode = detect_mode(first_line, 0) - content = convert_line_endings(r_post.get('content', ''), line_ending_mode) + content = convert_line_endings(r_post.get("content", ""), line_ending_mode) - message = r_post.get('message') or c.default_message + message = r_post.get("message") or c.default_message org_node_path = c.file.str_path - filename = r_post['filename'] + filename = r_post["filename"] root_path = c.file.dir_path pure_path = self.create_pure_path(root_path, filename) node_path = pure_path.as_posix() - default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name, - commit_id=commit_id) + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit_id) if content == old_content and node_path == org_node_path: - h.flash(_('No changes detected on {}').format(h.escape(org_node_path)), - category='warning') + h.flash(_("No changes detected on {}").format(h.escape(org_node_path)), category="warning") raise HTTPFound(default_redirect_url) try: mapping = { c.file.bytes_path: { - 'org_filename': org_node_path, - 'filename': safe_bytes(node_path), - 'content': safe_bytes(content), - 'lexer': '', - 'op': 'mod', - 'mode': c.file.mode + "org_filename": org_node_path, + "filename": safe_bytes(node_path), + "content": safe_bytes(content), + "lexer": "", + "op": "mod", + "mode": c.file.mode, } } @@ -1318,19 +1272,17 @@ class RepoFilesView(RepoAppView): parent_commit=c.commit, ) - h.flash(_('Successfully committed changes to file `{}`').format( - h.escape(f_path)), category='success') - default_redirect_url = h.route_path( - 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id) + h.flash(_("Successfully committed changes to file `{}`").format(h.escape(f_path)), category="success") + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id) except Exception: - log.exception('Error occurred during commit') - h.flash(_('Error occurred during commit'), category='error') + log.exception("Error occurred during commit") + h.flash(_("Error occurred during commit"), category="error") raise HTTPFound(default_redirect_url) @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") def repo_files_add_file(self): _ = self.request.translate c = self.load_default_context() @@ -1339,7 +1291,7 @@ class RepoFilesView(RepoAppView): self._ensure_not_locked() # Check if we need to use this page to upload binary - upload_binary = str2bool(self.request.params.get('upload_binary', False)) + upload_binary = str2bool(self.request.params.get("upload_binary", False)) c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False) if c.commit is None: @@ -1348,24 +1300,27 @@ class RepoFilesView(RepoAppView): if self.rhodecode_vcs_repo.is_empty(): # for empty repository we cannot check for current branch, we rely on # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True + _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True else: - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) self.forbid_non_head(is_head, f_path, commit_id=commit_id) self.check_branch_permission(_branch_name, commit_id=commit_id) - c.default_message = (_('Added file via RhodeCode Enterprise')) \ - if not upload_binary else (_('Edited file {} via RhodeCode Enterprise').format(f_path)) - c.f_path = f_path.lstrip('/') # ensure not relative path + c.default_message = ( + (_("Added file via RhodeCode Enterprise")) + if not upload_binary + else (_("Edited file {} via RhodeCode Enterprise").format(f_path)) + ) + c.f_path = f_path.lstrip("/") # ensure not relative path c.replace_binary = upload_binary return self._get_template_context(c) @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") @CSRFRequired() def repo_files_create_file(self): _ = self.request.translate @@ -1380,56 +1335,48 @@ class RepoFilesView(RepoAppView): # calculate redirect URL if self.rhodecode_vcs_repo.is_empty(): - default_redirect_url = h.route_path( - 'repo_summary', repo_name=self.db_repo_name) + default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name) else: - default_redirect_url = h.route_path( - 'repo_commit', repo_name=self.db_repo_name, commit_id='tip') + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") if self.rhodecode_vcs_repo.is_empty(): # for empty repository we cannot check for current branch, we rely on # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True + _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True else: - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) self.forbid_non_head(is_head, f_path, commit_id=commit_id) self.check_branch_permission(_branch_name, commit_id=commit_id) - c.default_message = (_('Added file via RhodeCode Enterprise')) + c.default_message = _("Added file via RhodeCode Enterprise") c.f_path = f_path r_post = self.request.POST - message = r_post.get('message') or c.default_message - filename = r_post.get('filename') + message = r_post.get("message") or c.default_message + filename = r_post.get("filename") unix_mode = 0 if not filename: # If there's no commit, redirect to repo summary if type(c.commit) is EmptyCommit: - redirect_url = h.route_path( - 'repo_summary', repo_name=self.db_repo_name) + redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name) else: redirect_url = default_redirect_url - h.flash(_('No filename specified'), category='warning') + h.flash(_("No filename specified"), category="warning") raise HTTPFound(redirect_url) root_path = f_path pure_path = self.create_pure_path(root_path, filename) - node_path = pure_path.as_posix().lstrip('/') + node_path = pure_path.as_posix().lstrip("/") author = self._rhodecode_db_user.full_contact - content = convert_line_endings(r_post.get('content', ''), unix_mode) - nodes = { - safe_bytes(node_path): { - 'content': safe_bytes(content) - } - } + content = convert_line_endings(r_post.get("content", ""), unix_mode) + nodes = {safe_bytes(node_path): {"content": safe_bytes(content)}} try: - commit = ScmModel().create_nodes( user=self._rhodecode_db_user.user_id, repo=self.db_repo, @@ -1439,26 +1386,27 @@ class RepoFilesView(RepoAppView): author=author, ) - h.flash(_('Successfully committed new file `{}`').format(h.escape(node_path)), category='success') + h.flash(_("Successfully committed new file `{}`").format(h.escape(node_path)), category="success") - default_redirect_url = h.route_path( - 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id) + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id) except NonRelativePathError: - log.exception('Non Relative path found') - h.flash(_('The location specified must be a relative path and must not ' - 'contain .. in the path'), category='warning') + log.exception("Non Relative path found") + h.flash( + _("The location specified must be a relative path and must not " "contain .. in the path"), + category="warning", + ) raise HTTPFound(default_redirect_url) except (NodeError, NodeAlreadyExistsError) as e: - h.flash(h.escape(safe_str(e)), category='error') + h.flash(h.escape(safe_str(e)), category="error") except Exception: - log.exception('Error occurred during commit') - h.flash(_('Error occurred during commit'), category='error') + log.exception("Error occurred during commit") + h.flash(_("Error occurred during commit"), category="error") raise HTTPFound(default_redirect_url) @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") @CSRFRequired() def repo_files_upload_file(self): _ = self.request.translate @@ -1473,65 +1421,52 @@ class RepoFilesView(RepoAppView): # calculate redirect URL if self.rhodecode_vcs_repo.is_empty(): - default_redirect_url = h.route_path( - 'repo_summary', repo_name=self.db_repo_name) + default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name) else: - default_redirect_url = h.route_path( - 'repo_commit', repo_name=self.db_repo_name, commit_id='tip') + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") if self.rhodecode_vcs_repo.is_empty(): # for empty repository we cannot check for current branch, we rely on # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True + _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True else: - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) error = self.forbid_non_head(is_head, f_path, json_mode=True) if error: - return { - 'error': error, - 'redirect_url': default_redirect_url - } + return {"error": error, "redirect_url": default_redirect_url} error = self.check_branch_permission(_branch_name, json_mode=True) if error: - return { - 'error': error, - 'redirect_url': default_redirect_url - } + return {"error": error, "redirect_url": default_redirect_url} - c.default_message = (_('Added file via RhodeCode Enterprise')) + c.default_message = _("Added file via RhodeCode Enterprise") c.f_path = f_path r_post = self.request.POST message = c.default_message - user_message = r_post.getall('message') + user_message = r_post.getall("message") if isinstance(user_message, list) and user_message: # we take the first from duplicated results if it's not empty message = user_message[0] if user_message[0] else message nodes = {} - for file_obj in r_post.getall('files_upload') or []: + for file_obj in r_post.getall("files_upload") or []: content = file_obj.file filename = file_obj.filename root_path = f_path pure_path = self.create_pure_path(root_path, filename) - node_path = pure_path.as_posix().lstrip('/') + node_path = pure_path.as_posix().lstrip("/") - nodes[safe_bytes(node_path)] = { - 'content': content - } + nodes[safe_bytes(node_path)] = {"content": content} if not nodes: - error = 'missing files' - return { - 'error': error, - 'redirect_url': default_redirect_url - } + error = "missing files" + return {"error": error, "redirect_url": default_redirect_url} author = self._rhodecode_db_user.full_contact @@ -1545,49 +1480,35 @@ class RepoFilesView(RepoAppView): author=author, ) if len(nodes) == 1: - flash_message = _('Successfully committed {} new files').format(len(nodes)) + flash_message = _("Successfully committed {} new files").format(len(nodes)) else: - flash_message = _('Successfully committed 1 new file') + flash_message = _("Successfully committed 1 new file") - h.flash(flash_message, category='success') + h.flash(flash_message, category="success") - default_redirect_url = h.route_path( - 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id) + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id) except NonRelativePathError: - log.exception('Non Relative path found') - error = _('The location specified must be a relative path and must not ' - 'contain .. in the path') - h.flash(error, category='warning') + log.exception("Non Relative path found") + error = _("The location specified must be a relative path and must not " "contain .. in the path") + h.flash(error, category="warning") - return { - 'error': error, - 'redirect_url': default_redirect_url - } + return {"error": error, "redirect_url": default_redirect_url} except (NodeError, NodeAlreadyExistsError) as e: error = h.escape(e) - h.flash(error, category='error') + h.flash(error, category="error") - return { - 'error': error, - 'redirect_url': default_redirect_url - } + return {"error": error, "redirect_url": default_redirect_url} except Exception: - log.exception('Error occurred during commit') - error = _('Error occurred during commit') - h.flash(error, category='error') - return { - 'error': error, - 'redirect_url': default_redirect_url - } + log.exception("Error occurred during commit") + error = _("Error occurred during commit") + h.flash(error, category="error") + return {"error": error, "redirect_url": default_redirect_url} - return { - 'error': None, - 'redirect_url': default_redirect_url - } + return {"error": None, "redirect_url": default_redirect_url} @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @HasRepoPermissionAnyDecorator("repository.write", "repository.admin") @CSRFRequired() def repo_files_replace_file(self): _ = self.request.translate @@ -1601,64 +1522,50 @@ class RepoFilesView(RepoAppView): c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias) if self.rhodecode_vcs_repo.is_empty(): - default_redirect_url = h.route_path( - 'repo_summary', repo_name=self.db_repo_name) + default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name) else: - default_redirect_url = h.route_path( - 'repo_commit', repo_name=self.db_repo_name, commit_id='tip') + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") if self.rhodecode_vcs_repo.is_empty(): # for empty repository we cannot check for current branch, we rely on # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True + _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True else: - _branch_name, _sha_commit_id, is_head = \ - self._is_valid_head(commit_id, self.rhodecode_vcs_repo, - landing_ref=self.db_repo.landing_ref_name) + _branch_name, _sha_commit_id, is_head = self._is_valid_head( + commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name + ) error = self.forbid_non_head(is_head, f_path, json_mode=True) if error: - return { - 'error': error, - 'redirect_url': default_redirect_url - } + return {"error": error, "redirect_url": default_redirect_url} error = self.check_branch_permission(_branch_name, json_mode=True) if error: - return { - 'error': error, - 'redirect_url': default_redirect_url - } + return {"error": error, "redirect_url": default_redirect_url} - c.default_message = (_('Edited file {} via RhodeCode Enterprise').format(f_path)) + c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path) c.f_path = f_path r_post = self.request.POST message = c.default_message - user_message = r_post.getall('message') + user_message = r_post.getall("message") if isinstance(user_message, list) and user_message: # we take the first from duplicated results if it's not empty message = user_message[0] if user_message[0] else message - data_for_replacement = r_post.getall('files_upload') or [] + data_for_replacement = r_post.getall("files_upload") or [] if (objects_count := len(data_for_replacement)) > 1: - return { - 'error': 'too many files for replacement', - 'redirect_url': default_redirect_url - } + return {"error": "too many files for replacement", "redirect_url": default_redirect_url} elif not objects_count: - return { - 'error': 'missing files', - 'redirect_url': default_redirect_url - } + return {"error": "missing files", "redirect_url": default_redirect_url} content = data_for_replacement[0].file retrieved_filename = data_for_replacement[0].filename - if retrieved_filename.split('.')[-1] != f_path.split('.')[-1]: + if retrieved_filename.split(".")[-1] != f_path.split(".")[-1]: return { - 'error': 'file extension of uploaded file doesn\'t match an original file\'s extension', - 'redirect_url': default_redirect_url + "error": "file extension of uploaded file doesn't match an original file's extension", + "redirect_url": default_redirect_url, } author = self._rhodecode_db_user.full_contact @@ -1669,36 +1576,26 @@ class RepoFilesView(RepoAppView): repo=self.db_repo, message=message, node={ - 'content': content, - 'file_path': f_path.encode(), + "content": content, + "file_path": f_path.encode(), }, parent_commit=c.commit, author=author, ) - h.flash(_('Successfully committed 1 new file'), category='success') + h.flash(_("Successfully committed 1 new file"), category="success") - default_redirect_url = h.route_path( - 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id) + default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id) except (NodeError, NodeAlreadyExistsError) as e: error = h.escape(e) - h.flash(error, category='error') + h.flash(error, category="error") - return { - 'error': error, - 'redirect_url': default_redirect_url - } + return {"error": error, "redirect_url": default_redirect_url} except Exception: - log.exception('Error occurred during commit') - error = _('Error occurred during commit') - h.flash(error, category='error') - return { - 'error': error, - 'redirect_url': default_redirect_url - } + log.exception("Error occurred during commit") + error = _("Error occurred during commit") + h.flash(error, category="error") + return {"error": error, "redirect_url": default_redirect_url} - return { - 'error': None, - 'redirect_url': default_redirect_url - } + return {"error": None, "redirect_url": default_redirect_url} diff --git a/rhodecode/lib/rc_cache/__init__.py b/rhodecode/lib/rc_cache/__init__.py index bbb9c6ec..37a08d37 100644 --- a/rhodecode/lib/rc_cache/__init__.py +++ b/rhodecode/lib/rc_cache/__init__.py @@ -55,7 +55,7 @@ register_backend( log = logging.getLogger(__name__) -FILE_TREE_CACHE_VER = 'v5' +FILE_TREE_CACHE_VER = 'v6' LICENSE_CACHE_VER = 'v3' PERMISSIONS_CACHE_VER = 'v2' diff --git a/rhodecode/lib/utils2.py b/rhodecode/lib/utils2.py index 11918581..12378723 100644 --- a/rhodecode/lib/utils2.py +++ b/rhodecode/lib/utils2.py @@ -153,6 +153,7 @@ def remove_prefix(s, prefix): def find_calling_context(ignore_modules=None, depth=4, output_writer=None, indent=True): """ + How to find calling context: Look through the calling stack and return the frame which called this function and is part of core module ( ie. rhodecode.* ) diff --git a/rhodecode/lib/vcs/backends/git/commit.py b/rhodecode/lib/vcs/backends/git/commit.py index 2b658135..72c37634 100644 --- a/rhodecode/lib/vcs/backends/git/commit.py +++ b/rhodecode/lib/vcs/backends/git/commit.py @@ -295,19 +295,16 @@ class GitCommit(base.BaseCommit): raise CommitError(f"Directory does not exist for commit {self.raw_id} at '{path}'") path = self._fix_path(path) - # call and check tree_id for this path - tree_id, _ = self._get_path_tree_id_and_type(path) - path_nodes = [] - for bytes_name, stat_, tree_item_id, node_kind in self._remote.tree_items(tree_id): + for obj_name, stat_, tree_item_id, node_kind, pre_load_data in self._remote.get_nodes(self.raw_id, path, pre_load): if node_kind is None: raise CommitError(f"Requested object type={node_kind} cannot be determined") - if path != b"": - obj_path = b"/".join((path, bytes_name)) + if path == b"": + obj_path = obj_name else: - obj_path = bytes_name + obj_path = b"/".join((path, obj_name)) # cache file mode for git, since we have it already if obj_path not in self._path_mode_cache: @@ -322,12 +319,12 @@ class GitCommit(base.BaseCommit): entry = self.nodes[obj_path] else: if node_kind == NodeKind.SUBMODULE: - url = self._get_submodule_url(b"/".join((path, bytes_name))) - entry= SubModuleNode(bytes_name, url=url, commit=tree_item_id, alias=self.repository.alias) + url = self._get_submodule_url(obj_path) + entry= SubModuleNode(obj_name, url=url, commit=tree_item_id, alias=self.repository.alias) elif node_kind == NodeKind.DIR: entry = DirNode(safe_bytes(obj_path), commit=self) elif node_kind == NodeKind.FILE: - entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load) + entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load_data=pre_load_data) if entry: self.nodes[obj_path] = entry diff --git a/rhodecode/lib/vcs/backends/hg/commit.py b/rhodecode/lib/vcs/backends/hg/commit.py index 57b206ff..d215a1aa 100644 --- a/rhodecode/lib/vcs/backends/hg/commit.py +++ b/rhodecode/lib/vcs/backends/hg/commit.py @@ -274,7 +274,7 @@ class MercurialCommit(base.BaseCommit): path_nodes = [] - for obj_path, (node_kind, flags) in self._remote.dir_items(self.raw_id, path): + for obj_path, node_kind, flags, pre_load_data in self._remote.get_nodes(self.raw_id, path, pre_load): if node_kind is None: raise CommitError(f"Requested object type={node_kind} cannot be mapped to a proper type") @@ -295,7 +295,7 @@ class MercurialCommit(base.BaseCommit): if node_kind == NodeKind.DIR: entry = DirNode(safe_bytes(obj_path), commit=self) elif node_kind == NodeKind.FILE: - entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load) + entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load, pre_load_data=pre_load_data) if entry: self.nodes[obj_path] = entry path_nodes.append(entry) diff --git a/rhodecode/lib/vcs/backends/svn/commit.py b/rhodecode/lib/vcs/backends/svn/commit.py index 4d26e731..8ad6bb0d 100644 --- a/rhodecode/lib/vcs/backends/svn/commit.py +++ b/rhodecode/lib/vcs/backends/svn/commit.py @@ -201,8 +201,8 @@ class SubversionCommit(base.BaseCommit): path = self._fix_path(path) path_nodes = [] - for name, node_kind in self._remote.get_nodes(self._svn_rev, path): - obj_path = vcspath.join(path, name) + + for obj_path, node_kind, pre_load_data in self._remote.get_nodes(self._svn_rev, path, pre_load): if node_kind is None: raise CommitError(f"Requested object type={node_kind} cannot be determined") @@ -224,7 +224,7 @@ class SubversionCommit(base.BaseCommit): if node_kind == NodeKind.DIR: entry = nodes.DirNode(safe_bytes(obj_path), commit=self) elif node_kind == NodeKind.FILE: - entry = nodes.FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load) + entry = nodes.FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load, pre_load_data=pre_load_data) if entry: self.nodes[obj_path] = entry path_nodes.append(entry) diff --git a/rhodecode/lib/vcs/nodes.py b/rhodecode/lib/vcs/nodes.py index b5b0139d..ff8cbca0 100644 --- a/rhodecode/lib/vcs/nodes.py +++ b/rhodecode/lib/vcs/nodes.py @@ -241,7 +241,7 @@ class FileNode(Node): _filter_pre_load = [] - def __init__(self, path: bytes, content: bytes | None = None, commit=None, mode=None, pre_load=None): + def __init__(self, path: bytes, content: bytes | None = None, commit=None, mode=None, pre_load=None, pre_load_data=None): """ Only one of ``content`` and ``commit`` may be given. Passing both would raise ``NodeError`` exception. @@ -263,8 +263,10 @@ class FileNode(Node): content = safe_bytes(content) self._content = content self._mode = mode or FILEMODE_DEFAULT - - self._set_bulk_properties(pre_load) + if pre_load_data: + self._store_pre_load(pre_load_data) + else: + self._set_bulk_properties(pre_load) def __eq__(self, other): eq = super().__eq__(other) @@ -296,7 +298,10 @@ class FileNode(Node): remote = self.commit.get_remote() result = remote.bulk_file_request(self.commit.raw_id, self.bytes_path, pre_load) - for attr, value in result.items(): + self._store_pre_load(result.items()) + + def _store_pre_load(self, pre_load_data): + for attr, value in pre_load_data: if attr == "flags": self.__dict__["mode"] = safe_str(value) elif attr == "size": diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py index 059c8795..aadb697b 100644 --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -1141,7 +1141,7 @@ class ReadmeFinder: self._default_renderer = default_renderer self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(default_renderer, []) - def search(self, commit, path=b'/'): + def search(self, commit, path=b'/', nodes=None): """ Find a readme in the given `commit`. """ @@ -1150,7 +1150,9 @@ class ReadmeFinder: if commit.get_node(bytes_path).kind != NodeKind.DIR: return None - nodes = commit.get_nodes(bytes_path) + if not nodes: + nodes = commit.get_nodes(bytes_path) + matches = self._match_readmes(nodes) matches = self._sort_according_to_priority(matches) if matches: diff --git a/rhodecode/templates/files/files_browser_tree.mako b/rhodecode/templates/files/files_browser_tree.mako index 5e718a2d..ef0ade52 100644 --- a/rhodecode/templates/files/files_browser_tree.mako +++ b/rhodecode/templates/files/files_browser_tree.mako @@ -38,7 +38,7 @@ http://docker-dev:10020/ipython/files/master/IPython/frontend/html/notebook/stat <% has_files = False %> % if not c.file.is_submodule(): - % for cnt, node in enumerate(c.file): + % for cnt, node in enumerate(c.file_nodes): <% has_files = True %> @@ -105,6 +105,8 @@ http://docker-dev:10020/ipython/files/master/IPython/frontend/html/notebook/stat ${h.escape(c.file.name)}
    ${c.file.url}
    + % else: +
    ${_('Empty directory')}
    %endif diff --git a/rhodecode/tests/vcs/test_git.py b/rhodecode/tests/vcs/test_git.py index d9d40405..9f3c2a54 100644 --- a/rhodecode/tests/vcs/test_git.py +++ b/rhodecode/tests/vcs/test_git.py @@ -1218,19 +1218,21 @@ class TestGetSubmoduleUrl(object): def test_get_nodes_returns_links(self): repository = mock.MagicMock() repository.alias = "git" - repository._remote.tree_items.return_value = [(b"subrepo", "stat", 1, NodeKind.SUBMODULE)] + # obj_name, stat_, tree_item_id, node_kind, pre_load_data + repository._remote.get_nodes.return_value = [(b"subrepo", "stat", 1, NodeKind.SUBMODULE, [])] commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1) submodule_url = "https://code.rhodecode.com/dulwich" + get_id_patch = mock.patch.object(commit, "_get_path_tree_id_and_type", return_value=(1, NodeKind.DIR)) get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url) with get_id_patch, get_submodule_patch as submodule_mock: nodes = commit.get_nodes(b"/abcde") - submodule_mock.assert_called_once_with(b"/abcde/subrepo") assert len(nodes) == 1 assert type(nodes[0]) == SubModuleNode assert nodes[0].url == submodule_url + submodule_mock.assert_called_once_with(b"/abcde/subrepo") class TestGetShadowInstance(object): From a1989bf442319a2328b16f1cae6fdba5ae057fa6 Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Wed, 11 Dec 2024 15:11:51 +0200 Subject: [PATCH 90/99] fix: removed change status dropdown for observers. Fixes: RCCE-171 --- docs/release-notes/release-notes-5.4.0.rst | 1 + rhodecode/apps/repository/views/repo_pull_requests.py | 3 +++ rhodecode/templates/pullrequests/pullrequest_show.mako | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/docs/release-notes/release-notes-5.4.0.rst b/docs/release-notes/release-notes-5.4.0.rst index d4766e3c..2443fb0a 100644 --- a/docs/release-notes/release-notes-5.4.0.rst +++ b/docs/release-notes/release-notes-5.4.0.rst @@ -39,6 +39,7 @@ Fixes - Fixed UI visualization issues with long commit messages - Fixed masking for some fields in admin settings - Fixed issues with caches calculations when passed search fields were empty +- Fixed observer's ability to change a PR status Upgrade notes ^^^^^^^^^^^^^ diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py index 6df293b5..ee434ec7 100644 --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -487,7 +487,9 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView): c.pull_request_set_reviewers_data_json = ext_json.str_json(c.pull_request_set_reviewers_data_json) # observers + observer_ids = [] for observer_obj, member in pull_request_at_ver.observers(): + observer_ids.append(observer_obj.user_id) member_observer = h.reviewer_as_json( member, reasons=[], mandatory=False, role=observer_obj.role, @@ -497,6 +499,7 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView): c.pull_request_set_observers_data_json['observers'].append(member_observer) c.pull_request_set_observers_data_json = ext_json.str_json(c.pull_request_set_observers_data_json) + c.status_change_disabled = self._rhodecode_user.user_id in observer_ids general_comments, inline_comments = \ self.register_comments_vars(c, pull_request_latest, versions) diff --git a/rhodecode/templates/pullrequests/pullrequest_show.mako b/rhodecode/templates/pullrequests/pullrequest_show.mako index 64c81f9f..14388051 100644 --- a/rhodecode/templates/pullrequests/pullrequest_show.mako +++ b/rhodecode/templates/pullrequests/pullrequest_show.mako @@ -1054,6 +1054,16 @@ $(document).ready(function () { new ReviewerPresenceController(channel) // register globally so inject comment logic can re-use it. window.commentsController = commentsController; + + // hiding status change dropdown for observers + const isHidden = ${'true' if c.status_change_disabled else 'false'}; + + if (isHidden) { + const element = document.getElementById("s2id_change_status_general"); + if (element) { + element.style.display = "none"; + } + } }) From 279e84da82c34011431b61eade0d526adbb306cd Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Thu, 5 Dec 2024 16:33:07 +0200 Subject: [PATCH 91/99] fix(tests): added ability for test to get an up-to-date version mercurial. --- .../tests/vcs_operations/test_vcs_operations_bad_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py index 960b2c9f..ead09b20 100644 --- a/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_bad_client.py @@ -21,6 +21,7 @@ Test suite for making push/pull operations, on specially modified INI files """ import pytest +from importlib.metadata import version from rhodecode.model.meta import Session from rhodecode.model.settings import SettingsModel @@ -65,7 +66,8 @@ class TestVCSOperationsOnUsingBadClient(object): # push fails repo is locked by other user ! push_url = rcstack.repo_clone_url(HG_REPO) stdout, stderr = _add_files_and_push("hg", tmpdir.strpath, clone_url=push_url) - msg = "Your hg client (version=mercurial/proto-1.0 (Mercurial 6.7.4)) is forbidden by security rules" + current_version = version('mercurial') + msg = f"Your hg client (version=mercurial/proto-1.0 (Mercurial {current_version})) is forbidden by security rules" assert msg in stderr def test_push_with_bad_client_repo_by_other_user_git(self, rcstack, tmpdir): From 88107421c849d0562f63627b9f174734eb2ef988 Mon Sep 17 00:00:00 2001 From: Andrii Verbytskyi Date: Fri, 13 Dec 2024 17:47:26 +0100 Subject: [PATCH 92/99] docs: updated docs for importing repos and fixed howto on building them --- docs/.howto | 2 +- docs/install/migrate-repos.rst | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/.howto b/docs/.howto index c31bcf35..bab3a292 100644 --- a/docs/.howto +++ b/docs/.howto @@ -3,4 +3,4 @@ # docker build --tag sphinx-doc-build-rc . # Build Docs -# docker run --rm -v $(PWD):/project --workdir=/project/docs sphinx-doc-build-rc make clean html \ No newline at end of file +# cd && docker run --rm -v $(pwd):/project --workdir=/project/docs sphinx-doc-build-rc make clean html \ No newline at end of file diff --git a/docs/install/migrate-repos.rst b/docs/install/migrate-repos.rst index 8e5f6f59..79bd0f60 100644 --- a/docs/install/migrate-repos.rst +++ b/docs/install/migrate-repos.rst @@ -2,22 +2,24 @@ Migrating |repos| ----------------- +When you install |RCE| you will be presented with 2 choices: -If you have installed |RCE| and have |repos| that you wish to migrate into -the system, use the following instructions. +1. Mount existing folder on your local filesystem, and this is what |RCE| will use to store your repos. In this case |RCE| will automatically import repos present in that folder. You can also do :ref:`remap-rescan` from admin interface to force new repos discovery. +2. Use docker named volume ``rc_reposvolume`` - in this case you can either set create new |repo| in |RCE| UI and set it as a remote for your local repo, and then push. If your repo is hosted somewhere else, click "Import Existing Repository ?" and provide url to it. |RCE| will attempt to pull it and import. -1. On the |RCE| interface, check your |repo| storage location under - :menuselection:`Admin --> Settings --> System Info`. For example, - Storage location: /home/{username}/repos. - -2. Copy the |repos| that you want |RCE| to manage to this location. -3. Remap and rescan the |repos|, see :ref:`remap-rescan` .. important:: + Repo storage path inside docker is configurable in ``rhodecode.ini`` file: + ``repo_store.path = /var/opt/rhodecode_repo_store`` - Directories create |repo| groups inside |RCE|. + Mounting of local filesystem folder is configurable in ``docker-compose-apps.override.yaml`` - Importing adds |RCE| git hooks to your |repos|. + Importing adds |RCE| git hooks to your |repos|. - You should verify if custom ``.hg`` or ``.hgrc`` files inside - repositories should be adjusted since |RCE| reads the content of them. + You should verify if custom ``.hg`` or ``.hgrc`` files inside + repositories should be adjusted since |RCE| reads the content of them. + +.. note:: + Theres another override file for dev setup, which mounts default workspace folder as a repo storage by default. + you can configure this by ``changing docker-compose-apps.dev.yaml`` + (and commenting out all lines ``$WORKSPACE_HOME:/var/opt/rhodecode_repo_store:delegated``) From 15f793c8604edd9158c7db5e9fb5d751b5d57a85 Mon Sep 17 00:00:00 2001 From: Andrii Verbytskyi Date: Mon, 16 Dec 2024 16:25:30 +0100 Subject: [PATCH 93/99] docs: fixed typos in docs --- docs/install/migrate-repos.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/install/migrate-repos.rst b/docs/install/migrate-repos.rst index 79bd0f60..c8d14834 100644 --- a/docs/install/migrate-repos.rst +++ b/docs/install/migrate-repos.rst @@ -20,6 +20,6 @@ When you install |RCE| you will be presented with 2 choices: repositories should be adjusted since |RCE| reads the content of them. .. note:: - Theres another override file for dev setup, which mounts default workspace folder as a repo storage by default. - you can configure this by ``changing docker-compose-apps.dev.yaml`` + There's another override file for dev setup, which mounts default workspace folder as a repo storage by default. + you can configure this by changing ``docker-compose-apps.dev.yaml`` (and commenting out all lines ``$WORKSPACE_HOME:/var/opt/rhodecode_repo_store:delegated``) From be9dd9b1e790018c72c0fd3a91a18906be3dc779 Mon Sep 17 00:00:00 2001 From: Andrii Verbytskyi Date: Mon, 16 Dec 2024 16:27:46 +0100 Subject: [PATCH 94/99] docs: added newline --- docs/.howto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.howto b/docs/.howto index bab3a292..5bae66a5 100644 --- a/docs/.howto +++ b/docs/.howto @@ -3,4 +3,4 @@ # docker build --tag sphinx-doc-build-rc . # Build Docs -# cd && docker run --rm -v $(pwd):/project --workdir=/project/docs sphinx-doc-build-rc make clean html \ No newline at end of file +# cd && docker run --rm -v $(pwd):/project --workdir=/project/docs sphinx-doc-build-rc make clean html From 9b2d3e0f5686ceba97c54af7408eabc9cebdd73c Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Thu, 21 Nov 2024 14:57:09 +0200 Subject: [PATCH 95/99] fixed import --- rhodecode/model/comment.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py index 15c77d50..f65197d2 100644 --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -25,12 +25,13 @@ import logging import traceback import collections -from pyramid.threadlocal import get_current_registry, get_current_request +from pyramid.threadlocal import get_current_registry from sqlalchemy.sql.expression import null from sqlalchemy.sql.functions import coalesce from rhodecode.lib import helpers as h, diffs, channelstream, hooks_utils from rhodecode.lib import audit_logger +from rhodecode.lib.pyramid_utils import get_current_request from rhodecode.lib.exceptions import CommentVersionMismatch from rhodecode.lib.utils2 import extract_mentioned_users, safe_str, safe_int from rhodecode.model import BaseModel From 3e31d2a638a3447a5f5dd1c2a2f0d4b9eef52ed6 Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Thu, 21 Nov 2024 16:43:38 +0200 Subject: [PATCH 96/99] added delay before doing an auto merge --- rhodecode/subscribers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py index 7b410bcc..9f5b3399 100644 --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -81,7 +81,7 @@ def auto_merge_pr_if_needed(event): scm=pull_request.target_repo.repo_type) from rc_ee.lib.celerylib.tasks import auto_merge_repo auto_merge_repo.apply_async( - args=(pull_request.pull_request_id, extras) + args=(pull_request.pull_request_id, extras), countdown=3 ) From 2863a29a8b0819a0cf7b592af4676ede9aa016c4 Mon Sep 17 00:00:00 2001 From: Andrii Verbytskyi Date: Tue, 17 Dec 2024 10:07:16 +0100 Subject: [PATCH 97/99] release: added 2 ee-only features to release notes --- docs/release-notes/release-notes-5.4.0.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/release-notes/release-notes-5.4.0.rst b/docs/release-notes/release-notes-5.4.0.rst index 2443fb0a..4a76437c 100644 --- a/docs/release-notes/release-notes-5.4.0.rst +++ b/docs/release-notes/release-notes-5.4.0.rst @@ -13,6 +13,8 @@ New Features - RhodeCode and VcsServer operate now on bytestrings instead of strings for diffs and files. This allows a mixed or non-utf8 encoded files to be used. - Remap and rescan: added more resilient remap and removal option, and also split the logic to either add or cleanup. +- Automatic merge of approved pull requests (available only for enterprise edition). +- Basic implementation of security scan (secrets, keys, passwords, etc) for repositories (available only for enterprise edition). General ^^^^^^^ From a5e5325b847163bfbc6e6994873510de8d6a12ac Mon Sep 17 00:00:00 2001 From: Serhii Ilin Date: Wed, 18 Dec 2024 09:51:23 +0200 Subject: [PATCH 98/99] fix: changed imports. --- rhodecode/lib/helpers.py | 2 +- rhodecode/model/notification.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py index 3bd9f2c1..767dcd51 100644 --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -51,7 +51,6 @@ from pygments.formatters.html import HtmlFormatter from pygments.lexers import ( get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype) -from pyramid.threadlocal import get_current_request from tempita import looper from webhelpers2.html import literal, HTML, escape from webhelpers2.html._autolink import _auto_link_urls @@ -89,6 +88,7 @@ from rhodecode.lib.utils2 import ( str2bool, get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, AttributeDict, safe_int, md5, md5_safe, get_host_info) +from rhodecode.lib.pyramid_utils import get_current_request from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py index de95c674..1fe9f606 100644 --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -25,11 +25,11 @@ import logging import traceback import premailer -from pyramid.threadlocal import get_current_request from sqlalchemy.sql.expression import false, true import rhodecode from rhodecode.lib import helpers as h +from rhodecode.lib.pyramid_utils import get_current_request from rhodecode.model import BaseModel from rhodecode.model.db import Notification, User, UserNotification from rhodecode.model.meta import Session From 2433f3fbfc8e07e7e9e58b3eacf49adc975df87e Mon Sep 17 00:00:00 2001 From: Andrii Verbytskyi Date: Tue, 24 Dec 2024 18:40:18 +0100 Subject: [PATCH 99/99] chore: disabled auto-merge feature until next bugfix release --- rhodecode/subscribers.py | 52 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py index 9f5b3399..e74dfa63 100644 --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -58,31 +58,33 @@ def add_renderer_globals(event): def auto_merge_pr_if_needed(event): - from rhodecode.model.db import PullRequest - from rhodecode.model.pull_request import ( - PullRequestModel, ChangesetStatus, MergeCheck - ) - - pr_event_data = event.as_dict()['pullrequest'] - pull_request = PullRequest.get(pr_event_data['pull_request_id']) - calculated_status = pr_event_data['status'] - if (calculated_status == ChangesetStatus.STATUS_APPROVED - and PullRequestModel().is_automatic_merge_enabled(pull_request)): - user = pull_request.author.AuthUser() - - merge_check = MergeCheck.validate( - pull_request, user, translator=lambda x: x, fail_early=True - ) - if merge_check.merge_possible: - from rhodecode.lib.base import vcs_operation_context - extras = vcs_operation_context( - event.request.environ, repo_name=pull_request.target_repo.repo_name, - username=user.username, action='push', - scm=pull_request.target_repo.repo_type) - from rc_ee.lib.celerylib.tasks import auto_merge_repo - auto_merge_repo.apply_async( - args=(pull_request.pull_request_id, extras), countdown=3 - ) + #TODO To be re-enabled later + pass + # from rhodecode.model.db import PullRequest + # from rhodecode.model.pull_request import ( + # PullRequestModel, ChangesetStatus, MergeCheck + # ) + # + # pr_event_data = event.as_dict()['pullrequest'] + # pull_request = PullRequest.get(pr_event_data['pull_request_id']) + # calculated_status = pr_event_data['status'] + # if (calculated_status == ChangesetStatus.STATUS_APPROVED + # and PullRequestModel().is_automatic_merge_enabled(pull_request)): + # user = pull_request.author.AuthUser() + # + # merge_check = MergeCheck.validate( + # pull_request, user, translator=lambda x: x, fail_early=True + # ) + # if merge_check.merge_possible: + # from rhodecode.lib.base import vcs_operation_context + # extras = vcs_operation_context( + # event.request.environ, repo_name=pull_request.target_repo.repo_name, + # username=user.username, action='push', + # scm=pull_request.target_repo.repo_type) + # from rc_ee.lib.celerylib.tasks import auto_merge_repo + # auto_merge_repo.apply_async( + # args=(pull_request.pull_request_id, extras), countdown=3 + # ) def set_user_lang(event):