##// END OF EJS Templates
release: version 5.4.0
andverb -
r5665:cdbc80b0 merge v5.4.0 stable
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,41
1 |RCE| 5.3.1 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2024-09-21
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18
19
20 Security
21 ^^^^^^^^
22
23
24
25 Performance
26 ^^^^^^^^^^^
27
28 - svn: small performance bump by restoring path cache in previous builds
29
30
31 Fixes
32 ^^^^^
33
34 - Fixed problems with incorrect version string
35 - Fixed accidentally removed svn path check cache
36
37
38 Upgrade notes
39 ^^^^^^^^^^^^^
40
41 - RhodeCode 5.3.1 is unscheduled security release to address some build issues with 5.X images
@@ -0,0 +1,49
1 |RCE| 5.4.0 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - RhodeCode and VcsServer operate now on bytestrings instead of strings for diffs and files. This allows a mixed or non-utf8
14 encoded files to be used.
15 - Remap and rescan: added more resilient remap and removal option, and also split the logic to either add or cleanup.
16 - Automatic merge of approved pull requests (available only for enterprise edition).
17 - Basic implementation of security scan (secrets, keys, passwords, etc) for repositories (available only for enterprise edition).
18
19 General
20 ^^^^^^^
21
22 - Ported build system to use pyproject.toml.
23 - Bumped version of mercurial and hg-evolve.
24
25
26 Security
27 ^^^^^^^^
28
29 - Multiple library bumps to fix security issues found in those python packages.
30
31
32 Performance
33 ^^^^^^^^^^^
34
35 - SVN: added same caches for stored file nodes as git and mercurial have for improved performance.
36 - GIT: moved certain operations into vcsserver
37
38 Fixes
39 ^^^^^
40
41 - Fixed UI visualization issues with long commit messages
42 - Fixed masking for some fields in admin settings
43 - Fixed issues with caches calculations when passed search fields were empty
44 - Fixed observer's ability to change a PR status
45
46 Upgrade notes
47 ^^^^^^^^^^^^^
48
49
@@ -0,0 +1,137
1 [build-system]
2 requires = ["setuptools>=61.0.0", "wheel", "pastescript"]
3 build-backend = "setuptools.build_meta"
4
5 [project]
6 name = "rhodecode-enterprise-ce"
7 description = "Enterprise Source Code Management Platform"
8 authors = [
9 {name = "RhodeCode GmbH", email = "support@rhodecode.com"},
10 ]
11 keywords = [
12 'rhodecode', 'mercurial', 'git', 'svn',
13 'code review',
14 'repo groups', 'ldap', 'repository management', 'hgweb',
15 'hgwebdir', 'gitweb', 'serving hgweb',
16 ]
17 license = {text = "GPL V3"}
18 requires-python = ">=3.10"
19 dynamic = ["version", "readme", "dependencies", "optional-dependencies"]
20 classifiers = [
21 'Development Status :: 6 - Mature',
22 'Intended Audience :: Developers',
23 'Operating System :: OS Independent',
24 'Topic :: Software Development :: Version Control',
25 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
26 'Programming Language :: Python :: 3.11',
27 ]
28
29 [project.entry-points."paste.app_factory"]
30 main = "rhodecode.config.middleware:make_pyramid_app"
31
32 [project.entry-points."pyramid.pshell_runner"]
33 ipython = "rhodecode.lib.pyramid_shell:ipython_shell_runner"
34
35 [project.entry-points."beaker.backends"]
36 memorylru_base="rhodecode.lib.memory_lru_dict:MemoryLRUNamespaceManagerBase"
37 memorylru_debug="rhodecode.lib.memory_lru_dict:MemoryLRUNamespaceManagerDebug"
38
39 [project.scripts]
40
41 rc-setup-app = "rhodecode.lib.rc_commands.setup_rc:main"
42 rc-upgrade-db = "rhodecode.lib.rc_commands.upgrade_db:main"
43 rc-ishell = "rhodecode.lib.rc_commands.ishell:main"
44 rc-add-artifact = "rhodecode.lib.rc_commands.add_artifact:main"
45 rc-migrate-artifact = "rhodecode.lib.rc_commands.migrate_artifact:main"
46 rc-ssh-wrapper = "rhodecode.apps.ssh_support.lib.ssh_wrapper_v1:main"
47 rc-ssh-wrapper-v2 = "rhodecode.apps.ssh_support.lib.ssh_wrapper_v2:main"
48
49 [tool.setuptools]
50 packages = ["rhodecode"]
51 include-package-data = true
52
53 [tools.setuptools.package-data]
54 "" = ['*.txt', '*.rst']
55 "configs" = ['*.ini']
56 "rhodecode" = ['VERSION', 'i18n/*/LC_MESSAGES/*.mo', ]
57
58 [tool.setuptools.exclude-package-data]
59 "rhodecode" = ["__pycache__"]
60
61 [tool.setuptools.dynamic]
62 readme = {file = ["README.rst"], content-type = "text/rst"}
63 version = {file = "rhodecode/VERSION"}
64 dependencies = {file = ["requirements.txt"]}
65 optional-dependencies.tests = {file = ["requirements_test.txt"]}
66
67 [tool.ruff]
68 # Line length max same as Black.
69 line-length = 120
70
71 # Assume Python 3.11
72 target-version = "py311"
73
74
75 [tool.ruff.lint]
76 select = [
77 # Pyflakes
78 "F",
79 # Pycodestyle
80 "E",
81 "W",
82 # isort
83 "I001"
84 ]
85
86 ignore = [
87 "E501", # line too long, handled by black
88 ]
89
90 [tool.ruff.lint.isort]
91 known-first-party = ["rhodecode"]
92
93 [tool.ruff.format]
94 # Like Black, use double quotes for strings.
95 quote-style = "double"
96
97 # Like Black, indent with spaces, rather than tabs.
98 indent-style = "space"
99
100 # Like Black, respect magic trailing commas.
101 skip-magic-trailing-comma = false
102
103 # Like Black, automatically detect the appropriate line ending.
104 line-ending = "auto"
105
106
107 [tool.bumpversion]
108 current_version = "5.4.0"
109 parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
110 serialize = ["{major}.{minor}.{patch}"]
111 search = "{current_version}"
112 replace = "{new_version}"
113 regex = false
114 ignore_missing_version = false
115 ignore_missing_files = false
116 tag = false
117 sign_tags = false
118 tag_name = "v{new_version}"
119 tag_message = "release(version-bump): {current_version} → {new_version}"
120 allow_dirty = false
121 commit = false
122 message = "release(version-bump): {current_version} → {new_version}"
123 commit_args = ""
124 setup_hooks = []
125 pre_commit_hooks = []
126 post_commit_hooks = []
127
128
129 # message_extractors={
130 # 'rhodecode': [
131 # ('**.py', 'python', None),
132 # ('**.js', 'javascript', None),
133 # ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
134 # ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
135 # ('public/**', 'ignore', None),
136 # ]
137 # },
@@ -0,0 +1,44
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19 import mock
20 import pytest
21
22 from rhodecode.model.scm import ScmModel
23 from rhodecode.api.tests.utils import (
24 build_data, api_call, assert_ok, assert_error, crash)
25
26
27 @pytest.mark.usefixtures("testuser_api", "app")
28 class TestCleanupRepos(object):
29 def test_api_cleanup_repos(self):
30 id_, params = build_data(self.apikey, 'cleanup_repos')
31 response = api_call(self.app, params)
32
33 expected = {'removed': [], 'errors': []}
34 assert_ok(id_, expected, given=response.body)
35
36 def test_api_cleanup_repos_error(self):
37
38 id_, params = build_data(self.apikey, 'cleanup_repos', )
39
40 with mock.patch('rhodecode.lib.utils.repo2db_cleanup', side_effect=crash):
41 response = api_call(self.app, params)
42
43 expected = 'Error occurred during repo storage cleanup action'
44 assert_error(id_, expected, given=response.body)
@@ -0,0 +1,41
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19 # bootstrap data available for tests and setup clean install
20
21 TEST_USER_ADMIN_LOGIN = 'test_admin'
22 TEST_USER_ADMIN_PASS = 'test12'
23 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
24
25 TEST_USER_REGULAR_LOGIN = 'test_regular'
26 TEST_USER_REGULAR_PASS = 'test12'
27 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
28
29 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
30 TEST_USER_REGULAR2_PASS = 'test12'
31 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
32
33 HG_REPO = 'vcs_test_hg'
34 GIT_REPO = 'vcs_test_git'
35 SVN_REPO = 'vcs_test_svn'
36
37 NEW_HG_REPO = 'vcs_test_hg_new'
38 NEW_GIT_REPO = 'vcs_test_git_new'
39
40 HG_FORK = 'vcs_test_hg_fork'
41 GIT_FORK = 'vcs_test_git_fork'
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -54,7 +54,7 clean:
54 54 # test: run test-clean and tests
55 55 test:
56 56 make test-clean
57 unset RC_SQLALCHEMY_DB1_URL && unset RC_DB_URL && make test-only
57 make test-only
58 58
59 59
60 60 .PHONY: test-clean
@@ -71,7 +71,25 test-only:
71 71 PYTHONHASHSEED=random \
72 72 py.test -x -vv -r xw -p no:sugar \
73 73 --cov-report=term-missing --cov-report=html \
74 --cov=rhodecode rhodecode
74 --cov=rhodecode rhodecode \
75 --ignore=rhodecode/tests/vcs_operations \
76 --ignore=rhodecode/tests/database
77
78 PYTHONHASHSEED=random \
79 py.test -x -vv -r xw -p no:sugar \
80 rhodecode/tests/vcs_operations
81
82 PYTHONHASHSEED=random \
83 py.test -x -vv -r xw -p no:sugar \
84 rhodecode/tests/database
85
86 .PHONY: test-simple
87 # test-simple: Run tests only for main test suite witout coverage
88 test-simple:
89 PYTHONHASHSEED=random \
90 py.test -x -vv -r xw -p no:sugar \
91 --ignore=rhodecode/tests/vcs_operations \
92 --ignore=rhodecode/tests/database
75 93
76 94 # >>> Docs commands
77 95
@@ -485,6 +485,10 rc_cache.cache_general.expiration_time =
485 485 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
486 486 #rc_cache.cache_general.arguments.lock_auto_renewal = true
487 487
488 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
489 #rc_cache.cache_general.arguments.key_prefix = custom-prefix-
490
491
488 492 ; *************************************************
489 493 ; `cache_perms` cache for permission tree, auth TTL
490 494 ; for simplicity use rc.file_namespace backend,
@@ -512,6 +516,10 rc_cache.cache_perms.expiration_time = 3
512 516 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
513 517 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
514 518
519 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
520 #rc_cache.cache_perms.arguments.key_prefix = custom-prefix-
521
522
515 523 ; ***************************************************
516 524 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
517 525 ; for simplicity use rc.file_namespace backend,
@@ -539,6 +547,40 rc_cache.cache_repo.expiration_time = 25
539 547 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
540 548 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
541 549
550 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
551 #rc_cache.cache_repo.arguments.key_prefix = custom-prefix-
552
553 ; *********************************************
554 ; `cache_license` cache for storing license info
555 ; for simplicity use rc.file_namespace backend,
556 ; for performance and scale use rc.redis
557 ; *********************************************
558 rc_cache.cache_license.backend = dogpile.cache.rc.file_namespace
559 rc_cache.cache_license.expiration_time = 300
560 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
561 #rc_cache.cache_license.arguments.filename = /tmp/cache_general_db
562
563 ; alternative `cache_license` redis backend with distributed lock
564 #rc_cache.cache_license.backend = dogpile.cache.rc.redis
565 #rc_cache.cache_license.expiration_time = 300
566
567 ; redis_expiration_time needs to be greater then expiration_time
568 #rc_cache.cache_license.arguments.redis_expiration_time = 360
569
570 #rc_cache.cache_license.arguments.host = localhost
571 #rc_cache.cache_license.arguments.port = 6379
572 #rc_cache.cache_license.arguments.db = 0
573 #rc_cache.cache_license.arguments.socket_timeout = 30
574 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
575 #rc_cache.cache_license.arguments.distributed_lock = true
576
577 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
578 #rc_cache.cache_license.arguments.lock_auto_renewal = true
579
580 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
581 #rc_cache.cache_license.arguments.key_prefix = custom-prefix-
582
583
542 584 ; ##############
543 585 ; BEAKER SESSION
544 586 ; ##############
@@ -547,7 +589,7 rc_cache.cache_repo.expiration_time = 25
547 589 ; types are file, ext:redis, ext:database, ext:memcached
548 590 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
549 591 #beaker.session.type = file
550 #beaker.session.data_dir = %(here)s/data/sessions
592 #beaker.session.data_dir = /var/opt/rhodecode_data/sessions
551 593
552 594 ; Redis based sessions
553 595 beaker.session.type = ext:redis
@@ -453,6 +453,10 rc_cache.cache_general.expiration_time =
453 453 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
454 454 #rc_cache.cache_general.arguments.lock_auto_renewal = true
455 455
456 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
457 #rc_cache.cache_general.arguments.key_prefix = custom-prefix-
458
459
456 460 ; *************************************************
457 461 ; `cache_perms` cache for permission tree, auth TTL
458 462 ; for simplicity use rc.file_namespace backend,
@@ -480,6 +484,10 rc_cache.cache_perms.expiration_time = 3
480 484 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
481 485 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
482 486
487 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
488 #rc_cache.cache_perms.arguments.key_prefix = custom-prefix-
489
490
483 491 ; ***************************************************
484 492 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
485 493 ; for simplicity use rc.file_namespace backend,
@@ -507,6 +515,40 rc_cache.cache_repo.expiration_time = 25
507 515 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
508 516 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
509 517
518 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
519 #rc_cache.cache_repo.arguments.key_prefix = custom-prefix-
520
521 ; *********************************************
522 ; `cache_license` cache for storing license info
523 ; for simplicity use rc.file_namespace backend,
524 ; for performance and scale use rc.redis
525 ; *********************************************
526 rc_cache.cache_license.backend = dogpile.cache.rc.file_namespace
527 rc_cache.cache_license.expiration_time = 300
528 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
529 #rc_cache.cache_license.arguments.filename = /tmp/cache_general_db
530
531 ; alternative `cache_license` redis backend with distributed lock
532 #rc_cache.cache_license.backend = dogpile.cache.rc.redis
533 #rc_cache.cache_license.expiration_time = 300
534
535 ; redis_expiration_time needs to be greater then expiration_time
536 #rc_cache.cache_license.arguments.redis_expiration_time = 360
537
538 #rc_cache.cache_license.arguments.host = localhost
539 #rc_cache.cache_license.arguments.port = 6379
540 #rc_cache.cache_license.arguments.db = 0
541 #rc_cache.cache_license.arguments.socket_timeout = 30
542 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
543 #rc_cache.cache_license.arguments.distributed_lock = true
544
545 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
546 #rc_cache.cache_license.arguments.lock_auto_renewal = true
547
548 ; prefix for redis keys used for this cache backend, the final key is constructed using {custom-prefix}{key}
549 #rc_cache.cache_license.arguments.key_prefix = custom-prefix-
550
551
510 552 ; ##############
511 553 ; BEAKER SESSION
512 554 ; ##############
@@ -515,7 +557,7 rc_cache.cache_repo.expiration_time = 25
515 557 ; types are file, ext:redis, ext:database, ext:memcached
516 558 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
517 559 #beaker.session.type = file
518 #beaker.session.data_dir = %(here)s/data/sessions
560 #beaker.session.data_dir = /var/opt/rhodecode_data/sessions
519 561
520 562 ; Redis based sessions
521 563 beaker.session.type = ext:redis
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -27,8 +27,11 from rhodecode.tests.conftest_common imp
27 27
28 28
29 29 pytest_plugins = [
30 "rhodecode.tests.fixture_mods.fixture_pyramid",
31 "rhodecode.tests.fixture_mods.fixture_utils",
30 "rhodecode.tests.fixtures.fixture_pyramid",
31 "rhodecode.tests.fixtures.fixture_utils",
32 "rhodecode.tests.fixtures.function_scoped_baseapp",
33 "rhodecode.tests.fixtures.module_scoped_baseapp",
34 "rhodecode.tests.fixtures.rcextensions_fixtures",
32 35 ]
33 36
34 37
@@ -3,4 +3,4
3 3 # docker build --tag sphinx-doc-build-rc .
4 4
5 5 # Build Docs
6 # docker run --rm -v $(PWD):/project --workdir=/project/docs sphinx-doc-build-rc make clean html No newline at end of file
6 # cd && docker run --rm -v $(pwd):/project --workdir=/project/docs sphinx-doc-build-rc make clean html
@@ -2,22 +2,24
2 2
3 3 Migrating |repos|
4 4 -----------------
5
6 If you have installed |RCE| and have |repos| that you wish to migrate into
7 the system, use the following instructions.
5 When you install |RCE| you will be presented with 2 choices:
8 6
9 1. On the |RCE| interface, check your |repo| storage location under
10 :menuselection:`Admin --> Settings --> System Info`. For example,
11 Storage location: /home/{username}/repos.
7 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.
8 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.
12 9
13 2. Copy the |repos| that you want |RCE| to manage to this location.
14 3. Remap and rescan the |repos|, see :ref:`remap-rescan`
15 10
16 11 .. important::
12 Repo storage path inside docker is configurable in ``rhodecode.ini`` file:
13 ``repo_store.path = /var/opt/rhodecode_repo_store``
17 14
18 Directories create |repo| groups inside |RCE|.
15 Mounting of local filesystem folder is configurable in ``docker-compose-apps.override.yaml``
16
17 Importing adds |RCE| git hooks to your |repos|.
19 18
20 Importing adds |RCE| git hooks to your |repos|.
19 You should verify if custom ``.hg`` or ``.hgrc`` files inside
20 repositories should be adjusted since |RCE| reads the content of them.
21 21
22 You should verify if custom ``.hg`` or ``.hgrc`` files inside
23 repositories should be adjusted since |RCE| reads the content of them.
22 .. note::
23 There's another override file for dev setup, which mounts default workspace folder as a repo storage by default.
24 you can configure this by changing ``docker-compose-apps.dev.yaml``
25 (and commenting out all lines ``$WORKSPACE_HOME:/var/opt/rhodecode_repo_store:delegated``)
@@ -9,6 +9,8 Release Notes
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-5.4.0.rst
13 release-notes-5.3.1.rst
12 14 release-notes-5.3.0.rst
13 15 release-notes-5.2.1.rst
14 16 release-notes-5.2.0.rst
@@ -8,4 +8,4 pygments==2.18.0
8 8
9 9 docutils<0.19
10 10 markupsafe==2.1.3
11 jinja2==3.1.2
11 jinja2==3.1.4
@@ -12,6 +12,26 The commands available with |RCT| can be
12 12 - Local configuration commands used to help set up your |RCT| configuration.
13 13
14 14
15 .. _tools-rhodecode-list-instance:
16
17 rhodecode-list-instances
18 ------------------------
19
20 Use this command to list the instance details configured in the
21 :file:`/etc/rhodecode/conf/.rhoderc` file.
22
23 .. code-block:: bash
24
25 $ ./rcstack cli cmd rhodecode-list-instances --config=/etc/rhodecode/conf/.rhoderc
26 [instance:production] - Config only
27 API-HOST: https://some.url.com
28 API-KEY: some.auth.token
29
30 [instance:development] - Config only
31 API-HOST: http://some.ip.address
32 API-KEY: some.auth.token
33
34
15 35 rhodecode-tools
16 36 ---------------
17 37
@@ -163,6 +183,7 Example usage:
163 183 removing gist /home/brian/repos/.rc_gist_store/5
164 184 removing gist /home/brian/repos/.rc_gist_store/8FtCKdcbRKmEvRzTVsEt
165 185
186
166 187 rhodecode-cleanup-repos
167 188 -----------------------
168 189
@@ -201,30 +222,37 Example usage:
201 222
202 223 .. code-block:: bash
203 224
204 # Cleaning up repos using tools installed with RCE 350 and above
205 $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-cleanup-repos \
206 --instance-name=enterprise-4 --older-than=1d
207 Scanning for repositories in /home/brian/repos...
208 preparing to remove [2] found repositories older than 1 day, 0:00:00 (1d)
225
226 # create a .rhoderc file in your host config directory (in :file:`config/_shared/.rhoderc`):
227
228 [instance:rcstack-instance]
229 api_host = http://rhodecode:10020
230 api_key = <API_KEY>
231 repo_dir = /var/opt/rhodecode_repo_store
232
233 # Run rcstack cli
234 ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc
235
236 checking if config files needs bootstrapping
237 Scanning for repositories in /var/opt/rhodecode_repo_store...
209 238
210 239 the following repositories will be deleted completely:
211 * REMOVED: 2015-08-05 00:23:18 | /home/brian/repos/rm__20150805_002318_831
212 * REMOVED: 2015-08-04 01:22:10 | /home/brian/repos/rm__20150804_012210_336
240 * REMOVED: 2015-08-05 00:23:18 | /var/opt/rhodecode_repo_store/rm__20150805_002318_831
241 * REMOVED: 2015-08-04 01:22:10 | /var/opt/rhodecode_repo_store/rm__20150804_012210_336
213 242 are you sure you want to remove them? [y/N]:
214 243
215 244 # Clean up repos older than 1 year
216 # If using virtualenv and pre RCE 350 tools installation
217 (venv)$ rhodecode-cleanup-repos --instance-name=enterprise-1 \
218 --older-than=365d
245 ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --older-than=365d
219 246
220 Scanning for repositories in /home/brian/repos...
247 checking if config files needs bootstrapping
248 Scanning for repositories in /var/opt/rhodecode_repo_store...
221 249 preparing to remove [343] found repositories older than 365 days
222 250
223 251 # clean up repos older than 3 days
224 # If using virtualenv and pre RCE 350 tools installation
225 (venv)$ rhodecode-cleanup-repos --instance-name=enterprise-1 \
226 --older-than=3d
227 Scanning for repositories in /home/brian/repos...
252 ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --older-than=3d
253
254 checking if config files needs bootstrapping
255 Scanning for repositories in /var/opt/rhodecode_repo_store...
228 256 preparing to remove [3] found repositories older than 3 days
229 257
230 258 .. _tools-config:
@@ -420,9 +448,8 Example usage:
420 448 }
421 449
422 450 # Cat a file and pipe to gist
423 # in RCE 3.5.0 tools and above
424 $ cat ~/.rhoderc | ~/.rccontrol/{instance-id}/profile/bin/rhodecode-gist \
425 --instance-name=enterprise-4 -d '.rhoderc copy' create
451
452 $ cat ~/.rhoderc | ./rcstack cli cmd rhodecode-gist --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc -d '.rhoderc copy' create
426 453 {
427 454 "error": null,
428 455 "id": 9253,
@@ -512,41 +539,18 Example usage:
512 539
513 540 .. code-block:: bash
514 541
515 # Run the indexer
516 $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-index \
517 --instance-name=enterprise-4
542 # Create the indexing mapping file
543 $ ./rcstack cli cmd rhodecode-index --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --create-mapping search_mapping.ini
518 544
519 # Run indexer based on search_mapping.ini file
520 # This is using pre-350 virtualenv
521 (venv)$ rhodecode-index --instance-name=enterprise-1
545 # Run the indexer
546 $ ./rcstack cli cmd rhodecode-index --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc
547
548 # Run indexer based on search_mapping.ini file using rhodecode-tools virtualenv
549 (venv)$ rhodecode-index --instance-name=rcstack-instance
522 550
523 551 # Index from the command line without creating
524 552 # the .rhoderc file
525 $ rhodecode-index --apikey=key --apihost=http://rhodecode.server \
526 --instance-name=enterprise-2 --save-config
527
528 # Create the indexing mapping file
529 $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-index \
530 --create-mapping search_mapping.ini --instance-name=enterprise-4
531
532 .. _tools-rhodecode-list-instance:
533
534 rhodecode-list-instances
535 ------------------------
536
537 Use this command to list the instance details configured in the
538 :file:`~/.rhoderc` file.
539
540 .. code-block:: bash
541
542 $ .rccontrol/enterprise-1/profile/bin/rhodecode-list-instances
543 [instance:production] - Config only
544 API-HOST: https://some.url.com
545 API-KEY: some.auth.token
546
547 [instance:development] - Config only
548 API-HOST: http://some.ip.address
549 API-KEY: some.auth.token
553 $ rhodecode-index --apikey=key --apihost=http://rhodecode.server --instance-name=rcstack-instance --save-config
550 554
551 555
552 556 .. _tools-setup-config:
@@ -4,8 +4,10 norecursedirs = rhodecode/public rhodeco
4 4 cache_dir = /tmp/.pytest_cache
5 5
6 6 pyramid_config = rhodecode/tests/rhodecode.ini
7 vcsserver_protocol = http
8 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
7
8 vcsserver_config = rhodecode/tests/vcsserver_http.ini
9 rhodecode_config = rhodecode/tests/rhodecode.ini
10 celery_config = rhodecode/tests/rhodecode.ini
9 11
10 12 addopts =
11 13 --pdbcls=IPython.terminal.debugger:TerminalPdb
@@ -1,39 +1,39
1 1 # deps, generated via pipdeptree --exclude setuptools,wheel,pipdeptree,pip -f | tr '[:upper:]' '[:lower:]'
2 2
3 3 alembic==1.13.1
4 mako==1.2.4
5 markupsafe==2.1.2
4 mako==1.3.6
5 markupsafe==3.0.2
6 6 sqlalchemy==1.4.52
7 greenlet==3.0.3
7 greenlet==3.1.1
8 8 typing_extensions==4.12.2
9 9 async-timeout==4.0.3
10 10 babel==2.12.1
11 beaker==1.12.1
12 celery==5.3.6
13 billiard==4.2.0
14 click==8.1.3
15 click-didyoumean==0.3.0
16 click==8.1.3
11 beaker==1.13.0
12 celery==5.4.0
13 billiard==4.2.1
14 click==8.1.7
15 click-didyoumean==0.3.1
16 click==8.1.7
17 17 click-plugins==1.1.1
18 click==8.1.3
19 click-repl==0.2.0
20 click==8.1.3
21 prompt_toolkit==3.0.47
18 click==8.1.7
19 click-repl==0.3.0
20 click==8.1.7
21 prompt_toolkit==3.0.48
22 22 wcwidth==0.2.13
23 six==1.16.0
24 kombu==5.3.5
23 kombu==5.4.2
25 24 amqp==5.2.0
26 25 vine==5.1.0
26 tzdata==2024.2
27 27 vine==5.1.0
28 python-dateutil==2.8.2
28 python-dateutil==2.9.0.post0
29 29 six==1.16.0
30 tzdata==2024.1
30 tzdata==2024.2
31 31 vine==5.1.0
32 32 channelstream==0.7.1
33 gevent==24.2.1
34 greenlet==3.0.3
33 gevent==24.11.1
34 greenlet==3.1.1
35 35 zope.event==5.0.0
36 zope.interface==7.0.3
36 zope.interface==7.1.1
37 37 itsdangerous==1.1.0
38 38 marshmallow==2.18.0
39 39 pyramid==2.0.2
@@ -43,14 +43,14 channelstream==0.7.1
43 43 pastedeploy==3.1.0
44 44 plaster==1.1.2
45 45 translationstring==1.4
46 venusian==3.0.0
47 webob==1.8.7
46 venusian==3.1.0
47 webob==1.8.9
48 48 zope.deprecation==5.0.0
49 zope.interface==7.0.3
49 zope.interface==7.1.1
50 50 pyramid-jinja2==2.10
51 jinja2==3.1.2
52 markupsafe==2.1.2
53 markupsafe==2.1.2
51 jinja2==3.1.4
52 markupsafe==3.0.2
53 markupsafe==3.0.2
54 54 pyramid==2.0.2
55 55 hupper==1.12
56 56 plaster==1.1.2
@@ -58,18 +58,18 channelstream==0.7.1
58 58 pastedeploy==3.1.0
59 59 plaster==1.1.2
60 60 translationstring==1.4
61 venusian==3.0.0
62 webob==1.8.7
61 venusian==3.1.0
62 webob==1.8.9
63 63 zope.deprecation==5.0.0
64 zope.interface==7.0.3
64 zope.interface==7.1.1
65 65 zope.deprecation==5.0.0
66 python-dateutil==2.8.2
66 python-dateutil==2.9.0.post0
67 67 six==1.16.0
68 requests==2.28.2
69 certifi==2022.12.7
70 charset-normalizer==3.1.0
71 idna==3.4
72 urllib3==1.26.14
68 requests==2.32.3
69 certifi==2024.8.30
70 charset-normalizer==3.4.0
71 idna==3.10
72 urllib3==1.26.20
73 73 ws4py==0.5.1
74 74 deform==2.0.15
75 75 chameleon==3.10.2
@@ -87,13 +87,13 dogpile.cache==1.3.3
87 87 pbr==5.11.1
88 88 formencode==2.1.0
89 89 six==1.16.0
90 fsspec==2024.9.0
90 fsspec==2024.10.0
91 91 gunicorn==23.0.0
92 92 packaging==24.1
93 gevent==24.2.1
94 greenlet==3.0.3
93 gevent==24.11.1
94 greenlet==3.1.1
95 95 zope.event==5.0.0
96 zope.interface==7.0.3
96 zope.interface==7.1.1
97 97 ipython==8.26.0
98 98 decorator==5.1.1
99 99 jedi==0.19.1
@@ -102,7 +102,7 ipython==8.26.0
102 102 traitlets==5.14.3
103 103 pexpect==4.9.0
104 104 ptyprocess==0.7.0
105 prompt_toolkit==3.0.47
105 prompt_toolkit==3.0.48
106 106 wcwidth==0.2.13
107 107 pygments==2.18.0
108 108 stack-data==0.6.3
@@ -113,7 +113,7 ipython==8.26.0
113 113 traitlets==5.14.3
114 114 typing_extensions==4.12.2
115 115 markdown==3.4.3
116 msgpack==1.0.8
116 msgpack==1.1.0
117 117 mysqlclient==2.1.1
118 118 nbconvert==7.7.3
119 119 beautifulsoup4==4.12.3
@@ -122,23 +122,23 nbconvert==7.7.3
122 122 six==1.16.0
123 123 webencodings==0.5.1
124 124 defusedxml==0.7.1
125 jinja2==3.1.2
126 markupsafe==2.1.2
125 jinja2==3.1.4
126 markupsafe==3.0.2
127 127 jupyter_core==5.3.1
128 128 platformdirs==3.10.0
129 129 traitlets==5.14.3
130 130 jupyterlab-pygments==0.2.2
131 markupsafe==2.1.2
131 markupsafe==3.0.2
132 132 mistune==2.0.5
133 133 nbclient==0.8.0
134 134 jupyter_client==8.3.0
135 135 jupyter_core==5.3.1
136 136 platformdirs==3.10.0
137 137 traitlets==5.14.3
138 python-dateutil==2.8.2
138 python-dateutil==2.9.0.post0
139 139 six==1.16.0
140 pyzmq==25.0.0
141 tornado==6.2
140 pyzmq==26.2.0
141 tornado==6.4.2
142 142 traitlets==5.14.3
143 143 jupyter_core==5.3.1
144 144 platformdirs==3.10.0
@@ -146,7 +146,7 nbconvert==7.7.3
146 146 nbformat==5.9.2
147 147 fastjsonschema==2.18.0
148 148 jsonschema==4.18.6
149 attrs==22.2.0
149 attrs==24.2.0
150 150 pyrsistent==0.19.3
151 151 jupyter_core==5.3.1
152 152 platformdirs==3.10.0
@@ -156,7 +156,7 nbconvert==7.7.3
156 156 nbformat==5.9.2
157 157 fastjsonschema==2.18.0
158 158 jsonschema==4.18.6
159 attrs==22.2.0
159 attrs==24.2.0
160 160 pyrsistent==0.19.3
161 161 jupyter_core==5.3.1
162 162 platformdirs==3.10.0
@@ -174,20 +174,20 premailer==3.10.0
174 174 cssselect==1.2.0
175 175 cssutils==2.6.0
176 176 lxml==5.3.0
177 requests==2.28.2
178 certifi==2022.12.7
179 charset-normalizer==3.1.0
180 idna==3.4
181 urllib3==1.26.14
177 requests==2.32.3
178 certifi==2024.8.30
179 charset-normalizer==3.4.0
180 idna==3.10
181 urllib3==1.26.20
182 182 psutil==5.9.8
183 psycopg2==2.9.9
183 psycopg2==2.9.10
184 184 py-bcrypt==0.4
185 185 pycmarkgfm==1.2.0
186 cffi==1.16.0
187 pycparser==2.21
188 pycryptodome==3.17
186 cffi==1.17.1
187 pycparser==2.22
188 pycryptodome==3.21.0
189 189 pycurl==7.45.3
190 pymysql==1.0.3
190 pymysql==1.1.1
191 191 pyotp==2.8.0
192 192 pyparsing==3.1.1
193 193 pyramid-mailer==0.15.1
@@ -198,19 +198,19 pyramid-mailer==0.15.1
198 198 pastedeploy==3.1.0
199 199 plaster==1.1.2
200 200 translationstring==1.4
201 venusian==3.0.0
202 webob==1.8.7
201 venusian==3.1.0
202 webob==1.8.9
203 203 zope.deprecation==5.0.0
204 zope.interface==7.0.3
204 zope.interface==7.1.1
205 205 repoze.sendmail==4.4.1
206 206 transaction==5.0.0
207 zope.interface==7.0.3
208 zope.interface==7.0.3
207 zope.interface==7.1.1
208 zope.interface==7.1.1
209 209 transaction==5.0.0
210 zope.interface==7.0.3
210 zope.interface==7.1.1
211 211 pyramid-mako==1.1.0
212 mako==1.2.4
213 markupsafe==2.1.2
212 mako==1.3.6
213 markupsafe==3.0.2
214 214 pyramid==2.0.2
215 215 hupper==1.12
216 216 plaster==1.1.2
@@ -218,16 +218,15 pyramid-mako==1.1.0
218 218 pastedeploy==3.1.0
219 219 plaster==1.1.2
220 220 translationstring==1.4
221 venusian==3.0.0
222 webob==1.8.7
221 venusian==3.1.0
222 webob==1.8.9
223 223 zope.deprecation==5.0.0
224 zope.interface==7.0.3
224 zope.interface==7.1.1
225 225 python-ldap==3.4.3
226 226 pyasn1==0.4.8
227 227 pyasn1-modules==0.2.8
228 228 pyasn1==0.4.8
229 python-memcached==1.59
230 six==1.16.0
229 python-memcached==1.62
231 230 python-pam==2.0.2
232 231 python3-saml==1.16.0
233 232 isodate==0.6.1
@@ -236,60 +235,66 python3-saml==1.16.0
236 235 xmlsec==1.3.14
237 236 lxml==5.3.0
238 237 pyyaml==6.0.1
239 redis==5.1.0
238 redis==5.2.0
240 239 async-timeout==4.0.3
241 240 regex==2022.10.31
242 241 routes==2.5.1
243 242 repoze.lru==0.7
244 243 six==1.16.0
245 s3fs==2024.9.0
246 aiobotocore==2.13.0
247 aiohttp==3.9.5
244 s3fs==2024.10.0
245 aiobotocore==2.15.2
246 aiohttp==3.11.4
247 aiohappyeyeballs==2.4.3
248 248 aiosignal==1.3.1
249 frozenlist==1.4.1
250 attrs==22.2.0
251 frozenlist==1.4.1
252 multidict==6.0.5
253 yarl==1.9.4
254 idna==3.4
255 multidict==6.0.5
256 aioitertools==0.11.0
257 botocore==1.34.106
249 frozenlist==1.5.0
250 attrs==24.2.0
251 frozenlist==1.5.0
252 multidict==6.1.0
253 propcache==0.2.0
254 yarl==1.17.2
255 idna==3.10
256 multidict==6.1.0
257 propcache==0.2.0
258 aioitertools==0.12.0
259 botocore==1.35.36
258 260 jmespath==1.0.1
259 python-dateutil==2.8.2
261 python-dateutil==2.9.0.post0
260 262 six==1.16.0
261 urllib3==1.26.14
263 urllib3==1.26.20
262 264 wrapt==1.16.0
263 aiohttp==3.9.5
265 aiohttp==3.11.4
266 aiohappyeyeballs==2.4.3
264 267 aiosignal==1.3.1
265 frozenlist==1.4.1
266 attrs==22.2.0
267 frozenlist==1.4.1
268 multidict==6.0.5
269 yarl==1.9.4
270 idna==3.4
271 multidict==6.0.5
272 fsspec==2024.9.0
268 frozenlist==1.5.0
269 attrs==24.2.0
270 frozenlist==1.5.0
271 multidict==6.1.0
272 propcache==0.2.0
273 yarl==1.17.2
274 idna==3.10
275 multidict==6.1.0
276 propcache==0.2.0
277 fsspec==2024.10.0
273 278 simplejson==3.19.2
274 279 sshpubkeys==3.3.1
275 cryptography==40.0.2
276 cffi==1.16.0
277 pycparser==2.21
278 ecdsa==0.18.0
280 cryptography==43.0.3
281 cffi==1.17.1
282 pycparser==2.22
283 ecdsa==0.19.0
279 284 six==1.16.0
280 285 sqlalchemy==1.4.52
281 greenlet==3.0.3
286 greenlet==3.1.1
282 287 typing_extensions==4.12.2
283 288 supervisor==4.2.5
284 289 tzlocal==4.3
285 290 pytz-deprecation-shim==0.1.0.post0
286 tzdata==2024.1
291 tzdata==2024.2
287 292 tempita==0.5.2
288 unidecode==1.3.6
293 unidecode==1.3.8
289 294 urlobject==2.4.3
290 waitress==3.0.0
295 waitress==3.0.1
291 296 webhelpers2==2.1
292 markupsafe==2.1.2
297 markupsafe==3.0.2
293 298 six==1.16.0
294 299 whoosh==2.7.4
295 300 zope.cachedescriptors==5.0.0
@@ -15,10 +15,8 pyramid-debugtoolbar
15 15 flake8
16 16 ruff
17 17
18 pipdeptree==2.7.1
18 pipdeptree
19 19 invoke==2.0.0
20 bumpversion==0.6.0
21 bump2version==1.0.1
22 20
23 21 docutils-stubs
24 22 types-redis
@@ -1,3 +1,3
1 1 ## rhodecode-tools, special case, use file://PATH.tar.gz#egg=rhodecode-tools==X.Y.Z, to test local version
2 rhodecode-tools @ https://code.rhodecode.com/_file_store/download/0-36d03a63-36f7-47af-a33c-d81c39f88251.0.0.tar.gz#egg=rhodecode-tools
3 rhodecode-tools==3.0.0
2 rhodecode-tools @ https://code.rhodecode.com/_file_store/download/0-51fb13c9-6175-480c-b9dc-630c7601d47c.1.0.tar.gz#egg=rhodecode-tools
3 rhodecode-tools==4.1.0
@@ -1,47 +1,54
1 1 # test related requirements
2
2 3 mock==5.1.0
3 pytest-cov==4.1.0
4 coverage==7.4.3
5 pytest==8.1.1
6 iniconfig==2.0.0
7 packaging==24.1
8 pluggy==1.4.0
9 pytest-env==1.1.3
10 pytest==8.1.1
4
5 pytest==8.3.3
6 iniconfig==2.0.0
7 packaging==24.1
8 pluggy==1.5.0
9 pytest-cov==5.0.0
10 coverage==7.6.4
11 pytest==8.3.3
11 12 iniconfig==2.0.0
12 13 packaging==24.1
13 pluggy==1.4.0
14 pytest-profiling==1.7.0
15 gprof2dot==2022.7.29
16 pytest==8.1.1
14 pluggy==1.5.0
15 pytest-env==1.1.5
16 pytest==8.3.3
17 17 iniconfig==2.0.0
18 18 packaging==24.1
19 pluggy==1.4.0
20 six==1.16.0
21 pytest-rerunfailures==13.0
22 packaging==24.1
23 pytest==8.1.1
19 pluggy==1.5.0
20 pytest-profiling==1.7.0
21 gprof2dot==2024.6.6
22 pytest==8.3.3
24 23 iniconfig==2.0.0
25 24 packaging==24.1
26 pluggy==1.4.0
25 pluggy==1.5.0
26 six==1.16.0
27 pytest-rerunfailures==14.0
28 packaging==24.1
29 pytest==8.3.3
30 iniconfig==2.0.0
31 packaging==24.1
32 pluggy==1.5.0
27 33 pytest-runner==6.0.1
28 34 pytest-sugar==1.0.0
29 35 packaging==24.1
30 pytest==8.1.1
36 pytest==8.3.3
31 37 iniconfig==2.0.0
32 38 packaging==24.1
33 pluggy==1.4.0
34 termcolor==2.4.0
39 pluggy==1.5.0
40 termcolor==2.5.0
35 41 pytest-timeout==2.3.1
36 pytest==8.1.1
42 pytest==8.3.3
37 43 iniconfig==2.0.0
38 44 packaging==24.1
39 pluggy==1.4.0
40 webtest==3.0.0
45 pluggy==1.5.0
46
47 webtest==3.0.1
41 48 beautifulsoup4==4.12.3
42 49 soupsieve==2.5
43 waitress==3.0.0
44 webob==1.8.7
50 waitress==3.0.1
51 webob==1.8.9
45 52
46 53 # RhodeCode test-data
47 54 rc_testdata @ https://code.rhodecode.com/upstream/rc-testdata-dist/raw/77378e9097f700b4c1b9391b56199fe63566b5c9/rc_testdata-0.11.0.tar.gz#egg=rc_testdata
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -45,13 +45,20 CELERY_EAGER = False
45 45 # link to config for pyramid
46 46 CONFIG = {}
47 47
48 class NotGivenMeta:
49
50 def __repr__(self):
51 return 'NotGivenObject()'
52 __str__ = __repr__
53
54 NotGiven = NotGivenMeta()
48 55
49 56 class ConfigGet:
50 NotGiven = object()
51 57
52 def _get_val_or_missing(self, key, missing):
58 @classmethod
59 def _get_val_or_missing(cls, key, missing):
53 60 if key not in CONFIG:
54 if missing == self.NotGiven:
61 if missing != NotGiven:
55 62 return missing
56 63 # we don't get key, we don't get missing value, return nothing similar as config.get(key)
57 64 return None
@@ -74,6 +81,12 class ConfigGet:
74 81 val = self._get_val_or_missing(key, missing)
75 82 return str2bool(val)
76 83
84 def get_list(self, key, missing=NotGiven):
85 from rhodecode.lib.type_utils import aslist
86 val = self._get_val_or_missing(key, missing)
87 return aslist(val, sep=',')
88
89
77 90 # Populated with the settings dictionary from application init in
78 91 # rhodecode.conf.environment.load_pyramid_environment
79 92 PYRAMID_SETTINGS = {}
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,12 +21,12 import pytest
21 21 from rhodecode.model.meta import Session
22 22 from rhodecode.model.user import UserModel
23 23 from rhodecode.model.auth_token import AuthTokenModel
24 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
25 24
26 25
27 26 @pytest.fixture(scope="class")
28 27 def testuser_api(request, baseapp):
29 28 cls = request.cls
29 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
30 30
31 31 # ADMIN USER
32 32 cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,7 +23,7 from rhodecode.model.db import Gist
24 23 from rhodecode.model.gist import GistModel
25 24 from rhodecode.api.tests.utils import (
26 25 build_data, api_call, assert_error, assert_ok, crash)
27 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixtures.rc_fixture import Fixture
28 27
29 28
30 29 @pytest.mark.usefixtures("testuser_api", "app")
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -27,7 +26,7 from rhodecode.model.user import UserMod
27 26 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
28 27 from rhodecode.api.tests.utils import (
29 28 build_data, api_call, assert_ok, assert_error, crash)
30 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.fixtures.rc_fixture import Fixture
31 30 from rhodecode.lib.ext_json import json
32 31 from rhodecode.lib.str_utils import safe_str
33 32
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,7 +25,7 from rhodecode.model.user import UserMod
26 25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
27 26 from rhodecode.api.tests.utils import (
28 27 build_data, api_call, assert_ok, assert_error, crash)
29 from rhodecode.tests.fixture import Fixture
28 from rhodecode.tests.fixtures.rc_fixture import Fixture
30 29
31 30
32 31 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,7 +25,7 from rhodecode.tests import (
26 25 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL)
27 26 from rhodecode.api.tests.utils import (
28 27 build_data, api_call, assert_ok, assert_error, jsonify, crash)
29 from rhodecode.tests.fixture import Fixture
28 from rhodecode.tests.fixtures.rc_fixture import Fixture
30 29 from rhodecode.model.db import RepoGroup
31 30
32 31
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -25,7 +24,7 from rhodecode.model.user import UserMod
25 24 from rhodecode.model.user_group import UserGroupModel
26 25 from rhodecode.api.tests.utils import (
27 26 build_data, api_call, assert_error, assert_ok, crash, jsonify)
28 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixtures.rc_fixture import Fixture
29 28
30 29
31 30 @pytest.mark.usefixtures("testuser_api", "app")
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -28,7 +27,7 from rhodecode.model.user import UserMod
28 27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
29 28 from rhodecode.api.tests.utils import (
30 29 build_data, api_call, assert_error, assert_ok, crash)
31 from rhodecode.tests.fixture import Fixture
30 from rhodecode.tests.fixtures.rc_fixture import Fixture
32 31
33 32
34 33 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,6 +19,7
20 19
21 20 import pytest
22 21
22 from rhodecode.lib.str_utils import safe_str
23 23 from rhodecode.model.db import User, ChangesetComment
24 24 from rhodecode.model.meta import Session
25 25 from rhodecode.model.comment import CommentsModel
@@ -37,7 +37,7 def make_repo_comments_factory(request):
37 37 commit = repo.scm_instance()[0]
38 38
39 39 commit_id = commit.raw_id
40 file_0 = commit.affected_files[0]
40 file_0 = safe_str(commit.affected_files[0])
41 41 comments = []
42 42
43 43 # general
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +19,6
20 19 import mock
21 20 import pytest
22 21
23 from rhodecode.model.scm import ScmModel
24 22 from rhodecode.api.tests.utils import (
25 23 build_data, api_call, assert_ok, assert_error, crash)
26 24
@@ -31,13 +29,14 class TestRescanRepos(object):
31 29 id_, params = build_data(self.apikey, 'rescan_repos')
32 30 response = api_call(self.app, params)
33 31
34 expected = {'added': [], 'removed': []}
32 expected = {'added': [], 'errors': []}
35 33 assert_ok(id_, expected, given=response.body)
36 34
37 @mock.patch.object(ScmModel, 'repo_scan', crash)
38 def test_api_rescann_error(self):
35 def test_api_rescan_repos_error(self):
39 36 id_, params = build_data(self.apikey, 'rescan_repos', )
40 response = api_call(self.app, params)
37
38 with mock.patch('rhodecode.lib.utils.repo2db_mapper', side_effect=crash):
39 response = api_call(self.app, params)
41 40
42 41 expected = 'Error occurred during rescan repositories action'
43 42 assert_error(id_, expected, given=response.body)
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -25,8 +24,8 from rhodecode.model.scm import ScmModel
25 24 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
26 25 from rhodecode.api.tests.utils import (
27 26 build_data, api_call, assert_error, assert_ok, crash, jsonify)
28 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.fixture_mods.fixture_utils import plain_http_host_only_stub
27 from rhodecode.tests.fixtures.rc_fixture import Fixture
28 from rhodecode.tests.fixtures.fixture_utils import plain_http_host_only_stub
30 29
31 30 fixture = Fixture()
32 31
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2014-2023 RhodeCode GmbH
1 # Copyright (C) 2014-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2015-2023 RhodeCode GmbH
1 # Copyright (C) 2015-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -317,8 +317,7 def get_repo_changeset(request, apiuser,
317 317 ','.join(_changes_details_types)))
318 318
319 319 vcs_repo = repo.scm_instance()
320 pre_load = ['author', 'branch', 'date', 'message', 'parents',
321 'status', '_commit', '_file_paths']
320 pre_load = ['author', 'branch', 'date', 'message', 'parents', 'status', '_commit']
322 321
323 322 try:
324 323 commit = repo.get_commit(commit_id=revision, pre_load=pre_load)
@@ -376,8 +375,7 def get_repo_changesets(request, apiuser
376 375 ','.join(_changes_details_types)))
377 376
378 377 limit = int(limit)
379 pre_load = ['author', 'branch', 'date', 'message', 'parents',
380 'status', '_commit', '_file_paths']
378 pre_load = ['author', 'branch', 'date', 'message', 'parents', 'status', '_commit']
381 379
382 380 vcs_repo = repo.scm_instance()
383 381 # SVN needs a special case to distinguish its index and commit id
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -18,14 +18,13
18 18
19 19 import logging
20 20 import itertools
21 import base64
22 21
23 22 from rhodecode.api import (
24 23 jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods)
25 24
26 25 from rhodecode.api.utils import (
27 26 Optional, OAttr, has_superadmin_permission, get_user_or_error)
28 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_repo_store_path
27 from rhodecode.lib.utils import get_rhodecode_repo_store_path
29 28 from rhodecode.lib import system_info
30 29 from rhodecode.lib import user_sessions
31 30 from rhodecode.lib import exc_tracking
@@ -33,9 +32,7 from rhodecode.lib.ext_json import json
33 32 from rhodecode.lib.utils2 import safe_int
34 33 from rhodecode.model.db import UserIpMap
35 34 from rhodecode.model.scm import ScmModel
36 from rhodecode.apps.file_store import utils as store_utils
37 from rhodecode.apps.file_store.exceptions import FileNotAllowedException, \
38 FileOverSizeException
35
39 36
40 37 log = logging.getLogger(__name__)
41 38
@@ -158,13 +155,10 def get_ip(request, apiuser, userid=Opti
158 155
159 156
160 157 @jsonrpc_method()
161 def rescan_repos(request, apiuser, remove_obsolete=Optional(False)):
158 def rescan_repos(request, apiuser):
162 159 """
163 160 Triggers a rescan of the specified repositories.
164
165 * If the ``remove_obsolete`` option is set, it also deletes repositories
166 that are found in the database but not on the file system, so called
167 "clean zombies".
161 It returns list of added repositories, and errors during scan.
168 162
169 163 This command can only be run using an |authtoken| with admin rights to
170 164 the specified repository.
@@ -173,9 +167,6 def rescan_repos(request, apiuser, remov
173 167
174 168 :param apiuser: This is filled automatically from the |authtoken|.
175 169 :type apiuser: AuthUser
176 :param remove_obsolete: Deletes repositories from the database that
177 are not found on the filesystem.
178 :type remove_obsolete: Optional(``True`` | ``False``)
179 170
180 171 Example output:
181 172
@@ -184,7 +175,7 def rescan_repos(request, apiuser, remov
184 175 id : <id_given_in_input>
185 176 result : {
186 177 'added': [<added repository name>,...]
187 'removed': [<removed repository name>,...]
178 'errors': [<error_list>,...]
188 179 }
189 180 error : null
190 181
@@ -199,20 +190,69 def rescan_repos(request, apiuser, remov
199 190 }
200 191
201 192 """
193 from rhodecode.lib.utils import repo2db_mapper # re-import for testing patches
194
202 195 if not has_superadmin_permission(apiuser):
203 196 raise JSONRPCForbidden()
204 197
205 198 try:
206 rm_obsolete = Optional.extract(remove_obsolete)
207 added, removed = repo2db_mapper(ScmModel().repo_scan(),
208 remove_obsolete=rm_obsolete, force_hooks_rebuild=True)
209 return {'added': added, 'removed': removed}
199 added, errors = repo2db_mapper(ScmModel().repo_scan(), force_hooks_rebuild=True)
200 return {'added': added, 'errors': errors}
210 201 except Exception:
211 log.exception('Failed to run repo rescann')
202 log.exception('Failed to run repo rescan')
212 203 raise JSONRPCError(
213 204 'Error occurred during rescan repositories action'
214 205 )
215 206
207 @jsonrpc_method()
208 def cleanup_repos(request, apiuser):
209 """
210 Triggers a cleanup of non-existing repositories or repository groups in filesystem.
211
212 This command can only be run using an |authtoken| with admin rights to
213 the specified repository.
214
215 This command takes the following options:
216
217 :param apiuser: This is filled automatically from the |authtoken|.
218 :type apiuser: AuthUser
219
220 Example output:
221
222 .. code-block:: bash
223
224 id : <id_given_in_input>
225 result : {
226 'removed': [<removed repository name or repository group name>,...]
227 'errors': [<error list of failures to remove>,...]
228 }
229 error : null
230
231 Example error output:
232
233 .. code-block:: bash
234
235 id : <id_given_in_input>
236 result : null
237 error : {
238 'Error occurred during repo storage cleanup action'
239 }
240
241 """
242 from rhodecode.lib.utils import repo2db_cleanup # re-import for testing patches
243
244 if not has_superadmin_permission(apiuser):
245 raise JSONRPCForbidden()
246
247 try:
248 removed, errors = repo2db_cleanup()
249 return {'removed': removed, 'errors': errors}
250 except Exception:
251 log.exception('Failed to run repo storage cleanup')
252 raise JSONRPCError(
253 'Error occurred during repo storage cleanup action'
254 )
255
216 256
217 257 @jsonrpc_method()
218 258 def cleanup_sessions(request, apiuser, older_then=Optional(60)):
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -395,7 +395,7 class RepoAppView(BaseAppView):
395 395 settings = settings_model.get_repo_settings_inherited()
396 396 return settings.get(settings_key, default)
397 397
398 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path="/"):
398 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path="/", nodes=None):
399 399 log.debug("Looking for README file at path %s", path)
400 400 if commit_id:
401 401 landing_commit_id = commit_id
@@ -413,16 +413,14 class RepoAppView(BaseAppView):
413 413
414 414 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
415 415 def generate_repo_readme(
416 repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type
416 _repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type
417 417 ):
418 readme_data = None
419 readme_filename = None
418 _readme_data = None
419 _readme_filename = None
420 420
421 421 commit = db_repo.get_commit(_commit_id)
422 422 log.debug("Searching for a README file at commit %s.", _commit_id)
423 readme_node = ReadmeFinder(_renderer_type).search(
424 commit, path=_readme_search_path
425 )
423 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path, nodes=nodes)
426 424
427 425 if readme_node:
428 426 log.debug("Found README node: %s", readme_node)
@@ -442,19 +440,19 class RepoAppView(BaseAppView):
442 440 ),
443 441 }
444 442
445 readme_data = self._render_readme_or_none(
443 _readme_data = self._render_readme_or_none(
446 444 commit, readme_node, relative_urls
447 445 )
448 readme_filename = readme_node.str_path
446 _readme_filename = readme_node.str_path
449 447
450 return readme_data, readme_filename
448 return _readme_data, _readme_filename
451 449
452 450 readme_data, readme_filename = generate_repo_readme(
453 451 db_repo.repo_id,
454 452 landing_commit_id,
455 453 db_repo.repo_name,
456 454 path,
457 renderer_type,
455 renderer_type
458 456 )
459 457
460 458 compute_time = time.time() - start
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,5
1 1
2 import dataclasses
3 # Copyright (C) 2016-2023 RhodeCode GmbH
2 import dataclasses# Copyright (C) 2016-2024 RhodeCode GmbH
4 3 #
5 4 # This program is free software: you can redistribute it and/or modify
6 5 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -361,12 +361,21 def admin_routes(config):
361 361 renderer='rhodecode:templates/admin/settings/settings.mako')
362 362
363 363 config.add_route(
364 name='admin_settings_mapping_update',
365 pattern='/settings/mapping/update')
364 name='admin_settings_mapping_create',
365 pattern='/settings/mapping/create')
366 366 config.add_view(
367 367 AdminSettingsView,
368 attr='settings_mapping_update',
369 route_name='admin_settings_mapping_update', request_method='POST',
368 attr='settings_mapping_create',
369 route_name='admin_settings_mapping_create', request_method='POST',
370 renderer='rhodecode:templates/admin/settings/settings.mako')
371
372 config.add_route(
373 name='admin_settings_mapping_cleanup',
374 pattern='/settings/mapping/cleanup')
375 config.add_view(
376 AdminSettingsView,
377 attr='settings_mapping_cleanup',
378 route_name='admin_settings_mapping_cleanup', request_method='POST',
370 379 renderer='rhodecode:templates/admin/settings/settings.mako')
371 380
372 381 config.add_route(
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,7 +25,7 import pytest
26 25 from rhodecode.lib.str_utils import safe_str
27 26 from rhodecode.tests import *
28 27 from rhodecode.tests.routes import route_path
29 from rhodecode.tests.fixture import FIXTURES
28 from rhodecode.tests.fixtures.rc_fixture import FIXTURES
30 29 from rhodecode.model.db import UserLog
31 30 from rhodecode.model.meta import Session
32 31
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +19,7
20 19 import pytest
21 20
22 21 from rhodecode.tests import TestController
23 from rhodecode.tests.fixture import Fixture
22 from rhodecode.tests.fixtures.rc_fixture import Fixture
24 23 from rhodecode.tests.routes import route_path
25 24
26 25 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -37,7 +36,7 from rhodecode.model.user import UserMod
37 36 from rhodecode.tests import (
38 37 login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
39 38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
40 from rhodecode.tests.fixture import Fixture, error_function
39 from rhodecode.tests.fixtures.rc_fixture import Fixture, error_function
41 40 from rhodecode.tests.utils import repo_on_filesystem
42 41 from rhodecode.tests.routes import route_path
43 42
@@ -111,9 +110,11 class TestAdminRepos(object):
111 110 repo_type=backend.alias,
112 111 repo_description=description,
113 112 csrf_token=csrf_token))
114
115 self.assert_repository_is_created_correctly(
116 repo_name, description, backend)
113 try:
114 self.assert_repository_is_created_correctly(repo_name, description, backend)
115 finally:
116 RepoModel().delete(numeric_repo)
117 Session().commit()
117 118
118 119 @pytest.mark.parametrize("suffix", ['', '_ąćę'], ids=['', 'non-ascii'])
119 120 def test_create_in_group(
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -27,7 +26,7 from rhodecode.model.meta import Session
27 26 from rhodecode.model.repo_group import RepoGroupModel
28 27 from rhodecode.tests import (
29 28 assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH)
30 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.fixtures.rc_fixture import Fixture
31 30 from rhodecode.tests.routes import route_path
32 31
33 32
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,7 +23,7 from rhodecode.model.meta import Session
24 23
25 24 from rhodecode.tests import (
26 25 TestController, assert_session_flash)
27 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixtures.rc_fixture import Fixture
28 27 from rhodecode.tests.routes import route_path
29 28
30 29 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -28,7 +27,7 from rhodecode.model.user import UserMod
28 27
29 28 from rhodecode.tests import (
30 29 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
31 from rhodecode.tests.fixture import Fixture
30 from rhodecode.tests.fixtures.rc_fixture import Fixture
32 31 from rhodecode.tests.routes import route_path
33 32
34 33 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +21,7 import pytest
22 21 from rhodecode.model.db import User, UserSshKeys
23 22
24 23 from rhodecode.tests import TestController, assert_session_flash
25 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 25 from rhodecode.tests.routes import route_path
27 26
28 27 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2018-2023 RhodeCode GmbH
1 # Copyright (C) 2018-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -38,7 +38,7 from rhodecode.lib.auth import (
38 38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
39 39 from rhodecode.lib.celerylib import tasks, run_task
40 40 from rhodecode.lib.str_utils import safe_str
41 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_repo_store_path
41 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_repo_store_path, repo2db_cleanup
42 42 from rhodecode.lib.utils2 import str2bool, AttributeDict
43 43 from rhodecode.lib.index import searcher_from_config
44 44
@@ -77,13 +77,16 class AdminSettingsView(BaseAppView):
77 77 raise Exception('Could not get application ui settings !')
78 78 settings = {
79 79 # legacy param that needs to be kept
80 'web_push_ssl': False
80 'web_push_ssl': False,
81 'extensions_hgsubversion': False
81 82 }
82 83 for each in ret:
83 84 k = each.ui_key
84 85 v = each.ui_value
86 section = each.ui_section
87
85 88 # skip some options if they are defined
86 if k in ['push_ssl']:
89 if f"{section}_{k}" in ['web_push_ssl', 'extensions_hgsubversion']:
87 90 continue
88 91
89 92 if k == '/':
@@ -98,7 +101,7 class AdminSettingsView(BaseAppView):
98 101 if each.ui_section in ['hooks', 'extensions']:
99 102 v = each.ui_active
100 103
101 settings[each.ui_section + '_' + k] = v
104 settings[section + '_' + k] = v
102 105
103 106 return settings
104 107
@@ -125,8 +128,6 class AdminSettingsView(BaseAppView):
125 128 c.svn_config_path = rhodecode.ConfigGet().get_str(config_keys.config_file_path)
126 129 defaults = self._form_defaults()
127 130
128 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
129
130 131 data = render('rhodecode:templates/admin/settings/settings.mako',
131 132 self._get_template_context(c), self.request)
132 133 html = formencode.htmlfill.render(
@@ -232,13 +233,12 class AdminSettingsView(BaseAppView):
232 233 @LoginRequired()
233 234 @HasPermissionAllDecorator('hg.admin')
234 235 @CSRFRequired()
235 def settings_mapping_update(self):
236 def settings_mapping_create(self):
236 237 _ = self.request.translate
237 238 c = self.load_default_context()
238 239 c.active = 'mapping'
239 rm_obsolete = self.request.POST.get('destroy', False)
240 240 invalidate_cache = self.request.POST.get('invalidate', False)
241 log.debug('rescanning repo location with destroy obsolete=%s', rm_obsolete)
241 log.debug('rescanning repo location')
242 242
243 243 if invalidate_cache:
244 244 log.debug('invalidating all repositories cache')
@@ -246,16 +246,34 class AdminSettingsView(BaseAppView):
246 246 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
247 247
248 248 filesystem_repos = ScmModel().repo_scan()
249 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete, force_hooks_rebuild=True)
249 added, errors = repo2db_mapper(filesystem_repos, force_hooks_rebuild=True)
250 250 PermissionModel().trigger_permission_flush()
251 251
252 252 def _repr(rm_repo):
253 253 return ', '.join(map(safe_str, rm_repo)) or '-'
254 254
255 h.flash(_('Repositories successfully '
256 'rescanned added: %s ; removed: %s') %
257 (_repr(added), _repr(removed)),
258 category='success')
255 if errors:
256 h.flash(_('Errors during scan: {}').format(_repr(errors), ), category='error')
257
258 h.flash(_('Repositories successfully scanned: Added: {}').format(_repr(added)), category='success')
259 raise HTTPFound(h.route_path('admin_settings_mapping'))
260
261 @LoginRequired()
262 @HasPermissionAllDecorator('hg.admin')
263 @CSRFRequired()
264 def settings_mapping_cleanup(self):
265 _ = self.request.translate
266 c = self.load_default_context()
267 c.active = 'mapping'
268 log.debug('rescanning repo location')
269
270 removed, errors = repo2db_cleanup()
271 PermissionModel().trigger_permission_flush()
272
273 def _repr(rm_repo):
274 return ', '.join(map(safe_str, rm_repo)) or '-'
275
276 h.flash(_('Repositories successfully scanned: Errors: {}, Added: {}').format(errors, _repr(removed)), category='success')
259 277 raise HTTPFound(h.route_path('admin_settings_mapping'))
260 278
261 279 @LoginRequired()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
4 2 #
5 3 # This program is free software: you can redistribute it and/or modify
6 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
4 2 #
5 3 # This program is free software: you can redistribute it and/or modify
6 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
4 2 #
5 3 # This program is free software: you can redistribute it and/or modify
6 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
4 2 #
5 3 # This program is free software: you can redistribute it and/or modify
6 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2013-2023 RhodeCode GmbH
1 # Copyright (C) 2013-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -27,7 +27,7 from rhodecode.model.repo_group import R
27 27 from rhodecode.model.db import Session, Repository, RepoGroup
28 28
29 29 from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN
30 from rhodecode.tests.fixture import Fixture
30 from rhodecode.tests.fixtures.rc_fixture import Fixture
31 31 from rhodecode.tests.routes import route_path
32 32
33 33 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 from rhodecode.model.db import Repositor
22 22 from rhodecode.lib.ext_json import json
23 23
24 24 from rhodecode.tests import TestController
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 26 from rhodecode.tests.routes import route_path
27 27
28 28 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +20,7 import pytest
20 20 from rhodecode.lib.ext_json import json
21 21
22 22 from rhodecode.tests import TestController
23 from rhodecode.tests.fixture import Fixture
23 from rhodecode.tests.fixtures.rc_fixture import Fixture
24 24 from rhodecode.tests.routes import route_path
25 25
26 26 fixture = Fixture()
@@ -1,23 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19
20 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
21 2 #
22 3 # This program is free software: you can redistribute it and/or modify
23 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -40,7 +21,7 import pytest
40 21 from rhodecode.lib.ext_json import json
41 22
42 23 from rhodecode.tests import TestController
43 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixtures.rc_fixture import Fixture
44 25 from rhodecode.tests.routes import route_path
45 26
46 27 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,7 +24,7 from rhodecode.model.db import Repositor
24 24 from rhodecode.model.meta import Session
25 25 from rhodecode.model.settings import SettingsModel
26 26 from rhodecode.tests import TestController
27 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixtures.rc_fixture import Fixture
28 28 from rhodecode.tests.routes import route_path
29 29
30 30
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2018-2023 RhodeCode GmbH
1 # Copyright (C) 2018-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -3,7 +3,7 import mock
3 3
4 4 from rhodecode.lib.type_utils import AttributeDict
5 5 from rhodecode.model.meta import Session
6 from rhodecode.tests.fixture import Fixture
6 from rhodecode.tests.fixtures.rc_fixture import Fixture
7 7 from rhodecode.tests.routes import route_path
8 8 from rhodecode.model.settings import SettingsModel
9 9
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -31,7 +31,7 from rhodecode.model.meta import Session
31 31 from rhodecode.tests import (
32 32 assert_session_flash, HG_REPO, TEST_USER_ADMIN_LOGIN,
33 33 no_newline_id_generator)
34 from rhodecode.tests.fixture import Fixture
34 from rhodecode.tests.fixtures.rc_fixture import Fixture
35 35 from rhodecode.tests.routes import route_path
36 36
37 37 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 from rhodecode.lib import helpers as h
22 22 from rhodecode.tests import (
23 23 TestController, clear_cache_regions,
24 24 TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 26 from rhodecode.tests.utils import AssertResponse
27 27 from rhodecode.tests.routes import route_path
28 28
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 from rhodecode.apps._base import ADMIN_P
22 22 from rhodecode.model.db import User
23 23 from rhodecode.tests import (
24 24 TestController, assert_session_flash)
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 26 from rhodecode.tests.routes import route_path
27 27
28 28
@@ -1,23 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19
20 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
21 2 #
22 3 # This program is free software: you can redistribute it and/or modify
23 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -23,7 +23,7 from rhodecode.model.db import User, Use
23 23 from rhodecode.tests import (
24 24 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
25 25 assert_session_flash, TEST_USER_REGULAR_PASS)
26 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixtures.rc_fixture import Fixture
27 27 from rhodecode.tests.routes import route_path
28 28
29 29
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +21,7 import pytest
21 21 from rhodecode.tests import (
22 22 TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
23 23 TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
24 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixtures.rc_fixture import Fixture
25 25 from rhodecode.tests.routes import route_path
26 26
27 27 from rhodecode.model.db import Notification, User
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,7 +23,7 from rhodecode.lib.auth import check_pas
24 23 from rhodecode.model.meta import Session
25 24 from rhodecode.model.user import UserModel
26 25 from rhodecode.tests import assert_session_flash, TestController
27 from rhodecode.tests.fixture import Fixture, error_function
26 from rhodecode.tests.fixtures.rc_fixture import Fixture, error_function
28 27 from rhodecode.tests.routes import route_path
29 28
30 29 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +19,7
20 19 from rhodecode.tests import (
21 20 TestController, TEST_USER_ADMIN_LOGIN,
22 21 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
23 from rhodecode.tests.fixture import Fixture
22 from rhodecode.tests.fixtures.rc_fixture import Fixture
24 23 from rhodecode.tests.routes import route_path
25 24
26 25 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,7 +18,7
19 18
20 19 from rhodecode.model.db import User, Repository, UserFollowing
21 20 from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN
22 from rhodecode.tests.fixture import Fixture
21 from rhodecode.tests.fixtures.rc_fixture import Fixture
23 22 from rhodecode.tests.routes import route_path
24 23
25 24 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +20,7
21 20 from rhodecode.model.db import User, UserSshKeys
22 21
23 22 from rhodecode.tests import TestController, assert_session_flash
24 from rhodecode.tests.fixture import Fixture
23 from rhodecode.tests.fixtures.rc_fixture import Fixture
25 24 from rhodecode.tests.routes import route_path
26 25
27 26 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +21,7 import pytest
22 21 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
23 22 from rhodecode.lib.vcs import nodes
24 23 from rhodecode.lib.vcs.backends.base import EmptyCommit
25 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 25 from rhodecode.tests.utils import commit_change
27 26 from rhodecode.tests.routes import route_path
28 27
@@ -166,14 +165,15 class TestSideBySideDiff(object):
166 165 response.mustcontain('Collapse 2 commits')
167 166 response.mustcontain('123 file changed')
168 167
169 response.mustcontain(
170 'r%s:%s...r%s:%s' % (
171 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
168 response.mustcontain(f'r{commit1.idx}:{commit1.short_id}...r{commit2.idx}:{commit2.short_id}')
172 169
173 170 response.mustcontain(f_path)
174 171
175 @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
172 #@pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
176 173 def test_diff_side_by_side_from_0_commit_with_file_filter(self, app, backend, backend_stub):
174 if backend.alias == 'git':
175 pytest.skip('GIT does not handle empty commit compare correct (missing 1 commit)')
176
177 177 f_path = b'test_sidebyside_file.py'
178 178 commit1_content = b'content-25d7e49c18b159446c\n'
179 179 commit2_content = b'content-603d6c72c46d953420\n'
@@ -200,9 +200,7 class TestSideBySideDiff(object):
200 200 response.mustcontain('Collapse 2 commits')
201 201 response.mustcontain('1 file changed')
202 202
203 response.mustcontain(
204 'r%s:%s...r%s:%s' % (
205 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
203 response.mustcontain(f'r{commit1.idx}:{commit1.short_id}...r{commit2.idx}:{commit2.short_id}')
206 204
207 205 response.mustcontain(f_path)
208 206
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
This diff has been collapsed as it changes many lines, (1045 lines changed) Show them Hide them
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -33,7 +32,7 from rhodecode.lib.vcs.conf import setti
33 32 from rhodecode.model.db import Session, Repository
34 33
35 34 from rhodecode.tests import assert_session_flash
36 from rhodecode.tests.fixture import Fixture
35 from rhodecode.tests.fixtures.rc_fixture import Fixture
37 36 from rhodecode.tests.routes import route_path
38 37
39 38
@@ -42,207 +41,171 fixture = Fixture()
42 41
43 42 def get_node_history(backend_type):
44 43 return {
45 'hg': json.loads(fixture.load_resource('hg_node_history_response.json')),
46 'git': json.loads(fixture.load_resource('git_node_history_response.json')),
47 'svn': json.loads(fixture.load_resource('svn_node_history_response.json')),
44 "hg": json.loads(fixture.load_resource("hg_node_history_response.json")),
45 "git": json.loads(fixture.load_resource("git_node_history_response.json")),
46 "svn": json.loads(fixture.load_resource("svn_node_history_response.json")),
48 47 }[backend_type]
49 48
50 49
51 50 def assert_files_in_response(response, files, params):
52 template = (
53 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
51 template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"'
54 52 _assert_items_in_response(response, files, template, params)
55 53
56 54
57 55 def assert_dirs_in_response(response, dirs, params):
58 template = (
59 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
56 template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"'
60 57 _assert_items_in_response(response, dirs, template, params)
61 58
62 59
63 60 def _assert_items_in_response(response, items, template, params):
64 61 for item in items:
65 item_params = {'name': item}
62 item_params = {"name": item}
66 63 item_params.update(params)
67 64 response.mustcontain(template % item_params)
68 65
69 66
70 67 def assert_timeago_in_response(response, items, params):
71 68 for item in items:
72 response.mustcontain(h.age_component(params['date']))
69 response.mustcontain(h.age_component(params["date"]))
73 70
74 71
75 72 @pytest.mark.usefixtures("app")
76 73 class TestFilesViews(object):
77
78 74 def test_show_files(self, backend):
79 response = self.app.get(
80 route_path('repo_files',
81 repo_name=backend.repo_name,
82 commit_id='tip', f_path='/'))
75 response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=""))
83 76 commit = backend.repo.get_commit()
84 77
85 params = {
86 'repo_name': backend.repo_name,
87 'commit_id': commit.raw_id,
88 'date': commit.date
89 }
90 assert_dirs_in_response(response, ['docs', 'vcs'], params)
78 params = {"repo_name": backend.repo_name, "commit_id": commit.raw_id, "date": commit.date}
79 assert_dirs_in_response(response, ["docs", "vcs"], params)
91 80 files = [
92 '.gitignore',
93 '.hgignore',
94 '.hgtags',
81 ".gitignore",
82 ".hgignore",
83 ".hgtags",
95 84 # TODO: missing in Git
96 85 # '.travis.yml',
97 'MANIFEST.in',
98 'README.rst',
86 "MANIFEST.in",
87 "README.rst",
99 88 # TODO: File is missing in svn repository
100 89 # 'run_test_and_report.sh',
101 'setup.cfg',
102 'setup.py',
103 'test_and_report.sh',
104 'tox.ini',
90 "setup.cfg",
91 "setup.py",
92 "test_and_report.sh",
93 "tox.ini",
105 94 ]
106 95 assert_files_in_response(response, files, params)
107 96 assert_timeago_in_response(response, files, params)
108 97
109 98 def test_show_files_links_submodules_with_absolute_url(self, backend_hg):
110 repo = backend_hg['subrepos']
111 response = self.app.get(
112 route_path('repo_files',
113 repo_name=repo.repo_name,
114 commit_id='tip', f_path='/'))
99 repo = backend_hg["subrepos"]
100 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
115 101 assert_response = response.assert_response()
116 assert_response.contains_one_link(
117 'absolute-path @ 000000000000', 'http://example.com/absolute-path')
102 assert_response.contains_one_link("absolute-path @ 000000000000", "http://example.com/absolute-path")
118 103
119 def test_show_files_links_submodules_with_absolute_url_subpaths(
120 self, backend_hg):
121 repo = backend_hg['subrepos']
122 response = self.app.get(
123 route_path('repo_files',
124 repo_name=repo.repo_name,
125 commit_id='tip', f_path='/'))
104 def test_show_files_links_submodules_with_absolute_url_subpaths(self, backend_hg):
105 repo = backend_hg["subrepos"]
106 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
126 107 assert_response = response.assert_response()
127 assert_response.contains_one_link(
128 'subpaths-path @ 000000000000',
129 'http://sub-base.example.com/subpaths-path')
108 assert_response.contains_one_link("subpaths-path @ 000000000000", "http://sub-base.example.com/subpaths-path")
130 109
131 110 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
132 111 def test_files_menu(self, backend):
133 112 new_branch = "temp_branch_name"
134 commits = [
135 {'message': 'a'},
136 {'message': 'b', 'branch': new_branch}
137 ]
113 commits = [{"message": "a"}, {"message": "b", "branch": new_branch}]
138 114 backend.create_repo(commits)
139 115 backend.repo.landing_rev = f"branch:{new_branch}"
140 116 Session().commit()
141 117
142 118 # get response based on tip and not new commit
143 response = self.app.get(
144 route_path('repo_files',
145 repo_name=backend.repo_name,
146 commit_id='tip', f_path='/'))
119 response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=""))
147 120
148 121 # make sure Files menu url is not tip but new commit
149 122 landing_rev = backend.repo.landing_ref_name
150 files_url = route_path('repo_files:default_path',
151 repo_name=backend.repo_name,
152 commit_id=landing_rev, params={'at': landing_rev})
123 files_url = route_path(
124 "repo_files:default_path", repo_name=backend.repo_name, commit_id=landing_rev, params={"at": landing_rev}
125 )
153 126
154 assert landing_rev != 'tip'
127 assert landing_rev != "tip"
155 128 response.mustcontain(f'<li class="active"><a class="menulink" href="{files_url}">')
156 129
157 130 def test_show_files_commit(self, backend):
158 131 commit = backend.repo.get_commit(commit_idx=32)
159 132
160 133 response = self.app.get(
161 route_path('repo_files',
162 repo_name=backend.repo_name,
163 commit_id=commit.raw_id, f_path='/'))
134 route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="")
135 )
164 136
165 dirs = ['docs', 'tests']
166 files = ['README.rst']
137 dirs = ["docs", "tests"]
138 files = ["README.rst"]
167 139 params = {
168 'repo_name': backend.repo_name,
169 'commit_id': commit.raw_id,
140 "repo_name": backend.repo_name,
141 "commit_id": commit.raw_id,
170 142 }
171 143 assert_dirs_in_response(response, dirs, params)
172 144 assert_files_in_response(response, files, params)
173 145
174 146 def test_show_files_different_branch(self, backend):
175 147 branches = dict(
176 hg=(150, ['git']),
148 hg=(150, ["git"]),
177 149 # TODO: Git test repository does not contain other branches
178 git=(633, ['master']),
150 git=(633, ["master"]),
179 151 # TODO: Branch support in Subversion
180 svn=(150, [])
152 svn=(150, []),
181 153 )
182 154 idx, branches = branches[backend.alias]
183 155 commit = backend.repo.get_commit(commit_idx=idx)
184 156 response = self.app.get(
185 route_path('repo_files',
186 repo_name=backend.repo_name,
187 commit_id=commit.raw_id, f_path='/'))
157 route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="")
158 )
188 159
189 160 assert_response = response.assert_response()
190 161 for branch in branches:
191 assert_response.element_contains('.tags .branchtag', branch)
162 assert_response.element_contains(".tags .branchtag", branch)
192 163
193 164 def test_show_files_paging(self, backend):
194 165 repo = backend.repo
195 166 indexes = [73, 92, 109, 1, 0]
196 idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id)
197 for rev in indexes]
167 idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id) for rev in indexes]
198 168
199 169 for idx in idx_map:
200 response = self.app.get(
201 route_path('repo_files',
202 repo_name=backend.repo_name,
203 commit_id=idx[1], f_path='/'))
170 response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id=idx[1], f_path=""))
204 171
205 172 response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8]))
206 173
207 174 def test_file_source(self, backend):
208 175 commit = backend.repo.get_commit(commit_idx=167)
209 176 response = self.app.get(
210 route_path('repo_files',
211 repo_name=backend.repo_name,
212 commit_id=commit.raw_id, f_path='vcs/nodes.py'))
177 route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py")
178 )
213 179
214 180 msgbox = """<div class="commit">%s</div>"""
215 response.mustcontain(msgbox % (commit.message, ))
181 response.mustcontain(msgbox % (commit.message,))
216 182
217 183 assert_response = response.assert_response()
218 184 if commit.branch:
219 assert_response.element_contains(
220 '.tags.tags-main .branchtag', commit.branch)
185 assert_response.element_contains(".tags.tags-main .branchtag", commit.branch)
221 186 if commit.tags:
222 187 for tag in commit.tags:
223 assert_response.element_contains('.tags.tags-main .tagtag', tag)
188 assert_response.element_contains(".tags.tags-main .tagtag", tag)
224 189
225 190 def test_file_source_annotated(self, backend):
226 191 response = self.app.get(
227 route_path('repo_files:annotated',
228 repo_name=backend.repo_name,
229 commit_id='tip', f_path='vcs/nodes.py'))
192 route_path("repo_files:annotated", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py")
193 )
230 194 expected_commits = {
231 'hg': 'r356',
232 'git': 'r345',
233 'svn': 'r208',
195 "hg": "r356",
196 "git": "r345",
197 "svn": "r208",
234 198 }
235 199 response.mustcontain(expected_commits[backend.alias])
236 200
237 201 def test_file_source_authors(self, backend):
238 202 response = self.app.get(
239 route_path('repo_file_authors',
240 repo_name=backend.repo_name,
241 commit_id='tip', f_path='vcs/nodes.py'))
203 route_path("repo_file_authors", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py")
204 )
242 205 expected_authors = {
243 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
244 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
245 'svn': ('marcin', 'lukasz'),
206 "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"),
207 "git": ("Marcin Kuzminski", "Lukasz Balcerzak"),
208 "svn": ("marcin", "lukasz"),
246 209 }
247 210
248 211 for author in expected_authors[backend.alias]:
@@ -250,14 +213,18 class TestFilesViews(object):
250 213
251 214 def test_file_source_authors_with_annotation(self, backend):
252 215 response = self.app.get(
253 route_path('repo_file_authors',
254 repo_name=backend.repo_name,
255 commit_id='tip', f_path='vcs/nodes.py',
256 params=dict(annotate=1)))
216 route_path(
217 "repo_file_authors",
218 repo_name=backend.repo_name,
219 commit_id="tip",
220 f_path="vcs/nodes.py",
221 params=dict(annotate=1),
222 )
223 )
257 224 expected_authors = {
258 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
259 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
260 'svn': ('marcin', 'lukasz'),
225 "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"),
226 "git": ("Marcin Kuzminski", "Lukasz Balcerzak"),
227 "svn": ("marcin", "lukasz"),
261 228 }
262 229
263 230 for author in expected_authors[backend.alias]:
@@ -265,94 +232,88 class TestFilesViews(object):
265 232
266 233 def test_file_source_history(self, backend, xhr_header):
267 234 response = self.app.get(
268 route_path('repo_file_history',
269 repo_name=backend.repo_name,
270 commit_id='tip', f_path='vcs/nodes.py'),
271 extra_environ=xhr_header)
235 route_path("repo_file_history", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py"),
236 extra_environ=xhr_header,
237 )
272 238 assert get_node_history(backend.alias) == json.loads(response.body)
273 239
274 240 def test_file_source_history_svn(self, backend_svn, xhr_header):
275 simple_repo = backend_svn['svn-simple-layout']
241 simple_repo = backend_svn["svn-simple-layout"]
276 242 response = self.app.get(
277 route_path('repo_file_history',
278 repo_name=simple_repo.repo_name,
279 commit_id='tip', f_path='trunk/example.py'),
280 extra_environ=xhr_header)
243 route_path(
244 "repo_file_history", repo_name=simple_repo.repo_name, commit_id="tip", f_path="trunk/example.py"
245 ),
246 extra_environ=xhr_header,
247 )
281 248
282 expected_data = json.loads(
283 fixture.load_resource('svn_node_history_branches.json'))
249 expected_data = json.loads(fixture.load_resource("svn_node_history_branches.json"))
284 250
285 251 assert expected_data == response.json
286 252
287 253 def test_file_source_history_with_annotation(self, backend, xhr_header):
288 254 response = self.app.get(
289 route_path('repo_file_history',
290 repo_name=backend.repo_name,
291 commit_id='tip', f_path='vcs/nodes.py',
292 params=dict(annotate=1)),
293
294 extra_environ=xhr_header)
255 route_path(
256 "repo_file_history",
257 repo_name=backend.repo_name,
258 commit_id="tip",
259 f_path="vcs/nodes.py",
260 params=dict(annotate=1),
261 ),
262 extra_environ=xhr_header,
263 )
295 264 assert get_node_history(backend.alias) == json.loads(response.body)
296 265
297 266 def test_tree_search_top_level(self, backend, xhr_header):
298 267 commit = backend.repo.get_commit(commit_idx=173)
299 268 response = self.app.get(
300 route_path('repo_files_nodelist',
301 repo_name=backend.repo_name,
302 commit_id=commit.raw_id, f_path='/'),
303 extra_environ=xhr_header)
304 assert 'nodes' in response.json
305 assert {'name': 'docs', 'type': 'dir'} in response.json['nodes']
269 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""),
270 extra_environ=xhr_header,
271 )
272 assert "nodes" in response.json
273 assert {"name": "docs", "type": "dir"} in response.json["nodes"]
306 274
307 275 def test_tree_search_missing_xhr(self, backend):
308 276 self.app.get(
309 route_path('repo_files_nodelist',
310 repo_name=backend.repo_name,
311 commit_id='tip', f_path='/'),
312 status=404)
277 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404
278 )
313 279
314 280 def test_tree_search_at_path(self, backend, xhr_header):
315 281 commit = backend.repo.get_commit(commit_idx=173)
316 282 response = self.app.get(
317 route_path('repo_files_nodelist',
318 repo_name=backend.repo_name,
319 commit_id=commit.raw_id, f_path='/docs'),
320 extra_environ=xhr_header)
321 assert 'nodes' in response.json
322 nodes = response.json['nodes']
323 assert {'name': 'docs/api', 'type': 'dir'} in nodes
324 assert {'name': 'docs/index.rst', 'type': 'file'} in nodes
283 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs"),
284 extra_environ=xhr_header,
285 )
286 assert "nodes" in response.json
287 nodes = response.json["nodes"]
288 assert {"name": "docs/api", "type": "dir"} in nodes
289 assert {"name": "docs/index.rst", "type": "file"} in nodes
325 290
326 291 def test_tree_search_at_path_2nd_level(self, backend, xhr_header):
327 292 commit = backend.repo.get_commit(commit_idx=173)
328 293 response = self.app.get(
329 route_path('repo_files_nodelist',
330 repo_name=backend.repo_name,
331 commit_id=commit.raw_id, f_path='/docs/api'),
332 extra_environ=xhr_header)
333 assert 'nodes' in response.json
334 nodes = response.json['nodes']
335 assert {'name': 'docs/api/index.rst', 'type': 'file'} in nodes
294 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs/api"),
295 extra_environ=xhr_header,
296 )
297 assert "nodes" in response.json
298 nodes = response.json["nodes"]
299 assert {"name": "docs/api/index.rst", "type": "file"} in nodes
336 300
337 301 def test_tree_search_at_path_missing_xhr(self, backend):
338 302 self.app.get(
339 route_path('repo_files_nodelist',
340 repo_name=backend.repo_name,
341 commit_id='tip', f_path='/docs'),
342 status=404)
303 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path="/docs"), status=404
304 )
343 305
344 306 def test_nodetree(self, backend, xhr_header):
345 307 commit = backend.repo.get_commit(commit_idx=173)
346 308 response = self.app.get(
347 route_path('repo_nodetree_full',
348 repo_name=backend.repo_name,
349 commit_id=commit.raw_id, f_path='/'),
350 extra_environ=xhr_header)
309 route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""),
310 extra_environ=xhr_header,
311 )
351 312
352 313 assert_response = response.assert_response()
353 314
354 for attr in ['data-commit-id', 'data-date', 'data-author']:
355 elements = assert_response.get_elements('[{}]'.format(attr))
315 for attr in ["data-commit-id", "data-date", "data-author"]:
316 elements = assert_response.get_elements("[{}]".format(attr))
356 317 assert len(elements) > 1
357 318
358 319 for element in elements:
@@ -361,124 +322,113 class TestFilesViews(object):
361 322 def test_nodetree_if_file(self, backend, xhr_header):
362 323 commit = backend.repo.get_commit(commit_idx=173)
363 324 response = self.app.get(
364 route_path('repo_nodetree_full',
365 repo_name=backend.repo_name,
366 commit_id=commit.raw_id, f_path='README.rst'),
367 extra_environ=xhr_header)
368 assert response.text == ''
325 route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="README.rst"),
326 extra_environ=xhr_header,
327 )
328 assert response.text == ""
369 329
370 330 def test_nodetree_wrong_path(self, backend, xhr_header):
371 331 commit = backend.repo.get_commit(commit_idx=173)
372 332 response = self.app.get(
373 route_path('repo_nodetree_full',
374 repo_name=backend.repo_name,
375 commit_id=commit.raw_id, f_path='/dont-exist'),
376 extra_environ=xhr_header)
333 route_path(
334 "repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/dont-exist"
335 ),
336 extra_environ=xhr_header,
337 )
377 338
378 err = 'error: There is no file nor ' \
379 'directory at the given path'
339 err = "error: There is no file nor " "directory at the given path"
380 340 assert err in response.text
381 341
382 342 def test_nodetree_missing_xhr(self, backend):
383 343 self.app.get(
384 route_path('repo_nodetree_full',
385 repo_name=backend.repo_name,
386 commit_id='tip', f_path='/'),
387 status=404)
344 route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404
345 )
388 346
389 347
390 348 @pytest.mark.usefixtures("app", "autologin_user")
391 349 class TestRawFileHandling(object):
392
393 350 def test_download_file(self, backend):
394 351 commit = backend.repo.get_commit(commit_idx=173)
395 352 response = self.app.get(
396 route_path('repo_file_download',
397 repo_name=backend.repo_name,
398 commit_id=commit.raw_id, f_path='vcs/nodes.py'),)
353 route_path(
354 "repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py"
355 ),
356 )
399 357
400 assert response.content_disposition == 'attachment; filename="nodes.py"; filename*=UTF-8\'\'nodes.py'
358 assert response.content_disposition == "attachment; filename=\"nodes.py\"; filename*=UTF-8''nodes.py"
401 359 assert response.content_type == "text/x-python"
402 360
403 361 def test_download_file_wrong_cs(self, backend):
404 raw_id = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
362 raw_id = "ERRORce30c96924232dffcd24178a07ffeb5dfc"
405 363
406 364 response = self.app.get(
407 route_path('repo_file_download',
408 repo_name=backend.repo_name,
409 commit_id=raw_id, f_path='vcs/nodes.svg'),
410 status=404)
365 route_path("repo_file_download", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"),
366 status=404,
367 )
411 368
412 369 msg = """No such commit exists for this repository"""
413 370 response.mustcontain(msg)
414 371
415 372 def test_download_file_wrong_f_path(self, backend):
416 373 commit = backend.repo.get_commit(commit_idx=173)
417 f_path = 'vcs/ERRORnodes.py'
374 f_path = "vcs/ERRORnodes.py"
418 375
419 376 response = self.app.get(
420 route_path('repo_file_download',
421 repo_name=backend.repo_name,
422 commit_id=commit.raw_id, f_path=f_path),
423 status=404)
377 route_path("repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path),
378 status=404,
379 )
424 380
425 msg = (
426 "There is no file nor directory at the given path: "
427 "`%s` at commit %s" % (f_path, commit.short_id))
381 msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id)
428 382 response.mustcontain(msg)
429 383
430 384 def test_file_raw(self, backend):
431 385 commit = backend.repo.get_commit(commit_idx=173)
432 386 response = self.app.get(
433 route_path('repo_file_raw',
434 repo_name=backend.repo_name,
435 commit_id=commit.raw_id, f_path='vcs/nodes.py'),)
387 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py"),
388 )
436 389
437 390 assert response.content_type == "text/plain"
438 391
439 392 def test_file_raw_binary(self, backend):
440 393 commit = backend.repo.get_commit()
441 394 response = self.app.get(
442 route_path('repo_file_raw',
443 repo_name=backend.repo_name,
444 commit_id=commit.raw_id,
445 f_path='docs/theme/ADC/static/breadcrumb_background.png'),)
395 route_path(
396 "repo_file_raw",
397 repo_name=backend.repo_name,
398 commit_id=commit.raw_id,
399 f_path="docs/theme/ADC/static/breadcrumb_background.png",
400 ),
401 )
446 402
447 assert response.content_disposition == 'inline'
403 assert response.content_disposition == "inline"
448 404
449 405 def test_raw_file_wrong_cs(self, backend):
450 raw_id = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
406 raw_id = "ERRORcce30c96924232dffcd24178a07ffeb5dfc"
451 407
452 408 response = self.app.get(
453 route_path('repo_file_raw',
454 repo_name=backend.repo_name,
455 commit_id=raw_id, f_path='vcs/nodes.svg'),
456 status=404)
409 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"),
410 status=404,
411 )
457 412
458 413 msg = """No such commit exists for this repository"""
459 414 response.mustcontain(msg)
460 415
461 416 def test_raw_wrong_f_path(self, backend):
462 417 commit = backend.repo.get_commit(commit_idx=173)
463 f_path = 'vcs/ERRORnodes.py'
418 f_path = "vcs/ERRORnodes.py"
464 419 response = self.app.get(
465 route_path('repo_file_raw',
466 repo_name=backend.repo_name,
467 commit_id=commit.raw_id, f_path=f_path),
468 status=404)
420 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path), status=404
421 )
469 422
470 msg = (
471 "There is no file nor directory at the given path: "
472 "`%s` at commit %s" % (f_path, commit.short_id))
423 msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id)
473 424 response.mustcontain(msg)
474 425
475 426 def test_raw_svg_should_not_be_rendered(self, backend):
476 427 backend.create_repo()
477 428 backend.ensure_file(b"xss.svg")
478 429 response = self.app.get(
479 route_path('repo_file_raw',
480 repo_name=backend.repo_name,
481 commit_id='tip', f_path='xss.svg'),)
430 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id="tip", f_path="xss.svg"),
431 )
482 432 # If the content type is image/svg+xml then it allows to render HTML
483 433 # and malicious SVG.
484 434 assert response.content_type == "text/plain"
@@ -486,25 +436,23 class TestRawFileHandling(object):
486 436
487 437 @pytest.mark.usefixtures("app")
488 438 class TestRepositoryArchival(object):
489
490 439 def test_archival(self, backend):
491 440 backend.enable_downloads()
492 441 commit = backend.repo.get_commit(commit_idx=173)
493 442
494 443 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
495 path_sha = get_path_sha('/')
496 filename = get_archive_name(backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha)
444 path_sha = get_path_sha("/")
445 filename = get_archive_name(
446 backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha
447 )
497 448
498 449 fname = commit.raw_id + extension
499 response = self.app.get(
500 route_path('repo_archivefile',
501 repo_name=backend.repo_name,
502 fname=fname))
450 response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname))
503 451
504 assert response.status == '200 OK'
452 assert response.status == "200 OK"
505 453 headers = [
506 ('Content-Disposition', f'attachment; filename={filename}'),
507 ('Content-Type', content_type),
454 ("Content-Disposition", f"attachment; filename={filename}"),
455 ("Content-Type", content_type),
508 456 ]
509 457
510 458 for header in headers:
@@ -514,19 +462,25 class TestRepositoryArchival(object):
514 462 backend.enable_downloads()
515 463 commit = backend.repo.get_commit(commit_idx=173)
516 464 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
517 path_sha = get_path_sha('/')
518 filename = get_archive_name(backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha, with_hash=False)
465 path_sha = get_path_sha("/")
466 filename = get_archive_name(
467 backend.repo_id,
468 backend.repo_name,
469 commit_sha=commit.short_id,
470 ext=extension,
471 path_sha=path_sha,
472 with_hash=False,
473 )
519 474
520 475 fname = commit.raw_id + extension
521 476 response = self.app.get(
522 route_path('repo_archivefile',
523 repo_name=backend.repo_name,
524 fname=fname, params={'with_hash': 0}))
477 route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"with_hash": 0})
478 )
525 479
526 assert response.status == '200 OK'
480 assert response.status == "200 OK"
527 481 headers = [
528 ('Content-Disposition', f'attachment; filename={filename}'),
529 ('Content-Type', content_type),
482 ("Content-Disposition", f"attachment; filename={filename}"),
483 ("Content-Type", content_type),
530 484 ]
531 485
532 486 for header in headers:
@@ -535,359 +489,332 class TestRepositoryArchival(object):
535 489 def test_archival_at_path(self, backend):
536 490 backend.enable_downloads()
537 491 commit = backend.repo.get_commit(commit_idx=190)
538 at_path = 'vcs'
492 at_path = "vcs"
539 493
540 494 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
541 495 path_sha = get_path_sha(at_path)
542 filename = get_archive_name(backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha)
496 filename = get_archive_name(
497 backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha
498 )
543 499
544 500 fname = commit.raw_id + extension
545 501 response = self.app.get(
546 route_path('repo_archivefile',
547 repo_name=backend.repo_name,
548 fname=fname, params={'at_path': at_path}))
502 route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"at_path": at_path})
503 )
549 504
550 assert response.status == '200 OK'
505 assert response.status == "200 OK"
551 506 headers = [
552 ('Content-Disposition', f'attachment; filename={filename}'),
553 ('Content-Type', content_type),
507 ("Content-Disposition", f"attachment; filename={filename}"),
508 ("Content-Type", content_type),
554 509 ]
555 510
556 511 for header in headers:
557 512 assert header in list(response.headers.items())
558 513
559 @pytest.mark.parametrize('arch_ext',[
560 'tar', 'rar', 'x', '..ax', '.zipz', 'tar.gz.tar'])
514 @pytest.mark.parametrize("arch_ext", ["tar", "rar", "x", "..ax", ".zipz", "tar.gz.tar"])
561 515 def test_archival_wrong_ext(self, backend, arch_ext):
562 516 backend.enable_downloads()
563 517 commit = backend.repo.get_commit(commit_idx=173)
564 518
565 fname = commit.raw_id + '.' + arch_ext
519 fname = commit.raw_id + "." + arch_ext
566 520
567 response = self.app.get(
568 route_path('repo_archivefile',
569 repo_name=backend.repo_name,
570 fname=fname))
571 response.mustcontain(
572 'Unknown archive type for: `{}`'.format(fname))
521 response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname))
522 response.mustcontain("Unknown archive type for: `{}`".format(fname))
573 523
574 @pytest.mark.parametrize('commit_id', [
575 '00x000000', 'tar', 'wrong', '@$@$42413232', '232dffcd'])
524 @pytest.mark.parametrize("commit_id", ["00x000000", "tar", "wrong", "@$@$42413232", "232dffcd"])
576 525 def test_archival_wrong_commit_id(self, backend, commit_id):
577 526 backend.enable_downloads()
578 fname = f'{commit_id}.zip'
527 fname = f"{commit_id}.zip"
579 528
580 response = self.app.get(
581 route_path('repo_archivefile',
582 repo_name=backend.repo_name,
583 fname=fname))
584 response.mustcontain('Unknown commit_id')
529 response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname))
530 response.mustcontain("Unknown commit_id")
585 531
586 532
587 533 @pytest.mark.usefixtures("app")
588 534 class TestFilesDiff(object):
589
590 @pytest.mark.parametrize("diff", ['diff', 'download', 'raw'])
535 @pytest.mark.parametrize("diff", ["diff", "download", "raw"])
591 536 def test_file_full_diff(self, backend, diff):
592 537 commit1 = backend.repo.get_commit(commit_idx=-1)
593 538 commit2 = backend.repo.get_commit(commit_idx=-2)
594 539
595 540 response = self.app.get(
596 route_path('repo_files_diff',
597 repo_name=backend.repo_name,
598 f_path='README'),
541 route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README"),
599 542 params={
600 'diff1': commit2.raw_id,
601 'diff2': commit1.raw_id,
602 'fulldiff': '1',
603 'diff': diff,
604 })
543 "diff1": commit2.raw_id,
544 "diff2": commit1.raw_id,
545 "fulldiff": "1",
546 "diff": diff,
547 },
548 )
605 549
606 if diff == 'diff':
550 if diff == "diff":
607 551 # use redirect since this is OLD view redirecting to compare page
608 552 response = response.follow()
609 553
610 554 # It's a symlink to README.rst
611 response.mustcontain('README.rst')
612 response.mustcontain('No newline at end of file')
555 response.mustcontain("README.rst")
556 response.mustcontain("No newline at end of file")
613 557
614 558 def test_file_binary_diff(self, backend):
615 559 commits = [
616 {'message': 'First commit'},
617 {'message': 'Commit with binary',
618 'added': [nodes.FileNode(b'file.bin', content='\0BINARY\0')]},
560 {"message": "First commit"},
561 {"message": "Commit with binary", "added": [nodes.FileNode(b"file.bin", content="\0BINARY\0")]},
619 562 ]
620 563 repo = backend.create_repo(commits=commits)
621 564
622 565 response = self.app.get(
623 route_path('repo_files_diff',
624 repo_name=backend.repo_name,
625 f_path='file.bin'),
566 route_path("repo_files_diff", repo_name=backend.repo_name, f_path="file.bin"),
626 567 params={
627 'diff1': repo.get_commit(commit_idx=0).raw_id,
628 'diff2': repo.get_commit(commit_idx=1).raw_id,
629 'fulldiff': '1',
630 'diff': 'diff',
631 })
568 "diff1": repo.get_commit(commit_idx=0).raw_id,
569 "diff2": repo.get_commit(commit_idx=1).raw_id,
570 "fulldiff": "1",
571 "diff": "diff",
572 },
573 )
632 574 # use redirect since this is OLD view redirecting to compare page
633 575 response = response.follow()
634 response.mustcontain('Collapse 1 commit')
576 response.mustcontain("Collapse 1 commit")
635 577 file_changes = (1, 0, 0)
636 578
637 579 compare_page = ComparePage(response)
638 580 compare_page.contains_change_summary(*file_changes)
639 581
640 if backend.alias == 'svn':
641 response.mustcontain('new file 10644')
582 if backend.alias == "svn":
583 response.mustcontain("new file 10644")
642 584 # TODO(marcink): SVN doesn't yet detect binary changes
643 585 else:
644 response.mustcontain('new file 100644')
645 response.mustcontain('binary diff hidden')
586 response.mustcontain("new file 100644")
587 response.mustcontain("binary diff hidden")
646 588
647 589 def test_diff_2way(self, backend):
648 590 commit1 = backend.repo.get_commit(commit_idx=-1)
649 591 commit2 = backend.repo.get_commit(commit_idx=-2)
650 592 response = self.app.get(
651 route_path('repo_files_diff_2way_redirect',
652 repo_name=backend.repo_name,
653 f_path='README'),
593 route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"),
654 594 params={
655 'diff1': commit2.raw_id,
656 'diff2': commit1.raw_id,
657 })
595 "diff1": commit2.raw_id,
596 "diff2": commit1.raw_id,
597 },
598 )
658 599 # use redirect since this is OLD view redirecting to compare page
659 600 response = response.follow()
660 601
661 602 # It's a symlink to README.rst
662 response.mustcontain('README.rst')
663 response.mustcontain('No newline at end of file')
603 response.mustcontain("README.rst")
604 response.mustcontain("No newline at end of file")
664 605
665 606 def test_requires_one_commit_id(self, backend, autologin_user):
666 607 response = self.app.get(
667 route_path('repo_files_diff',
668 repo_name=backend.repo_name,
669 f_path='README.rst'),
670 status=400)
671 response.mustcontain(
672 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
608 route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README.rst"), status=400
609 )
610 response.mustcontain("Need query parameter", "diff1", "diff2", "to generate a diff.")
673 611
674 612 def test_returns_no_files_if_file_does_not_exist(self, vcsbackend):
675 613 repo = vcsbackend.repo
676 614 response = self.app.get(
677 route_path('repo_files_diff',
678 repo_name=repo.name,
679 f_path='does-not-exist-in-any-commit'),
680 params={
681 'diff1': repo[0].raw_id,
682 'diff2': repo[1].raw_id
683 })
615 route_path("repo_files_diff", repo_name=repo.name, f_path="does-not-exist-in-any-commit"),
616 params={"diff1": repo[0].raw_id, "diff2": repo[1].raw_id},
617 )
684 618
685 619 response = response.follow()
686 response.mustcontain('No files')
620 response.mustcontain("No files")
687 621
688 622 def test_returns_redirect_if_file_not_changed(self, backend):
689 623 commit = backend.repo.get_commit(commit_idx=-1)
690 624 response = self.app.get(
691 route_path('repo_files_diff_2way_redirect',
692 repo_name=backend.repo_name,
693 f_path='README'),
625 route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"),
694 626 params={
695 'diff1': commit.raw_id,
696 'diff2': commit.raw_id,
697 })
627 "diff1": commit.raw_id,
628 "diff2": commit.raw_id,
629 },
630 )
698 631
699 632 response = response.follow()
700 response.mustcontain('No files')
701 response.mustcontain('No commits in this compare')
633 response.mustcontain("No files")
634 response.mustcontain("No commits in this compare")
702 635
703 636 def test_supports_diff_to_different_path_svn(self, backend_svn):
704 #TODO: check this case
637 # TODO: check this case
705 638 return
706 639
707 repo = backend_svn['svn-simple-layout'].scm_instance()
708 commit_id_1 = '24'
709 commit_id_2 = '26'
640 repo = backend_svn["svn-simple-layout"].scm_instance()
641 commit_id_1 = "24"
642 commit_id_2 = "26"
710 643
711 644 response = self.app.get(
712 route_path('repo_files_diff',
713 repo_name=backend_svn.repo_name,
714 f_path='trunk/example.py'),
645 route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"),
715 646 params={
716 'diff1': 'tags/v0.2/example.py@' + commit_id_1,
717 'diff2': commit_id_2,
718 })
647 "diff1": "tags/v0.2/example.py@" + commit_id_1,
648 "diff2": commit_id_2,
649 },
650 )
719 651
720 652 response = response.follow()
721 653 response.mustcontain(
722 654 # diff contains this
723 "Will print out a useful message on invocation.")
655 "Will print out a useful message on invocation."
656 )
724 657
725 658 # Note: Expecting that we indicate the user what's being compared
726 659 response.mustcontain("trunk/example.py")
727 660 response.mustcontain("tags/v0.2/example.py")
728 661
729 662 def test_show_rev_redirects_to_svn_path(self, backend_svn):
730 #TODO: check this case
663 # TODO: check this case
731 664 return
732 665
733 repo = backend_svn['svn-simple-layout'].scm_instance()
666 repo = backend_svn["svn-simple-layout"].scm_instance()
734 667 commit_id = repo[-1].raw_id
735 668
736 669 response = self.app.get(
737 route_path('repo_files_diff',
738 repo_name=backend_svn.repo_name,
739 f_path='trunk/example.py'),
670 route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"),
740 671 params={
741 'diff1': 'branches/argparse/example.py@' + commit_id,
742 'diff2': commit_id,
672 "diff1": "branches/argparse/example.py@" + commit_id,
673 "diff2": commit_id,
743 674 },
744 status=302)
675 status=302,
676 )
745 677 response = response.follow()
746 assert response.headers['Location'].endswith(
747 'svn-svn-simple-layout/files/26/branches/argparse/example.py')
678 assert response.headers["Location"].endswith("svn-svn-simple-layout/files/26/branches/argparse/example.py")
748 679
749 680 def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
750 #TODO: check this case
681 # TODO: check this case
751 682 return
752 683
753 repo = backend_svn['svn-simple-layout'].scm_instance()
684 repo = backend_svn["svn-simple-layout"].scm_instance()
754 685 commit_id = repo[-1].raw_id
755 686 response = self.app.get(
756 route_path('repo_files_diff',
757 repo_name=backend_svn.repo_name,
758 f_path='trunk/example.py'),
687 route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"),
759 688 params={
760 'diff1': 'branches/argparse/example.py@' + commit_id,
761 'diff2': commit_id,
762 'show_rev': 'Show at Revision',
763 'annotate': 'true',
689 "diff1": "branches/argparse/example.py@" + commit_id,
690 "diff2": commit_id,
691 "show_rev": "Show at Revision",
692 "annotate": "true",
764 693 },
765 status=302)
694 status=302,
695 )
766 696 response = response.follow()
767 assert response.headers['Location'].endswith(
768 'svn-svn-simple-layout/annotate/26/branches/argparse/example.py')
697 assert response.headers["Location"].endswith("svn-svn-simple-layout/annotate/26/branches/argparse/example.py")
769 698
770 699
771 700 @pytest.mark.usefixtures("app", "autologin_user")
772 701 class TestModifyFilesWithWebInterface(object):
773
774 702 def test_add_file_view(self, backend):
775 self.app.get(
776 route_path('repo_files_add_file',
777 repo_name=backend.repo_name,
778 commit_id='tip', f_path='/')
779 )
703 self.app.get(route_path("repo_files_add_file", repo_name=backend.repo_name, commit_id="tip", f_path=""))
780 704
781 705 @pytest.mark.xfail_backends("svn", reason="Depends on online editing")
782 706 def test_add_file_into_repo_missing_content(self, backend, csrf_token):
783 707 backend.create_repo()
784 filename = 'init.py'
708 filename = "init.py"
785 709 response = self.app.post(
786 route_path('repo_files_create_file',
787 repo_name=backend.repo_name,
788 commit_id='tip', f_path='/'),
710 route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id="tip", f_path=""),
789 711 params={
790 'content': "",
791 'filename': filename,
792 'csrf_token': csrf_token,
712 "content": "",
713 "filename": filename,
714 "csrf_token": csrf_token,
793 715 },
794 status=302)
795 expected_msg = 'Successfully committed new file `{}`'.format(os.path.join(filename))
716 status=302,
717 )
718 expected_msg = "Successfully committed new file `{}`".format(os.path.join(filename))
796 719 assert_session_flash(response, expected_msg)
797 720
798 721 def test_add_file_into_repo_missing_filename(self, backend, csrf_token):
799 722 commit_id = backend.repo.get_commit().raw_id
800 723 response = self.app.post(
801 route_path('repo_files_create_file',
802 repo_name=backend.repo_name,
803 commit_id=commit_id, f_path='/'),
724 route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id=commit_id, f_path=""),
804 725 params={
805 'content': "foo",
806 'csrf_token': csrf_token,
726 "content": "foo",
727 "csrf_token": csrf_token,
807 728 },
808 status=302)
729 status=302,
730 )
809 731
810 assert_session_flash(response, 'No filename specified')
732 assert_session_flash(response, "No filename specified")
811 733
812 def test_add_file_into_repo_errors_and_no_commits(
813 self, backend, csrf_token):
734 def test_add_file_into_repo_errors_and_no_commits(self, backend, csrf_token):
814 735 repo = backend.create_repo()
815 736 # Create a file with no filename, it will display an error but
816 737 # the repo has no commits yet
817 738 response = self.app.post(
818 route_path('repo_files_create_file',
819 repo_name=repo.repo_name,
820 commit_id='tip', f_path='/'),
739 route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id="tip", f_path=""),
821 740 params={
822 'content': "foo",
823 'csrf_token': csrf_token,
741 "content": "foo",
742 "csrf_token": csrf_token,
824 743 },
825 status=302)
744 status=302,
745 )
826 746
827 assert_session_flash(response, 'No filename specified')
747 assert_session_flash(response, "No filename specified")
828 748
829 749 # Not allowed, redirect to the summary
830 750 redirected = response.follow()
831 summary_url = h.route_path('repo_summary', repo_name=repo.repo_name)
751 summary_url = h.route_path("repo_summary", repo_name=repo.repo_name)
832 752
833 753 # As there are no commits, displays the summary page with the error of
834 754 # creating a file with no filename
835 755
836 756 assert redirected.request.path == summary_url
837 757
838 @pytest.mark.parametrize("filename, clean_filename", [
839 ('/abs/foo', 'abs/foo'),
840 ('../rel/foo', 'rel/foo'),
841 ('file/../foo/foo', 'file/foo/foo'),
842 ])
758 @pytest.mark.parametrize(
759 "filename, clean_filename",
760 [
761 ("/abs/foo", "abs/foo"),
762 ("../rel/foo", "rel/foo"),
763 ("file/../foo/foo", "file/foo/foo"),
764 ],
765 )
843 766 def test_add_file_into_repo_bad_filenames(self, filename, clean_filename, backend, csrf_token):
844 767 repo = backend.create_repo()
845 768 commit_id = repo.get_commit().raw_id
846 769
847 770 response = self.app.post(
848 route_path('repo_files_create_file',
849 repo_name=repo.repo_name,
850 commit_id=commit_id, f_path='/'),
771 route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""),
851 772 params={
852 'content': "foo",
853 'filename': filename,
854 'csrf_token': csrf_token,
773 "content": "foo",
774 "filename": filename,
775 "csrf_token": csrf_token,
855 776 },
856 status=302)
777 status=302,
778 )
857 779
858 expected_msg = 'Successfully committed new file `{}`'.format(clean_filename)
780 expected_msg = "Successfully committed new file `{}`".format(clean_filename)
859 781 assert_session_flash(response, expected_msg)
860 782
861 @pytest.mark.parametrize("cnt, filename, content", [
862 (1, 'foo.txt', "Content"),
863 (2, 'dir/foo.rst', "Content"),
864 (3, 'dir/foo-second.rst', "Content"),
865 (4, 'rel/dir/foo.bar', "Content"),
866 ])
783 @pytest.mark.parametrize(
784 "cnt, filename, content",
785 [
786 (1, "foo.txt", "Content"),
787 (2, "dir/foo.rst", "Content"),
788 (3, "dir/foo-second.rst", "Content"),
789 (4, "rel/dir/foo.bar", "Content"),
790 ],
791 )
867 792 def test_add_file_into_empty_repo(self, cnt, filename, content, backend, csrf_token):
868 793 repo = backend.create_repo()
869 794 commit_id = repo.get_commit().raw_id
870 795 response = self.app.post(
871 route_path('repo_files_create_file',
872 repo_name=repo.repo_name,
873 commit_id=commit_id, f_path='/'),
796 route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""),
874 797 params={
875 'content': content,
876 'filename': filename,
877 'csrf_token': csrf_token,
798 "content": content,
799 "filename": filename,
800 "csrf_token": csrf_token,
878 801 },
879 status=302)
802 status=302,
803 )
880 804
881 expected_msg = 'Successfully committed new file `{}`'.format(filename)
805 expected_msg = "Successfully committed new file `{}`".format(filename)
882 806 assert_session_flash(response, expected_msg)
883 807
884 808 def test_edit_file_view(self, backend):
885 809 response = self.app.get(
886 route_path('repo_files_edit_file',
887 repo_name=backend.repo_name,
888 commit_id=backend.default_head_id,
889 f_path='vcs/nodes.py'),
890 status=200)
810 route_path(
811 "repo_files_edit_file",
812 repo_name=backend.repo_name,
813 commit_id=backend.default_head_id,
814 f_path="vcs/nodes.py",
815 ),
816 status=200,
817 )
891 818 response.mustcontain("Module holding everything related to vcs nodes.")
892 819
893 820 def test_edit_file_view_not_on_branch(self, backend):
@@ -895,229 +822,221 class TestModifyFilesWithWebInterface(ob
895 822 backend.ensure_file(b"vcs/nodes.py")
896 823
897 824 response = self.app.get(
898 route_path('repo_files_edit_file',
899 repo_name=repo.repo_name,
900 commit_id='tip',
901 f_path='vcs/nodes.py'),
902 status=302)
903 assert_session_flash(
904 response, 'Cannot modify file. Given commit `tip` is not head of a branch.')
825 route_path("repo_files_edit_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"),
826 status=302,
827 )
828 assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.")
905 829
906 830 def test_edit_file_view_commit_changes(self, backend, csrf_token):
907 831 repo = backend.create_repo()
908 832 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
909 833
910 834 response = self.app.post(
911 route_path('repo_files_update_file',
912 repo_name=repo.repo_name,
913 commit_id=backend.default_head_id,
914 f_path='vcs/nodes.py'),
835 route_path(
836 "repo_files_update_file",
837 repo_name=repo.repo_name,
838 commit_id=backend.default_head_id,
839 f_path="vcs/nodes.py",
840 ),
915 841 params={
916 'content': "print 'hello world'",
917 'message': 'I committed',
918 'filename': "vcs/nodes.py",
919 'csrf_token': csrf_token,
842 "content": "print 'hello world'",
843 "message": "I committed",
844 "filename": "vcs/nodes.py",
845 "csrf_token": csrf_token,
920 846 },
921 status=302)
922 assert_session_flash(
923 response, 'Successfully committed changes to file `vcs/nodes.py`')
847 status=302,
848 )
849 assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`")
924 850 tip = repo.get_commit(commit_idx=-1)
925 assert tip.message == 'I committed'
851 assert tip.message == "I committed"
926 852
927 853 def test_replace_binary_file_view_commit_changes(self, backend, csrf_token):
928 854 repo = backend.create_repo()
929 855 backend.ensure_file(b"vcs/nodes.docx", content=b"PREVIOUS CONTENT'")
930 856
931 857 response = self.app.post(
932 route_path('repo_files_replace_binary',
933 repo_name=repo.repo_name,
934 commit_id=backend.default_head_id,
935 f_path='vcs/nodes.docx'),
858 route_path(
859 "repo_files_replace_binary",
860 repo_name=repo.repo_name,
861 commit_id=backend.default_head_id,
862 f_path="vcs/nodes.docx",
863 ),
936 864 params={
937 'message': 'I committed',
938 'csrf_token': csrf_token,
865 "message": "I committed",
866 "csrf_token": csrf_token,
939 867 },
940 upload_files=[('files_upload', 'vcs/nodes.docx', b'SOME CONTENT')],
941 status=200)
942 assert_session_flash(
943 response, 'Successfully committed 1 new file')
868 upload_files=[("files_upload", "vcs/nodes.docx", b"SOME CONTENT")],
869 status=200,
870 )
871 assert_session_flash(response, "Successfully committed 1 new file")
944 872 tip = repo.get_commit(commit_idx=-1)
945 assert tip.message == 'I committed'
873 assert tip.message == "I committed"
946 874
947 def test_edit_file_view_commit_changes_default_message(self, backend,
948 csrf_token):
875 def test_edit_file_view_commit_changes_default_message(self, backend, csrf_token):
949 876 repo = backend.create_repo()
950 877 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
951 878
952 commit_id = (
953 backend.default_branch_name or
954 backend.repo.scm_instance().commit_ids[-1])
879 commit_id = backend.default_branch_name or backend.repo.scm_instance().commit_ids[-1]
955 880
956 881 response = self.app.post(
957 route_path('repo_files_update_file',
958 repo_name=repo.repo_name,
959 commit_id=commit_id,
960 f_path='vcs/nodes.py'),
882 route_path("repo_files_update_file", repo_name=repo.repo_name, commit_id=commit_id, f_path="vcs/nodes.py"),
961 883 params={
962 'content': "print 'hello world'",
963 'message': '',
964 'filename': "vcs/nodes.py",
965 'csrf_token': csrf_token,
884 "content": "print 'hello world'",
885 "message": "",
886 "filename": "vcs/nodes.py",
887 "csrf_token": csrf_token,
966 888 },
967 status=302)
968 assert_session_flash(
969 response, 'Successfully committed changes to file `vcs/nodes.py`')
889 status=302,
890 )
891 assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`")
970 892 tip = repo.get_commit(commit_idx=-1)
971 assert tip.message == 'Edited file vcs/nodes.py via RhodeCode Enterprise'
893 assert tip.message == "Edited file vcs/nodes.py via RhodeCode Enterprise"
972 894
973 895 def test_replace_binary_file_content_with_content_that_not_belong_to_original_type(self, backend, csrf_token):
974 896 repo = backend.create_repo()
975 897 backend.ensure_file(b"vcs/sheet.xlsx", content=b"PREVIOUS CONTENT'")
976 898
977 899 response = self.app.post(
978 route_path('repo_files_replace_binary',
979 repo_name=repo.repo_name,
980 commit_id=backend.default_head_id,
981 f_path='vcs/sheet.xlsx'),
900 route_path(
901 "repo_files_replace_binary",
902 repo_name=repo.repo_name,
903 commit_id=backend.default_head_id,
904 f_path="vcs/sheet.xlsx",
905 ),
982 906 params={
983 'message': 'I committed',
984 'csrf_token': csrf_token,
907 "message": "I committed",
908 "csrf_token": csrf_token,
985 909 },
986 upload_files=[('files_upload', 'vcs/sheet.docx', b'SOME CONTENT')],
987 status=200)
988 assert response.json['error'] == "file extension of uploaded file doesn't match an original file's extension"
910 upload_files=[("files_upload", "vcs/sheet.docx", b"SOME CONTENT")],
911 status=200,
912 )
913 assert response.json["error"] == "file extension of uploaded file doesn't match an original file's extension"
989 914
990 @pytest.mark.parametrize("replacement_files, expected_error", [
991 ([], 'missing files'),
992 (
993 [('files_upload', 'vcs/node1.docx', b'SOME CONTENT'),
994 ('files_upload', 'vcs/node2.docx', b'SOME CONTENT')],
995 'too many files for replacement'),
996 ])
997 def test_replace_binary_with_wrong_amount_of_content_sources(self, replacement_files, expected_error, backend,
998 csrf_token):
915 @pytest.mark.parametrize(
916 "replacement_files, expected_error",
917 [
918 ([], "missing files"),
919 (
920 [
921 ("files_upload", "vcs/node1.docx", b"SOME CONTENT"),
922 ("files_upload", "vcs/node2.docx", b"SOME CONTENT"),
923 ],
924 "too many files for replacement",
925 ),
926 ],
927 )
928 def test_replace_binary_with_wrong_amount_of_content_sources(
929 self, replacement_files, expected_error, backend, csrf_token
930 ):
999 931 repo = backend.create_repo()
1000 932 backend.ensure_file(b"vcs/node.docx", content=b"PREVIOUS CONTENT'")
1001 933
1002 934 response = self.app.post(
1003 route_path('repo_files_replace_binary',
1004 repo_name=repo.repo_name,
1005 commit_id=backend.default_head_id,
1006 f_path='vcs/node.docx'),
935 route_path(
936 "repo_files_replace_binary",
937 repo_name=repo.repo_name,
938 commit_id=backend.default_head_id,
939 f_path="vcs/node.docx",
940 ),
1007 941 params={
1008 'message': 'I committed',
1009 'csrf_token': csrf_token,
942 "message": "I committed",
943 "csrf_token": csrf_token,
1010 944 },
1011 945 upload_files=replacement_files,
1012 status=200)
1013 assert response.json['error'] == expected_error
946 status=200,
947 )
948 assert response.json["error"] == expected_error
1014 949
1015 950 def test_delete_file_view(self, backend):
1016 951 self.app.get(
1017 route_path('repo_files_remove_file',
1018 repo_name=backend.repo_name,
1019 commit_id=backend.default_head_id,
1020 f_path='vcs/nodes.py'),
1021 status=200)
952 route_path(
953 "repo_files_remove_file",
954 repo_name=backend.repo_name,
955 commit_id=backend.default_head_id,
956 f_path="vcs/nodes.py",
957 ),
958 status=200,
959 )
1022 960
1023 961 def test_delete_file_view_not_on_branch(self, backend):
1024 962 repo = backend.create_repo()
1025 backend.ensure_file(b'vcs/nodes.py')
963 backend.ensure_file(b"vcs/nodes.py")
1026 964
1027 965 response = self.app.get(
1028 route_path('repo_files_remove_file',
1029 repo_name=repo.repo_name,
1030 commit_id='tip',
1031 f_path='vcs/nodes.py'),
1032 status=302)
1033 assert_session_flash(
1034 response, 'Cannot modify file. Given commit `tip` is not head of a branch.')
966 route_path("repo_files_remove_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"),
967 status=302,
968 )
969 assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.")
1035 970
1036 971 def test_delete_file_view_commit_changes(self, backend, csrf_token):
1037 972 repo = backend.create_repo()
1038 973 backend.ensure_file(b"vcs/nodes.py")
1039 974
1040 975 response = self.app.post(
1041 route_path('repo_files_delete_file',
1042 repo_name=repo.repo_name,
1043 commit_id=backend.default_head_id,
1044 f_path='vcs/nodes.py'),
976 route_path(
977 "repo_files_delete_file",
978 repo_name=repo.repo_name,
979 commit_id=backend.default_head_id,
980 f_path="vcs/nodes.py",
981 ),
1045 982 params={
1046 'message': 'i committed',
1047 'csrf_token': csrf_token,
983 "message": "i committed",
984 "csrf_token": csrf_token,
1048 985 },
1049 status=302)
1050 assert_session_flash(
1051 response, 'Successfully deleted file `vcs/nodes.py`')
986 status=302,
987 )
988 assert_session_flash(response, "Successfully deleted file `vcs/nodes.py`")
1052 989
1053 990
1054 991 @pytest.mark.usefixtures("app")
1055 992 class TestFilesViewOtherCases(object):
1056
1057 993 def test_access_empty_repo_redirect_to_summary_with_alert_write_perms(
1058 self, backend_stub, autologin_regular_user, user_regular,
1059 user_util):
1060
994 self, backend_stub, autologin_regular_user, user_regular, user_util
995 ):
1061 996 repo = backend_stub.create_repo()
1062 user_util.grant_user_permission_to_repo(
1063 repo, user_regular, 'repository.write')
1064 response = self.app.get(
1065 route_path('repo_files',
1066 repo_name=repo.repo_name,
1067 commit_id='tip', f_path='/'))
997 user_util.grant_user_permission_to_repo(repo, user_regular, "repository.write")
998 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
1068 999
1069 repo_file_add_url = route_path(
1070 'repo_files_add_file',
1071 repo_name=repo.repo_name,
1072 commit_id=0, f_path='')
1000 repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="")
1073 1001 add_new = f'<a class="alert-link" href="{repo_file_add_url}">add a new file</a>'
1074 1002
1075 repo_file_upload_url = route_path(
1076 'repo_files_upload_file',
1077 repo_name=repo.repo_name,
1078 commit_id=0, f_path='')
1003 repo_file_upload_url = route_path("repo_files_upload_file", repo_name=repo.repo_name, commit_id=0, f_path="")
1079 1004 upload_new = f'<a class="alert-link" href="{repo_file_upload_url}">upload a new file</a>'
1080 1005
1081 assert_session_flash(
1082 response,
1083 'There are no files yet. Click here to %s or %s.' % (add_new, upload_new)
1084 )
1006 assert_session_flash(response, "There are no files yet. Click here to %s or %s." % (add_new, upload_new))
1085 1007
1086 1008 def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms(
1087 self, backend_stub, autologin_regular_user):
1009 self, backend_stub, autologin_regular_user
1010 ):
1088 1011 repo = backend_stub.create_repo()
1089 1012 # init session for anon user
1090 route_path('repo_summary', repo_name=repo.repo_name)
1013 route_path("repo_summary", repo_name=repo.repo_name)
1091 1014
1092 repo_file_add_url = route_path(
1093 'repo_files_add_file',
1094 repo_name=repo.repo_name,
1095 commit_id=0, f_path='')
1015 repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="")
1096 1016
1097 response = self.app.get(
1098 route_path('repo_files',
1099 repo_name=repo.repo_name,
1100 commit_id='tip', f_path='/'))
1017 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
1101 1018
1102 1019 assert_session_flash(response, no_=repo_file_add_url)
1103 1020
1104 @pytest.mark.parametrize('file_node', [
1105 b'archive/file.zip',
1106 b'diff/my-file.txt',
1107 b'render.py',
1108 b'render',
1109 b'remove_file',
1110 b'remove_file/to-delete.txt',
1111 ])
1021 @pytest.mark.parametrize(
1022 "file_node",
1023 [
1024 b"archive/file.zip",
1025 b"diff/my-file.txt",
1026 b"render.py",
1027 b"render",
1028 b"remove_file",
1029 b"remove_file/to-delete.txt",
1030 ],
1031 )
1112 1032 def test_file_names_equal_to_routes_parts(self, backend, file_node):
1113 1033 backend.create_repo()
1114 1034 backend.ensure_file(file_node)
1115 1035
1116 1036 self.app.get(
1117 route_path('repo_files',
1118 repo_name=backend.repo_name,
1119 commit_id='tip', f_path=safe_str(file_node)),
1120 status=200)
1037 route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=safe_str(file_node)),
1038 status=200,
1039 )
1121 1040
1122 1041
1123 1042 class TestAdjustFilePathForSvn(object):
@@ -1126,20 +1045,20 class TestAdjustFilePathForSvn(object):
1126 1045 """
1127 1046
1128 1047 def test_returns_path_relative_to_matched_reference(self):
1129 repo = self._repo(branches=['trunk'])
1130 self.assert_file_adjustment('trunk/file', 'file', repo)
1048 repo = self._repo(branches=["trunk"])
1049 self.assert_file_adjustment("trunk/file", "file", repo)
1131 1050
1132 1051 def test_does_not_modify_file_if_no_reference_matches(self):
1133 repo = self._repo(branches=['trunk'])
1134 self.assert_file_adjustment('notes/file', 'notes/file', repo)
1052 repo = self._repo(branches=["trunk"])
1053 self.assert_file_adjustment("notes/file", "notes/file", repo)
1135 1054
1136 1055 def test_does_not_adjust_partial_directory_names(self):
1137 repo = self._repo(branches=['trun'])
1138 self.assert_file_adjustment('trunk/file', 'trunk/file', repo)
1056 repo = self._repo(branches=["trun"])
1057 self.assert_file_adjustment("trunk/file", "trunk/file", repo)
1139 1058
1140 1059 def test_is_robust_to_patterns_which_prefix_other_patterns(self):
1141 repo = self._repo(branches=['trunk', 'trunk/new', 'trunk/old'])
1142 self.assert_file_adjustment('trunk/new/file', 'file', repo)
1060 repo = self._repo(branches=["trunk", "trunk/new", "trunk/old"])
1061 self.assert_file_adjustment("trunk/new/file", "file", repo)
1143 1062
1144 1063 def assert_file_adjustment(self, f_path, expected, repo):
1145 1064 result = RepoFilesView.adjust_file_path_for_svn(f_path, repo)
@@ -1147,6 +1066,6 class TestAdjustFilePathForSvn(object):
1147 1066
1148 1067 def _repo(self, branches=None):
1149 1068 repo = mock.Mock()
1150 repo.branches = OrderedDict((name, '0') for name in branches or [])
1069 repo.branches = OrderedDict((name, "0") for name in branches or [])
1151 1070 repo.tags = {}
1152 1071 return repo
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +20,7 import pytest
21 20
22 21 from rhodecode.tests import TestController, assert_session_flash, HG_FORK, GIT_FORK
23 22
24 from rhodecode.tests.fixture import Fixture
23 from rhodecode.tests.fixtures.rc_fixture import Fixture
25 24 from rhodecode.lib import helpers as h
26 25
27 26 from rhodecode.model.db import Repository
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +20,7 import pytest
21 20
22 21 from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
23 22
24 from rhodecode.tests.fixture import Fixture
23 from rhodecode.tests.fixtures.rc_fixture import Fixture
25 24 from rhodecode.tests.routes import route_path
26 25
27 26 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -15,6 +15,9
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import logging
19 import os
20
18 21 import mock
19 22 import pytest
20 23
@@ -41,7 +44,7 from rhodecode.tests import (
41 44 TEST_USER_ADMIN_LOGIN,
42 45 TEST_USER_REGULAR_LOGIN,
43 46 )
44 from rhodecode.tests.fixture_mods.fixture_utils import PRTestUtility
47 from rhodecode.tests.fixtures.fixture_utils import PRTestUtility
45 48 from rhodecode.tests.routes import route_path
46 49
47 50
@@ -1050,7 +1053,6 class TestPullrequestsView(object):
1050 1053 )
1051 1054 assert len(notifications.all()) == 2
1052 1055
1053 @pytest.mark.xfail(reason="unable to fix this test after python3 migration")
1054 1056 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
1055 1057 commits = [
1056 1058 {
@@ -1125,20 +1127,38 class TestPullrequestsView(object):
1125 1127 response.mustcontain(no=["content_of_ancestor-child"])
1126 1128 response.mustcontain("content_of_change")
1127 1129
1128 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
1129 # Clear any previous calls to rcextensions
1130 rhodecode.EXTENSIONS.calls.clear()
1130 def test_merge_pull_request_enabled(self, pr_util, csrf_token, rcextensions_modification):
1131 1131
1132 1132 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
1133 1133 pull_request_id = pull_request.pull_request_id
1134 repo_name = (pull_request.target_repo.scm_instance().name,)
1134 repo_name = pull_request.target_repo.scm_instance().name
1135 1135
1136 1136 url = route_path(
1137 1137 "pullrequest_merge",
1138 repo_name=str(repo_name[0]),
1138 repo_name=repo_name,
1139 1139 pull_request_id=pull_request_id,
1140 1140 )
1141 response = self.app.post(url, params={"csrf_token": csrf_token}).follow()
1141
1142 rcstack_location = os.path.dirname(self.app._pyramid_registry.settings['__file__'])
1143 rc_ext_location = os.path.join(rcstack_location, 'rcextension-output.txt')
1144
1145
1146 mods = [
1147 ('_push_hook',
1148 f"""
1149 import os
1150 action = kwargs['action']
1151 commit_ids = kwargs['commit_ids']
1152 with open('{rc_ext_location}', 'w') as f:
1153 f.write('test-execution'+os.linesep)
1154 f.write(f'{{action}}'+os.linesep)
1155 f.write(f'{{commit_ids}}'+os.linesep)
1156 return HookResponse(0, 'HOOK_TEST')
1157 """)
1158 ]
1159 # Add the hook
1160 with rcextensions_modification(rcstack_location, mods, create_if_missing=True, force_create=True):
1161 response = self.app.post(url, params={"csrf_token": csrf_token}).follow()
1142 1162
1143 1163 pull_request = PullRequest.get(pull_request_id)
1144 1164
@@ -1162,12 +1182,39 class TestPullrequestsView(object):
1162 1182 assert actions[-1].action == "user.push"
1163 1183 assert actions[-1].action_data["commit_ids"] == pr_commit_ids
1164 1184
1165 # Check post_push rcextension was really executed
1166 push_calls = rhodecode.EXTENSIONS.calls["_push_hook"]
1167 assert len(push_calls) == 1
1168 unused_last_call_args, last_call_kwargs = push_calls[0]
1169 assert last_call_kwargs["action"] == "push"
1170 assert last_call_kwargs["commit_ids"] == pr_commit_ids
1185 with open(rc_ext_location) as f:
1186 f_data = f.read()
1187 assert 'test-execution' in f_data
1188 for commit_id in pr_commit_ids:
1189 assert f'{commit_id}' in f_data
1190
1191 def test_merge_pull_request_forbidden_by_pre_push_hook(self, pr_util, csrf_token, rcextensions_modification, caplog):
1192 caplog.set_level(logging.WARNING, logger="rhodecode.model.pull_request")
1193
1194 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
1195 pull_request_id = pull_request.pull_request_id
1196 repo_name = pull_request.target_repo.scm_instance().name
1197
1198 url = route_path(
1199 "pullrequest_merge",
1200 repo_name=repo_name,
1201 pull_request_id=pull_request_id,
1202 )
1203
1204 rcstack_location = os.path.dirname(self.app._pyramid_registry.settings['__file__'])
1205
1206 mods = [
1207 ('_pre_push_hook',
1208 f"""
1209 return HookResponse(1, 'HOOK_TEST_FORBIDDEN')
1210 """)
1211 ]
1212 # Add the hook
1213 with rcextensions_modification(rcstack_location, mods, create_if_missing=True, force_create=True):
1214 self.app.post(url, params={"csrf_token": csrf_token})
1215
1216 assert 'Merge failed, not updating the pull request.' in [r[2] for r in caplog.record_tuples]
1217
1171 1218
1172 1219 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
1173 1220 pull_request = pr_util.create_pull_request(mergeable=False)
@@ -1523,7 +1570,6 class TestPullrequestsView(object):
1523 1570
1524 1571 assert pull_request.revisions == [commit_ids["change-rebased"]]
1525 1572
1526
1527 1573 def test_remove_pull_request_branch(self, backend_git, csrf_token):
1528 1574 branch_name = "development"
1529 1575 commits = [
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,7 +25,7 from rhodecode.model.db import Repositor
26 25 from rhodecode.model.meta import Session
27 26 from rhodecode.tests import (
28 27 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, assert_session_flash)
29 from rhodecode.tests.fixture import Fixture
28 from rhodecode.tests.fixtures.rc_fixture import Fixture
30 29 from rhodecode.tests.routes import route_path
31 30
32 31 fixture = Fixture()
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,7 +23,7 from rhodecode.model.db import Repositor
24 23 from rhodecode.model.repo import RepoModel
25 24 from rhodecode.tests import (
26 25 HG_REPO, GIT_REPO, assert_session_flash, no_newline_id_generator)
27 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixtures.rc_fixture import Fixture
28 27 from rhodecode.tests.utils import repo_on_filesystem
29 28 from rhodecode.tests.routes import route_path
30 29
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -31,7 +31,7 from rhodecode.model.meta import Session
31 31 from rhodecode.model.repo import RepoModel
32 32 from rhodecode.model.scm import ScmModel
33 33 from rhodecode.tests import assert_session_flash
34 from rhodecode.tests.fixture import Fixture
34 from rhodecode.tests.fixtures.rc_fixture import Fixture
35 35 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
36 36 from rhodecode.tests.routes import route_path
37 37
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -30,7 +29,7 from rhodecode.model.user import UserMod
30 29 from rhodecode.tests import (
31 30 login_user_session, logout_user_session,
32 31 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
33 from rhodecode.tests.fixture import Fixture
32 from rhodecode.tests.fixtures.rc_fixture import Fixture
34 33 from rhodecode.tests.utils import AssertResponse
35 34 from rhodecode.tests.routes import route_path
36 35
@@ -48,6 +47,7 class TestVcsSettings(object):
48 47 'extensions_evolve': False,
49 48 'phases_publish': 'False',
50 49 'rhodecode_pr_merge_enabled': False,
50 'rhodecode_auto_merge_enabled': False,
51 51 'rhodecode_use_outdated_comments': False,
52 52 'new_svn_branch': '',
53 53 'new_svn_tag': ''
@@ -59,7 +59,7 class TestVcsSettings(object):
59 59 response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name))
60 60
61 61 expected_settings = (
62 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled',
62 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled', 'rhodecode_auto_merge_enabled',
63 63 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
64 64 'hooks_outgoing_pull_logger'
65 65 )
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -32,16 +31,13 class TestAdminRepoVcsSettings(object):
32 31 @pytest.mark.parametrize('setting_name, setting_backends', [
33 32 ('hg_use_rebase_for_merging', ['hg']),
34 33 ])
35 def test_labs_settings_visible_if_enabled(
36 self, setting_name, setting_backends, backend):
34 def test_labs_settings_visible_if_enabled(self, setting_name, setting_backends, backend):
37 35 if backend.alias not in setting_backends:
38 36 pytest.skip('Setting not available for backend {}'.format(backend))
39 37
40 vcs_settings_url = route_path(
41 'edit_repo_vcs', repo_name=backend.repo.repo_name)
38 vcs_settings_url = route_path('edit_repo_vcs', repo_name=backend.repo.repo_name)
42 39
43 with mock.patch.dict(
44 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
40 with mock.patch.dict(rhodecode.CONFIG, {'labs_settings_active': 'true'}):
45 41 response = self.app.get(vcs_settings_url)
46 42
47 43 assertr = response.assert_response()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -33,7 +33,7 from rhodecode.lib.auth import (
33 33 from rhodecode.lib.graphmod import _colored, _dagwalker
34 34 from rhodecode.lib.helpers import RepoPage
35 35 from rhodecode.lib.utils2 import str2bool
36 from rhodecode.lib.str_utils import safe_int, safe_str
36 from rhodecode.lib.str_utils import safe_int, safe_str, safe_bytes
37 37 from rhodecode.lib.vcs.exceptions import (
38 38 RepositoryError, CommitDoesNotExistError,
39 39 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
@@ -204,10 +204,9 class RepoChangelogView(RepoAppView):
204 204 log.debug('generating changelog for path %s', f_path)
205 205 # get the history for the file !
206 206 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
207
207 bytes_path = safe_bytes(f_path)
208 208 try:
209 collection = base_commit.get_path_history(
210 f_path, limit=hist_limit, pre_load=pre_load)
209 collection = base_commit.get_path_history(bytes_path, limit=hist_limit, pre_load=pre_load)
211 210 if collection and partial_xhr:
212 211 # for ajax call we remove first one since we're looking
213 212 # at it right now in the context of a file commit
@@ -216,7 +215,7 class RepoChangelogView(RepoAppView):
216 215 # this node is not present at tip!
217 216 try:
218 217 commit = self._get_commit_or_redirect(commit_id)
219 collection = commit.get_path_history(f_path)
218 collection = commit.get_path_history(bytes_path)
220 219 except RepositoryError as e:
221 220 h.flash(safe_str(e), category='warning')
222 221 redirect_url = h.route_path(
@@ -310,9 +309,8 class RepoChangelogView(RepoAppView):
310 309 log.exception(safe_str(e))
311 310 raise HTTPFound(
312 311 h.route_path('repo_commits', repo_name=self.db_repo_name))
313
314 collection = base_commit.get_path_history(
315 f_path, limit=hist_limit, pre_load=pre_load)
312 bytes_path = safe_bytes(f_path)
313 collection = base_commit.get_path_history(bytes_path, limit=hist_limit, pre_load=pre_load)
316 314 collection = list(reversed(collection))
317 315 else:
318 316 collection = self.rhodecode_vcs_repo.get_commits(
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -89,8 +89,7 class RepoCommitsView(RepoAppView):
89 89 commit_range = commit_id_range.split('...')[:2]
90 90
91 91 try:
92 pre_load = ['affected_files', 'author', 'branch', 'date',
93 'message', 'parents']
92 pre_load = ['author', 'branch', 'date', 'message', 'parents']
94 93 if self.rhodecode_vcs_repo.alias == 'hg':
95 94 pre_load += ['hidden', 'obsolete', 'phase']
96 95
@@ -100,8 +99,7 class RepoCommitsView(RepoAppView):
100 99 pre_load=pre_load, translate_tags=False)
101 100 commits = list(commits)
102 101 else:
103 commits = [self.rhodecode_vcs_repo.get_commit(
104 commit_id=commit_id_range, pre_load=pre_load)]
102 commits = [self.rhodecode_vcs_repo.get_commit(commit_id=commit_id_range, pre_load=pre_load)]
105 103
106 104 c.commit_ranges = commits
107 105 if not c.commit_ranges:
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
This diff has been collapsed as it changes many lines, (1093 lines changed) Show them Hide them
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -40,25 +40,34 from rhodecode.lib import diffs, helpers
40 40 from rhodecode.lib import audit_logger
41 41 from rhodecode.lib.hash_utils import sha1_safe
42 42 from rhodecode.lib.archive_cache import (
43 get_archival_cache_store, get_archival_config, ArchiveCacheGenerationLock, archive_iterator)
43 get_archival_cache_store,
44 get_archival_config,
45 ArchiveCacheGenerationLock,
46 archive_iterator,
47 )
44 48 from rhodecode.lib.str_utils import safe_bytes, convert_special_chars
45 49 from rhodecode.lib.view_utils import parse_path_ref
46 50 from rhodecode.lib.exceptions import NonRelativePathError
47 from rhodecode.lib.codeblocks import (
48 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
51 from rhodecode.lib.codeblocks import filenode_as_lines_tokens, filenode_as_annotated_lines_tokens
49 52 from rhodecode.lib.utils2 import convert_line_endings, detect_mode
50 53 from rhodecode.lib.type_utils import str2bool
51 54 from rhodecode.lib.str_utils import safe_str, safe_int, header_safe_str
52 from rhodecode.lib.auth import (
53 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
55 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired
54 56 from rhodecode.lib.vcs import path as vcspath
55 57 from rhodecode.lib.vcs.backends.base import EmptyCommit
56 58 from rhodecode.lib.vcs.conf import settings
57 59 from rhodecode.lib.vcs.nodes import FileNode
58 60 from rhodecode.lib.vcs.exceptions import (
59 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
60 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
61 NodeDoesNotExistError, CommitError, NodeError)
61 RepositoryError,
62 CommitDoesNotExistError,
63 EmptyRepositoryError,
64 ImproperArchiveTypeError,
65 VCSError,
66 NodeAlreadyExistsError,
67 NodeDoesNotExistError,
68 CommitError,
69 NodeError,
70 )
62 71
63 72 from rhodecode.model.scm import ScmModel
64 73 from rhodecode.model.db import Repository
@@ -66,17 +75,17 from rhodecode.model.db import Repositor
66 75 log = logging.getLogger(__name__)
67 76
68 77
69 def get_archive_name(db_repo_id, db_repo_name, commit_sha, ext, subrepos=False, path_sha='', with_hash=True):
78 def get_archive_name(db_repo_id, db_repo_name, commit_sha, ext, subrepos=False, path_sha="", with_hash=True):
70 79 # original backward compat name of archive
71 clean_name = safe_str(convert_special_chars(db_repo_name).replace('/', '_'))
80 clean_name = safe_str(convert_special_chars(db_repo_name).replace("/", "_"))
72 81
73 82 # e.g vcsserver-id-abcd-sub-1-abcfdef-archive-all.zip
74 83 # vcsserver-id-abcd-sub-0-abcfdef-COMMIT_SHA-PATH_SHA.zip
75 84 id_sha = sha1_safe(str(db_repo_id))[:4]
76 sub_repo = 'sub-1' if subrepos else 'sub-0'
77 commit = commit_sha if with_hash else 'archive'
78 path_marker = (path_sha if with_hash else '') or 'all'
79 archive_name = f'{clean_name}-id-{id_sha}-{sub_repo}-{commit}-{path_marker}{ext}'
85 sub_repo = "sub-1" if subrepos else "sub-0"
86 commit = commit_sha if with_hash else "archive"
87 path_marker = (path_sha if with_hash else "") or "all"
88 archive_name = f"{clean_name}-id-{id_sha}-{sub_repo}-{commit}-{path_marker}{ext}"
80 89
81 90 return archive_name
82 91
@@ -86,16 +95,15 def get_path_sha(at_path):
86 95
87 96
88 97 def _get_archive_spec(fname):
89 log.debug('Detecting archive spec for: `%s`', fname)
98 log.debug("Detecting archive spec for: `%s`", fname)
90 99
91 100 fileformat = None
92 101 ext = None
93 102 content_type = None
94 103 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
95
96 104 if fname.endswith(extension):
97 105 fileformat = a_type
98 log.debug('archive is of type: %s', fileformat)
106 log.debug("archive is of type: %s", fileformat)
99 107 ext = extension
100 108 break
101 109
@@ -103,13 +111,12 def _get_archive_spec(fname):
103 111 raise ValueError()
104 112
105 113 # left over part of whole fname is the commit
106 commit_id = fname[:-len(ext)]
114 commit_id = fname[: -len(ext)]
107 115
108 116 return commit_id, ext, fileformat, content_type
109 117
110 118
111 119 class RepoFilesView(RepoAppView):
112
113 120 @staticmethod
114 121 def adjust_file_path_for_svn(f_path, repo):
115 122 """
@@ -118,13 +125,11 class RepoFilesView(RepoAppView):
118 125 This is mainly based on prefix matching of the recognized tags and
119 126 branches in the underlying repository.
120 127 """
121 tags_and_branches = itertools.chain(
122 repo.branches.keys(),
123 repo.tags.keys())
128 tags_and_branches = itertools.chain(repo.branches.keys(), repo.tags.keys())
124 129 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
125 130
126 131 for name in tags_and_branches:
127 if f_path.startswith(f'{name}/'):
132 if f_path.startswith(f"{name}/"):
128 133 f_path = vcspath.relpath(f_path, name)
129 134 break
130 135 return f_path
@@ -135,66 +140,61 class RepoFilesView(RepoAppView):
135 140 c.enable_downloads = self.db_repo.enable_downloads
136 141 return c
137 142
138 def _ensure_not_locked(self, commit_id='tip'):
143 def _ensure_not_locked(self, commit_id="tip"):
139 144 _ = self.request.translate
140 145
141 146 repo = self.db_repo
142 147 if repo.enable_locking and repo.locked[0]:
143 h.flash(_('This repository has been locked by %s on %s')
144 % (h.person_by_id(repo.locked[0]),
145 h.format_date(h.time_to_datetime(repo.locked[1]))),
146 'warning')
147 files_url = h.route_path(
148 'repo_files:default_path',
149 repo_name=self.db_repo_name, commit_id=commit_id)
148 h.flash(
149 _("This repository has been locked by %s on %s")
150 % (h.person_by_id(repo.locked[0]), h.format_date(h.time_to_datetime(repo.locked[1]))),
151 "warning",
152 )
153 files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id)
150 154 raise HTTPFound(files_url)
151 155
152 def forbid_non_head(self, is_head, f_path, commit_id='tip', json_mode=False):
156 def forbid_non_head(self, is_head, f_path, commit_id="tip", json_mode=False):
153 157 _ = self.request.translate
154 158
155 159 if not is_head:
156 message = _('Cannot modify file. '
157 'Given commit `{}` is not head of a branch.').format(commit_id)
158 h.flash(message, category='warning')
160 message = _("Cannot modify file. " "Given commit `{}` is not head of a branch.").format(commit_id)
161 h.flash(message, category="warning")
159 162
160 163 if json_mode:
161 164 return message
162 165
163 files_url = h.route_path(
164 'repo_files', repo_name=self.db_repo_name, commit_id=commit_id,
165 f_path=f_path)
166 files_url = h.route_path("repo_files", repo_name=self.db_repo_name, commit_id=commit_id, f_path=f_path)
166 167 raise HTTPFound(files_url)
167 168
168 def check_branch_permission(self, branch_name, commit_id='tip', json_mode=False):
169 def check_branch_permission(self, branch_name, commit_id="tip", json_mode=False):
169 170 _ = self.request.translate
170 171
171 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(
172 self.db_repo_name, branch_name)
173 if branch_perm and branch_perm not in ['branch.push', 'branch.push_force']:
174 message = _('Branch `{}` changes forbidden by rule {}.').format(
175 h.escape(branch_name), h.escape(rule))
176 h.flash(message, 'warning')
172 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(self.db_repo_name, branch_name)
173 if branch_perm and branch_perm not in ["branch.push", "branch.push_force"]:
174 message = _("Branch `{}` changes forbidden by rule {}.").format(h.escape(branch_name), h.escape(rule))
175 h.flash(message, "warning")
177 176
178 177 if json_mode:
179 178 return message
180 179
181 files_url = h.route_path(
182 'repo_files:default_path', repo_name=self.db_repo_name, commit_id=commit_id)
180 files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id)
183 181
184 182 raise HTTPFound(files_url)
185 183
186 184 def _get_commit_and_path(self):
187 185 default_commit_id = self.db_repo.landing_ref_name
188 default_f_path = '/'
186 default_f_path = "/"
189 187
190 commit_id = self.request.matchdict.get(
191 'commit_id', default_commit_id)
188 commit_id = self.request.matchdict.get("commit_id", default_commit_id)
192 189 f_path = self._get_f_path(self.request.matchdict, default_f_path)
193 return commit_id, f_path
190
191 bytes_path = safe_bytes(f_path)
192 return commit_id, f_path, bytes_path
194 193
195 def _get_default_encoding(self, c):
196 enc_list = getattr(c, 'default_encodings', [])
197 return enc_list[0] if enc_list else 'UTF-8'
194 @classmethod
195 def _get_default_encoding(cls, c):
196 enc_list = getattr(c, "default_encodings", [])
197 return enc_list[0] if enc_list else "UTF-8"
198 198
199 199 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
200 200 """
@@ -213,31 +213,25 class RepoFilesView(RepoAppView):
213 213 return None
214 214
215 215 add_new = upload_new = ""
216 if h.HasRepoPermissionAny(
217 'repository.write', 'repository.admin')(self.db_repo_name):
218 _url = h.route_path(
219 'repo_files_add_file',
220 repo_name=self.db_repo_name, commit_id=0, f_path='')
221 add_new = h.link_to(
222 _('add a new file'), _url, class_="alert-link")
216 if h.HasRepoPermissionAny("repository.write", "repository.admin")(self.db_repo_name):
217 _url = h.route_path("repo_files_add_file", repo_name=self.db_repo_name, commit_id=0, f_path="")
218 add_new = h.link_to(_("add a new file"), _url, class_="alert-link")
223 219
224 _url_upld = h.route_path(
225 'repo_files_upload_file',
226 repo_name=self.db_repo_name, commit_id=0, f_path='')
227 upload_new = h.link_to(
228 _('upload a new file'), _url_upld, class_="alert-link")
220 _url_upld = h.route_path("repo_files_upload_file", repo_name=self.db_repo_name, commit_id=0, f_path="")
221 upload_new = h.link_to(_("upload a new file"), _url_upld, class_="alert-link")
229 222
230 h.flash(h.literal(
231 _('There are no files yet. Click here to %s or %s.') % (add_new, upload_new)), category='warning')
232 raise HTTPFound(
233 h.route_path('repo_summary', repo_name=self.db_repo_name))
223 h.flash(
224 h.literal(_("There are no files yet. Click here to %s or %s.") % (add_new, upload_new)),
225 category="warning",
226 )
227 raise HTTPFound(h.route_path("repo_summary", repo_name=self.db_repo_name))
234 228
235 229 except (CommitDoesNotExistError, LookupError) as e:
236 msg = _('No such commit exists for this repository. Commit: {}').format(commit_id)
237 h.flash(msg, category='error')
230 msg = _("No such commit exists for this repository. Commit: {}").format(commit_id)
231 h.flash(msg, category="error")
238 232 raise HTTPNotFound()
239 233 except RepositoryError as e:
240 h.flash(h.escape(safe_str(e)), category='error')
234 h.flash(h.escape(safe_str(e)), category="error")
241 235 raise HTTPNotFound()
242 236
243 237 def _get_filenode_or_redirect(self, commit_obj, path, pre_load=None):
@@ -250,22 +244,22 class RepoFilesView(RepoAppView):
250 244 try:
251 245 file_node = commit_obj.get_node(path, pre_load=pre_load)
252 246 if file_node.is_dir():
253 raise RepositoryError('The given path is a directory')
247 raise RepositoryError("The given path is a directory")
254 248 except CommitDoesNotExistError:
255 log.exception('No such commit exists for this repository')
256 h.flash(_('No such commit exists for this repository'), category='error')
249 log.exception("No such commit exists for this repository")
250 h.flash(_("No such commit exists for this repository"), category="error")
257 251 raise HTTPNotFound()
258 252 except RepositoryError as e:
259 log.warning('Repository error while fetching filenode `%s`. Err:%s', path, e)
260 h.flash(h.escape(safe_str(e)), category='error')
253 log.warning("Repository error while fetching filenode `%s`. Err:%s", path, e)
254 h.flash(h.escape(safe_str(e)), category="error")
261 255 raise HTTPNotFound()
262 256
263 257 return file_node
264 258
265 259 def _is_valid_head(self, commit_id, repo, landing_ref):
266 branch_name = sha_commit_id = ''
260 branch_name = sha_commit_id = ""
267 261 is_head = False
268 log.debug('Checking if commit_id `%s` is a head for %s.', commit_id, repo)
262 log.debug("Checking if commit_id `%s` is a head for %s.", commit_id, repo)
269 263
270 264 for _branch_name, branch_commit_id in repo.branches.items():
271 265 # simple case we pass in branch name, it's a HEAD
@@ -301,39 +295,39 class RepoFilesView(RepoAppView):
301 295 return branch_name, sha_commit_id, is_head
302 296
303 297 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None):
304
305 298 repo_id = self.db_repo.repo_id
306 299 force_recache = self.get_recache_flag()
307 300
308 cache_seconds = safe_int(
309 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
301 cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time")
310 302 cache_on = not force_recache and cache_seconds > 0
303
311 304 log.debug(
312 'Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`'
313 'with caching: %s[TTL: %ss]' % (
314 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
305 "Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`"
306 "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0)
307 )
315 308
316 cache_namespace_uid = f'repo.{rc_cache.FILE_TREE_CACHE_VER}.{repo_id}'
317 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
309 cache_namespace_uid = f"repo.{rc_cache.FILE_TREE_CACHE_VER}.{repo_id}"
310 region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid)
318 311
319 312 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
320 313 def compute_file_tree(_name_hash, _repo_id, _commit_id, _f_path, _full_load, _at_rev):
321 log.debug('Generating cached file tree at for repo_id: %s, %s, %s',
322 _repo_id, _commit_id, _f_path)
314 log.debug("Generating cached file tree at for repo_id: %s, %s, %s", _repo_id, _commit_id, _f_path)
323 315
324 316 c.full_load = _full_load
325 317 return render(
326 'rhodecode:templates/files/files_browser_tree.mako',
327 self._get_template_context(c), self.request, _at_rev)
318 "rhodecode:templates/files/files_browser_tree.mako",
319 self._get_template_context(c),
320 self.request,
321 _at_rev,
322 )
328 323
329 324 return compute_file_tree(
330 self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path, full_load, at_rev)
325 self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path, full_load, at_rev
326 )
331 327
332 328 def create_pure_path(self, *parts):
333 329 # Split paths and sanitize them, removing any ../ etc
334 sanitized_path = [
335 x for x in pathlib.PurePath(*parts).parts
336 if x not in ['.', '..']]
330 sanitized_path = [x for x in pathlib.PurePath(*parts).parts if x not in [".", ".."]]
337 331
338 332 pure_path = pathlib.PurePath(*sanitized_path)
339 333 return pure_path
@@ -341,10 +335,7 class RepoFilesView(RepoAppView):
341 335 def _is_lf_enabled(self, target_repo):
342 336 lf_enabled = False
343 337
344 lf_key_for_vcs_map = {
345 'hg': 'extensions_largefiles',
346 'git': 'vcs_git_lfs_enabled'
347 }
338 lf_key_for_vcs_map = {"hg": "extensions_largefiles", "git": "vcs_git_lfs_enabled"}
348 339
349 340 lf_key_for_vcs = lf_key_for_vcs_map.get(target_repo.repo_type)
350 341
@@ -354,66 +345,77 class RepoFilesView(RepoAppView):
354 345 return lf_enabled
355 346
356 347 @LoginRequired()
357 @HasRepoPermissionAnyDecorator(
358 'repository.read', 'repository.write', 'repository.admin')
348 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
359 349 def repo_archivefile(self):
360 350 # archive cache config
361 351 from rhodecode import CONFIG
352
362 353 _ = self.request.translate
363 354 self.load_default_context()
364 default_at_path = '/'
365 fname = self.request.matchdict['fname']
366 subrepos = self.request.GET.get('subrepos') == 'true'
367 with_hash = str2bool(self.request.GET.get('with_hash', '1'))
368 at_path = self.request.GET.get('at_path') or default_at_path
355
356 subrepos = self.request.GET.get("subrepos") == "true"
357 with_hash = str2bool(self.request.GET.get("with_hash", "1"))
358
359 default_at_path = "/"
360 fname = self.request.matchdict["fname"]
361 at_path = self.request.GET.get("at_path") or default_at_path
369 362
370 363 if not self.db_repo.enable_downloads:
371 return Response(_('Downloads disabled'))
364 return Response(_("Downloads disabled"))
372 365
373 366 try:
374 commit_id, ext, fileformat, content_type = \
375 _get_archive_spec(fname)
367 commit_id, ext, file_format, content_type = _get_archive_spec(fname)
376 368 except ValueError:
377 return Response(_('Unknown archive type for: `{}`').format(
378 h.escape(fname)))
369 return Response(_("Unknown archive type for: `{}`").format(h.escape(fname)))
379 370
380 371 try:
381 372 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
382 373 except CommitDoesNotExistError:
383 return Response(_('Unknown commit_id {}').format(
384 h.escape(commit_id)))
374 return Response(_("Unknown commit_id {}").format(h.escape(commit_id)))
385 375 except EmptyRepositoryError:
386 return Response(_('Empty repository'))
376 return Response(_("Empty repository"))
387 377
388 378 # we used a ref, or a shorter version, lets redirect client ot use explicit hash
389 379 if commit_id != commit.raw_id:
390 fname=f'{commit.raw_id}{ext}'
380 fname = f"{commit.raw_id}{ext}"
391 381 raise HTTPFound(self.request.current_route_path(fname=fname))
392 382
393 383 try:
394 at_path = commit.get_node(at_path).path or default_at_path
384 at_path = commit.get_node(safe_bytes(at_path)).path or default_at_path
395 385 except Exception:
396 return Response(_('No node at path {} for this repository').format(h.escape(at_path)))
386 return Response(_("No node at path {} for this repository").format(h.escape(at_path)))
397 387
398 388 path_sha = get_path_sha(at_path)
399 389
400 390 # used for cache etc, consistent unique archive name
401 391 archive_name_key = get_archive_name(
402 self.db_repo.repo_id, self.db_repo_name, commit_sha=commit.short_id, ext=ext, subrepos=subrepos,
403 path_sha=path_sha, with_hash=True)
392 self.db_repo.repo_id,
393 self.db_repo_name,
394 commit_sha=commit.short_id,
395 ext=ext,
396 subrepos=subrepos,
397 path_sha=path_sha,
398 with_hash=True,
399 )
404 400
405 401 if not with_hash:
406 path_sha = ''
402 path_sha = ""
407 403
408 404 # what end client gets served
409 405 response_archive_name = get_archive_name(
410 self.db_repo.repo_id, self.db_repo_name, commit_sha=commit.short_id, ext=ext, subrepos=subrepos,
411 path_sha=path_sha, with_hash=with_hash)
406 self.db_repo.repo_id,
407 self.db_repo_name,
408 commit_sha=commit.short_id,
409 ext=ext,
410 subrepos=subrepos,
411 path_sha=path_sha,
412 with_hash=with_hash,
413 )
412 414
413 415 # remove extension from our archive directory name
414 archive_dir_name = response_archive_name[:-len(ext)]
416 archive_dir_name = response_archive_name[: -len(ext)]
415 417
416 archive_cache_disable = self.request.GET.get('no_cache')
418 archive_cache_disable = self.request.GET.get("no_cache")
417 419
418 420 d_cache = get_archival_cache_store(config=CONFIG)
419 421
@@ -421,29 +423,38 class RepoFilesView(RepoAppView):
421 423 d_cache_conf = get_archival_config(config=CONFIG)
422 424
423 425 # This is also a cache key, and lock key
424 reentrant_lock_key = archive_name_key + '.lock'
426 reentrant_lock_key = archive_name_key + ".lock"
425 427
426 428 use_cached_archive = False
427 429 if not archive_cache_disable and archive_name_key in d_cache:
428 430 reader, metadata = d_cache.fetch(archive_name_key)
429 431
430 432 use_cached_archive = True
431 log.debug('Found cached archive as key=%s tag=%s, serving archive from cache reader=%s',
432 archive_name_key, metadata, reader.name)
433 log.debug(
434 "Found cached archive as key=%s tag=%s, serving archive from cache reader=%s",
435 archive_name_key,
436 metadata,
437 reader.name,
438 )
433 439 else:
434 440 reader = None
435 log.debug('Archive with key=%s is not yet cached, creating one now...', archive_name_key)
441 log.debug("Archive with key=%s is not yet cached, creating one now...", archive_name_key)
436 442
437 443 if not reader:
438 444 # generate new archive, as previous was not found in the cache
439 445 try:
440 446 with d_cache.get_lock(reentrant_lock_key):
441 447 try:
442 commit.archive_repo(archive_name_key, archive_dir_name=archive_dir_name,
443 kind=fileformat, subrepos=subrepos,
444 archive_at_path=at_path, cache_config=d_cache_conf)
448 commit.archive_repo(
449 archive_name_key,
450 archive_dir_name=archive_dir_name,
451 kind=file_format,
452 subrepos=subrepos,
453 archive_at_path=at_path,
454 cache_config=d_cache_conf,
455 )
445 456 except ImproperArchiveTypeError:
446 return _('Unknown archive type')
457 return _("Unknown archive type")
447 458
448 459 except ArchiveCacheGenerationLock:
449 460 retry_after = round(random.uniform(0.3, 3.0), 1)
@@ -462,7 +473,7 class RepoFilesView(RepoAppView):
462 473 reader, metadata = d_cache.fetch(archive_name_key, retry=True, retry_attempts=30)
463 474
464 475 response = Response(app_iter=archive_iterator(reader))
465 response.content_disposition = f'attachment; filename={response_archive_name}'
476 response.content_disposition = f"attachment; filename={response_archive_name}"
466 477 response.content_type = str(content_type)
467 478
468 479 try:
@@ -470,23 +481,25 class RepoFilesView(RepoAppView):
470 481 finally:
471 482 # store download action
472 483 audit_logger.store_web(
473 'repo.archive.download', action_data={
474 'user_agent': self.request.user_agent,
475 'archive_name': archive_name_key,
476 'archive_spec': fname,
477 'archive_cached': use_cached_archive},
484 "repo.archive.download",
485 action_data={
486 "user_agent": self.request.user_agent,
487 "archive_name": archive_name_key,
488 "archive_spec": fname,
489 "archive_cached": use_cached_archive,
490 },
478 491 user=self._rhodecode_user,
479 492 repo=self.db_repo,
480 commit=True
493 commit=True,
481 494 )
482 495
483 496 def _get_file_node(self, commit_id, f_path):
484 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
497 if commit_id not in ["", None, "None", "0" * 12, "0" * 40]:
485 498 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
486 499 try:
487 node = commit.get_node(f_path)
500 node = commit.get_node(safe_bytes(f_path))
488 501 if node.is_dir():
489 raise NodeError(f'{node} path is a {type(node)} not a file')
502 raise NodeError(f"{node} path is a {type(node)} not a file")
490 503 except NodeDoesNotExistError:
491 504 commit = EmptyCommit(
492 505 commit_id=commit_id,
@@ -495,46 +508,43 class RepoFilesView(RepoAppView):
495 508 alias=commit.repository.alias,
496 509 message=commit.message,
497 510 author=commit.author,
498 date=commit.date)
499 node = FileNode(safe_bytes(f_path), b'', commit=commit)
511 date=commit.date,
512 )
513 node = FileNode(safe_bytes(f_path), b"", commit=commit)
500 514 else:
501 commit = EmptyCommit(
502 repo=self.rhodecode_vcs_repo,
503 alias=self.rhodecode_vcs_repo.alias)
504 node = FileNode(safe_bytes(f_path), b'', commit=commit)
515 commit = EmptyCommit(repo=self.rhodecode_vcs_repo, alias=self.rhodecode_vcs_repo.alias)
516 node = FileNode(safe_bytes(f_path), b"", commit=commit)
505 517 return node
506 518
507 519 @LoginRequired()
508 @HasRepoPermissionAnyDecorator(
509 'repository.read', 'repository.write', 'repository.admin')
520 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
510 521 def repo_files_diff(self):
511 522 c = self.load_default_context()
512 523 f_path = self._get_f_path(self.request.matchdict)
513 diff1 = self.request.GET.get('diff1', '')
514 diff2 = self.request.GET.get('diff2', '')
524 diff1 = self.request.GET.get("diff1", "")
525 diff2 = self.request.GET.get("diff2", "")
515 526
516 527 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
517 528
518 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
519 line_context = self.request.GET.get('context', 3)
529 ignore_whitespace = str2bool(self.request.GET.get("ignorews"))
530 line_context = self.request.GET.get("context", 3)
520 531
521 532 if not any((diff1, diff2)):
522 h.flash(
523 'Need query parameter "diff1" or "diff2" to generate a diff.',
524 category='error')
533 h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error")
525 534 raise HTTPBadRequest()
526 535
527 c.action = self.request.GET.get('diff')
528 if c.action not in ['download', 'raw']:
536 c.action = self.request.GET.get("diff")
537 if c.action not in ["download", "raw"]:
529 538 compare_url = h.route_path(
530 'repo_compare',
539 "repo_compare",
531 540 repo_name=self.db_repo_name,
532 source_ref_type='rev',
541 source_ref_type="rev",
533 542 source_ref=diff1,
534 543 target_repo=self.db_repo_name,
535 target_ref_type='rev',
544 target_ref_type="rev",
536 545 target_ref=diff2,
537 _query=dict(f_path=f_path))
546 _query=dict(f_path=f_path),
547 )
538 548 # redirect to new view if we render diff
539 549 raise HTTPFound(compare_url)
540 550
@@ -543,43 +553,34 class RepoFilesView(RepoAppView):
543 553 node2 = self._get_file_node(diff2, f_path)
544 554 except (RepositoryError, NodeError):
545 555 log.exception("Exception while trying to get node from repository")
546 raise HTTPFound(
547 h.route_path('repo_files', repo_name=self.db_repo_name,
548 commit_id='tip', f_path=f_path))
556 raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path=f_path))
549 557
550 if all(isinstance(node.commit, EmptyCommit)
551 for node in (node1, node2)):
558 if all(isinstance(node.commit, EmptyCommit) for node in (node1, node2)):
552 559 raise HTTPNotFound()
553 560
554 561 c.commit_1 = node1.commit
555 562 c.commit_2 = node2.commit
556 563
557 if c.action == 'download':
558 _diff = diffs.get_gitdiff(node1, node2,
559 ignore_whitespace=ignore_whitespace,
560 context=line_context)
564 if c.action == "download":
565 _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context)
561 566 # NOTE: this was using diff_format='gitdiff'
562 diff = diffs.DiffProcessor(_diff, diff_format='newdiff')
567 diff = diffs.DiffProcessor(_diff, diff_format="newdiff")
563 568
564 569 response = Response(self.path_filter.get_raw_patch(diff))
565 response.content_type = 'text/plain'
566 response.content_disposition = (
567 f'attachment; filename={f_path}_{diff1}_vs_{diff2}.diff'
568 )
570 response.content_type = "text/plain"
571 response.content_disposition = f"attachment; filename={f_path}_{diff1}_vs_{diff2}.diff"
569 572 charset = self._get_default_encoding(c)
570 573 if charset:
571 574 response.charset = charset
572 575 return response
573 576
574 elif c.action == 'raw':
575 _diff = diffs.get_gitdiff(node1, node2,
576 ignore_whitespace=ignore_whitespace,
577 context=line_context)
577 elif c.action == "raw":
578 _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context)
578 579 # NOTE: this was using diff_format='gitdiff'
579 diff = diffs.DiffProcessor(_diff, diff_format='newdiff')
580 diff = diffs.DiffProcessor(_diff, diff_format="newdiff")
580 581
581 582 response = Response(self.path_filter.get_raw_patch(diff))
582 response.content_type = 'text/plain'
583 response.content_type = "text/plain"
583 584 charset = self._get_default_encoding(c)
584 585 if charset:
585 586 response.charset = charset
@@ -589,31 +590,32 class RepoFilesView(RepoAppView):
589 590 raise HTTPNotFound()
590 591
591 592 @LoginRequired()
592 @HasRepoPermissionAnyDecorator(
593 'repository.read', 'repository.write', 'repository.admin')
593 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
594 594 def repo_files_diff_2way_redirect(self):
595 595 """
596 596 Kept only to make OLD links work
597 597 """
598 598 f_path = self._get_f_path_unchecked(self.request.matchdict)
599 diff1 = self.request.GET.get('diff1', '')
600 diff2 = self.request.GET.get('diff2', '')
599 diff1 = self.request.GET.get("diff1", "")
600 diff2 = self.request.GET.get("diff2", "")
601 601
602 602 if not any((diff1, diff2)):
603 h.flash(
604 'Need query parameter "diff1" or "diff2" to generate a diff.',
605 category='error')
603 h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error")
606 604 raise HTTPBadRequest()
607 605
608 606 compare_url = h.route_path(
609 'repo_compare',
607 "repo_compare",
610 608 repo_name=self.db_repo_name,
611 source_ref_type='rev',
609 source_ref_type="rev",
612 610 source_ref=diff1,
613 target_ref_type='rev',
611 target_ref_type="rev",
614 612 target_ref=diff2,
615 _query=dict(f_path=f_path, diffmode='sideside',
616 target_repo=self.db_repo_name,))
613 _query=dict(
614 f_path=f_path,
615 diffmode="sideside",
616 target_repo=self.db_repo_name,
617 ),
618 )
617 619 raise HTTPFound(compare_url)
618 620
619 621 @LoginRequired()
@@ -627,52 +629,51 class RepoFilesView(RepoAppView):
627 629 landing_url = h.repo_files_by_ref_url(
628 630 c.rhodecode_db_repo.repo_name,
629 631 c.rhodecode_db_repo.repo_type,
630 f_path='',
632 f_path="",
631 633 ref_name=ref_name,
632 commit_id='tip',
633 query=dict(at=ref_name)
634 commit_id="tip",
635 query=dict(at=ref_name),
634 636 )
635 637
636 638 raise HTTPFound(landing_url)
637 639
638 640 @LoginRequired()
639 @HasRepoPermissionAnyDecorator(
640 'repository.read', 'repository.write', 'repository.admin')
641 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
641 642 def repo_files(self):
642 643 c = self.load_default_context()
643 644
644 view_name = getattr(self.request.matched_route, 'name', None)
645 view_name = getattr(self.request.matched_route, "name", None)
645 646
646 c.annotate = view_name == 'repo_files:annotated'
647 c.annotate = view_name == "repo_files:annotated"
647 648 # default is false, but .rst/.md files later are auto rendered, we can
648 649 # overwrite auto rendering by setting this GET flag
649 c.renderer = view_name == 'repo_files:rendered' or not self.request.GET.get('no-render', False)
650 c.renderer = view_name == "repo_files:rendered" or not self.request.GET.get("no-render", False)
650 651
651 commit_id, f_path = self._get_commit_and_path()
652 commit_id, f_path, bytes_path = self._get_commit_and_path()
652 653
653 654 c.commit = self._get_commit_or_redirect(commit_id)
654 c.branch = self.request.GET.get('branch', None)
655 c.branch = self.request.GET.get("branch", None)
655 656 c.f_path = f_path
656 at_rev = self.request.GET.get('at')
657 at_rev = self.request.GET.get("at")
657 658
658 659 # files or dirs
659 660 try:
660 c.file = c.commit.get_node(f_path, pre_load=['is_binary', 'size', 'data'])
661 c.file = c.commit.get_node(bytes_path, pre_load=["is_binary", "size", "data"])
661 662
662 663 c.file_author = True
663 c.file_tree = ''
664 c.file_tree = ""
664 665
665 666 # prev link
666 667 try:
667 668 prev_commit = c.commit.prev(c.branch)
668 669 c.prev_commit = prev_commit
669 670 c.url_prev = h.route_path(
670 'repo_files', repo_name=self.db_repo_name,
671 commit_id=prev_commit.raw_id, f_path=f_path)
671 "repo_files", repo_name=self.db_repo_name, commit_id=prev_commit.raw_id, f_path=f_path
672 )
672 673 if c.branch:
673 c.url_prev += '?branch=%s' % c.branch
674 c.url_prev += f"?branch={c.branch}"
674 675 except (CommitDoesNotExistError, VCSError):
675 c.url_prev = '#'
676 c.url_prev = "#"
676 677 c.prev_commit = EmptyCommit()
677 678
678 679 # next link
@@ -680,110 +681,101 class RepoFilesView(RepoAppView):
680 681 next_commit = c.commit.next(c.branch)
681 682 c.next_commit = next_commit
682 683 c.url_next = h.route_path(
683 'repo_files', repo_name=self.db_repo_name,
684 commit_id=next_commit.raw_id, f_path=f_path)
684 "repo_files", repo_name=self.db_repo_name, commit_id=next_commit.raw_id, f_path=f_path
685 )
685 686 if c.branch:
686 c.url_next += '?branch=%s' % c.branch
687 c.url_next += f"?branch={c.branch}"
687 688 except (CommitDoesNotExistError, VCSError):
688 c.url_next = '#'
689 c.url_next = "#"
689 690 c.next_commit = EmptyCommit()
690 691
691 692 # load file content
692 693 if c.file.is_file():
693
694 694 c.lf_node = {}
695 695
696 696 has_lf_enabled = self._is_lf_enabled(self.db_repo)
697 697 if has_lf_enabled:
698 698 c.lf_node = c.file.get_largefile_node()
699 699
700 c.file_source_page = 'true'
700 c.file_source_page = "true"
701 701 c.file_last_commit = c.file.last_commit
702 702
703 703 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
704 704
705 705 if not (c.file_size_too_big or c.file.is_binary):
706 706 if c.annotate: # annotation has precedence over renderer
707 c.annotated_lines = filenode_as_annotated_lines_tokens(
708 c.file
709 )
707 c.annotated_lines = filenode_as_annotated_lines_tokens(c.file)
710 708 else:
711 c.renderer = (
712 c.renderer and h.renderer_from_filename(c.file.path)
713 )
709 c.renderer = c.renderer and h.renderer_from_filename(c.file.path)
714 710 if not c.renderer:
715 711 c.lines = filenode_as_lines_tokens(c.file)
716 712
717 _branch_name, _sha_commit_id, is_head = \
718 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
719 landing_ref=self.db_repo.landing_ref_name)
713 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
714 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
715 )
720 716 c.on_branch_head = is_head
721 717
722 branch = c.commit.branch if (
723 c.commit.branch and '/' not in c.commit.branch) else None
718 branch = c.commit.branch if (c.commit.branch and "/" not in c.commit.branch) else None
724 719 c.branch_or_raw_id = branch or c.commit.raw_id
725 720 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
726 721
727 722 author = c.file_last_commit.author
728 c.authors = [[
729 h.email(author),
730 h.person(author, 'username_or_name_or_email'),
731 1
732 ]]
723 c.authors = [[h.email(author), h.person(author, "username_or_name_or_email"), 1]]
733 724
734 else: # load tree content at path
735 c.file_source_page = 'false'
725 else: # load tree content (dir content) at path
726 c.file_source_page = "false"
736 727 c.authors = []
728
729 dir_node = c.file
730 c.file_nodes = dir_node.commit.get_nodes(dir_node.bytes_path, pre_load=dir_node.default_pre_load)
737 731 # this loads a simple tree without metadata to speed things up
738 732 # later via ajax we call repo_nodetree_full and fetch whole
739 733 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev)
740 734
741 c.readme_data, c.readme_file = \
742 self._get_readme_data(self.db_repo, c.visual.default_renderer,
743 c.commit.raw_id, f_path)
735 c.readme_data, c.readme_file = self._get_readme_data(
736 self.db_repo, c.visual.default_renderer, c.commit.raw_id, bytes_path, nodes=c.file_nodes
737 )
744 738
745 739 except RepositoryError as e:
746 h.flash(h.escape(safe_str(e)), category='error')
740 h.flash(h.escape(safe_str(e)), category="error")
747 741 raise HTTPNotFound()
748 742
749 if self.request.environ.get('HTTP_X_PJAX'):
750 html = render('rhodecode:templates/files/files_pjax.mako',
751 self._get_template_context(c), self.request)
743 if self.request.environ.get("HTTP_X_PJAX"):
744 html = render("rhodecode:templates/files/files_pjax.mako", self._get_template_context(c), self.request)
752 745 else:
753 html = render('rhodecode:templates/files/files.mako',
754 self._get_template_context(c), self.request)
746 html = render("rhodecode:templates/files/files.mako", self._get_template_context(c), self.request)
755 747 return Response(html)
756 748
757 @HasRepoPermissionAnyDecorator(
758 'repository.read', 'repository.write', 'repository.admin')
749 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
759 750 def repo_files_annotated_previous(self):
760 751 self.load_default_context()
761 752
762 commit_id, f_path = self._get_commit_and_path()
753 commit_id, bytes_path, bytes_path = self._get_commit_and_path()
763 754 commit = self._get_commit_or_redirect(commit_id)
764 755 prev_commit_id = commit.raw_id
765 line_anchor = self.request.GET.get('line_anchor')
756 line_anchor = self.request.GET.get("line_anchor")
766 757 is_file = False
767 758 try:
768 _file = commit.get_node(f_path)
759 _file = commit.get_node(bytes_path)
769 760 is_file = _file.is_file()
770 761 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
771 762 pass
772 763
773 764 if is_file:
774 history = commit.get_path_history(f_path)
775 prev_commit_id = history[1].raw_id \
776 if len(history) > 1 else prev_commit_id
765 history = commit.get_path_history(bytes_path)
766 prev_commit_id = history[1].raw_id if len(history) > 1 else prev_commit_id
777 767 prev_url = h.route_path(
778 'repo_files:annotated', repo_name=self.db_repo_name,
779 commit_id=prev_commit_id, f_path=f_path,
780 _anchor=f'L{line_anchor}')
768 "repo_files:annotated",
769 repo_name=self.db_repo_name,
770 commit_id=prev_commit_id,
771 f_path=bytes_path,
772 _anchor=f"L{line_anchor}",
773 )
781 774
782 775 raise HTTPFound(prev_url)
783 776
784 777 @LoginRequired()
785 @HasRepoPermissionAnyDecorator(
786 'repository.read', 'repository.write', 'repository.admin')
778 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
787 779 def repo_nodetree_full(self):
788 780 """
789 781 Returns rendered html of file tree that contains commit date,
@@ -792,22 +784,22 class RepoFilesView(RepoAppView):
792 784 """
793 785 c = self.load_default_context()
794 786
795 commit_id, f_path = self._get_commit_and_path()
787 commit_id, f_path, bytes_path = self._get_commit_and_path()
796 788 commit = self._get_commit_or_redirect(commit_id)
797 789 try:
798 dir_node = commit.get_node(f_path)
790 dir_node = commit.get_node(bytes_path)
799 791 except RepositoryError as e:
800 return Response(f'error: {h.escape(safe_str(e))}')
792 return Response(f"error: {h.escape(safe_str(e))}")
801 793
802 794 if dir_node.is_file():
803 return Response('')
795 return Response("")
804 796
805 797 c.file = dir_node
798 c.file_nodes = dir_node.commit.get_nodes(dir_node.bytes_path, pre_load=dir_node.default_pre_load)
806 799 c.commit = commit
807 at_rev = self.request.GET.get('at')
800 at_rev = self.request.GET.get("at")
808 801
809 html = self._get_tree_at_commit(
810 c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
802 html = self._get_tree_at_commit(c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
811 803
812 804 return Response(html)
813 805
@@ -816,15 +808,12 class RepoFilesView(RepoAppView):
816 808 safe_path = f_name.replace('"', '\\"')
817 809 encoded_path = urllib.parse.quote(f_name)
818 810
819 headers = "attachment; " \
820 "filename=\"{}\"; " \
821 "filename*=UTF-8\'\'{}".format(safe_path, encoded_path)
811 headers = f"attachment; " f'filename="{safe_path}"; ' f"filename*=UTF-8''{encoded_path}"
822 812
823 813 return header_safe_str(headers)
824 814
825 815 @LoginRequired()
826 @HasRepoPermissionAnyDecorator(
827 'repository.read', 'repository.write', 'repository.admin')
816 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
828 817 def repo_file_raw(self):
829 818 """
830 819 Action for show as raw, some mimetypes are "rendered",
@@ -832,25 +821,24 class RepoFilesView(RepoAppView):
832 821 """
833 822 c = self.load_default_context()
834 823
835 commit_id, f_path = self._get_commit_and_path()
824 commit_id, f_path, bytes_path = self._get_commit_and_path()
836 825 commit = self._get_commit_or_redirect(commit_id)
837 file_node = self._get_filenode_or_redirect(commit, f_path)
826 file_node = self._get_filenode_or_redirect(commit, bytes_path)
838 827
839 828 raw_mimetype_mapping = {
840 829 # map original mimetype to a mimetype used for "show as raw"
841 830 # you can also provide a content-disposition to override the
842 831 # default "attachment" disposition.
843 832 # orig_type: (new_type, new_dispo)
844
845 833 # show images inline:
846 834 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
847 835 # for example render an SVG with javascript inside or even render
848 836 # HTML.
849 'image/x-icon': ('image/x-icon', 'inline'),
850 'image/png': ('image/png', 'inline'),
851 'image/gif': ('image/gif', 'inline'),
852 'image/jpeg': ('image/jpeg', 'inline'),
853 'application/pdf': ('application/pdf', 'inline'),
837 "image/x-icon": ("image/x-icon", "inline"),
838 "image/png": ("image/png", "inline"),
839 "image/gif": ("image/gif", "inline"),
840 "image/jpeg": ("image/jpeg", "inline"),
841 "application/pdf": ("application/pdf", "inline"),
854 842 }
855 843
856 844 mimetype = file_node.mimetype
@@ -860,7 +848,7 class RepoFilesView(RepoAppView):
860 848 # we don't know anything special about this, handle it safely
861 849 if file_node.is_binary:
862 850 # do same as download raw for binary files
863 mimetype, disposition = 'application/octet-stream', 'attachment'
851 mimetype, disposition = "application/octet-stream", "attachment"
864 852 else:
865 853 # do not just use the original mimetype, but force text/plain,
866 854 # otherwise it would serve text/html and that might be unsafe.
@@ -869,9 +857,9 class RepoFilesView(RepoAppView):
869 857 # binary.This might lead to erroneous text display in some
870 858 # cases, but helps in other cases, like with text files
871 859 # without extension.
872 mimetype, disposition = 'text/plain', 'inline'
860 mimetype, disposition = "text/plain", "inline"
873 861
874 if disposition == 'attachment':
862 if disposition == "attachment":
875 863 disposition = self._get_attachement_headers(f_path)
876 864
877 865 stream_content = file_node.stream_bytes()
@@ -887,16 +875,15 class RepoFilesView(RepoAppView):
887 875 return response
888 876
889 877 @LoginRequired()
890 @HasRepoPermissionAnyDecorator(
891 'repository.read', 'repository.write', 'repository.admin')
878 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
892 879 def repo_file_download(self):
893 880 c = self.load_default_context()
894 881
895 commit_id, f_path = self._get_commit_and_path()
882 commit_id, f_path, bytes_path = self._get_commit_and_path()
896 883 commit = self._get_commit_or_redirect(commit_id)
897 file_node = self._get_filenode_or_redirect(commit, f_path)
884 file_node = self._get_filenode_or_redirect(commit, bytes_path)
898 885
899 if self.request.GET.get('lf'):
886 if self.request.GET.get("lf"):
900 887 # only if lf get flag is passed, we download this file
901 888 # as LFS/Largefile
902 889 lf_node = file_node.get_largefile_node()
@@ -919,49 +906,41 class RepoFilesView(RepoAppView):
919 906 return response
920 907
921 908 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
922
923 cache_seconds = safe_int(
924 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
909 cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time")
925 910 cache_on = cache_seconds > 0
926 911 log.debug(
927 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`'
928 'with caching: %s[TTL: %ss]' % (
929 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
912 "Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`"
913 "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0)
914 )
930 915
931 cache_namespace_uid = f'repo.{repo_id}'
932 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
916 cache_namespace_uid = f"repo.{repo_id}"
917 region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid)
933 918
934 919 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
935 920 def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path):
936 log.debug('Generating cached nodelist for repo_id:%s, %s, %s',
937 _repo_id, commit_id, f_path)
921 log.debug("Generating cached nodelist for repo_id:%s, %s, %s", _repo_id, commit_id, f_path)
938 922 try:
939 923 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path)
940 924 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
941 925 log.exception(safe_str(e))
942 h.flash(h.escape(safe_str(e)), category='error')
943 raise HTTPFound(h.route_path(
944 'repo_files', repo_name=self.db_repo_name,
945 commit_id='tip', f_path='/'))
926 h.flash(h.escape(safe_str(e)), category="error")
927 raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path="/"))
946 928
947 929 return _d + _f
948 930
949 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id,
950 commit_id, f_path)
951 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
931 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path)
932 return filter(lambda n: self.path_filter.path_access_allowed(n["name"]), result)
952 933
953 934 @LoginRequired()
954 @HasRepoPermissionAnyDecorator(
955 'repository.read', 'repository.write', 'repository.admin')
935 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
956 936 def repo_nodelist(self):
957 937 self.load_default_context()
958 938
959 commit_id, f_path = self._get_commit_and_path()
939 commit_id, f_path, bytes_path = self._get_commit_and_path()
960 940 commit = self._get_commit_or_redirect(commit_id)
961 941
962 metadata = self._get_nodelist_at_commit(
963 self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
964 return {'nodes': [x for x in metadata]}
942 metadata = self._get_nodelist_at_commit(self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
943 return {"nodes": [x for x in metadata]}
965 944
966 945 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
967 946 items = []
@@ -978,7 +957,7 class RepoFilesView(RepoAppView):
978 957
979 958 # NOTE(dan): old code we used in "diff" mode compare
980 959 new_f_path = vcspath.join(name, f_path)
981 return f'{new_f_path}@{commit_id}'
960 return f"{new_f_path}@{commit_id}"
982 961
983 962 def _get_node_history(self, commit_obj, f_path, commits=None):
984 963 """
@@ -996,37 +975,34 class RepoFilesView(RepoAppView):
996 975 if commits is None:
997 976 pre_load = ["author", "branch"]
998 977 try:
999 commits = tip.get_path_history(f_path, pre_load=pre_load)
978 commits = tip.get_path_history(safe_bytes(f_path), pre_load=pre_load)
1000 979 except (NodeDoesNotExistError, CommitError):
1001 980 # this node is not present at tip!
1002 commits = commit_obj.get_path_history(f_path, pre_load=pre_load)
981 commits = commit_obj.get_path_history(safe_bytes(f_path), pre_load=pre_load)
1003 982
1004 983 history = []
1005 984 commits_group = ([], _("Changesets"))
1006 985 for commit in commits:
1007 branch = ' (%s)' % commit.branch if commit.branch else ''
1008 n_desc = f'r{commit.idx}:{commit.short_id}{branch}'
1009 commits_group[0].append((commit.raw_id, n_desc, 'sha'))
986 branch = " (%s)" % commit.branch if commit.branch else ""
987 n_desc = f"r{commit.idx}:{commit.short_id}{branch}"
988 commits_group[0].append((commit.raw_id, n_desc, "sha"))
1010 989 history.append(commits_group)
1011 990
1012 991 symbolic_reference = self._symbolic_reference
1013 992
1014 if self.rhodecode_vcs_repo.alias == 'svn':
1015 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
1016 f_path, self.rhodecode_vcs_repo)
993 if self.rhodecode_vcs_repo.alias == "svn":
994 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(f_path, self.rhodecode_vcs_repo)
1017 995 if adjusted_f_path != f_path:
1018 996 log.debug(
1019 'Recognized svn tag or branch in file "%s", using svn '
1020 'specific symbolic references', f_path)
997 'Recognized svn tag or branch in file "%s", using svn ' "specific symbolic references", f_path
998 )
1021 999 f_path = adjusted_f_path
1022 1000 symbolic_reference = self._symbolic_reference_svn
1023 1001
1024 branches = self._create_references(
1025 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch')
1002 branches = self._create_references(self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, "branch")
1026 1003 branches_group = (branches, _("Branches"))
1027 1004
1028 tags = self._create_references(
1029 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag')
1005 tags = self._create_references(self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, "tag")
1030 1006 tags_group = (tags, _("Tags"))
1031 1007
1032 1008 history.append(branches_group)
@@ -1035,14 +1011,13 class RepoFilesView(RepoAppView):
1035 1011 return history, commits
1036 1012
1037 1013 @LoginRequired()
1038 @HasRepoPermissionAnyDecorator(
1039 'repository.read', 'repository.write', 'repository.admin')
1014 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
1040 1015 def repo_file_history(self):
1041 1016 self.load_default_context()
1042 1017
1043 commit_id, f_path = self._get_commit_and_path()
1018 commit_id, f_path, bytes_path = self._get_commit_and_path()
1044 1019 commit = self._get_commit_or_redirect(commit_id)
1045 file_node = self._get_filenode_or_redirect(commit, f_path)
1020 file_node = self._get_filenode_or_redirect(commit, bytes_path)
1046 1021
1047 1022 if file_node.is_file():
1048 1023 file_history, _hist = self._get_node_history(commit, f_path)
@@ -1051,52 +1026,38 class RepoFilesView(RepoAppView):
1051 1026 for section_items, section in file_history:
1052 1027 items = []
1053 1028 for obj_id, obj_text, obj_type in section_items:
1054 at_rev = ''
1055 if obj_type in ['branch', 'bookmark', 'tag']:
1029 at_rev = ""
1030 if obj_type in ["branch", "bookmark", "tag"]:
1056 1031 at_rev = obj_text
1057 entry = {
1058 'id': obj_id,
1059 'text': obj_text,
1060 'type': obj_type,
1061 'at_rev': at_rev
1062 }
1032 entry = {"id": obj_id, "text": obj_text, "type": obj_type, "at_rev": at_rev}
1063 1033
1064 1034 items.append(entry)
1065 1035
1066 res.append({
1067 'text': section,
1068 'children': items
1069 })
1036 res.append({"text": section, "children": items})
1070 1037
1071 data = {
1072 'more': False,
1073 'results': res
1074 }
1038 data = {"more": False, "results": res}
1075 1039 return data
1076 1040
1077 log.warning('Cannot fetch history for directory')
1041 log.warning("Cannot fetch history for directory")
1078 1042 raise HTTPBadRequest()
1079 1043
1080 1044 @LoginRequired()
1081 @HasRepoPermissionAnyDecorator(
1082 'repository.read', 'repository.write', 'repository.admin')
1045 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
1083 1046 def repo_file_authors(self):
1084 1047 c = self.load_default_context()
1085 1048
1086 commit_id, f_path = self._get_commit_and_path()
1049 commit_id, f_path, bytes_path = self._get_commit_and_path()
1087 1050 commit = self._get_commit_or_redirect(commit_id)
1088 file_node = self._get_filenode_or_redirect(commit, f_path)
1051 file_node = self._get_filenode_or_redirect(commit, bytes_path)
1089 1052
1090 1053 if not file_node.is_file():
1091 1054 raise HTTPBadRequest()
1092 1055
1093 1056 c.file_last_commit = file_node.last_commit
1094 if self.request.GET.get('annotate') == '1':
1057 if self.request.GET.get("annotate") == "1":
1095 1058 # use _hist from annotation if annotation mode is on
1096 1059 commit_ids = {x[1] for x in file_node.annotate}
1097 _hist = (
1098 self.rhodecode_vcs_repo.get_commit(commit_id)
1099 for commit_id in commit_ids)
1060 _hist = (self.rhodecode_vcs_repo.get_commit(commit_id) for commit_id in commit_ids)
1100 1061 else:
1101 1062 _f_history, _hist = self._get_node_history(commit, f_path)
1102 1063 c.file_author = False
@@ -1107,8 +1068,8 class RepoFilesView(RepoAppView):
1107 1068 if author not in unique:
1108 1069 unique[commit.author] = [
1109 1070 h.email(author),
1110 h.person(author, 'username_or_name_or_email'),
1111 1 # counter
1071 h.person(author, "username_or_name_or_email"),
1072 1, # counter
1112 1073 ]
1113 1074
1114 1075 else:
@@ -1120,204 +1081,186 class RepoFilesView(RepoAppView):
1120 1081 return self._get_template_context(c)
1121 1082
1122 1083 @LoginRequired()
1123 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1084 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1124 1085 def repo_files_check_head(self):
1125 1086 self.load_default_context()
1126 1087
1127 commit_id, f_path = self._get_commit_and_path()
1128 _branch_name, _sha_commit_id, is_head = \
1129 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1130 landing_ref=self.db_repo.landing_ref_name)
1088 commit_id, f_path, bytes_path = self._get_commit_and_path()
1089 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1090 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1091 )
1131 1092
1132 new_path = self.request.POST.get('path')
1133 operation = self.request.POST.get('operation')
1134 path_exist = ''
1093 new_path = self.request.POST.get("path")
1094 operation = self.request.POST.get("operation")
1095 path_exist = ""
1135 1096
1136 if new_path and operation in ['create', 'upload']:
1137 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1097 if new_path and operation in ["create", "upload"]:
1098 new_f_path = os.path.join(f_path.lstrip("/"), new_path)
1138 1099 try:
1139 1100 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1140 1101 # NOTE(dan): construct whole path without leading /
1141 file_node = commit_obj.get_node(new_f_path)
1142 if file_node is not None:
1102 file_node = commit_obj.get_node(safe_bytes(new_f_path))
1103 if file_node:
1143 1104 path_exist = new_f_path
1144 except EmptyRepositoryError:
1145 pass
1146 except Exception:
1105 except (EmptyRepositoryError, NodeDoesNotExistError):
1147 1106 pass
1148 1107
1149 return {
1150 'branch': _branch_name,
1151 'sha': _sha_commit_id,
1152 'is_head': is_head,
1153 'path_exists': path_exist
1154 }
1108 return {"branch": _branch_name, "sha": _sha_commit_id, "is_head": is_head, "path_exists": path_exist}
1155 1109
1156 1110 @LoginRequired()
1157 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1111 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1158 1112 def repo_files_remove_file(self):
1159 1113 _ = self.request.translate
1160 1114 c = self.load_default_context()
1161 commit_id, f_path = self._get_commit_and_path()
1115 commit_id, f_path, bytes_path = self._get_commit_and_path()
1162 1116
1163 1117 self._ensure_not_locked()
1164 _branch_name, _sha_commit_id, is_head = \
1165 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1166 landing_ref=self.db_repo.landing_ref_name)
1118 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1119 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1120 )
1167 1121
1168 1122 self.forbid_non_head(is_head, f_path)
1169 1123 self.check_branch_permission(_branch_name)
1170 1124
1171 1125 c.commit = self._get_commit_or_redirect(commit_id)
1172 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1126 c.file = self._get_filenode_or_redirect(c.commit, bytes_path)
1173 1127
1174 c.default_message = _(
1175 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1128 c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path)
1176 1129 c.f_path = f_path
1177 1130
1178 1131 return self._get_template_context(c)
1179 1132
1180 1133 @LoginRequired()
1181 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1134 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1182 1135 @CSRFRequired()
1183 1136 def repo_files_delete_file(self):
1184 1137 _ = self.request.translate
1185 1138
1186 1139 c = self.load_default_context()
1187 commit_id, f_path = self._get_commit_and_path()
1140 commit_id, f_path, bytes_path = self._get_commit_and_path()
1188 1141
1189 1142 self._ensure_not_locked()
1190 _branch_name, _sha_commit_id, is_head = \
1191 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1192 landing_ref=self.db_repo.landing_ref_name)
1143 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1144 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1145 )
1193 1146
1194 1147 self.forbid_non_head(is_head, f_path)
1195 1148 self.check_branch_permission(_branch_name)
1196 1149
1197 1150 c.commit = self._get_commit_or_redirect(commit_id)
1198 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1151 c.file = self._get_filenode_or_redirect(c.commit, bytes_path)
1199 1152
1200 c.default_message = _(
1201 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1153 c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path)
1202 1154 c.f_path = f_path
1203 1155 node_path = f_path
1204 1156 author = self._rhodecode_db_user.full_contact
1205 message = self.request.POST.get('message') or c.default_message
1157 message = self.request.POST.get("message") or c.default_message
1206 1158 try:
1207 nodes = {
1208 safe_bytes(node_path): {
1209 'content': b''
1210 }
1211 }
1159 nodes = {safe_bytes(node_path): {"content": b""}}
1212 1160 ScmModel().delete_nodes(
1213 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1161 user=self._rhodecode_db_user.user_id,
1162 repo=self.db_repo,
1214 1163 message=message,
1215 1164 nodes=nodes,
1216 1165 parent_commit=c.commit,
1217 1166 author=author,
1218 1167 )
1219 1168
1220 h.flash(
1221 _('Successfully deleted file `{}`').format(
1222 h.escape(f_path)), category='success')
1169 h.flash(_("Successfully deleted file `{}`").format(h.escape(f_path)), category="success")
1223 1170 except Exception:
1224 log.exception('Error during commit operation')
1225 h.flash(_('Error occurred during commit'), category='error')
1226 raise HTTPFound(
1227 h.route_path('repo_commit', repo_name=self.db_repo_name,
1228 commit_id='tip'))
1171 log.exception("Error during commit operation")
1172 h.flash(_("Error occurred during commit"), category="error")
1173 raise HTTPFound(h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip"))
1229 1174
1230 1175 @LoginRequired()
1231 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1176 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1232 1177 def repo_files_edit_file(self):
1233 1178 _ = self.request.translate
1234 1179 c = self.load_default_context()
1235 commit_id, f_path = self._get_commit_and_path()
1180 commit_id, f_path, bytes_path = self._get_commit_and_path()
1236 1181
1237 1182 self._ensure_not_locked()
1238 _branch_name, _sha_commit_id, is_head = \
1239 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1240 landing_ref=self.db_repo.landing_ref_name)
1183 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1184 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1185 )
1241 1186
1242 1187 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1243 1188 self.check_branch_permission(_branch_name, commit_id=commit_id)
1244 1189
1245 1190 c.commit = self._get_commit_or_redirect(commit_id)
1246 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1191 c.file = self._get_filenode_or_redirect(c.commit, bytes_path)
1247 1192
1248 1193 if c.file.is_binary:
1249 1194 files_url = h.route_path(
1250 'repo_files',
1251 repo_name=self.db_repo_name,
1252 commit_id=c.commit.raw_id, f_path=f_path)
1195 "repo_files", repo_name=self.db_repo_name, commit_id=c.commit.raw_id, f_path=f_path
1196 )
1253 1197 raise HTTPFound(files_url)
1254 1198
1255 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1199 c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path)
1256 1200 c.f_path = f_path
1257 1201
1258 1202 return self._get_template_context(c)
1259 1203
1260 1204 @LoginRequired()
1261 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1205 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1262 1206 @CSRFRequired()
1263 1207 def repo_files_update_file(self):
1264 1208 _ = self.request.translate
1265 1209 c = self.load_default_context()
1266 commit_id, f_path = self._get_commit_and_path()
1210 commit_id, f_path, bytes_path = self._get_commit_and_path()
1267 1211
1268 1212 self._ensure_not_locked()
1269 1213
1270 1214 c.commit = self._get_commit_or_redirect(commit_id)
1271 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1215 c.file = self._get_filenode_or_redirect(c.commit, bytes_path)
1272 1216
1273 1217 if c.file.is_binary:
1274 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1275 commit_id=c.commit.raw_id, f_path=f_path))
1218 raise HTTPFound(
1219 h.route_path("repo_files", repo_name=self.db_repo_name, commit_id=c.commit.raw_id, f_path=f_path)
1220 )
1276 1221
1277 _branch_name, _sha_commit_id, is_head = \
1278 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1279 landing_ref=self.db_repo.landing_ref_name)
1222 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1223 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1224 )
1280 1225
1281 1226 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1282 1227 self.check_branch_permission(_branch_name, commit_id=commit_id)
1283 1228
1284 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1229 c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path)
1285 1230 c.f_path = f_path
1286 1231
1287 1232 old_content = c.file.str_content
1288 1233 sl = old_content.splitlines(1)
1289 first_line = sl[0] if sl else ''
1234 first_line = sl[0] if sl else ""
1290 1235
1291 1236 r_post = self.request.POST
1292 1237 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1293 1238 line_ending_mode = detect_mode(first_line, 0)
1294 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1239 content = convert_line_endings(r_post.get("content", ""), line_ending_mode)
1295 1240
1296 message = r_post.get('message') or c.default_message
1241 message = r_post.get("message") or c.default_message
1297 1242
1298 1243 org_node_path = c.file.str_path
1299 filename = r_post['filename']
1244 filename = r_post["filename"]
1300 1245
1301 1246 root_path = c.file.dir_path
1302 1247 pure_path = self.create_pure_path(root_path, filename)
1303 1248 node_path = pure_path.as_posix()
1304 1249
1305 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1306 commit_id=commit_id)
1250 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit_id)
1307 1251 if content == old_content and node_path == org_node_path:
1308 h.flash(_('No changes detected on {}').format(h.escape(org_node_path)),
1309 category='warning')
1252 h.flash(_("No changes detected on {}").format(h.escape(org_node_path)), category="warning")
1310 1253 raise HTTPFound(default_redirect_url)
1311 1254
1312 1255 try:
1313 1256 mapping = {
1314 1257 c.file.bytes_path: {
1315 'org_filename': org_node_path,
1316 'filename': safe_bytes(node_path),
1317 'content': safe_bytes(content),
1318 'lexer': '',
1319 'op': 'mod',
1320 'mode': c.file.mode
1258 "org_filename": org_node_path,
1259 "filename": safe_bytes(node_path),
1260 "content": safe_bytes(content),
1261 "lexer": "",
1262 "op": "mod",
1263 "mode": c.file.mode,
1321 1264 }
1322 1265 }
1323 1266
@@ -1329,28 +1272,26 class RepoFilesView(RepoAppView):
1329 1272 parent_commit=c.commit,
1330 1273 )
1331 1274
1332 h.flash(_('Successfully committed changes to file `{}`').format(
1333 h.escape(f_path)), category='success')
1334 default_redirect_url = h.route_path(
1335 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1275 h.flash(_("Successfully committed changes to file `{}`").format(h.escape(f_path)), category="success")
1276 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1336 1277
1337 1278 except Exception:
1338 log.exception('Error occurred during commit')
1339 h.flash(_('Error occurred during commit'), category='error')
1279 log.exception("Error occurred during commit")
1280 h.flash(_("Error occurred during commit"), category="error")
1340 1281
1341 1282 raise HTTPFound(default_redirect_url)
1342 1283
1343 1284 @LoginRequired()
1344 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1285 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1345 1286 def repo_files_add_file(self):
1346 1287 _ = self.request.translate
1347 1288 c = self.load_default_context()
1348 commit_id, f_path = self._get_commit_and_path()
1289 commit_id, f_path, bytes_path = self._get_commit_and_path()
1349 1290
1350 1291 self._ensure_not_locked()
1351 1292
1352 1293 # Check if we need to use this page to upload binary
1353 upload_binary = str2bool(self.request.params.get('upload_binary', False))
1294 upload_binary = str2bool(self.request.params.get("upload_binary", False))
1354 1295
1355 1296 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1356 1297 if c.commit is None:
@@ -1359,29 +1300,32 class RepoFilesView(RepoAppView):
1359 1300 if self.rhodecode_vcs_repo.is_empty():
1360 1301 # for empty repository we cannot check for current branch, we rely on
1361 1302 # c.commit.branch instead
1362 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1303 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1363 1304 else:
1364 _branch_name, _sha_commit_id, is_head = \
1365 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1366 landing_ref=self.db_repo.landing_ref_name)
1305 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1306 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1307 )
1367 1308
1368 1309 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1369 1310 self.check_branch_permission(_branch_name, commit_id=commit_id)
1370 1311
1371 c.default_message = (_('Added file via RhodeCode Enterprise')) \
1372 if not upload_binary else (_('Edited file {} via RhodeCode Enterprise').format(f_path))
1373 c.f_path = f_path.lstrip('/') # ensure not relative path
1312 c.default_message = (
1313 (_("Added file via RhodeCode Enterprise"))
1314 if not upload_binary
1315 else (_("Edited file {} via RhodeCode Enterprise").format(f_path))
1316 )
1317 c.f_path = f_path.lstrip("/") # ensure not relative path
1374 1318 c.replace_binary = upload_binary
1375 1319
1376 1320 return self._get_template_context(c)
1377 1321
1378 1322 @LoginRequired()
1379 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1323 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1380 1324 @CSRFRequired()
1381 1325 def repo_files_create_file(self):
1382 1326 _ = self.request.translate
1383 1327 c = self.load_default_context()
1384 commit_id, f_path = self._get_commit_and_path()
1328 commit_id, f_path, bytes_path = self._get_commit_and_path()
1385 1329
1386 1330 self._ensure_not_locked()
1387 1331
@@ -1391,56 +1335,48 class RepoFilesView(RepoAppView):
1391 1335
1392 1336 # calculate redirect URL
1393 1337 if self.rhodecode_vcs_repo.is_empty():
1394 default_redirect_url = h.route_path(
1395 'repo_summary', repo_name=self.db_repo_name)
1338 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1396 1339 else:
1397 default_redirect_url = h.route_path(
1398 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1340 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1399 1341
1400 1342 if self.rhodecode_vcs_repo.is_empty():
1401 1343 # for empty repository we cannot check for current branch, we rely on
1402 1344 # c.commit.branch instead
1403 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1345 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1404 1346 else:
1405 _branch_name, _sha_commit_id, is_head = \
1406 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1407 landing_ref=self.db_repo.landing_ref_name)
1347 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1348 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1349 )
1408 1350
1409 1351 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1410 1352 self.check_branch_permission(_branch_name, commit_id=commit_id)
1411 1353
1412 c.default_message = (_('Added file via RhodeCode Enterprise'))
1354 c.default_message = _("Added file via RhodeCode Enterprise")
1413 1355 c.f_path = f_path
1414 1356
1415 1357 r_post = self.request.POST
1416 message = r_post.get('message') or c.default_message
1417 filename = r_post.get('filename')
1358 message = r_post.get("message") or c.default_message
1359 filename = r_post.get("filename")
1418 1360 unix_mode = 0
1419 1361
1420 1362 if not filename:
1421 1363 # If there's no commit, redirect to repo summary
1422 1364 if type(c.commit) is EmptyCommit:
1423 redirect_url = h.route_path(
1424 'repo_summary', repo_name=self.db_repo_name)
1365 redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1425 1366 else:
1426 1367 redirect_url = default_redirect_url
1427 h.flash(_('No filename specified'), category='warning')
1368 h.flash(_("No filename specified"), category="warning")
1428 1369 raise HTTPFound(redirect_url)
1429 1370
1430 1371 root_path = f_path
1431 1372 pure_path = self.create_pure_path(root_path, filename)
1432 node_path = pure_path.as_posix().lstrip('/')
1373 node_path = pure_path.as_posix().lstrip("/")
1433 1374
1434 1375 author = self._rhodecode_db_user.full_contact
1435 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1436 nodes = {
1437 safe_bytes(node_path): {
1438 'content': safe_bytes(content)
1439 }
1440 }
1376 content = convert_line_endings(r_post.get("content", ""), unix_mode)
1377 nodes = {safe_bytes(node_path): {"content": safe_bytes(content)}}
1441 1378
1442 1379 try:
1443
1444 1380 commit = ScmModel().create_nodes(
1445 1381 user=self._rhodecode_db_user.user_id,
1446 1382 repo=self.db_repo,
@@ -1450,32 +1386,32 class RepoFilesView(RepoAppView):
1450 1386 author=author,
1451 1387 )
1452 1388
1453 h.flash(_('Successfully committed new file `{}`').format(
1454 h.escape(node_path)), category='success')
1389 h.flash(_("Successfully committed new file `{}`").format(h.escape(node_path)), category="success")
1455 1390
1456 default_redirect_url = h.route_path(
1457 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1391 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1458 1392
1459 1393 except NonRelativePathError:
1460 log.exception('Non Relative path found')
1461 h.flash(_('The location specified must be a relative path and must not '
1462 'contain .. in the path'), category='warning')
1394 log.exception("Non Relative path found")
1395 h.flash(
1396 _("The location specified must be a relative path and must not " "contain .. in the path"),
1397 category="warning",
1398 )
1463 1399 raise HTTPFound(default_redirect_url)
1464 1400 except (NodeError, NodeAlreadyExistsError) as e:
1465 h.flash(h.escape(safe_str(e)), category='error')
1401 h.flash(h.escape(safe_str(e)), category="error")
1466 1402 except Exception:
1467 log.exception('Error occurred during commit')
1468 h.flash(_('Error occurred during commit'), category='error')
1403 log.exception("Error occurred during commit")
1404 h.flash(_("Error occurred during commit"), category="error")
1469 1405
1470 1406 raise HTTPFound(default_redirect_url)
1471 1407
1472 1408 @LoginRequired()
1473 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1409 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1474 1410 @CSRFRequired()
1475 1411 def repo_files_upload_file(self):
1476 1412 _ = self.request.translate
1477 1413 c = self.load_default_context()
1478 commit_id, f_path = self._get_commit_and_path()
1414 commit_id, f_path, bytes_path = self._get_commit_and_path()
1479 1415
1480 1416 self._ensure_not_locked()
1481 1417
@@ -1485,65 +1421,52 class RepoFilesView(RepoAppView):
1485 1421
1486 1422 # calculate redirect URL
1487 1423 if self.rhodecode_vcs_repo.is_empty():
1488 default_redirect_url = h.route_path(
1489 'repo_summary', repo_name=self.db_repo_name)
1424 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1490 1425 else:
1491 default_redirect_url = h.route_path(
1492 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1426 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1493 1427
1494 1428 if self.rhodecode_vcs_repo.is_empty():
1495 1429 # for empty repository we cannot check for current branch, we rely on
1496 1430 # c.commit.branch instead
1497 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1431 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1498 1432 else:
1499 _branch_name, _sha_commit_id, is_head = \
1500 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1501 landing_ref=self.db_repo.landing_ref_name)
1433 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1434 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1435 )
1502 1436
1503 1437 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1504 1438 if error:
1505 return {
1506 'error': error,
1507 'redirect_url': default_redirect_url
1508 }
1439 return {"error": error, "redirect_url": default_redirect_url}
1509 1440 error = self.check_branch_permission(_branch_name, json_mode=True)
1510 1441 if error:
1511 return {
1512 'error': error,
1513 'redirect_url': default_redirect_url
1514 }
1442 return {"error": error, "redirect_url": default_redirect_url}
1515 1443
1516 c.default_message = (_('Added file via RhodeCode Enterprise'))
1444 c.default_message = _("Added file via RhodeCode Enterprise")
1517 1445 c.f_path = f_path
1518 1446
1519 1447 r_post = self.request.POST
1520 1448
1521 1449 message = c.default_message
1522 user_message = r_post.getall('message')
1450 user_message = r_post.getall("message")
1523 1451 if isinstance(user_message, list) and user_message:
1524 1452 # we take the first from duplicated results if it's not empty
1525 1453 message = user_message[0] if user_message[0] else message
1526 1454
1527 1455 nodes = {}
1528 1456
1529 for file_obj in r_post.getall('files_upload') or []:
1457 for file_obj in r_post.getall("files_upload") or []:
1530 1458 content = file_obj.file
1531 1459 filename = file_obj.filename
1532 1460
1533 1461 root_path = f_path
1534 1462 pure_path = self.create_pure_path(root_path, filename)
1535 node_path = pure_path.as_posix().lstrip('/')
1463 node_path = pure_path.as_posix().lstrip("/")
1536 1464
1537 nodes[safe_bytes(node_path)] = {
1538 'content': content
1539 }
1465 nodes[safe_bytes(node_path)] = {"content": content}
1540 1466
1541 1467 if not nodes:
1542 error = 'missing files'
1543 return {
1544 'error': error,
1545 'redirect_url': default_redirect_url
1546 }
1468 error = "missing files"
1469 return {"error": error, "redirect_url": default_redirect_url}
1547 1470
1548 1471 author = self._rhodecode_db_user.full_contact
1549 1472
@@ -1557,54 +1480,40 class RepoFilesView(RepoAppView):
1557 1480 author=author,
1558 1481 )
1559 1482 if len(nodes) == 1:
1560 flash_message = _('Successfully committed {} new files').format(len(nodes))
1483 flash_message = _("Successfully committed {} new files").format(len(nodes))
1561 1484 else:
1562 flash_message = _('Successfully committed 1 new file')
1485 flash_message = _("Successfully committed 1 new file")
1563 1486
1564 h.flash(flash_message, category='success')
1487 h.flash(flash_message, category="success")
1565 1488
1566 default_redirect_url = h.route_path(
1567 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1489 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1568 1490
1569 1491 except NonRelativePathError:
1570 log.exception('Non Relative path found')
1571 error = _('The location specified must be a relative path and must not '
1572 'contain .. in the path')
1573 h.flash(error, category='warning')
1492 log.exception("Non Relative path found")
1493 error = _("The location specified must be a relative path and must not " "contain .. in the path")
1494 h.flash(error, category="warning")
1574 1495
1575 return {
1576 'error': error,
1577 'redirect_url': default_redirect_url
1578 }
1496 return {"error": error, "redirect_url": default_redirect_url}
1579 1497 except (NodeError, NodeAlreadyExistsError) as e:
1580 1498 error = h.escape(e)
1581 h.flash(error, category='error')
1499 h.flash(error, category="error")
1582 1500
1583 return {
1584 'error': error,
1585 'redirect_url': default_redirect_url
1586 }
1501 return {"error": error, "redirect_url": default_redirect_url}
1587 1502 except Exception:
1588 log.exception('Error occurred during commit')
1589 error = _('Error occurred during commit')
1590 h.flash(error, category='error')
1591 return {
1592 'error': error,
1593 'redirect_url': default_redirect_url
1594 }
1503 log.exception("Error occurred during commit")
1504 error = _("Error occurred during commit")
1505 h.flash(error, category="error")
1506 return {"error": error, "redirect_url": default_redirect_url}
1595 1507
1596 return {
1597 'error': None,
1598 'redirect_url': default_redirect_url
1599 }
1508 return {"error": None, "redirect_url": default_redirect_url}
1600 1509
1601 1510 @LoginRequired()
1602 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1511 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1603 1512 @CSRFRequired()
1604 1513 def repo_files_replace_file(self):
1605 1514 _ = self.request.translate
1606 1515 c = self.load_default_context()
1607 commit_id, f_path = self._get_commit_and_path()
1516 commit_id, f_path, bytes_path = self._get_commit_and_path()
1608 1517
1609 1518 self._ensure_not_locked()
1610 1519
@@ -1613,64 +1522,50 class RepoFilesView(RepoAppView):
1613 1522 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1614 1523
1615 1524 if self.rhodecode_vcs_repo.is_empty():
1616 default_redirect_url = h.route_path(
1617 'repo_summary', repo_name=self.db_repo_name)
1525 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1618 1526 else:
1619 default_redirect_url = h.route_path(
1620 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1527 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1621 1528
1622 1529 if self.rhodecode_vcs_repo.is_empty():
1623 1530 # for empty repository we cannot check for current branch, we rely on
1624 1531 # c.commit.branch instead
1625 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1532 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1626 1533 else:
1627 _branch_name, _sha_commit_id, is_head = \
1628 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1629 landing_ref=self.db_repo.landing_ref_name)
1534 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1535 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1536 )
1630 1537
1631 1538 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1632 1539 if error:
1633 return {
1634 'error': error,
1635 'redirect_url': default_redirect_url
1636 }
1540 return {"error": error, "redirect_url": default_redirect_url}
1637 1541 error = self.check_branch_permission(_branch_name, json_mode=True)
1638 1542 if error:
1639 return {
1640 'error': error,
1641 'redirect_url': default_redirect_url
1642 }
1543 return {"error": error, "redirect_url": default_redirect_url}
1643 1544
1644 c.default_message = (_('Edited file {} via RhodeCode Enterprise').format(f_path))
1545 c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path)
1645 1546 c.f_path = f_path
1646 1547
1647 1548 r_post = self.request.POST
1648 1549
1649 1550 message = c.default_message
1650 user_message = r_post.getall('message')
1551 user_message = r_post.getall("message")
1651 1552 if isinstance(user_message, list) and user_message:
1652 1553 # we take the first from duplicated results if it's not empty
1653 1554 message = user_message[0] if user_message[0] else message
1654 1555
1655 data_for_replacement = r_post.getall('files_upload') or []
1556 data_for_replacement = r_post.getall("files_upload") or []
1656 1557 if (objects_count := len(data_for_replacement)) > 1:
1657 return {
1658 'error': 'too many files for replacement',
1659 'redirect_url': default_redirect_url
1660 }
1558 return {"error": "too many files for replacement", "redirect_url": default_redirect_url}
1661 1559 elif not objects_count:
1662 return {
1663 'error': 'missing files',
1664 'redirect_url': default_redirect_url
1665 }
1560 return {"error": "missing files", "redirect_url": default_redirect_url}
1666 1561
1667 1562 content = data_for_replacement[0].file
1668 1563 retrieved_filename = data_for_replacement[0].filename
1669 1564
1670 if retrieved_filename.split('.')[-1] != f_path.split('.')[-1]:
1565 if retrieved_filename.split(".")[-1] != f_path.split(".")[-1]:
1671 1566 return {
1672 'error': 'file extension of uploaded file doesn\'t match an original file\'s extension',
1673 'redirect_url': default_redirect_url
1567 "error": "file extension of uploaded file doesn't match an original file's extension",
1568 "redirect_url": default_redirect_url,
1674 1569 }
1675 1570
1676 1571 author = self._rhodecode_db_user.full_contact
@@ -1681,36 +1576,26 class RepoFilesView(RepoAppView):
1681 1576 repo=self.db_repo,
1682 1577 message=message,
1683 1578 node={
1684 'content': content,
1685 'file_path': f_path.encode(),
1579 "content": content,
1580 "file_path": f_path.encode(),
1686 1581 },
1687 1582 parent_commit=c.commit,
1688 1583 author=author,
1689 1584 )
1690 1585
1691 h.flash(_('Successfully committed 1 new file'), category='success')
1586 h.flash(_("Successfully committed 1 new file"), category="success")
1692 1587
1693 default_redirect_url = h.route_path(
1694 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1588 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1695 1589
1696 1590 except (NodeError, NodeAlreadyExistsError) as e:
1697 1591 error = h.escape(e)
1698 h.flash(error, category='error')
1592 h.flash(error, category="error")
1699 1593
1700 return {
1701 'error': error,
1702 'redirect_url': default_redirect_url
1703 }
1594 return {"error": error, "redirect_url": default_redirect_url}
1704 1595 except Exception:
1705 log.exception('Error occurred during commit')
1706 error = _('Error occurred during commit')
1707 h.flash(error, category='error')
1708 return {
1709 'error': error,
1710 'redirect_url': default_redirect_url
1711 }
1596 log.exception("Error occurred during commit")
1597 error = _("Error occurred during commit")
1598 h.flash(error, category="error")
1599 return {"error": error, "redirect_url": default_redirect_url}
1712 1600
1713 return {
1714 'error': None,
1715 'redirect_url': default_redirect_url
1716 }
1601 return {"error": None, "redirect_url": default_redirect_url}
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -487,7 +487,9 class RepoPullRequestsView(RepoAppView,
487 487 c.pull_request_set_reviewers_data_json = ext_json.str_json(c.pull_request_set_reviewers_data_json)
488 488
489 489 # observers
490 observer_ids = []
490 491 for observer_obj, member in pull_request_at_ver.observers():
492 observer_ids.append(observer_obj.user_id)
491 493 member_observer = h.reviewer_as_json(
492 494 member, reasons=[], mandatory=False,
493 495 role=observer_obj.role,
@@ -497,6 +499,7 class RepoPullRequestsView(RepoAppView,
497 499 c.pull_request_set_observers_data_json['observers'].append(member_observer)
498 500
499 501 c.pull_request_set_observers_data_json = ext_json.str_json(c.pull_request_set_observers_data_json)
502 c.status_change_disabled = self._rhodecode_user.user_id in observer_ids
500 503
501 504 general_comments, inline_comments = \
502 505 self.register_comments_vars(c, pull_request_latest, versions)
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +20,7 import os
20 20 import sys
21 21 import logging
22 22
23 from rhodecode.lib.hook_daemon.base import prepare_callback_daemon
23 from rhodecode.lib.hook_daemon.utils import prepare_callback_daemon
24 24 from rhodecode.lib.ext_json import sjson as json
25 25 from rhodecode.lib.vcs.conf import settings as vcs_settings
26 26 from rhodecode.lib.api_utils import call_service_api
@@ -162,9 +162,7 class SshVcsServer(object):
162 162 extras = {}
163 163 extras.update(tunnel_extras)
164 164
165 callback_daemon, extras = prepare_callback_daemon(
166 extras, protocol=self.hooks_protocol,
167 host=vcs_settings.HOOKS_HOST)
165 callback_daemon, extras = prepare_callback_daemon(extras, protocol=self.hooks_protocol)
168 166
169 167 with callback_daemon:
170 168 try:
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -25,7 +25,7 import tempfile
25 25 from subprocess import Popen, PIPE
26 26 import urllib.parse
27 27
28 from rhodecode_tools.lib.utils import safe_str
28 from rhodecode.lib.str_utils import safe_str
29 29 from .base import SshVcsServer
30 30
31 31 log = logging.getLogger(__name__)
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -33,19 +33,24 class GitServerCreator(object):
33 33 'app:main': {
34 34 'ssh.executable.git': git_path,
35 35 'vcs.hooks.protocol.v2': 'celery',
36 'app.service_api.host': 'http://localhost',
37 'app.service_api.token': 'secret4',
38 'rhodecode.api.url': '/_admin/api',
36 39 }
37 40 }
38 41 repo_name = 'test_git'
39 42 repo_mode = 'receive-pack'
40 43 user = plain_dummy_user()
41 44
42 def __init__(self):
43 pass
45 def __init__(self, service_api_url, ini_file):
46 self.service_api_url = service_api_url
47 self.ini_file = ini_file
44 48
45 49 def create(self, **kwargs):
50 self.config_data['app:main']['app.service_api.host'] = self.service_api_url
46 51 parameters = {
47 52 'store': self.root,
48 'ini_path': '',
53 'ini_path': self.ini_file,
49 54 'user': self.user,
50 55 'repo_name': self.repo_name,
51 56 'repo_mode': self.repo_mode,
@@ -60,12 +65,30 class GitServerCreator(object):
60 65 return server
61 66
62 67
63 @pytest.fixture()
64 def git_server(app):
65 return GitServerCreator()
68 @pytest.fixture(scope='module')
69 def git_server(request, module_app, rhodecode_factory, available_port_factory):
70 ini_file = module_app._pyramid_settings['__file__']
71 vcsserver_host = module_app._pyramid_settings['vcs.server']
72
73 store_dir = os.path.dirname(ini_file)
74
75 # start rhodecode for service API
76 rc = rhodecode_factory(
77 request,
78 store_dir=store_dir,
79 port=available_port_factory(),
80 overrides=(
81 {'handler_console': {'level': 'DEBUG'}},
82 {'app:main': {'vcs.server': vcsserver_host}},
83 {'app:main': {'repo_store.path': store_dir}}
84 ))
85
86 service_api_url = f'http://{rc.bind_addr}'
87
88 return GitServerCreator(service_api_url, ini_file)
66 89
67 90
68 class TestGitServer(object):
91 class TestGitServer:
69 92
70 93 def test_command(self, git_server):
71 94 server = git_server.create()
@@ -102,14 +125,14 class TestGitServer(object):
102 125 assert result is value
103 126
104 127 def test_run_returns_executes_command(self, git_server):
128 from rhodecode.apps.ssh_support.lib.backends.git import GitTunnelWrapper
105 129 server = git_server.create()
106 from rhodecode.apps.ssh_support.lib.backends.git import GitTunnelWrapper
107 130
108 131 os.environ['SSH_CLIENT'] = '127.0.0.1'
109 132 with mock.patch.object(GitTunnelWrapper, 'create_hooks_env') as _patch:
110 133 _patch.return_value = 0
111 134 with mock.patch.object(GitTunnelWrapper, 'command', return_value='date'):
112 exit_code = server.run()
135 exit_code = server.run(tunnel_extras={'config': server.ini_path})
113 136
114 137 assert exit_code == (0, False)
115 138
@@ -135,7 +158,7 class TestGitServer(object):
135 158 'action': action,
136 159 'ip': '10.10.10.10',
137 160 'locked_by': [None, None],
138 'config': '',
161 'config': git_server.ini_file,
139 162 'repo_store': store,
140 163 'server_url': None,
141 164 'hooks': ['push', 'pull'],
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -17,6 +17,7
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import os
20
20 21 import mock
21 22 import pytest
22 23
@@ -32,22 +33,27 class MercurialServerCreator(object):
32 33 'app:main': {
33 34 'ssh.executable.hg': hg_path,
34 35 'vcs.hooks.protocol.v2': 'celery',
36 'app.service_api.host': 'http://localhost',
37 'app.service_api.token': 'secret4',
38 'rhodecode.api.url': '/_admin/api',
35 39 }
36 40 }
37 41 repo_name = 'test_hg'
38 42 user = plain_dummy_user()
39 43
40 def __init__(self):
41 pass
44 def __init__(self, service_api_url, ini_file):
45 self.service_api_url = service_api_url
46 self.ini_file = ini_file
42 47
43 48 def create(self, **kwargs):
49 self.config_data['app:main']['app.service_api.host'] = self.service_api_url
44 50 parameters = {
45 51 'store': self.root,
46 'ini_path': '',
52 'ini_path': self.ini_file,
47 53 'user': self.user,
48 54 'repo_name': self.repo_name,
49 55 'user_permissions': {
50 'test_hg': 'repository.admin'
56 self.repo_name: 'repository.admin'
51 57 },
52 58 'settings': self.config_data['app:main'],
53 59 'env': plain_dummy_env()
@@ -57,12 +63,30 class MercurialServerCreator(object):
57 63 return server
58 64
59 65
60 @pytest.fixture()
61 def hg_server(app):
62 return MercurialServerCreator()
66 @pytest.fixture(scope='module')
67 def hg_server(request, module_app, rhodecode_factory, available_port_factory):
68 ini_file = module_app._pyramid_settings['__file__']
69 vcsserver_host = module_app._pyramid_settings['vcs.server']
70
71 store_dir = os.path.dirname(ini_file)
72
73 # start rhodecode for service API
74 rc = rhodecode_factory(
75 request,
76 store_dir=store_dir,
77 port=available_port_factory(),
78 overrides=(
79 {'handler_console': {'level': 'DEBUG'}},
80 {'app:main': {'vcs.server': vcsserver_host}},
81 {'app:main': {'repo_store.path': store_dir}}
82 ))
83
84 service_api_url = f'http://{rc.bind_addr}'
85
86 return MercurialServerCreator(service_api_url, ini_file)
63 87
64 88
65 class TestMercurialServer(object):
89 class TestMercurialServer:
66 90
67 91 def test_command(self, hg_server, tmpdir):
68 92 server = hg_server.create()
@@ -107,7 +131,7 class TestMercurialServer(object):
107 131 with mock.patch.object(MercurialTunnelWrapper, 'create_hooks_env') as _patch:
108 132 _patch.return_value = 0
109 133 with mock.patch.object(MercurialTunnelWrapper, 'command', return_value='date'):
110 exit_code = server.run()
134 exit_code = server.run(tunnel_extras={'config': server.ini_path})
111 135
112 136 assert exit_code == (0, False)
113 137
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -15,7 +15,9
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18 19 import os
20
19 21 import mock
20 22 import pytest
21 23
@@ -26,39 +28,62 from rhodecode.apps.ssh_support.tests.co
26 28 class SubversionServerCreator(object):
27 29 root = '/tmp/repo/path/'
28 30 svn_path = '/usr/local/bin/svnserve'
31
29 32 config_data = {
30 33 'app:main': {
31 34 'ssh.executable.svn': svn_path,
32 35 'vcs.hooks.protocol.v2': 'celery',
36 'app.service_api.host': 'http://localhost',
37 'app.service_api.token': 'secret4',
38 'rhodecode.api.url': '/_admin/api',
33 39 }
34 40 }
35 41 repo_name = 'test-svn'
36 42 user = plain_dummy_user()
37 43
38 def __init__(self):
39 pass
44 def __init__(self, service_api_url, ini_file):
45 self.service_api_url = service_api_url
46 self.ini_file = ini_file
40 47
41 48 def create(self, **kwargs):
49 self.config_data['app:main']['app.service_api.host'] = self.service_api_url
42 50 parameters = {
43 51 'store': self.root,
52 'ini_path': self.ini_file,
53 'user': self.user,
44 54 'repo_name': self.repo_name,
45 'ini_path': '',
46 'user': self.user,
47 55 'user_permissions': {
48 56 self.repo_name: 'repository.admin'
49 57 },
50 58 'settings': self.config_data['app:main'],
51 59 'env': plain_dummy_env()
52 60 }
53
54 61 parameters.update(kwargs)
55 62 server = SubversionServer(**parameters)
56 63 return server
57 64
58 65
59 @pytest.fixture()
60 def svn_server(app):
61 return SubversionServerCreator()
66 @pytest.fixture(scope='module')
67 def svn_server(request, module_app, rhodecode_factory, available_port_factory):
68 ini_file = module_app._pyramid_settings['__file__']
69 vcsserver_host = module_app._pyramid_settings['vcs.server']
70
71 store_dir = os.path.dirname(ini_file)
72
73 # start rhodecode for service API
74 rc = rhodecode_factory(
75 request,
76 store_dir=store_dir,
77 port=available_port_factory(),
78 overrides=(
79 {'handler_console': {'level': 'DEBUG'}},
80 {'app:main': {'vcs.server': vcsserver_host}},
81 {'app:main': {'repo_store.path': store_dir}}
82 ))
83
84 service_api_url = f'http://{rc.bind_addr}'
85
86 return SubversionServerCreator(service_api_url, ini_file)
62 87
63 88
64 89 class TestSubversionServer(object):
@@ -168,8 +193,9 class TestSubversionServer(object):
168 193 assert repo_name == expected_match
169 194
170 195 def test_run_returns_executes_command(self, svn_server):
196 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
197
171 198 server = svn_server.create()
172 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
173 199 os.environ['SSH_CLIENT'] = '127.0.0.1'
174 200 with mock.patch.object(
175 201 SubversionTunnelWrapper, 'get_first_client_response',
@@ -184,20 +210,18 class TestSubversionServer(object):
184 210 SubversionTunnelWrapper, 'command',
185 211 return_value=['date']):
186 212
187 exit_code = server.run()
213 exit_code = server.run(tunnel_extras={'config': server.ini_path})
188 214 # SVN has this differently configured, and we get in our mock env
189 215 # None as return code
190 216 assert exit_code == (None, False)
191 217
192 218 def test_run_returns_executes_command_that_cannot_extract_repo_name(self, svn_server):
219 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
220
193 221 server = svn_server.create()
194 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
195 with mock.patch.object(
196 SubversionTunnelWrapper, 'command',
197 return_value=['date']):
198 with mock.patch.object(
199 SubversionTunnelWrapper, 'get_first_client_response',
222 with mock.patch.object(SubversionTunnelWrapper, 'command', return_value=['date']):
223 with mock.patch.object(SubversionTunnelWrapper, 'get_first_client_response',
200 224 return_value=None):
201 exit_code = server.run()
225 exit_code = server.run(tunnel_extras={'config': server.ini_path})
202 226
203 227 assert exit_code == (1, False)
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 from rhodecode.tests import (
22 22 TestController, assert_session_flash, TEST_USER_ADMIN_LOGIN)
23 23 from rhodecode.model.db import UserGroup
24 24 from rhodecode.model.meta import Session
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 26 from rhodecode.tests.routes import route_path
27 27
28 28 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -18,7 +18,7
18 18 from rhodecode.model.user_group import UserGroupModel
19 19 from rhodecode.tests import (
20 20 TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
21 from rhodecode.tests.fixture import Fixture
21 from rhodecode.tests.fixtures.rc_fixture import Fixture
22 22 from rhodecode.tests.routes import route_path
23 23
24 24 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 from rhodecode.model.db import User
22 22 from rhodecode.tests import (
23 23 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
24 24 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 26 from rhodecode.tests.utils import AssertResponse
27 27 from rhodecode.tests.routes import route_path
28 28
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2013-2023 RhodeCode GmbH
1 # Copyright (C) 2013-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -122,9 +122,12 def sanitize_settings_and_apply_defaults
122 122 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
123 123 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
124 124 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
125 settings_maker.make_setting('vcs.git.lfs.storage_location', '/var/opt/rhodecode_repo_store/.cache/git_lfs_store')
125 settings_maker.make_setting('vcs.git.lfs.storage_location',
126 '/var/opt/rhodecode_repo_store/.cache/git_lfs_store',
127 parser='dir:ensured', default_when_empty=True)
126 128 settings_maker.make_setting('vcs.hg.largefiles.storage_location',
127 '/var/opt/rhodecode_repo_store/.cache/hg_largefiles_store')
129 '/var/opt/rhodecode_repo_store/.cache/hg_largefiles_store',
130 parser='dir:ensured', default_when_empty=True)
128 131
129 132 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
130 133
@@ -162,7 +165,7 def sanitize_settings_and_apply_defaults
162 165 )
163 166
164 167 # celery
165 broker_url = settings_maker.make_setting('celery.broker_url', 'redis://redis:6379/8')
168 broker_url = settings_maker.make_setting('celery.broker_url', 'redis://redis:6379/8', default_when_empty=True)
166 169 settings_maker.make_setting('celery.result_backend', broker_url)
167 170
168 171 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -30,7 +30,7 from rhodecode.lib.vcs import connect_vc
30 30 log = logging.getLogger(__name__)
31 31
32 32
33 def propagate_rhodecode_config(global_config, settings, config):
33 def propagate_rhodecode_config(global_config, settings, config, full=True):
34 34 # Store the settings to make them available to other modules.
35 35 settings_merged = global_config.copy()
36 36 settings_merged.update(settings)
@@ -40,7 +40,7 def propagate_rhodecode_config(global_co
40 40 rhodecode.PYRAMID_SETTINGS = settings_merged
41 41 rhodecode.CONFIG = settings_merged
42 42
43 if 'default_user_id' not in rhodecode.CONFIG:
43 if full and 'default_user_id' not in rhodecode.CONFIG:
44 44 rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id()
45 45 log.debug('set rhodecode.CONFIG data')
46 46
@@ -93,6 +93,7 def load_pyramid_environment(global_conf
93 93 # first run, to store data...
94 94 propagate_rhodecode_config(global_config, settings, {})
95 95
96
96 97 if vcs_server_enabled:
97 98 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
98 99 else:
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,7 +49,7 from rhodecode.lib.plugins.utils import
49 49 from rhodecode.lib.utils2 import AttributeDict
50 50 from rhodecode.lib.exc_tracking import store_exception, format_exc
51 51 from rhodecode.subscribers import (
52 scan_repositories_if_enabled, write_js_routes_if_enabled,
52 auto_merge_pr_if_needed, scan_repositories_if_enabled, write_js_routes_if_enabled,
53 53 write_metadata_if_needed, write_usage_data, import_license_if_present)
54 54 from rhodecode.lib.statsd_client import StatsdClient
55 55
@@ -101,6 +101,9 def make_pyramid_app(global_config, **se
101 101 patches.inspect_getargspec()
102 102 patches.repoze_sendmail_lf_fix()
103 103
104 # first init, so load_pyramid_enviroment, can access some critical data, like __file__
105 propagate_rhodecode_config(global_config, {}, {}, full=False)
106
104 107 load_pyramid_environment(global_config, settings)
105 108
106 109 # Static file view comes first
@@ -392,6 +395,7 def includeme(config, auth_resources=Non
392 395 # Add subscribers.
393 396 if load_all:
394 397 log.debug('Adding subscribers...')
398 config.add_subscriber(auto_merge_pr_if_needed, rhodecode.events.PullRequestReviewEvent)
395 399 config.add_subscriber(scan_repositories_if_enabled,
396 400 pyramid.events.ApplicationCreated)
397 401 config.add_subscriber(write_metadata_if_needed,
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -17,7 +17,7
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 """
20 rcextensions module, please edit `hooks.py` to over write hooks logic
20 rcextensions module, please edit `hooks.py` to over-write hooks logic
21 21 """
22 22
23 23 from .hooks import (
@@ -85,7 +85,7 def _pre_push_hook(*args, **kwargs):
85 85
86 86 # check files names
87 87 if forbidden_files:
88 reason = 'File {} is forbidden to be pushed'.format(file_name)
88 reason = f'File {file_name} is forbidden to be pushed'
89 89 for forbidden_pattern in forbid_files:
90 90 # here we can also filter for operation, e.g if check for only ADDED files
91 91 # if operation == 'A':
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -55,7 +54,7 def run(*args, **kwargs):
55 54 return fields
56 55
57 56
58 class _Undefined(object):
57 class _Undefined:
59 58 pass
60 59
61 60
@@ -67,7 +66,7 def get_field(extra_fields_data, key, de
67 66
68 67 if key not in extra_fields_data:
69 68 if isinstance(default, _Undefined):
70 raise ValueError('key {} not present in extra_fields'.format(key))
69 raise ValueError(f'key {key} not present in extra_fields')
71 70 return default
72 71
73 72 # NOTE(dan): from metadata we get field_label, field_value, field_desc, field_type
@@ -1,5 +1,4
1
2 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -52,7 +51,7 def get_git_commits(repo, refs):
52 51 cmd = [
53 52 'log',
54 53 '--pretty=format:{"commit_id": "%H", "author": "%aN <%aE>", "date": "%ad", "message": "%s"}',
55 '{}...{}'.format(old_rev, new_rev)
54 f'{old_rev}...{new_rev}'
56 55 ]
57 56
58 57 stdout, stderr = repo.run_git_command(cmd, extra_env=git_env)
@@ -80,12 +79,12 def run(*args, **kwargs):
80 79
81 80 if vcs_type == 'git':
82 81 for rev_data in kwargs['commit_ids']:
83 new_environ = dict((k, v) for k, v in rev_data['git_env'])
82 new_environ = {k: v for k, v in rev_data['git_env']}
84 83 commits = get_git_commits(vcs_repo, kwargs['commit_ids'])
85 84
86 85 if vcs_type == 'hg':
87 86 for rev_data in kwargs['commit_ids']:
88 new_environ = dict((k, v) for k, v in rev_data['hg_env'])
87 new_environ = {k: v for k, v in rev_data['hg_env']}
89 88 commits = get_hg_commits(vcs_repo, kwargs['commit_ids'])
90 89
91 90 return commits
@@ -1,5 +1,4
1
2 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -133,12 +132,12 def run(*args, **kwargs):
133 132
134 133 if vcs_type == 'git':
135 134 for rev_data in kwargs['commit_ids']:
136 new_environ = dict((k, v) for k, v in rev_data['git_env'])
135 new_environ = {k: v for k, v in rev_data['git_env']}
137 136 files = get_git_files(repo, vcs_repo, kwargs['commit_ids'])
138 137
139 138 if vcs_type == 'hg':
140 139 for rev_data in kwargs['commit_ids']:
141 new_environ = dict((k, v) for k, v in rev_data['hg_env'])
140 new_environ = {k: v for k, v in rev_data['hg_env']}
142 141 files = get_hg_files(repo, vcs_repo, kwargs['commit_ids'])
143 142
144 143 if vcs_type == 'svn':
@@ -1,5 +1,4
1
2 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -28,7 +28,7 import urllib.error
28 28 log = logging.getLogger('rhodecode.' + __name__)
29 29
30 30
31 class HookResponse(object):
31 class HookResponse:
32 32 def __init__(self, status, output):
33 33 self.status = status
34 34 self.output = output
@@ -44,6 +44,11 class HookResponse(object):
44 44 def __bool__(self):
45 45 return self.status == 0
46 46
47 def to_json(self):
48 return {'status': self.status, 'output': self.output}
49
50 def __repr__(self):
51 return self.to_json().__repr__()
47 52
48 53 class DotDict(dict):
49 54
@@ -91,8 +96,8 class DotDict(dict):
91 96 def __repr__(self):
92 97 keys = list(self.keys())
93 98 keys.sort()
94 args = ', '.join(['%s=%r' % (key, self[key]) for key in keys])
95 return '%s(%s)' % (self.__class__.__name__, args)
99 args = ', '.join(['{}={!r}'.format(key, self[key]) for key in keys])
100 return '{}({})'.format(self.__class__.__name__, args)
96 101
97 102 @staticmethod
98 103 def fromDict(d):
@@ -110,7 +115,7 def serialize(x):
110 115
111 116 def unserialize(x):
112 117 if isinstance(x, dict):
113 return dict((k, unserialize(v)) for k, v in x.items())
118 return {k: unserialize(v) for k, v in x.items()}
114 119 elif isinstance(x, (list, tuple)):
115 120 return type(x)(unserialize(v) for v in x)
116 121 else:
@@ -161,7 +166,8 def str2bool(_str) -> bool:
161 166 string into boolean
162 167
163 168 :param _str: string value to translate into boolean
164 :returns: bool from given string
169 :rtype: boolean
170 :returns: boolean from given string
165 171 """
166 172 if _str is None:
167 173 return False
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,22 +48,22 link_config = [
49 48 {
50 49 "name": "enterprise_docs",
51 50 "target": "https://rhodecode.com/r1/enterprise/docs/",
52 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/",
51 "external_target": "https://docs.rhodecode.com/4.x/rce/index.html",
53 52 },
54 53 {
55 54 "name": "enterprise_log_file_locations",
56 55 "target": "https://rhodecode.com/r1/enterprise/docs/admin-system-overview/",
57 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/system-overview.html#log-files",
56 "external_target": "https://docs.rhodecode.com/4.x/rce/admin/system-overview.html#log-files",
58 57 },
59 58 {
60 59 "name": "enterprise_issue_tracker_settings",
61 60 "target": "https://rhodecode.com/r1/enterprise/docs/issue-trackers-overview/",
62 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/issue-trackers/issue-trackers.html",
61 "external_target": "https://docs.rhodecode.com/4.x/rce/issue-trackers/issue-trackers.html",
63 62 },
64 63 {
65 64 "name": "enterprise_svn_setup",
66 65 "target": "https://rhodecode.com/r1/enterprise/docs/svn-setup/",
67 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/svn-http.html",
66 "external_target": "https://docs.rhodecode.com/4.x/rce/admin/svn-http.html",
68 67 },
69 68 {
70 69 "name": "enterprise_license_convert_from_old",
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,6 +19,8
19 19 import os
20 20 import platform
21 21
22 from rhodecode.lib.type_utils import str2bool
23
22 24 DEFAULT_USER = 'default'
23 25
24 26
@@ -37,7 +39,6 def configure_vcs(config):
37 39
38 40 conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol.v2']
39 41 conf.settings.HOOKS_HOST = config['vcs.hooks.host']
40 conf.settings.DEFAULT_ENCODINGS = config['default_encoding']
41 42 conf.settings.ALIASES[:] = config['vcs.backends']
42 43 conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version']
43 44
@@ -48,28 +49,23 def initialize_database(config):
48 49 engine = engine_from_config(config, 'sqlalchemy.db1.')
49 50 init_model(engine, encryption_key=get_encryption_key(config))
50 51
52 def initialize_test_environment(settings):
53 skip_test_env = str2bool(os.environ.get('RC_NO_TEST_ENV'))
54 if skip_test_env:
55 return
51 56
52 def initialize_test_environment(settings, test_env=None):
53 if test_env is None:
54 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
57 repo_store_path = os.environ.get('RC_TEST_ENV_REPO_STORE') or settings['repo_store.path']
55 58
56 59 from rhodecode.lib.utils import (
57 60 create_test_directory, create_test_database, create_test_repositories,
58 61 create_test_index)
59 from rhodecode.tests import TESTS_TMP_PATH
60 from rhodecode.lib.vcs.backends.hg import largefiles_store
61 from rhodecode.lib.vcs.backends.git import lfs_store
62 62
63 create_test_directory(repo_store_path)
64
65 create_test_database(repo_store_path, settings)
63 66 # test repos
64 if test_env:
65 create_test_directory(TESTS_TMP_PATH)
66 # large object stores
67 create_test_directory(largefiles_store(TESTS_TMP_PATH))
68 create_test_directory(lfs_store(TESTS_TMP_PATH))
69
70 create_test_database(TESTS_TMP_PATH, settings)
71 create_test_repositories(TESTS_TMP_PATH, settings)
72 create_test_index(TESTS_TMP_PATH, settings)
67 create_test_repositories(repo_store_path, settings)
68 create_test_index(repo_store_path, settings)
73 69
74 70
75 71 def get_vcs_server_protocol(config):
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -18,13 +18,10
18 18
19 19 import logging
20 20
21 from rhodecode.events.base import (
22 RhodeCodeIntegrationEvent,
23 RhodecodeEvent
24 )
21 from rhodecode.events.base import RhodeCodeIntegrationEvent, RhodecodeEvent
25 22
26 23 from rhodecode.events.base import ( # pragma: no cover
27 FtsBuild
24 FtsBuild,
28 25 )
29 26
30 27 from rhodecode.events.user import ( # pragma: no cover
@@ -37,11 +34,16 from rhodecode.events.user import ( # p
37 34
38 35 from rhodecode.events.repo import ( # pragma: no cover
39 36 RepoEvent,
40 RepoCommitCommentEvent, RepoCommitCommentEditEvent,
41 RepoPreCreateEvent, RepoCreateEvent,
42 RepoPreDeleteEvent, RepoDeleteEvent,
43 RepoPrePushEvent, RepoPushEvent,
44 RepoPrePullEvent, RepoPullEvent,
37 RepoCommitCommentEvent,
38 RepoCommitCommentEditEvent,
39 RepoPreCreateEvent,
40 RepoCreateEvent,
41 RepoPreDeleteEvent,
42 RepoDeleteEvent,
43 RepoPrePushEvent,
44 RepoPushEvent,
45 RepoPrePullEvent,
46 RepoPullEvent,
45 47 )
46 48
47 49 from rhodecode.events.repo_group import ( # pragma: no cover
@@ -77,12 +79,13 def trigger(event, registry=None):
77 79 from pyramid.threadlocal import get_current_registry
78 80
79 81 event_name = event.__class__
80 log.debug('event %s sent for execution', event_name)
82 log.debug("event %s sent for execution", event_name)
81 83 registry = registry or get_current_registry()
82 84 registry.notify(event)
83 log.debug('event %s triggered using registry %s', event_name, registry)
85 log.debug("event %s triggered using registry %s", event_name, registry)
84 86
85 87 # Send the events to integrations directly
86 88 from rhodecode.integrations import integrations_event_handler
89
87 90 if isinstance(event, RhodeCodeIntegrationEvent):
88 91 integrations_event_handler(event)
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,10 +26,7 from rhodecode.lib.utils2 import Attribu
26 26
27 27
28 28 # this is a user object to be used for events caused by the system (eg. shell)
29 SYSTEM_USER = AttributeDict(dict(
30 username='__SYSTEM__',
31 user_id='__SYSTEM_ID__'
32 ))
29 SYSTEM_USER = AttributeDict(dict(username="__SYSTEM__", user_id="__SYSTEM_ID__"))
33 30
34 31 log = logging.getLogger(__name__)
35 32
@@ -38,16 +35,18 class RhodecodeEvent(object):
38 35 """
39 36 Base event class for all RhodeCode events
40 37 """
38
41 39 name = "RhodeCodeEvent"
42 no_url_set = '<no server_url available>'
40 no_url_set = "<no server_url available>"
43 41
44 def __init__(self, request=None, actor=None):
42 def __init__(self, request=None, actor=None, context=None):
45 43 self._request = request
46 44 self._actor = actor
45 self._context = context
47 46 self.utc_timestamp = datetime.datetime.utcnow()
48 47
49 48 def __repr__(self):
50 return '<{}:({})>'.format(self.__class__.__name__, self.name)
49 return f"<{self.__class__.__name__}:(name={self.name}, context={self._context})>"
51 50
52 51 def get_request(self):
53 52 if self._request:
@@ -63,11 +62,11 class RhodecodeEvent(object):
63 62 if not self.request:
64 63 return
65 64
66 user = getattr(self.request, 'user', None)
65 user = getattr(self.request, "user", None)
67 66 if user:
68 67 return user
69 68
70 api_user = getattr(self.request, 'rpc_user', None)
69 api_user = getattr(self.request, "rpc_user", None)
71 70 if api_user:
72 71 return api_user
73 72
@@ -80,15 +79,17 class RhodecodeEvent(object):
80 79 return self._actor
81 80
82 81 auth_user = self.auth_user
83 log.debug('Got integration actor: %s', auth_user)
82 log.debug("Got integration actor: %s", auth_user)
84 83 if isinstance(auth_user, AuthUser):
85 84 instance = auth_user.get_instance()
86 85 # we can't find this DB user...
87 86 if not instance:
88 return AttributeDict(dict(
89 username=auth_user.username,
90 user_id=auth_user.user_id,
91 ))
87 return AttributeDict(
88 dict(
89 username=auth_user.username,
90 user_id=auth_user.user_id,
91 )
92 )
92 93 elif auth_user:
93 94 return auth_user
94 95 return SYSTEM_USER
@@ -98,29 +99,26 class RhodecodeEvent(object):
98 99 auth_user = self.auth_user
99 100 if auth_user:
100 101 return auth_user.ip_addr
101 return '<no ip available>'
102 return "<no ip available>"
102 103
103 104 @property
104 105 def server_url(self):
105 106 if self.request:
106 107 try:
107 return self.request.route_url('home')
108 return self.request.route_url("home")
108 109 except Exception:
109 log.exception('Failed to fetch URL for server')
110 log.exception("Failed to fetch URL for server")
110 111 return self.no_url_set
111 112
112 113 return self.no_url_set
113 114
114 115 def as_dict(self):
115 116 data = {
116 'name': self.name,
117 'utc_timestamp': self.utc_timestamp,
118 'actor_ip': self.actor_ip,
119 'actor': {
120 'username': self.actor.username,
121 'user_id': self.actor.user_id
122 },
123 'server_url': self.server_url
117 "name": self.name,
118 "utc_timestamp": self.utc_timestamp,
119 "actor_ip": self.actor_ip,
120 "actor": {"username": self.actor.username, "user_id": self.actor.user_id},
121 "server_url": self.server_url,
124 122 }
125 123 return data
126 124
@@ -129,13 +127,14 class RhodeCodeIntegrationEvent(Rhodecod
129 127 """
130 128 Special subclass for Integration events
131 129 """
132 description = ''
130
131 description = ""
133 132
134 133
135 134 class FtsBuild(RhodecodeEvent):
136 135 """
137 136 This event will be triggered when FTS Build is triggered
138 137 """
139 name = 'fts-build'
140 display_name = 'Start FTS Build'
141 138
139 name = "fts-build"
140 display_name = "Start FTS Build"
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,20 +24,23 class IUserRegistered(Interface):
24 24 An event type that is emitted whenever a new user registers a user
25 25 account.
26 26 """
27 user = Attribute('The user object.')
28 session = Attribute('The session while processing the register form post.')
27
28 user = Attribute("The user object.")
29 session = Attribute("The session while processing the register form post.")
29 30
30 31
31 32 class IUserPreCreate(Interface):
32 33 """
33 34 An event type that is emitted before a new user object is created.
34 35 """
35 user_data = Attribute('Data used to create the new user')
36
37 user_data = Attribute("Data used to create the new user")
36 38
37 39
38 40 class IUserPreUpdate(Interface):
39 41 """
40 42 An event type that is emitted before a user object is updated.
41 43 """
42 user = Attribute('The not yet updated user object')
43 user_data = Attribute('Data used to update the user')
44
45 user = Attribute("The not yet updated user object")
46 user_data = Attribute("Data used to update the user")
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,7 +19,7
19 19 import logging
20 20
21 21 from rhodecode.translation import lazy_ugettext
22 from rhodecode.events.repo import (RepoEvent, _commits_as_dict, _issues_as_dict)
22 from rhodecode.events.repo import RepoEvent, _commits_as_dict, _issues_as_dict
23 23
24 24 log = logging.getLogger(__name__)
25 25
@@ -30,45 +30,44 class PullRequestEvent(RepoEvent):
30 30
31 31 :param pullrequest: a :class:`PullRequest` instance
32 32 """
33 name = 'pullrequest-event'
34 display_name = lazy_ugettext('pullrequest generic event')
35 description = lazy_ugettext('All events within a context of a pull request')
33
34 name = "pullrequest-event"
35 display_name = lazy_ugettext("pullrequest generic event")
36 description = lazy_ugettext("All events within a context of a pull request")
36 37
37 def __init__(self, pullrequest):
38 super().__init__(pullrequest.target_repo)
38 def __init__(self, pullrequest, context=None):
39 super().__init__(pullrequest.target_repo, context=context)
39 40 self.pullrequest = pullrequest
41 self.context = self._context
40 42
41 43 def as_dict(self):
42 44 from rhodecode.lib.utils2 import md5_safe
43 45 from rhodecode.model.pull_request import PullRequestModel
46
44 47 data = super().as_dict()
45 48
46 commits = _commits_as_dict(
47 self,
48 commit_ids=self.pullrequest.revisions,
49 repos=[self.pullrequest.source_repo]
50 )
49 commits = _commits_as_dict(self, commit_ids=self.pullrequest.revisions, repos=[self.pullrequest.source_repo])
51 50 issues = _issues_as_dict(commits)
52 51 # calculate hashes of all commits for unique identifier of commits
53 52 # inside that pull request
54 commits_hash = md5_safe(':'.join(x.get('raw_id', '') for x in commits))
53 commits_hash = md5_safe(":".join(x.get("raw_id", "") for x in commits))
55 54
56 data.update({
57 'pullrequest': {
58 'title': self.pullrequest.title,
59 'issues': issues,
60 'pull_request_id': self.pullrequest.pull_request_id,
61 'url': PullRequestModel().get_url(
62 self.pullrequest, request=self.request),
63 'permalink_url': PullRequestModel().get_url(
64 self.pullrequest, request=self.request, permalink=True),
65 'shadow_url': PullRequestModel().get_shadow_clone_url(
66 self.pullrequest, request=self.request),
67 'status': self.pullrequest.calculated_review_status(),
68 'commits_uid': commits_hash,
69 'commits': commits,
55 data.update(
56 {
57 "pullrequest": {
58 "title": self.pullrequest.title,
59 "issues": issues,
60 "pull_request_id": self.pullrequest.pull_request_id,
61 "url": PullRequestModel().get_url(self.pullrequest, request=self.request),
62 "permalink_url": PullRequestModel().get_url(self.pullrequest, request=self.request, permalink=True),
63 "shadow_url": PullRequestModel().get_shadow_clone_url(self.pullrequest, request=self.request),
64 "status": self.pullrequest.calculated_review_status(),
65 "commits_uid": commits_hash,
66 "commits": commits,
67 },
68 "context": self.context,
70 69 }
71 })
70 )
72 71 return data
73 72
74 73
@@ -77,9 +76,10 class PullRequestCreateEvent(PullRequest
77 76 An instance of this class is emitted as an :term:`event` after a pull
78 77 request is created.
79 78 """
80 name = 'pullrequest-create'
81 display_name = lazy_ugettext('pullrequest created')
82 description = lazy_ugettext('Event triggered after pull request was created')
79
80 name = "pullrequest-create"
81 display_name = lazy_ugettext("pullrequest created")
82 description = lazy_ugettext("Event triggered after pull request was created")
83 83
84 84
85 85 class PullRequestCloseEvent(PullRequestEvent):
@@ -87,9 +87,10 class PullRequestCloseEvent(PullRequestE
87 87 An instance of this class is emitted as an :term:`event` after a pull
88 88 request is closed.
89 89 """
90 name = 'pullrequest-close'
91 display_name = lazy_ugettext('pullrequest closed')
92 description = lazy_ugettext('Event triggered after pull request was closed')
90
91 name = "pullrequest-close"
92 display_name = lazy_ugettext("pullrequest closed")
93 description = lazy_ugettext("Event triggered after pull request was closed")
93 94
94 95
95 96 class PullRequestUpdateEvent(PullRequestEvent):
@@ -97,9 +98,10 class PullRequestUpdateEvent(PullRequest
97 98 An instance of this class is emitted as an :term:`event` after a pull
98 99 request's commits have been updated.
99 100 """
100 name = 'pullrequest-update'
101 display_name = lazy_ugettext('pullrequest commits updated')
102 description = lazy_ugettext('Event triggered after pull requests was updated')
101
102 name = "pullrequest-update"
103 display_name = lazy_ugettext("pullrequest commits updated")
104 description = lazy_ugettext("Event triggered after pull requests was updated")
103 105
104 106
105 107 class PullRequestReviewEvent(PullRequestEvent):
@@ -107,13 +109,13 class PullRequestReviewEvent(PullRequest
107 109 An instance of this class is emitted as an :term:`event` after a pull
108 110 request review has changed. A status defines new status of review.
109 111 """
110 name = 'pullrequest-review'
111 display_name = lazy_ugettext('pullrequest review changed')
112 description = lazy_ugettext('Event triggered after a review status of a '
113 'pull requests has changed to other.')
114 112
115 def __init__(self, pullrequest, status):
116 super().__init__(pullrequest)
113 name = "pullrequest-review"
114 display_name = lazy_ugettext("pullrequest review changed")
115 description = lazy_ugettext("Event triggered after a review status of a pull requests has changed to other.")
116
117 def __init__(self, pullrequest, status, context=None):
118 super().__init__(pullrequest, context=context)
117 119 self.status = status
118 120
119 121
@@ -122,10 +124,10 class PullRequestMergeEvent(PullRequestE
122 124 An instance of this class is emitted as an :term:`event` after a pull
123 125 request is merged.
124 126 """
125 name = 'pullrequest-merge'
126 display_name = lazy_ugettext('pullrequest merged')
127 description = lazy_ugettext('Event triggered after a successful merge operation '
128 'was executed on a pull request')
127
128 name = "pullrequest-merge"
129 display_name = lazy_ugettext("pullrequest merged")
130 description = lazy_ugettext("Event triggered after a successful merge operation was executed on a pull request")
129 131
130 132
131 133 class PullRequestCommentEvent(PullRequestEvent):
@@ -133,37 +135,39 class PullRequestCommentEvent(PullReques
133 135 An instance of this class is emitted as an :term:`event` after a pull
134 136 request comment is created.
135 137 """
136 name = 'pullrequest-comment'
137 display_name = lazy_ugettext('pullrequest commented')
138 description = lazy_ugettext('Event triggered after a comment was made on a code '
139 'in the pull request')
140 138
141 def __init__(self, pullrequest, comment):
142 super().__init__(pullrequest)
139 name = "pullrequest-comment"
140 display_name = lazy_ugettext("pullrequest commented")
141 description = lazy_ugettext("Event triggered after a comment was made on a code in the pull request")
142
143 def __init__(self, pullrequest, comment, context=None):
144 super().__init__(pullrequest, context=context)
143 145 self.comment = comment
144 146
145 147 def as_dict(self):
146 148 from rhodecode.model.comment import CommentsModel
149
147 150 data = super().as_dict()
148 151
149 152 status = None
150 153 if self.comment.status_change:
151 154 status = self.comment.review_status
152 155
153 data.update({
154 'comment': {
155 'status': status,
156 'text': self.comment.text,
157 'type': self.comment.comment_type,
158 'file': self.comment.f_path,
159 'line': self.comment.line_no,
160 'version': self.comment.last_version,
161 'url': CommentsModel().get_url(
162 self.comment, request=self.request),
163 'permalink_url': CommentsModel().get_url(
164 self.comment, request=self.request, permalink=True),
156 data.update(
157 {
158 "comment": {
159 "status": status,
160 "text": self.comment.text,
161 "type": self.comment.comment_type,
162 "file": self.comment.f_path,
163 "line": self.comment.line_no,
164 "version": self.comment.last_version,
165 "url": CommentsModel().get_url(self.comment, request=self.request),
166 "permalink_url": CommentsModel().get_url(self.comment, request=self.request, permalink=True),
167 },
168 "context": self.context,
165 169 }
166 })
170 )
167 171 return data
168 172
169 173
@@ -172,10 +176,10 class PullRequestCommentEditEvent(PullRe
172 176 An instance of this class is emitted as an :term:`event` after a pull
173 177 request comment is edited.
174 178 """
175 name = 'pullrequest-comment-edit'
176 display_name = lazy_ugettext('pullrequest comment edited')
177 description = lazy_ugettext('Event triggered after a comment was edited on a code '
178 'in the pull request')
179
180 name = "pullrequest-comment-edit"
181 display_name = lazy_ugettext("pullrequest comment edited")
182 description = lazy_ugettext("Event triggered after a comment was edited on a code in the pull request")
179 183
180 184 def __init__(self, pullrequest, comment):
181 185 super().__init__(pullrequest)
@@ -183,24 +187,26 class PullRequestCommentEditEvent(PullRe
183 187
184 188 def as_dict(self):
185 189 from rhodecode.model.comment import CommentsModel
190
186 191 data = super().as_dict()
187 192
188 193 status = None
189 194 if self.comment.status_change:
190 195 status = self.comment.review_status
191 196
192 data.update({
193 'comment': {
194 'status': status,
195 'text': self.comment.text,
196 'type': self.comment.comment_type,
197 'file': self.comment.f_path,
198 'line': self.comment.line_no,
199 'version': self.comment.last_version,
200 'url': CommentsModel().get_url(
201 self.comment, request=self.request),
202 'permalink_url': CommentsModel().get_url(
203 self.comment, request=self.request, permalink=True),
197 data.update(
198 {
199 "comment": {
200 "status": status,
201 "text": self.comment.text,
202 "type": self.comment.comment_type,
203 "file": self.comment.f_path,
204 "line": self.comment.line_no,
205 "version": self.comment.last_version,
206 "url": CommentsModel().get_url(self.comment, request=self.request),
207 "permalink_url": CommentsModel().get_url(self.comment, request=self.request, permalink=True),
208 },
209 "context": self.context
204 210 }
205 })
211 )
206 212 return data
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -37,12 +37,11 def _commits_as_dict(event, commit_ids,
37 37 :param repos: a list of repos to check
38 38 """
39 39 from rhodecode.lib.utils2 import extract_mentioned_users
40 from rhodecode.lib.helpers import (
41 urlify_commit_message, process_patterns, chop_at_smart)
40 from rhodecode.lib.helpers import urlify_commit_message, process_patterns, chop_at_smart
42 41 from rhodecode.model.repo import RepoModel
43 42
44 43 if not repos:
45 raise Exception('no repo defined')
44 raise Exception("no repo defined")
46 45
47 46 if not isinstance(repos, (tuple, list)):
48 47 repos = [repos]
@@ -63,37 +62,31 def _commits_as_dict(event, commit_ids,
63 62 try:
64 63 # use copy of needed_commits since we modify it while iterating
65 64 for commit_id in list(needed_commits):
66 if commit_id.startswith('tag=>'):
65 if commit_id.startswith("tag=>"):
67 66 raw_id = commit_id[5:]
68 67 cs_data = {
69 'raw_id': commit_id, 'short_id': commit_id,
70 'branch': None,
71 'git_ref_change': 'tag_add',
72 'message': f'Added new tag {raw_id}',
73 'author': event.actor.full_contact,
74 'date': datetime.datetime.now(),
75 'refs': {
76 'branches': [],
77 'bookmarks': [],
78 'tags': []
79 }
68 "raw_id": commit_id,
69 "short_id": commit_id,
70 "branch": None,
71 "git_ref_change": "tag_add",
72 "message": f"Added new tag {raw_id}",
73 "author": event.actor.full_contact,
74 "date": datetime.datetime.now(),
75 "refs": {"branches": [], "bookmarks": [], "tags": []},
80 76 }
81 77 commits.append(cs_data)
82 78
83 elif commit_id.startswith('delete_branch=>'):
79 elif commit_id.startswith("delete_branch=>"):
84 80 raw_id = commit_id[15:]
85 81 cs_data = {
86 'raw_id': commit_id, 'short_id': commit_id,
87 'branch': None,
88 'git_ref_change': 'branch_delete',
89 'message': f'Deleted branch {raw_id}',
90 'author': event.actor.full_contact,
91 'date': datetime.datetime.now(),
92 'refs': {
93 'branches': [],
94 'bookmarks': [],
95 'tags': []
96 }
82 "raw_id": commit_id,
83 "short_id": commit_id,
84 "branch": None,
85 "git_ref_change": "branch_delete",
86 "message": f"Deleted branch {raw_id}",
87 "author": event.actor.full_contact,
88 "date": datetime.datetime.now(),
89 "refs": {"branches": [], "bookmarks": [], "tags": []},
97 90 }
98 91 commits.append(cs_data)
99 92
@@ -104,50 +97,45 def _commits_as_dict(event, commit_ids,
104 97 continue # maybe its in next repo
105 98
106 99 cs_data = cs.__json__()
107 cs_data['refs'] = cs._get_refs()
100 cs_data["refs"] = cs._get_refs()
108 101
109 cs_data['mentions'] = extract_mentioned_users(cs_data['message'])
110 cs_data['reviewers'] = reviewers
111 cs_data['url'] = RepoModel().get_commit_url(
112 repo, cs_data['raw_id'], request=event.request)
113 cs_data['permalink_url'] = RepoModel().get_commit_url(
114 repo, cs_data['raw_id'], request=event.request,
115 permalink=True)
116 urlified_message, issues_data, errors = process_patterns(
117 cs_data['message'], repo.repo_name)
118 cs_data['issues'] = issues_data
119 cs_data['message_html'] = urlify_commit_message(
120 cs_data['message'], repo.repo_name)
121 cs_data['message_html_title'] = chop_at_smart(
122 cs_data['message'], '\n', suffix_if_chopped='...')
102 cs_data["mentions"] = extract_mentioned_users(cs_data["message"])
103 cs_data["reviewers"] = reviewers
104 cs_data["url"] = RepoModel().get_commit_url(repo, cs_data["raw_id"], request=event.request)
105 cs_data["permalink_url"] = RepoModel().get_commit_url(
106 repo, cs_data["raw_id"], request=event.request, permalink=True
107 )
108 urlified_message, issues_data, errors = process_patterns(cs_data["message"], repo.repo_name)
109 cs_data["issues"] = issues_data
110 cs_data["message_html"] = urlify_commit_message(cs_data["message"], repo.repo_name)
111 cs_data["message_html_title"] = chop_at_smart(cs_data["message"], "\n", suffix_if_chopped="...")
123 112 commits.append(cs_data)
124 113
125 114 needed_commits.remove(commit_id)
126 115
127 116 except Exception:
128 log.exception('Failed to extract commits data')
117 log.exception("Failed to extract commits data")
129 118 # we don't send any commits when crash happens, only full list
130 119 # matters we short circuit then.
131 120 return []
132 121
133 122 # we failed to remove all needed_commits from all repositories
134 123 if needed_commits:
135 raise ValueError(f'Unexpectedly not found {needed_commits} in all available repos {repos}')
124 raise ValueError(f"Unexpectedly not found {needed_commits} in all available repos {repos}")
136 125
137 missing_commits = set(commit_ids) - set(c['raw_id'] for c in commits)
126 missing_commits = set(commit_ids) - set(c["raw_id"] for c in commits)
138 127 if missing_commits:
139 log.error('Inconsistent repository state. '
140 'Missing commits: %s', ', '.join(missing_commits))
128 log.error("Inconsistent repository state. " "Missing commits: %s", ", ".join(missing_commits))
141 129
142 130 return commits
143 131
144 132
145 133 def _issues_as_dict(commits):
146 """ Helper function to serialize issues from commits """
134 """Helper function to serialize issues from commits"""
147 135 issues = {}
148 136 for commit in commits:
149 for issue in commit['issues']:
150 issues[issue['id']] = issue
137 for issue in commit["issues"]:
138 issues[issue["id"]] = issue
151 139 return issues
152 140
153 141
@@ -156,33 +144,36 class RepoEvent(RhodeCodeIntegrationEven
156 144 Base class for events acting on a repository.
157 145 """
158 146
159 def __init__(self, repo, actor=None):
147 def __init__(self, repo, actor=None, context=None):
160 148 """
161 149 :param repo: a :class:`Repository` instance
162 150 """
163 super().__init__(actor=actor)
151 super().__init__(actor=actor, context=context)
164 152 self.repo = repo
153 self.context = self._context
165 154
166 155 def as_dict(self):
167 156 from rhodecode.model.repo import RepoModel
157
168 158 data = super().as_dict()
169 159
170 160 extra_fields = collections.OrderedDict()
171 161 for field in self.repo.extra_fields:
172 162 extra_fields[field.field_key] = field.field_value
173 163
174 data.update({
175 'repo': {
176 'repo_id': self.repo.repo_id,
177 'repo_name': self.repo.repo_name,
178 'repo_type': self.repo.repo_type,
179 'url': RepoModel().get_url(
180 self.repo, request=self.request),
181 'permalink_url': RepoModel().get_url(
182 self.repo, request=self.request, permalink=True),
183 'extra_fields': extra_fields
164 data.update(
165 {
166 "repo": {
167 "repo_id": self.repo.repo_id,
168 "repo_name": self.repo.repo_name,
169 "repo_type": self.repo.repo_type,
170 "url": RepoModel().get_url(self.repo, request=self.request),
171 "permalink_url": RepoModel().get_url(self.repo, request=self.request, permalink=True),
172 "extra_fields": extra_fields,
173 },
174 "context": self.context,
184 175 }
185 })
176 )
186 177 return data
187 178
188 179
@@ -192,32 +183,32 class RepoCommitCommentEvent(RepoEvent):
192 183 on repository commit.
193 184 """
194 185
195 name = 'repo-commit-comment'
196 display_name = lazy_ugettext('repository commit comment')
197 description = lazy_ugettext('Event triggered after a comment was made '
198 'on commit inside a repository')
186 name = "repo-commit-comment"
187 display_name = lazy_ugettext("repository commit comment")
188 description = lazy_ugettext("Event triggered after a comment was made on commit inside a repository")
199 189
200 def __init__(self, repo, commit, comment):
201 super().__init__(repo)
190 def __init__(self, repo, commit, comment, context=None):
191 super().__init__(repo, context=context)
202 192 self.commit = commit
203 193 self.comment = comment
204 194
205 195 def as_dict(self):
206 196 data = super().as_dict()
207 data['commit'] = {
208 'commit_id': self.commit.raw_id,
209 'commit_message': self.commit.message,
210 'commit_branch': self.commit.branch,
197 data["commit"] = {
198 "commit_id": self.commit.raw_id,
199 "commit_message": self.commit.message,
200 "commit_branch": self.commit.branch,
211 201 }
212 202
213 data['comment'] = {
214 'comment_id': self.comment.comment_id,
215 'comment_text': self.comment.text,
216 'comment_type': self.comment.comment_type,
217 'comment_f_path': self.comment.f_path,
218 'comment_line_no': self.comment.line_no,
219 'comment_version': self.comment.last_version,
203 data["comment"] = {
204 "comment_id": self.comment.comment_id,
205 "comment_text": self.comment.text,
206 "comment_type": self.comment.comment_type,
207 "comment_f_path": self.comment.f_path,
208 "comment_line_no": self.comment.line_no,
209 "comment_version": self.comment.last_version,
220 210 }
211 data["contex"] = self.context
221 212 return data
222 213
223 214
@@ -227,32 +218,32 class RepoCommitCommentEditEvent(RepoEve
227 218 on repository commit.
228 219 """
229 220
230 name = 'repo-commit-edit-comment'
231 display_name = lazy_ugettext('repository commit edit comment')
232 description = lazy_ugettext('Event triggered after a comment was edited '
233 'on commit inside a repository')
221 name = "repo-commit-edit-comment"
222 display_name = lazy_ugettext("repository commit edit comment")
223 description = lazy_ugettext("Event triggered after a comment was edited on commit inside a repository")
234 224
235 def __init__(self, repo, commit, comment):
236 super().__init__(repo)
225 def __init__(self, repo, commit, comment, context=None):
226 super().__init__(repo, context=context)
237 227 self.commit = commit
238 228 self.comment = comment
239 229
240 230 def as_dict(self):
241 231 data = super().as_dict()
242 data['commit'] = {
243 'commit_id': self.commit.raw_id,
244 'commit_message': self.commit.message,
245 'commit_branch': self.commit.branch,
232 data["commit"] = {
233 "commit_id": self.commit.raw_id,
234 "commit_message": self.commit.message,
235 "commit_branch": self.commit.branch,
246 236 }
247 237
248 data['comment'] = {
249 'comment_id': self.comment.comment_id,
250 'comment_text': self.comment.text,
251 'comment_type': self.comment.comment_type,
252 'comment_f_path': self.comment.f_path,
253 'comment_line_no': self.comment.line_no,
254 'comment_version': self.comment.last_version,
238 data["comment"] = {
239 "comment_id": self.comment.comment_id,
240 "comment_text": self.comment.text,
241 "comment_type": self.comment.comment_type,
242 "comment_f_path": self.comment.f_path,
243 "comment_line_no": self.comment.line_no,
244 "comment_version": self.comment.last_version,
255 245 }
246 data["context"] = "context"
256 247 return data
257 248
258 249
@@ -261,9 +252,10 class RepoPreCreateEvent(RepoEvent):
261 252 An instance of this class is emitted as an :term:`event` before a repo is
262 253 created.
263 254 """
264 name = 'repo-pre-create'
265 display_name = lazy_ugettext('repository pre create')
266 description = lazy_ugettext('Event triggered before repository is created')
255
256 name = "repo-pre-create"
257 display_name = lazy_ugettext("repository pre create")
258 description = lazy_ugettext("Event triggered before repository is created")
267 259
268 260
269 261 class RepoCreateEvent(RepoEvent):
@@ -271,9 +263,10 class RepoCreateEvent(RepoEvent):
271 263 An instance of this class is emitted as an :term:`event` whenever a repo is
272 264 created.
273 265 """
274 name = 'repo-create'
275 display_name = lazy_ugettext('repository created')
276 description = lazy_ugettext('Event triggered after repository was created')
266
267 name = "repo-create"
268 display_name = lazy_ugettext("repository created")
269 description = lazy_ugettext("Event triggered after repository was created")
277 270
278 271
279 272 class RepoPreDeleteEvent(RepoEvent):
@@ -281,9 +274,10 class RepoPreDeleteEvent(RepoEvent):
281 274 An instance of this class is emitted as an :term:`event` whenever a repo is
282 275 created.
283 276 """
284 name = 'repo-pre-delete'
285 display_name = lazy_ugettext('repository pre delete')
286 description = lazy_ugettext('Event triggered before a repository is deleted')
277
278 name = "repo-pre-delete"
279 display_name = lazy_ugettext("repository pre delete")
280 description = lazy_ugettext("Event triggered before a repository is deleted")
287 281
288 282
289 283 class RepoDeleteEvent(RepoEvent):
@@ -291,43 +285,45 class RepoDeleteEvent(RepoEvent):
291 285 An instance of this class is emitted as an :term:`event` whenever a repo is
292 286 created.
293 287 """
294 name = 'repo-delete'
295 display_name = lazy_ugettext('repository deleted')
296 description = lazy_ugettext('Event triggered after repository was deleted')
288
289 name = "repo-delete"
290 display_name = lazy_ugettext("repository deleted")
291 description = lazy_ugettext("Event triggered after repository was deleted")
297 292
298 293
299 294 class RepoVCSEvent(RepoEvent):
300 295 """
301 296 Base class for events triggered by the VCS
302 297 """
303 name = ''
304 display_name = 'generic_vcs_event'
305 298
306 def __init__(self, repo_name, extras):
299 name = ""
300 display_name = "generic_vcs_event"
301
302 def __init__(self, repo_name, extras, context=None):
307 303 self.repo = Repository.get_by_repo_name(repo_name)
308 304 if not self.repo:
309 raise Exception(f'repo by this name {repo_name} does not exist')
305 raise Exception(f"repo by this name {repo_name} does not exist")
310 306 self.extras = extras
311 super().__init__(self.repo)
307 super().__init__(self.repo, context=context)
312 308
313 309 @property
314 310 def actor(self):
315 if self.extras.get('username'):
316 return User.get_by_username(self.extras['username'])
311 if self.extras.get("username"):
312 return User.get_by_username(self.extras["username"])
317 313
318 314 @property
319 315 def actor_ip(self):
320 if self.extras.get('ip'):
321 return self.extras['ip']
316 if self.extras.get("ip"):
317 return self.extras["ip"]
322 318
323 319 @property
324 320 def server_url(self):
325 if self.extras.get('server_url'):
326 return self.extras['server_url']
321 if self.extras.get("server_url"):
322 return self.extras["server_url"]
327 323
328 324 @property
329 325 def request(self):
330 return self.extras.get('request') or self.get_request()
326 return self.extras.get("request") or self.get_request()
331 327
332 328
333 329 class RepoPrePullEvent(RepoVCSEvent):
@@ -335,9 +331,10 class RepoPrePullEvent(RepoVCSEvent):
335 331 An instance of this class is emitted as an :term:`event` before commits
336 332 are pulled from a repo.
337 333 """
338 name = 'repo-pre-pull'
339 display_name = lazy_ugettext('repository pre pull')
340 description = lazy_ugettext('Event triggered before repository code is pulled')
334
335 name = "repo-pre-pull"
336 display_name = lazy_ugettext("repository pre pull")
337 description = lazy_ugettext("Event triggered before repository code is pulled")
341 338
342 339
343 340 class RepoPullEvent(RepoVCSEvent):
@@ -345,9 +342,10 class RepoPullEvent(RepoVCSEvent):
345 342 An instance of this class is emitted as an :term:`event` after commits
346 343 are pulled from a repo.
347 344 """
348 name = 'repo-pull'
349 display_name = lazy_ugettext('repository pull')
350 description = lazy_ugettext('Event triggered after repository code was pulled')
345
346 name = "repo-pull"
347 display_name = lazy_ugettext("repository pull")
348 description = lazy_ugettext("Event triggered after repository code was pulled")
351 349
352 350
353 351 class RepoPrePushEvent(RepoVCSEvent):
@@ -355,10 +353,10 class RepoPrePushEvent(RepoVCSEvent):
355 353 An instance of this class is emitted as an :term:`event` before commits
356 354 are pushed to a repo.
357 355 """
358 name = 'repo-pre-push'
359 display_name = lazy_ugettext('repository pre push')
360 description = lazy_ugettext('Event triggered before the code is '
361 'pushed to a repository')
356
357 name = "repo-pre-push"
358 display_name = lazy_ugettext("repository pre push")
359 description = lazy_ugettext("Event triggered before the code is pushed to a repository")
362 360
363 361
364 362 class RepoPushEvent(RepoVCSEvent):
@@ -368,13 +366,13 class RepoPushEvent(RepoVCSEvent):
368 366
369 367 :param extras: (optional) dict of data from proxied VCS actions
370 368 """
371 name = 'repo-push'
372 display_name = lazy_ugettext('repository push')
373 description = lazy_ugettext('Event triggered after the code was '
374 'pushed to a repository')
375 369
376 def __init__(self, repo_name, pushed_commit_ids, extras):
377 super().__init__(repo_name, extras)
370 name = "repo-push"
371 display_name = lazy_ugettext("repository push")
372 description = lazy_ugettext("Event triggered after the code was pushed to a repository")
373
374 def __init__(self, repo_name, pushed_commit_ids, extras, context=None):
375 super().__init__(repo_name, extras, context=context)
378 376 self.pushed_commit_ids = pushed_commit_ids
379 377 self.new_refs = extras.new_refs
380 378
@@ -382,63 +380,48 class RepoPushEvent(RepoVCSEvent):
382 380 data = super().as_dict()
383 381
384 382 def branch_url(branch_name):
385 return '{}/changelog?branch={}'.format(
386 data['repo']['url'], branch_name)
383 return "{}/changelog?branch={}".format(data["repo"]["url"], branch_name)
387 384
388 385 def tag_url(tag_name):
389 return '{}/files/{}/'.format(
390 data['repo']['url'], tag_name)
386 return "{}/files/{}/".format(data["repo"]["url"], tag_name)
391 387
392 commits = _commits_as_dict(
393 self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
388 commits = _commits_as_dict(self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
394 389
395 390 last_branch = None
396 391 for commit in reversed(commits):
397 commit['branch'] = commit['branch'] or last_branch
398 last_branch = commit['branch']
392 commit["branch"] = commit["branch"] or last_branch
393 last_branch = commit["branch"]
399 394 issues = _issues_as_dict(commits)
400 395
401 396 branches = set()
402 397 tags = set()
403 398 for commit in commits:
404 if commit['refs']['tags']:
405 for tag in commit['refs']['tags']:
399 if commit["refs"]["tags"]:
400 for tag in commit["refs"]["tags"]:
406 401 tags.add(tag)
407 if commit['branch']:
408 branches.add(commit['branch'])
402 if commit["branch"]:
403 branches.add(commit["branch"])
409 404
410 405 # maybe we have branches in new_refs ?
411 406 try:
412 branches = branches.union(set(self.new_refs['branches']))
407 branches = branches.union(set(self.new_refs["branches"]))
413 408 except Exception:
414 409 pass
415 410
416 branches = [
417 {
418 'name': branch,
419 'url': branch_url(branch)
420 }
421 for branch in branches
422 ]
411 branches = [{"name": branch, "url": branch_url(branch)} for branch in branches]
423 412
424 413 # maybe we have branches in new_refs ?
425 414 try:
426 tags = tags.union(set(self.new_refs['tags']))
415 tags = tags.union(set(self.new_refs["tags"]))
427 416 except Exception:
428 417 pass
429 418
430 tags = [
431 {
432 'name': tag,
433 'url': tag_url(tag)
434 }
435 for tag in tags
436 ]
419 tags = [{"name": tag, "url": tag_url(tag)} for tag in tags]
437 420
438 data['push'] = {
439 'commits': commits,
440 'issues': issues,
441 'branches': branches,
442 'tags': tags,
421 data["push"] = {
422 "commits": commits,
423 "issues": issues,
424 "branches": branches,
425 "tags": tags,
443 426 }
444 427 return data
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -29,27 +29,31 class RepoGroupEvent(RhodeCodeIntegratio
29 29 """
30 30 Base class for events acting on a repository group.
31 31
32 :param repo: a :class:`RepositoryGroup` instance
32 :param repo_group: a :class:`RepositoryGroup` instance
33 33 """
34 34
35 def __init__(self, repo_group):
36 super().__init__()
35 def __init__(self, repo_group, context=None):
36 super().__init__(context=context)
37 37 self.repo_group = repo_group
38 self.context = self._context
38 39
39 40 def as_dict(self):
40 41 data = super().as_dict()
41 data.update({
42 'repo_group': {
43 'group_id': self.repo_group.group_id,
44 'group_name': self.repo_group.group_name,
45 'group_parent_id': self.repo_group.group_parent_id,
46 'group_description': self.repo_group.group_description,
47 'user_id': self.repo_group.user_id,
48 'created_by': self.repo_group.user.username,
49 'created_on': self.repo_group.created_on,
50 'enable_locking': self.repo_group.enable_locking,
42 data.update(
43 {
44 "repo_group": {
45 "group_id": self.repo_group.group_id,
46 "group_name": self.repo_group.group_name,
47 "group_parent_id": self.repo_group.group_parent_id,
48 "group_description": self.repo_group.group_description,
49 "user_id": self.repo_group.user_id,
50 "created_by": self.repo_group.user.username,
51 "created_on": self.repo_group.created_on,
52 "enable_locking": self.repo_group.enable_locking,
53 },
54 "context": self.context
51 55 }
52 })
56 )
53 57 return data
54 58
55 59
@@ -58,9 +62,10 class RepoGroupCreateEvent(RepoGroupEven
58 62 An instance of this class is emitted as an :term:`event` whenever a
59 63 repository group is created.
60 64 """
61 name = 'repo-group-create'
62 display_name = lazy_ugettext('repository group created')
63 description = lazy_ugettext('Event triggered after a repository group was created')
65
66 name = "repo-group-create"
67 display_name = lazy_ugettext("repository group created")
68 description = lazy_ugettext("Event triggered after a repository group was created")
64 69
65 70
66 71 class RepoGroupDeleteEvent(RepoGroupEvent):
@@ -68,9 +73,10 class RepoGroupDeleteEvent(RepoGroupEven
68 73 An instance of this class is emitted as an :term:`event` whenever a
69 74 repository group is deleted.
70 75 """
71 name = 'repo-group-delete'
72 display_name = lazy_ugettext('repository group deleted')
73 description = lazy_ugettext('Event triggered after a repository group was deleted')
76
77 name = "repo-group-delete"
78 display_name = lazy_ugettext("repository group deleted")
79 description = lazy_ugettext("Event triggered after a repository group was deleted")
74 80
75 81
76 82 class RepoGroupUpdateEvent(RepoGroupEvent):
@@ -78,6 +84,7 class RepoGroupUpdateEvent(RepoGroupEven
78 84 An instance of this class is emitted as an :term:`event` whenever a
79 85 repository group is updated.
80 86 """
81 name = 'repo-group-update'
82 display_name = lazy_ugettext('repository group update')
83 description = lazy_ugettext('Event triggered after a repository group was updated')
87
88 name = "repo-group-update"
89 display_name = lazy_ugettext("repository group update")
90 description = lazy_ugettext("Event triggered after a repository group was updated")
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,8 +21,7 from zope.interface import implementer
21 21
22 22 from rhodecode.translation import lazy_ugettext
23 23 from rhodecode.events.base import RhodecodeEvent, RhodeCodeIntegrationEvent
24 from rhodecode.events.interfaces import (
25 IUserRegistered, IUserPreCreate, IUserPreUpdate)
24 from rhodecode.events.interfaces import IUserRegistered, IUserPreCreate, IUserPreUpdate
26 25
27 26 log = logging.getLogger(__name__)
28 27
@@ -33,8 +32,9 class UserRegistered(RhodeCodeIntegratio
33 32 An instance of this class is emitted as an :term:`event` whenever a user
34 33 account is registered.
35 34 """
36 name = 'user-register'
37 display_name = lazy_ugettext('user registered')
35
36 name = "user-register"
37 display_name = lazy_ugettext("user registered")
38 38
39 39 def __init__(self, user, session):
40 40 super().__init__()
@@ -48,8 +48,9 class UserPreCreate(RhodeCodeIntegration
48 48 An instance of this class is emitted as an :term:`event` before a new user
49 49 object is created.
50 50 """
51 name = 'user-pre-create'
52 display_name = lazy_ugettext('user pre create')
51
52 name = "user-pre-create"
53 display_name = lazy_ugettext("user pre create")
53 54
54 55 def __init__(self, user_data):
55 56 super().__init__()
@@ -62,8 +63,9 class UserPostCreate(RhodeCodeIntegratio
62 63 An instance of this class is emitted as an :term:`event` after a new user
63 64 object is created.
64 65 """
65 name = 'user-post-create'
66 display_name = lazy_ugettext('user post create')
66
67 name = "user-post-create"
68 display_name = lazy_ugettext("user post create")
67 69
68 70 def __init__(self, user_data):
69 71 super().__init__()
@@ -76,8 +78,9 class UserPreUpdate(RhodeCodeIntegration
76 78 An instance of this class is emitted as an :term:`event` before a user
77 79 object is updated.
78 80 """
79 name = 'user-pre-update'
80 display_name = lazy_ugettext('user pre update')
81
82 name = "user-pre-update"
83 display_name = lazy_ugettext("user pre update")
81 84
82 85 def __init__(self, user, user_data):
83 86 super().__init__()
@@ -87,8 +90,8 class UserPreUpdate(RhodeCodeIntegration
87 90
88 91 class UserPermissionsChange(RhodecodeEvent):
89 92 """
90 This event should be triggered on an event that permissions of user might changed.
91 Currently this should be triggered on:
93 This event should be triggered on an event that permissions of user might be changed.
94 Currently, this should be triggered on:
92 95
93 96 - user added/removed from user group
94 97 - repo permissions changed
@@ -96,9 +99,11 class UserPermissionsChange(RhodecodeEve
96 99 - user group permissions changed
97 100
98 101 """
99 name = 'user-permissions-change'
100 display_name = lazy_ugettext('user permissions change')
102
103 name = "user-permissions-change"
104 display_name = lazy_ugettext("user permissions change")
101 105
102 def __init__(self, user_ids):
103 super().__init__()
106 def __init__(self, user_ids, context=None):
107 super().__init__(context=context)
104 108 self.user_ids = user_ids
109 self.context = self._context
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,7 +1,5
1 1
2 """Utilities for writing code that runs on Python 2 and 3"""
3
4 # Copyright (c) 2010-2015 Benjamin Peterson
2 """Utilities for writing code that runs on Python 2 and 3"""# Copyright (C) 2010-2015 Benjamin Peterson
5 3 #
6 4 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 5 # of this software and associated documentation files (the "Software"), to deal
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2017-2023 RhodeCode GmbH
1 # Copyright (C) 2017-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -478,7 +478,9 class DiffSet(object):
478 478 log.debug('rendering diff for %r', patch['filename'])
479 479
480 480 source_filename = patch['original_filename']
481 source_filename_bytes = patch['original_filename_bytes']
481 482 target_filename = patch['filename']
483 target_filename_bytes = patch['filename_bytes']
482 484
483 485 source_lexer = plain_text_lexer
484 486 target_lexer = plain_text_lexer
@@ -491,12 +493,12 class DiffSet(object):
491 493 if (source_filename and patch['operation'] in ('D', 'M')
492 494 and source_filename not in self.source_nodes):
493 495 self.source_nodes[source_filename] = (
494 self.source_node_getter(source_filename))
496 self.source_node_getter(source_filename_bytes))
495 497
496 498 if (target_filename and patch['operation'] in ('A', 'M')
497 499 and target_filename not in self.target_nodes):
498 500 self.target_nodes[target_filename] = (
499 self.target_node_getter(target_filename))
501 self.target_node_getter(target_filename_bytes))
500 502
501 503 elif hl_mode == self.HL_FAST:
502 504 source_lexer = self._get_lexer_for_filename(source_filename)
@@ -558,6 +560,7 class DiffSet(object):
558 560 })
559 561
560 562 file_chunks = patch['chunks'][1:]
563
561 564 for i, hunk in enumerate(file_chunks, 1):
562 565 hunkbit = self.parse_hunk(hunk, source_file, target_file)
563 566 hunkbit.source_file_path = source_file_path
@@ -593,12 +596,13 class DiffSet(object):
593 596 return filediff
594 597
595 598 def parse_hunk(self, hunk, source_file, target_file):
599
596 600 result = AttributeDict(dict(
597 601 source_start=hunk['source_start'],
598 602 source_length=hunk['source_length'],
599 603 target_start=hunk['target_start'],
600 604 target_length=hunk['target_length'],
601 section_header=hunk['section_header'],
605 section_header=safe_str(hunk['section_header']),
602 606 lines=[],
603 607 ))
604 608 before, after = [], []
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -307,7 +307,7 class DbManage(object):
307 307
308 308 def create_test_admin_and_users(self):
309 309 log.info('creating admin and regular test users')
310 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
310 from rhodecode.bootstrap import TEST_USER_ADMIN_LOGIN, \
311 311 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
312 312 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
313 313 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
@@ -328,8 +328,6 class DbManage(object):
328 328 and disables dotencode
329 329 """
330 330 settings_model = SettingsModel(sa=self.sa)
331 from rhodecode.lib.vcs.backends.hg import largefiles_store
332 from rhodecode.lib.vcs.backends.git import lfs_store
333 331
334 332 # Build HOOKS
335 333 hooks = [
@@ -360,24 +358,6 class DbManage(object):
360 358 largefiles.ui_value = ''
361 359 self.sa.add(largefiles)
362 360
363 # set default largefiles cache dir, defaults to
364 # /repo_store_location/.cache/largefiles
365 largefiles = RhodeCodeUi()
366 largefiles.ui_section = 'largefiles'
367 largefiles.ui_key = 'usercache'
368 largefiles.ui_value = largefiles_store(repo_store_path)
369
370 self.sa.add(largefiles)
371
372 # set default lfs cache dir, defaults to
373 # /repo_store_location/.cache/lfs_store
374 lfsstore = RhodeCodeUi()
375 lfsstore.ui_section = 'vcs_git_lfs'
376 lfsstore.ui_key = 'store_location'
377 lfsstore.ui_value = lfs_store(repo_store_path)
378
379 self.sa.add(lfsstore)
380
381 361 # enable hgevolve disabled by default
382 362 hgevolve = RhodeCodeUi()
383 363 hgevolve.ui_section = 'extensions'
@@ -1,6 +1,4
1
2
3 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2024 RhodeCode GmbH
4 2 #
5 3 # This program is free software: you can redistribute it and/or modify
6 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
3 2 #
4 3 # This program is free software: you can redistribute it and/or modify
5 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -455,7 +455,9 class DiffProcessor(object):
455 455 return arg
456 456
457 457 for chunk in self._diff.chunks():
458 bytes_head = chunk.header
458 459 head = chunk.header_as_str
460
459 461 log.debug('parsing diff chunk %r', chunk)
460 462
461 463 raw_diff = chunk.raw
@@ -598,10 +600,17 class DiffProcessor(object):
598 600
599 601 chunks.insert(0, frag)
600 602
601 original_filename = safe_str(head['a_path'])
603 original_filename = head['a_path']
604 original_filename_bytes = bytes_head['a_path']
605
606 filename = head['b_path']
607 filename_bytes = bytes_head['b_path']
608
602 609 _files.append({
603 610 'original_filename': original_filename,
604 'filename': safe_str(head['b_path']),
611 'original_filename_bytes': original_filename_bytes,
612 'filename': filename,
613 'filename_bytes': filename_bytes,
605 614 'old_revision': head['a_blob_id'],
606 615 'new_revision': head['b_blob_id'],
607 616 'chunks': chunks,
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2014-2023 RhodeCode GmbH
1 # Copyright (C) 2014-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,8 +20,7
20 20 Set of custom exceptions used in RhodeCode
21 21 """
22 22
23 from webob.exc import HTTPClientError
24 from pyramid.httpexceptions import HTTPBadGateway
23 from pyramid.httpexceptions import HTTPBadGateway, HTTPClientError
25 24
26 25
27 26 class LdapUsernameError(Exception):
@@ -102,12 +101,7 class HTTPRequirementError(HTTPClientErr
102 101 self.args = (message, )
103 102
104 103
105 class ClientNotSupportedError(HTTPRequirementError):
106 title = explanation = 'Client Not Supported'
107 reason = None
108
109
110 class HTTPLockedRC(HTTPClientError):
104 class HTTPLockedRepo(HTTPClientError):
111 105 """
112 106 Special Exception For locked Repos in RhodeCode, the return code can
113 107 be overwritten by _code keyword argument passed into constructors
@@ -131,14 +125,13 class HTTPBranchProtected(HTTPClientErro
131 125 Special Exception For Indicating that branch is protected in RhodeCode, the
132 126 return code can be overwritten by _code keyword argument passed into constructors
133 127 """
134 code = 403
135 128 title = explanation = 'Branch Protected'
136 129 reason = None
137 130
138 def __init__(self, message, *args, **kwargs):
139 self.title = self.explanation = message
140 super().__init__(*args, **kwargs)
141 self.args = (message, )
131
132 class ClientNotSupported(HTTPRequirementError):
133 title = explanation = 'Client Not Supported'
134 reason = None
142 135
143 136
144 137 class IMCCommitError(Exception):
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -51,7 +51,6 from pygments.formatters.html import Htm
51 51 from pygments.lexers import (
52 52 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
53 53
54 from pyramid.threadlocal import get_current_request
55 54 from tempita import looper
56 55 from webhelpers2.html import literal, HTML, escape
57 56 from webhelpers2.html._autolink import _auto_link_urls
@@ -89,6 +88,7 from rhodecode.lib.utils2 import (
89 88 str2bool,
90 89 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime,
91 90 AttributeDict, safe_int, md5, md5_safe, get_host_info)
91 from rhodecode.lib.pyramid_utils import get_current_request
92 92 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
93 93 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
94 94 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/fixture_mods/__init__.py to rhodecode/tests/fixtures/__init__.py
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/diff_with_diff_data.diff to rhodecode/tests/fixtures/diff_fixtures/diff_with_diff_data.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_binary_and_normal.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_and_normal.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_binary_special_files.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_special_files.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_binary_special_files_2.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_binary_special_files_2.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_chmod.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_chmod.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_js_chars.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_js_chars.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_mod_single_binary_file.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_mod_single_binary_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_rename_file.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_rename_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff to rhodecode/tests/fixtures/diff_fixtures/git_diff_rename_file_with_spaces.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/git_node_history_response.json to rhodecode/tests/fixtures/diff_fixtures/git_node_history_response.json
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_add_single_binary_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_add_single_binary_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_binary_and_normal.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_binary_and_normal.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_chmod.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_chmod.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_chmod_and_mod_single_binary_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_chmod_and_mod_single_binary_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_copy_and_chmod_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_and_chmod_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_copy_and_modify_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_and_modify_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_copy_chmod_and_edit_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_chmod_and_edit_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_copy_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_copy_file_with_spaces.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_del_single_binary_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_del_single_binary_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_double_file_change_double_newline.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_double_file_change_double_newline.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_double_file_change_newline.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_double_file_change_newline.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_four_file_change_newline.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_four_file_change_newline.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_mixed_filename_encodings.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mixed_filename_encodings.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_mod_file_and_rename.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_file_and_rename.diff
1 NO CONTENT: file copied from rhodecode/tests/fixtures/git_diff_mod_single_binary_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_single_binary_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_mod_single_file_and_rename_and_chmod.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_mod_single_file_and_rename_and_chmod.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_no_newline.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_no_newline.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_rename_and_chmod_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_and_chmod_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_rename_file.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_rename_file_with_spaces.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_diff_single_file_change_newline.diff to rhodecode/tests/fixtures/diff_fixtures/hg_diff_single_file_change_newline.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/hg_node_history_response.json to rhodecode/tests/fixtures/diff_fixtures/hg_node_history_response.json
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/journal_dump.csv to rhodecode/tests/fixtures/diff_fixtures/journal_dump.csv
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/large_diff.diff to rhodecode/tests/fixtures/diff_fixtures/large_diff.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/svn_diff_binary_add_file.diff to rhodecode/tests/fixtures/diff_fixtures/svn_diff_binary_add_file.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/svn_diff_multiple_changes.diff to rhodecode/tests/fixtures/diff_fixtures/svn_diff_multiple_changes.diff
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/svn_node_history_branches.json to rhodecode/tests/fixtures/diff_fixtures/svn_node_history_branches.json
1 NO CONTENT: file renamed from rhodecode/tests/fixtures/svn_node_history_response.json to rhodecode/tests/fixtures/diff_fixtures/svn_node_history_response.json
1 NO CONTENT: file renamed from rhodecode/tests/fixture_mods/fixture_pyramid.py to rhodecode/tests/fixtures/fixture_pyramid.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/fixture_mods/fixture_utils.py to rhodecode/tests/fixtures/fixture_utils.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/fixture.py to rhodecode/tests/fixtures/rc_fixture.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now