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/Makefile b/Makefile
index 9967d2ee..f90e046e 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,25 @@ 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
+
+.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/configs/development.ini b/configs/development.ini
index b0f38a8d..129cadcf 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,40 @@ 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-
+
+; *********************************************
+; `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
; ##############
@@ -547,7 +589,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..4312e3ed 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,40 @@ 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-
+
+; *********************************************
+; `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
; ##############
@@ -515,7 +557,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/conftest.py b/conftest.py
index f731dbc6..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
@@ -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/docs/.howto b/docs/.howto
index c31bcf35..5bae66a5 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
diff --git a/docs/install/migrate-repos.rst b/docs/install/migrate-repos.rst
index 8e5f6f59..c8d14834 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::
+ 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``)
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-5.4.0.rst b/docs/release-notes/release-notes-5.4.0.rst
new file mode 100644
index 00000000..4a76437c
--- /dev/null
+++ b/docs/release-notes/release-notes-5.4.0.rst
@@ -0,0 +1,49 @@
+|RCE| 5.4.0 |RNS|
+-----------------
+
+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.
+- 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
+^^^^^^^
+
+- Ported build system to use pyproject.toml.
+- Bumped version of mercurial and hg-evolve.
+
+
+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
+- Fixed observer's ability to change a PR status
+
+Upgrade notes
+^^^^^^^^^^^^^
+
+
diff --git a/docs/release-notes/release-notes.rst b/docs/release-notes/release-notes.rst
index ae3723f6..88767374 100644
--- a/docs/release-notes/release-notes.rst
+++ b/docs/release-notes/release-notes.rst
@@ -9,6 +9,8 @@ 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
release-notes-5.2.0.rst
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/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:
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..bd7c559f
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,137 @@
+[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.11',
+]
+
+[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]
+# Line length max same as Black.
+line-length = 120
+
+# Assume Python 3.11
+target-version = "py311"
+
+
+[tool.ruff.lint]
+select = [
+ # Pyflakes
+ "F",
+ # Pycodestyle
+ "E",
+ "W",
+ # isort
+ "I001"
+]
+
+ignore = [
+ "E501", # line too long, handled by black
+]
+
+[tool.ruff.lint.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/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/requirements.txt b/requirements.txt
index d3c228b9..43947508 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,39 +1,39 @@
# 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.0.3
+ greenlet==3.1.1
typing_extensions==4.12.2
async-timeout==4.0.3
babel==2.12.1
-beaker==1.12.1
-celery==5.3.6
- billiard==4.2.0
- click==8.1.3
- click-didyoumean==0.3.0
- click==8.1.3
+beaker==1.13.0
+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
+ click==8.1.7
+ click-repl==0.3.0
+ click==8.1.7
+ prompt_toolkit==3.0.48
wcwidth==0.2.13
- six==1.16.0
- kombu==5.3.5
+ kombu==5.4.2
amqp==5.2.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.1
+ tzdata==2024.2
vine==5.1.0
channelstream==0.7.1
- gevent==24.2.1
- greenlet==3.0.3
+ gevent==24.11.1
+ greenlet==3.1.1
zope.event==5.0.0
- zope.interface==7.0.3
+ zope.interface==7.1.1
itsdangerous==1.1.0
marshmallow==2.18.0
pyramid==2.0.2
@@ -43,14 +43,14 @@ channelstream==0.7.1
pastedeploy==3.1.0
plaster==1.1.2
translationstring==1.4
- venusian==3.0.0
- webob==1.8.7
+ venusian==3.1.0
+ webob==1.8.9
zope.deprecation==5.0.0
- zope.interface==7.0.3
+ zope.interface==7.1.1
pyramid-jinja2==2.10
- jinja2==3.1.2
- markupsafe==2.1.2
- markupsafe==2.1.2
+ jinja2==3.1.4
+ markupsafe==3.0.2
+ markupsafe==3.0.2
pyramid==2.0.2
hupper==1.12
plaster==1.1.2
@@ -58,18 +58,18 @@ channelstream==0.7.1
pastedeploy==3.1.0
plaster==1.1.2
translationstring==1.4
- venusian==3.0.0
- webob==1.8.7
+ venusian==3.1.0
+ webob==1.8.9
zope.deprecation==5.0.0
- zope.interface==7.0.3
+ 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
- charset-normalizer==3.1.0
- idna==3.4
- urllib3==1.26.14
+ 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
@@ -87,13 +87,13 @@ 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.2.1
- greenlet==3.0.3
+gevent==24.11.1
+ greenlet==3.1.1
zope.event==5.0.0
- zope.interface==7.0.3
+ zope.interface==7.1.1
ipython==8.26.0
decorator==5.1.1
jedi==0.19.1
@@ -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
@@ -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
@@ -122,23 +122,23 @@ nbconvert==7.7.3
six==1.16.0
webencodings==0.5.1
defusedxml==0.7.1
- jinja2==3.1.2
- markupsafe==2.1.2
+ jinja2==3.1.4
+ 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
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
+ pyzmq==26.2.0
+ tornado==6.4.2
traitlets==5.14.3
jupyter_core==5.3.1
platformdirs==3.10.0
@@ -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
@@ -174,20 +174,20 @@ premailer==3.10.0
cssselect==1.2.0
cssutils==2.6.0
lxml==5.3.0
- requests==2.28.2
- certifi==2022.12.7
- charset-normalizer==3.1.0
- idna==3.4
- urllib3==1.26.14
+ 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.9
+psycopg2==2.9.10
py-bcrypt==0.4
pycmarkgfm==1.2.0
- cffi==1.16.0
- pycparser==2.21
-pycryptodome==3.17
+ cffi==1.17.1
+ pycparser==2.22
+pycryptodome==3.21.0
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
@@ -198,19 +198,19 @@ pyramid-mailer==0.15.1
pastedeploy==3.1.0
plaster==1.1.2
translationstring==1.4
- venusian==3.0.0
- webob==1.8.7
+ venusian==3.1.0
+ webob==1.8.9
zope.deprecation==5.0.0
- zope.interface==7.0.3
+ zope.interface==7.1.1
repoze.sendmail==4.4.1
transaction==5.0.0
- zope.interface==7.0.3
- zope.interface==7.0.3
+ zope.interface==7.1.1
+ zope.interface==7.1.1
transaction==5.0.0
- zope.interface==7.0.3
+ zope.interface==7.1.1
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
@@ -218,16 +218,15 @@ pyramid-mako==1.1.0
pastedeploy==3.1.0
plaster==1.1.2
translationstring==1.4
- venusian==3.0.0
- webob==1.8.7
+ venusian==3.1.0
+ webob==1.8.9
zope.deprecation==5.0.0
- zope.interface==7.0.3
+ zope.interface==7.1.1
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
@@ -236,60 +235,66 @@ 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
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.11.4
+ aiohappyeyeballs==2.4.3
aiosignal==1.3.1
- frozenlist==1.4.1
- attrs==22.2.0
- frozenlist==1.4.1
- multidict==6.0.5
- yarl==1.9.4
- idna==3.4
- multidict==6.0.5
- aioitertools==0.11.0
- botocore==1.34.106
+ frozenlist==1.5.0
+ attrs==24.2.0
+ frozenlist==1.5.0
+ multidict==6.1.0
+ propcache==0.2.0
+ yarl==1.17.2
+ idna==3.10
+ multidict==6.1.0
+ propcache==0.2.0
+ aioitertools==0.12.0
+ botocore==1.35.36
jmespath==1.0.1
- python-dateutil==2.8.2
+ python-dateutil==2.9.0.post0
six==1.16.0
- urllib3==1.26.14
+ urllib3==1.26.20
wrapt==1.16.0
- aiohttp==3.9.5
+ aiohttp==3.11.4
+ aiohappyeyeballs==2.4.3
aiosignal==1.3.1
- frozenlist==1.4.1
- attrs==22.2.0
- frozenlist==1.4.1
- multidict==6.0.5
- yarl==1.9.4
- idna==3.4
- multidict==6.0.5
- fsspec==2024.9.0
+ frozenlist==1.5.0
+ attrs==24.2.0
+ frozenlist==1.5.0
+ multidict==6.1.0
+ propcache==0.2.0
+ yarl==1.17.2
+ idna==3.10
+ multidict==6.1.0
+ propcache==0.2.0
+ fsspec==2024.10.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.0.3
+ greenlet==3.1.1
typing_extensions==4.12.2
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
+unidecode==1.3.8
urlobject==2.4.3
-waitress==3.0.0
+waitress==3.0.1
webhelpers2==2.1
- markupsafe==2.1.2
+ markupsafe==3.0.2
six==1.16.0
whoosh==2.7.4
zope.cachedescriptors==5.0.0
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
diff --git a/requirements_rc_tools.txt b/requirements_rc_tools.txt
index c6c0eb11..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-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-51fb13c9-6175-480c-b9dc-630c7601d47c.1.0.tar.gz#egg=rhodecode-tools
+rhodecode-tools==4.1.0
diff --git a/requirements_test.txt b/requirements_test.txt
index 1e7cbcc6..7a111054 100644
--- a/requirements_test.txt
+++ b/requirements_test.txt
@@ -1,47 +1,54 @@
# 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
-webtest==3.0.0
+ pluggy==1.5.0
+
+webtest==3.0.1
beautifulsoup4==4.12.3
soupsieve==2.5
- waitress==3.0.0
- webob==1.8.7
+ 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
diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py
index 8754f00a..732b082b 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
@@ -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/__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..dcd34619 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
@@ -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/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_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_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_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 41b4bf77..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
@@ -24,7 +23,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_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 be373a42..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
@@ -27,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
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..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
@@ -26,7 +25,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..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
@@ -26,7 +25,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..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
@@ -25,7 +24,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_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 f24b1a90..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
@@ -28,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_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_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..09375f4c 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
@@ -20,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
@@ -37,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/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..36f2401f 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
@@ -20,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)
@@ -31,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/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 f978b9aa..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
@@ -25,8 +24,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/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..60ee6b64 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
@@ -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/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..150e4660 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
@@ -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,20 +190,69 @@ 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):
+ """
+ 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.
+
+ This command takes the following options:
+
+ :param apiuser: This is filled automatically from the |authtoken|.
+ :type apiuser: AuthUser
+
+ Example output:
+
+ .. code-block:: bash
+
+ id :
+ result : {
+ 'removed': [,...]
+ 'errors': [,...]
+ }
+ error : null
+
+ Example error output:
+
+ .. code-block:: bash
+
+ id :
+ result : null
+ error : {
+ '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:
+ removed, errors = repo2db_cleanup()
+ return {'removed': removed, 'errors': errors}
+ except Exception:
+ log.exception('Failed to run repo storage cleanup')
+ raise JSONRPCError(
+ 'Error occurred during repo storage cleanup action'
+ )
+
@jsonrpc_method()
def cleanup_sessions(request, apiuser, older_then=Optional(60)):
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..be7428bb 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
@@ -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/_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..838e1065 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
@@ -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_audit_logs.py b/rhodecode/apps/admin/tests/test_admin_audit_logs.py
index da127e3d..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
@@ -26,7 +25,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_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 b0b75eab..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
@@ -20,7 +19,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_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 014a79b7..18a4927f 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
@@ -37,7 +36,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
@@ -111,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/tests/test_admin_repository_groups.py b/rhodecode/apps/admin/tests/test_admin_repository_groups.py
index 1f94bce2..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
@@ -27,7 +26,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_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 c6dbe1d0..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
@@ -24,7 +23,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..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
@@ -28,7 +27,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..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
@@ -22,7 +21,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/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 4f226076..7f7c4abc 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
@@ -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
@@ -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(
@@ -232,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')
@@ -246,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/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 b7d6e269..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
@@ -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..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
@@ -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..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
@@ -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..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
@@ -40,7 +21,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..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
@@ -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/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_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..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
@@ -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..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
@@ -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/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 5923c751..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
@@ -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_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 ba4f542b..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
@@ -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..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
@@ -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..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
@@ -24,7 +23,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..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
@@ -20,7 +19,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..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
@@ -19,7 +18,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..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
@@ -21,7 +20,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/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 8ba459eb..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
@@ -22,7 +21,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 +165,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 +200,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_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 01301c8f..945b68aa 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
@@ -33,7 +32,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
@@ -42,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'
"""
- 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]:
@@ -250,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]:
@@ -265,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:
@@ -361,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"
@@ -486,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:
@@ -514,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:
@@ -535,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):
@@ -895,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):
@@ -1126,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)
@@ -1147,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/apps/repository/tests/test_repo_forks.py b/rhodecode/apps/repository/tests/test_repo_forks.py
index 978b86ff..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
@@ -21,7 +20,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_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 8b812084..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
@@ -21,7 +20,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_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 1df532a3..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
@@ -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..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
@@ -26,7 +25,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..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
@@ -24,7 +23,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..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
@@ -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_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 cd41d528..2e5307f4 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
@@ -30,7 +29,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
@@ -48,6 +47,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 +59,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/apps/repository/tests/test_vcs_settings.py b/rhodecode/apps/repository/tests/test_vcs_settings.py
index 72d9a07c..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
@@ -32,16 +31,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/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..d5878a25 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
@@ -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_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..76acd50f 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
@@ -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_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..0e3f0878 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
@@ -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,66 +140,61 @@ 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)
- return commit_id, f_path
- def _get_default_encoding(self, c):
- enc_list = getattr(c, 'default_encodings', [])
- return enc_list[0] if enc_list else 'UTF-8'
+ 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"
def _get_commit_or_redirect(self, commit_id, redirect_after=True):
"""
@@ -213,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):
@@ -250,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
@@ -301,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
@@ -341,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)
@@ -354,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()
- 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'))
- at_path = self.request.GET.get('at_path') or default_at_path
+
+ 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'))
+ 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)
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(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)))
+ 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)
@@ -421,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=fileformat, 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)
@@ -462,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:
@@ -470,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(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')
+ raise NodeError(f"{node} path is a {type(node)} not a file")
except NodeDoesNotExistError:
commit = EmptyCommit(
commit_id=commit_id,
@@ -495,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)
@@ -543,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
@@ -589,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()
@@ -627,52 +629,51 @@ 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 = 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)
+ 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(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 = ''
+ 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)
+ "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.url_prev = "#"
c.prev_commit = EmptyCommit()
# next link
@@ -680,110 +681,101 @@ class RepoFilesView(RepoAppView):
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)
+ "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.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, f_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, 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')
+ 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)
- prev_commit_id = history[1].raw_id \
- if len(history) > 1 else prev_commit_id
+ 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,
- _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,
@@ -792,22 +784,22 @@ 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))}')
+ 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)
@@ -816,15 +808,12 @@ 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)
@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",
@@ -832,25 +821,24 @@ 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"
# 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
@@ -860,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.
@@ -869,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()
@@ -887,16 +875,15 @@ 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()
- 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'):
+ 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()
@@ -919,49 +906,41 @@ class RepoFilesView(RepoAppView):
return response
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`'
- '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)
+ 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()
- @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 = 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(
- 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 = []
@@ -978,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):
"""
@@ -996,37 +975,34 @@ 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"))
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)
@@ -1035,14 +1011,13 @@ 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()
- 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)
@@ -1051,52 +1026,38 @@ 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()
- 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()
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
@@ -1107,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:
@@ -1120,204 +1081,186 @@ 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 = 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)
+ 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
+ )
- 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 /
- 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 {
- '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 = 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 = \
- 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)
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
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
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 = \
- 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)
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
- 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 = 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 = \
- 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.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(
- '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
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,
- 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,
}
}
@@ -1329,28 +1272,26 @@ 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()
- commit_id, f_path = self._get_commit_and_path()
+ commit_id, f_path, bytes_path = self._get_commit_and_path()
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:
@@ -1359,29 +1300,32 @@ 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
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()
@@ -1391,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,
@@ -1450,32 +1386,32 @@ 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
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()
@@ -1485,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
@@ -1557,54 +1480,40 @@ 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
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()
@@ -1613,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
@@ -1681,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/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..ee434ec7 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
@@ -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/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 ede5ad3a..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
@@ -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/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 ca27890f..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
@@ -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__)
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 843bec16..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
@@ -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..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
@@ -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..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
@@ -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/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 b2b5cae4..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
@@ -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/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 51096e70..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
@@ -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_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 c471adef..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
@@ -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/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/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/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 64cd3a72..1b1b8bf4 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
@@ -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')
@@ -162,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')
diff --git a/rhodecode/config/environment.py b/rhodecode/config/environment.py
index 1c699e65..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
@@ -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/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 c82395cd..f0b6eacc 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
@@ -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
@@ -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
@@ -392,6 +395,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/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 a4b661c4..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
@@ -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/__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 093de20d..3dbf7284 100644
--- a/rhodecode/config/rcextensions/helpers/extra_fields.py
+++ b/rhodecode/config/rcextensions/helpers/extra_fields.py
@@ -1,5 +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
@@ -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..5beac048 100644
--- a/rhodecode/config/rcextensions/helpers/extract_post_commits.py
+++ b/rhodecode/config/rcextensions/helpers/extract_post_commits.py
@@ -1,5 +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 b2c1df2b..589da689 100644
--- a/rhodecode/config/rcextensions/helpers/extract_pre_commits.py
+++ b/rhodecode/config/rcextensions/helpers/extract_pre_commits.py
@@ -1,5 +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
@@ -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..0839e211 100644
--- a/rhodecode/config/rcextensions/helpers/extract_pre_files.py
+++ b/rhodecode/config/rcextensions/helpers/extract_pre_files.py
@@ -1,5 +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
@@ -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..cf7c2a28 100644
--- a/rhodecode/config/rcextensions/helpers/http_call.py
+++ b/rhodecode/config/rcextensions/helpers/http_call.py
@@ -1,5 +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 2f31a873..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
@@ -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..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
@@ -49,22 +48,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/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 17236b25..c2d1fbf8 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
@@ -19,6 +19,8 @@
import os
import platform
+from rhodecode.lib.type_utils import str2bool
+
DEFAULT_USER = 'default'
@@ -37,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']
@@ -48,28 +49,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/events/__init__.py b/rhodecode/events/__init__.py
index 05903cbd..f82cb4ec 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
@@ -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 9496ed79..019c2ca6 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
@@ -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 9285f628..14120d13 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
@@ -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 e3e31cc4..9551e0a8 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
@@ -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 3794dd45..757d0a2f 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
@@ -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 ed56c91d..9b28c937 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
@@ -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 360b91be..afcaa3ef 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
@@ -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
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..0a475ae2 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
@@ -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/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 93c95ef1..2b2a3caf 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
@@ -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, \
@@ -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'
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..ff310961 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
@@ -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/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 d4cd6c17..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
@@ -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/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..767dcd51 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
@@ -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/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/base.py b/rhodecode/lib/hook_daemon/base.py
index d29dbec4..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_config
+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,48 +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
- callback_daemon = CeleryHooksCallbackDaemon(get_config(extras['config']))
- 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.')
+ def _call_hook(self, hook, extras):
+ extras = AttributeDict(extras)
+ _server_url = extras['server_url']
- 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()
+ 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()
+
+ 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
- # 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 636f27ad..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
@@ -22,12 +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, 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, 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})'
+ 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..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
@@ -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..bb10ee9f 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
@@ -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,68 @@ 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 +171,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 +215,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 +223,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 +238,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 +287,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 +308,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 +363,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/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 f709a646..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
@@ -40,16 +39,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 +140,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/simplehg.py b/rhodecode/lib/middleware/simplehg.py
index 5100f589..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
@@ -22,6 +21,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 +32,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 +96,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 +119,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:
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 b3f7c230..664ee739 100644
--- a/rhodecode/lib/middleware/simplevcs.py
+++ b/rhodecode/lib/middleware/simplevcs.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
@@ -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/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..37a08d37 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
@@ -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/rc_cache/backends.py b/rhodecode/lib/rc_cache/backends.py
index b4f64aa5..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
@@ -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,
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_cache/utils.py b/rhodecode/lib/rc_cache/utils.py
index 5d0c39f4..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):
@@ -174,7 +172,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 +189,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
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..97aa6bd5 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
@@ -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/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 11331c06..0d332b27 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
@@ -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':
@@ -564,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)
@@ -598,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,
@@ -615,93 +627,151 @@ 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)
- return added, removed
+ # 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
+
+
+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 +811,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 +856,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
- from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
+ try:
+ import rc_testdata
+ except ImportError:
+ raise ImportError('Failed to import rc_testdata, '
+ 'please make sure this package is installed from requirements_test.txt')
- log.debug('making test vcs repositories')
+ from rhodecode.bootstrap import HG_REPO, GIT_REPO, SVN_REPO
- 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/utils2.py b/rhodecode/lib/utils2.py
index 4030f998..12378723 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
@@ -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/__init__.py b/rhodecode/lib/vcs/__init__.py
index da7de79b..5c3aabd3 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
@@ -139,8 +139,8 @@ class CurlSession(object):
try:
curl.perform()
- except pycurl.error as exc:
- log.error('Failed to call endpoint url: {} using pycurl'.format(url))
+ except pycurl.error:
+ 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/__init__.py b/rhodecode/lib/vcs/backends/__init__.py
index f357fb33..1a56567e 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
@@ -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/lib/vcs/backends/base.py b/rhodecode/lib/vcs/backends/base.py
index b06e5914..a44e7621 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
@@ -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/__init__.py b/rhodecode/lib/vcs/backends/git/__init__.py
index a2632c07..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
@@ -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/git/commit.py b/rhodecode/lib/vcs/backends/git/commit.py
index b6a76dfc..72c37634 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
@@ -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,102 @@ 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)
+ path_nodes = []
- dirnodes = []
- filenodes = []
+ 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")
- # 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
-
- if str_path != '':
- obj_path = '/'.join((str_path, name))
+ if path == b"":
+ obj_path = obj_name
else:
- obj_path = name
- if obj_path not in self._stat_modes:
- self._stat_modes[obj_path] = stat_
+ obj_path = b"/".join((path, obj_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(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_data=pre_load_data)
- 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 +386,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 +413,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 +442,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/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..f3c4c4d7 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
@@ -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.
@@ -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
diff --git a/rhodecode/lib/vcs/backends/hg/__init__.py b/rhodecode/lib/vcs/backends/hg/__init__.py
index 60ebd19b..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
@@ -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/backends/hg/commit.py b/rhodecode/lib/vcs/backends/hg/commit.py
index 2033efca..d215a1aa 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
@@ -19,21 +19,26 @@
"""
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,92 @@ 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, flags, pre_load_data in self._remote.get_nodes(self.raw_id, path, pre_load):
- 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
+ stat_ = flags
+ # 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, pre_load_data=pre_load_data)
+ if entry:
+ 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
+
+ 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 +377,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/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 8b2330e1..ecd65ac2 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
@@ -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):
@@ -444,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`.
@@ -592,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/__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..8ad6bb0d 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
@@ -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 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")
+
+ # 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, pre_load_data=pre_load_data)
+ 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/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..97e91066 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
@@ -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 195d854e..59ed0110 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
@@ -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
@@ -216,7 +215,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'
@@ -287,13 +286,15 @@ 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',
]
- 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 +304,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/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/__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..6318cc9f 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
@@ -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/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..5af82684 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
@@ -102,10 +102,6 @@ class NodeError(VCSError):
pass
-class RemovedFileNodeError(NodeError):
- pass
-
-
class NodeAlreadyExistsError(CommittingError):
pass
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..ff8cbca0 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
@@ -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,9 +238,10 @@ 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):
+ 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.
@@ -349,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)
@@ -359,7 +275,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 +285,35 @@ 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():
+ 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)
+ 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 +322,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 +334,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 +345,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 +356,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 +380,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 +390,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 +414,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 +455,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 +490,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 +507,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 +516,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 +526,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 +538,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 +547,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 +564,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 +601,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 +622,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 +652,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 +665,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 +672,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 +682,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,37 +696,39 @@ 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.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.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
- # 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):
@@ -908,22 +736,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 +760,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 +768,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 +780,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/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/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/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 075c7efd..f65197d2 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
@@ -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
@@ -373,6 +374,7 @@ class CommentsModel(BaseModel):
Session().add(comment)
Session().flush()
+
kwargs = {
'user': user,
'renderer_type': renderer,
@@ -387,8 +389,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 +398,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 +487,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/db.py b/rhodecode/model/db.py
index 8c2466e3..d8cdbda7 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
@@ -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)
@@ -284,7 +300,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_):
@@ -1075,28 +1091,26 @@ class User(Base, BaseModel):
@classmethod
def get(cls, user_id, cache=False):
if not user_id:
- return
+ return None
- user = cls.query()
+ q = cls.select().where(cls.user_id == user_id)
if cache:
- user = user.options(
- FromCache("sql_cache_short", f"get_users_{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()
@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:
diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py
index bc82d930..22a89906 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
@@ -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/gist.py b/rhodecode/model/gist.py
index 3218306e..8127d205 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
@@ -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/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..1fe9f606 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
@@ -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
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 0848a826..ae7edbd5 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
@@ -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)
@@ -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:
@@ -980,9 +981,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
@@ -2125,6 +2124,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':
@@ -2225,8 +2228,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()
@@ -2287,12 +2289,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 af9fa47f..aadb697b 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
@@ -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 (
@@ -745,7 +746,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 +761,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):
"""
@@ -1102,47 +1110,49 @@ 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'/', nodes=None):
"""
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)
+ if not nodes:
+ nodes = commit.get_nodes(bytes_path)
+
matches = self._match_readmes(nodes)
matches = self._sort_according_to_priority(matches)
if matches:
@@ -1150,8 +1160,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
@@ -1161,7 +1171,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)
@@ -1171,28 +1181,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/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/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 30228e6b..c2f37890 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
@@ -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:
@@ -545,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']
@@ -616,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):
@@ -738,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):
@@ -776,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/model/settings.py b/rhodecode/model/settings.py
index 275cd868..1adc56ea 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
@@ -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',
@@ -862,27 +863,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/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 44ebe36b..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
@@ -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)
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/public/css/legacy_code_styles.less b/rhodecode/public/css/legacy_code_styles.less
index 8134be7e..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: white;
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;
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']);
diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py
index 4bf7f2b8..e74dfa63 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
@@ -57,6 +57,36 @@ def add_renderer_globals(event):
event['h'] = helpers
+def auto_merge_pr_if_needed(event):
+ #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):
request = event.request
cur_user = getattr(request, 'user', None)
@@ -121,7 +151,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/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() {
});
+
%def>
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})}
- ${_('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.')}
+ ${_('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.')}
+
${_('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}
+