##// 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
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
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
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
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
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
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
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
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
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
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
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
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 # test: run test-clean and tests
54 # test: run test-clean and tests
55 test:
55 test:
56 make test-clean
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 .PHONY: test-clean
60 .PHONY: test-clean
@@ -71,7 +71,25 test-only:
71 PYTHONHASHSEED=random \
71 PYTHONHASHSEED=random \
72 py.test -x -vv -r xw -p no:sugar \
72 py.test -x -vv -r xw -p no:sugar \
73 --cov-report=term-missing --cov-report=html \
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 # >>> Docs commands
94 # >>> Docs commands
77
95
@@ -485,6 +485,10 rc_cache.cache_general.expiration_time =
485 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
485 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
486 #rc_cache.cache_general.arguments.lock_auto_renewal = true
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 ; `cache_perms` cache for permission tree, auth TTL
493 ; `cache_perms` cache for permission tree, auth TTL
490 ; for simplicity use rc.file_namespace backend,
494 ; for simplicity use rc.file_namespace backend,
@@ -512,6 +516,10 rc_cache.cache_perms.expiration_time = 3
512 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
516 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
513 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
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 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
524 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
517 ; for simplicity use rc.file_namespace backend,
525 ; for simplicity use rc.file_namespace backend,
@@ -539,6 +547,40 rc_cache.cache_repo.expiration_time = 25
539 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
547 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
540 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
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 ; BEAKER SESSION
585 ; BEAKER SESSION
544 ; ##############
586 ; ##############
@@ -547,7 +589,7 rc_cache.cache_repo.expiration_time = 25
547 ; types are file, ext:redis, ext:database, ext:memcached
589 ; types are file, ext:redis, ext:database, ext:memcached
548 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
590 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
549 #beaker.session.type = file
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 ; Redis based sessions
594 ; Redis based sessions
553 beaker.session.type = ext:redis
595 beaker.session.type = ext:redis
@@ -453,6 +453,10 rc_cache.cache_general.expiration_time =
453 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
453 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
454 #rc_cache.cache_general.arguments.lock_auto_renewal = true
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 ; `cache_perms` cache for permission tree, auth TTL
461 ; `cache_perms` cache for permission tree, auth TTL
458 ; for simplicity use rc.file_namespace backend,
462 ; for simplicity use rc.file_namespace backend,
@@ -480,6 +484,10 rc_cache.cache_perms.expiration_time = 3
480 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
484 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
481 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
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 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
492 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
485 ; for simplicity use rc.file_namespace backend,
493 ; for simplicity use rc.file_namespace backend,
@@ -507,6 +515,40 rc_cache.cache_repo.expiration_time = 25
507 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
515 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
508 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
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 ; BEAKER SESSION
553 ; BEAKER SESSION
512 ; ##############
554 ; ##############
@@ -515,7 +557,7 rc_cache.cache_repo.expiration_time = 25
515 ; types are file, ext:redis, ext:database, ext:memcached
557 ; types are file, ext:redis, ext:database, ext:memcached
516 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
558 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
517 #beaker.session.type = file
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 ; Redis based sessions
562 ; Redis based sessions
521 beaker.session.type = ext:redis
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 # This program is free software: you can redistribute it and/or modify
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
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 pytest_plugins = [
29 pytest_plugins = [
30 "rhodecode.tests.fixture_mods.fixture_pyramid",
30 "rhodecode.tests.fixtures.fixture_pyramid",
31 "rhodecode.tests.fixture_mods.fixture_utils",
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 # docker build --tag sphinx-doc-build-rc .
3 # docker build --tag sphinx-doc-build-rc .
4
4
5 # Build Docs
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 Migrating |repos|
3 Migrating |repos|
4 -----------------
4 -----------------
5
5 When you install |RCE| you will be presented with 2 choices:
6 If you have installed |RCE| and have |repos| that you wish to migrate into
7 the system, use the following instructions.
8
6
9 1. On the |RCE| interface, check your |repo| storage location under
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.
10 :menuselection:`Admin --> Settings --> System Info`. For example,
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.
11 Storage location: /home/{username}/repos.
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 .. important::
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
22 .. note::
23 repositories should be adjusted since |RCE| reads the content of them.
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 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-5.4.0.rst
13 release-notes-5.3.1.rst
12 release-notes-5.3.0.rst
14 release-notes-5.3.0.rst
13 release-notes-5.2.1.rst
15 release-notes-5.2.1.rst
14 release-notes-5.2.0.rst
16 release-notes-5.2.0.rst
@@ -8,4 +8,4 pygments==2.18.0
8
8
9 docutils<0.19
9 docutils<0.19
10 markupsafe==2.1.3
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 - Local configuration commands used to help set up your |RCT| configuration.
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 rhodecode-tools
35 rhodecode-tools
16 ---------------
36 ---------------
17
37
@@ -163,6 +183,7 Example usage:
163 removing gist /home/brian/repos/.rc_gist_store/5
183 removing gist /home/brian/repos/.rc_gist_store/5
164 removing gist /home/brian/repos/.rc_gist_store/8FtCKdcbRKmEvRzTVsEt
184 removing gist /home/brian/repos/.rc_gist_store/8FtCKdcbRKmEvRzTVsEt
165
185
186
166 rhodecode-cleanup-repos
187 rhodecode-cleanup-repos
167 -----------------------
188 -----------------------
168
189
@@ -201,30 +222,37 Example usage:
201
222
202 .. code-block:: bash
223 .. code-block:: bash
203
224
204 # Cleaning up repos using tools installed with RCE 350 and above
225
205 $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-cleanup-repos \
226 # create a .rhoderc file in your host config directory (in :file:`config/_shared/.rhoderc`):
206 --instance-name=enterprise-4 --older-than=1d
227
207 Scanning for repositories in /home/brian/repos...
228 [instance:rcstack-instance]
208 preparing to remove [2] found repositories older than 1 day, 0:00:00 (1d)
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 the following repositories will be deleted completely:
239 the following repositories will be deleted completely:
211 * REMOVED: 2015-08-05 00:23:18 | /home/brian/repos/rm__20150805_002318_831
240 * REMOVED: 2015-08-05 00:23:18 | /var/opt/rhodecode_repo_store/rm__20150805_002318_831
212 * REMOVED: 2015-08-04 01:22:10 | /home/brian/repos/rm__20150804_012210_336
241 * REMOVED: 2015-08-04 01:22:10 | /var/opt/rhodecode_repo_store/rm__20150804_012210_336
213 are you sure you want to remove them? [y/N]:
242 are you sure you want to remove them? [y/N]:
214
243
215 # Clean up repos older than 1 year
244 # Clean up repos older than 1 year
216 # If using virtualenv and pre RCE 350 tools installation
245 ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --older-than=365d
217 (venv)$ rhodecode-cleanup-repos --instance-name=enterprise-1 \
218 --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 preparing to remove [343] found repositories older than 365 days
249 preparing to remove [343] found repositories older than 365 days
222
250
223 # clean up repos older than 3 days
251 # clean up repos older than 3 days
224 # If using virtualenv and pre RCE 350 tools installation
252 ./rcstack cli cmd rhodecode-cleanup-repos --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --older-than=3d
225 (venv)$ rhodecode-cleanup-repos --instance-name=enterprise-1 \
253
226 --older-than=3d
254 checking if config files needs bootstrapping
227 Scanning for repositories in /home/brian/repos...
255 Scanning for repositories in /var/opt/rhodecode_repo_store...
228 preparing to remove [3] found repositories older than 3 days
256 preparing to remove [3] found repositories older than 3 days
229
257
230 .. _tools-config:
258 .. _tools-config:
@@ -420,9 +448,8 Example usage:
420 }
448 }
421
449
422 # Cat a file and pipe to gist
450 # Cat a file and pipe to gist
423 # in RCE 3.5.0 tools and above
451
424 $ cat ~/.rhoderc | ~/.rccontrol/{instance-id}/profile/bin/rhodecode-gist \
452 $ cat ~/.rhoderc | ./rcstack cli cmd rhodecode-gist --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc -d '.rhoderc copy' create
425 --instance-name=enterprise-4 -d '.rhoderc copy' create
426 {
453 {
427 "error": null,
454 "error": null,
428 "id": 9253,
455 "id": 9253,
@@ -512,41 +539,18 Example usage:
512
539
513 .. code-block:: bash
540 .. code-block:: bash
514
541
515 # Run the indexer
542 # Create the indexing mapping file
516 $ ~/.rccontrol/enterprise-4/profile/bin/rhodecode-index \
543 $ ./rcstack cli cmd rhodecode-index --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc --create-mapping search_mapping.ini
517 --instance-name=enterprise-4
518
544
519 # Run indexer based on search_mapping.ini file
545 # Run the indexer
520 # This is using pre-350 virtualenv
546 $ ./rcstack cli cmd rhodecode-index --instance-name=rcstack-instance --config=/etc/rhodecode/conf/.rhoderc
521 (venv)$ rhodecode-index --instance-name=enterprise-1
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 # Index from the command line without creating
551 # Index from the command line without creating
524 # the .rhoderc file
552 # the .rhoderc file
525 $ rhodecode-index --apikey=key --apihost=http://rhodecode.server \
553 $ rhodecode-index --apikey=key --apihost=http://rhodecode.server --instance-name=rcstack-instance --save-config
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
550
554
551
555
552 .. _tools-setup-config:
556 .. _tools-setup-config:
@@ -4,8 +4,10 norecursedirs = rhodecode/public rhodeco
4 cache_dir = /tmp/.pytest_cache
4 cache_dir = /tmp/.pytest_cache
5
5
6 pyramid_config = rhodecode/tests/rhodecode.ini
6 pyramid_config = rhodecode/tests/rhodecode.ini
7 vcsserver_protocol = http
7
8 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
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 addopts =
12 addopts =
11 --pdbcls=IPython.terminal.debugger:TerminalPdb
13 --pdbcls=IPython.terminal.debugger:TerminalPdb
@@ -1,39 +1,39
1 # deps, generated via pipdeptree --exclude setuptools,wheel,pipdeptree,pip -f | tr '[:upper:]' '[:lower:]'
1 # deps, generated via pipdeptree --exclude setuptools,wheel,pipdeptree,pip -f | tr '[:upper:]' '[:lower:]'
2
2
3 alembic==1.13.1
3 alembic==1.13.1
4 mako==1.2.4
4 mako==1.3.6
5 markupsafe==2.1.2
5 markupsafe==3.0.2
6 sqlalchemy==1.4.52
6 sqlalchemy==1.4.52
7 greenlet==3.0.3
7 greenlet==3.1.1
8 typing_extensions==4.12.2
8 typing_extensions==4.12.2
9 async-timeout==4.0.3
9 async-timeout==4.0.3
10 babel==2.12.1
10 babel==2.12.1
11 beaker==1.12.1
11 beaker==1.13.0
12 celery==5.3.6
12 celery==5.4.0
13 billiard==4.2.0
13 billiard==4.2.1
14 click==8.1.3
14 click==8.1.7
15 click-didyoumean==0.3.0
15 click-didyoumean==0.3.1
16 click==8.1.3
16 click==8.1.7
17 click-plugins==1.1.1
17 click-plugins==1.1.1
18 click==8.1.3
18 click==8.1.7
19 click-repl==0.2.0
19 click-repl==0.3.0
20 click==8.1.3
20 click==8.1.7
21 prompt_toolkit==3.0.47
21 prompt_toolkit==3.0.48
22 wcwidth==0.2.13
22 wcwidth==0.2.13
23 six==1.16.0
23 kombu==5.4.2
24 kombu==5.3.5
25 amqp==5.2.0
24 amqp==5.2.0
26 vine==5.1.0
25 vine==5.1.0
26 tzdata==2024.2
27 vine==5.1.0
27 vine==5.1.0
28 python-dateutil==2.8.2
28 python-dateutil==2.9.0.post0
29 six==1.16.0
29 six==1.16.0
30 tzdata==2024.1
30 tzdata==2024.2
31 vine==5.1.0
31 vine==5.1.0
32 channelstream==0.7.1
32 channelstream==0.7.1
33 gevent==24.2.1
33 gevent==24.11.1
34 greenlet==3.0.3
34 greenlet==3.1.1
35 zope.event==5.0.0
35 zope.event==5.0.0
36 zope.interface==7.0.3
36 zope.interface==7.1.1
37 itsdangerous==1.1.0
37 itsdangerous==1.1.0
38 marshmallow==2.18.0
38 marshmallow==2.18.0
39 pyramid==2.0.2
39 pyramid==2.0.2
@@ -43,14 +43,14 channelstream==0.7.1
43 pastedeploy==3.1.0
43 pastedeploy==3.1.0
44 plaster==1.1.2
44 plaster==1.1.2
45 translationstring==1.4
45 translationstring==1.4
46 venusian==3.0.0
46 venusian==3.1.0
47 webob==1.8.7
47 webob==1.8.9
48 zope.deprecation==5.0.0
48 zope.deprecation==5.0.0
49 zope.interface==7.0.3
49 zope.interface==7.1.1
50 pyramid-jinja2==2.10
50 pyramid-jinja2==2.10
51 jinja2==3.1.2
51 jinja2==3.1.4
52 markupsafe==2.1.2
52 markupsafe==3.0.2
53 markupsafe==2.1.2
53 markupsafe==3.0.2
54 pyramid==2.0.2
54 pyramid==2.0.2
55 hupper==1.12
55 hupper==1.12
56 plaster==1.1.2
56 plaster==1.1.2
@@ -58,18 +58,18 channelstream==0.7.1
58 pastedeploy==3.1.0
58 pastedeploy==3.1.0
59 plaster==1.1.2
59 plaster==1.1.2
60 translationstring==1.4
60 translationstring==1.4
61 venusian==3.0.0
61 venusian==3.1.0
62 webob==1.8.7
62 webob==1.8.9
63 zope.deprecation==5.0.0
63 zope.deprecation==5.0.0
64 zope.interface==7.0.3
64 zope.interface==7.1.1
65 zope.deprecation==5.0.0
65 zope.deprecation==5.0.0
66 python-dateutil==2.8.2
66 python-dateutil==2.9.0.post0
67 six==1.16.0
67 six==1.16.0
68 requests==2.28.2
68 requests==2.32.3
69 certifi==2022.12.7
69 certifi==2024.8.30
70 charset-normalizer==3.1.0
70 charset-normalizer==3.4.0
71 idna==3.4
71 idna==3.10
72 urllib3==1.26.14
72 urllib3==1.26.20
73 ws4py==0.5.1
73 ws4py==0.5.1
74 deform==2.0.15
74 deform==2.0.15
75 chameleon==3.10.2
75 chameleon==3.10.2
@@ -87,13 +87,13 dogpile.cache==1.3.3
87 pbr==5.11.1
87 pbr==5.11.1
88 formencode==2.1.0
88 formencode==2.1.0
89 six==1.16.0
89 six==1.16.0
90 fsspec==2024.9.0
90 fsspec==2024.10.0
91 gunicorn==23.0.0
91 gunicorn==23.0.0
92 packaging==24.1
92 packaging==24.1
93 gevent==24.2.1
93 gevent==24.11.1
94 greenlet==3.0.3
94 greenlet==3.1.1
95 zope.event==5.0.0
95 zope.event==5.0.0
96 zope.interface==7.0.3
96 zope.interface==7.1.1
97 ipython==8.26.0
97 ipython==8.26.0
98 decorator==5.1.1
98 decorator==5.1.1
99 jedi==0.19.1
99 jedi==0.19.1
@@ -102,7 +102,7 ipython==8.26.0
102 traitlets==5.14.3
102 traitlets==5.14.3
103 pexpect==4.9.0
103 pexpect==4.9.0
104 ptyprocess==0.7.0
104 ptyprocess==0.7.0
105 prompt_toolkit==3.0.47
105 prompt_toolkit==3.0.48
106 wcwidth==0.2.13
106 wcwidth==0.2.13
107 pygments==2.18.0
107 pygments==2.18.0
108 stack-data==0.6.3
108 stack-data==0.6.3
@@ -113,7 +113,7 ipython==8.26.0
113 traitlets==5.14.3
113 traitlets==5.14.3
114 typing_extensions==4.12.2
114 typing_extensions==4.12.2
115 markdown==3.4.3
115 markdown==3.4.3
116 msgpack==1.0.8
116 msgpack==1.1.0
117 mysqlclient==2.1.1
117 mysqlclient==2.1.1
118 nbconvert==7.7.3
118 nbconvert==7.7.3
119 beautifulsoup4==4.12.3
119 beautifulsoup4==4.12.3
@@ -122,23 +122,23 nbconvert==7.7.3
122 six==1.16.0
122 six==1.16.0
123 webencodings==0.5.1
123 webencodings==0.5.1
124 defusedxml==0.7.1
124 defusedxml==0.7.1
125 jinja2==3.1.2
125 jinja2==3.1.4
126 markupsafe==2.1.2
126 markupsafe==3.0.2
127 jupyter_core==5.3.1
127 jupyter_core==5.3.1
128 platformdirs==3.10.0
128 platformdirs==3.10.0
129 traitlets==5.14.3
129 traitlets==5.14.3
130 jupyterlab-pygments==0.2.2
130 jupyterlab-pygments==0.2.2
131 markupsafe==2.1.2
131 markupsafe==3.0.2
132 mistune==2.0.5
132 mistune==2.0.5
133 nbclient==0.8.0
133 nbclient==0.8.0
134 jupyter_client==8.3.0
134 jupyter_client==8.3.0
135 jupyter_core==5.3.1
135 jupyter_core==5.3.1
136 platformdirs==3.10.0
136 platformdirs==3.10.0
137 traitlets==5.14.3
137 traitlets==5.14.3
138 python-dateutil==2.8.2
138 python-dateutil==2.9.0.post0
139 six==1.16.0
139 six==1.16.0
140 pyzmq==25.0.0
140 pyzmq==26.2.0
141 tornado==6.2
141 tornado==6.4.2
142 traitlets==5.14.3
142 traitlets==5.14.3
143 jupyter_core==5.3.1
143 jupyter_core==5.3.1
144 platformdirs==3.10.0
144 platformdirs==3.10.0
@@ -146,7 +146,7 nbconvert==7.7.3
146 nbformat==5.9.2
146 nbformat==5.9.2
147 fastjsonschema==2.18.0
147 fastjsonschema==2.18.0
148 jsonschema==4.18.6
148 jsonschema==4.18.6
149 attrs==22.2.0
149 attrs==24.2.0
150 pyrsistent==0.19.3
150 pyrsistent==0.19.3
151 jupyter_core==5.3.1
151 jupyter_core==5.3.1
152 platformdirs==3.10.0
152 platformdirs==3.10.0
@@ -156,7 +156,7 nbconvert==7.7.3
156 nbformat==5.9.2
156 nbformat==5.9.2
157 fastjsonschema==2.18.0
157 fastjsonschema==2.18.0
158 jsonschema==4.18.6
158 jsonschema==4.18.6
159 attrs==22.2.0
159 attrs==24.2.0
160 pyrsistent==0.19.3
160 pyrsistent==0.19.3
161 jupyter_core==5.3.1
161 jupyter_core==5.3.1
162 platformdirs==3.10.0
162 platformdirs==3.10.0
@@ -174,20 +174,20 premailer==3.10.0
174 cssselect==1.2.0
174 cssselect==1.2.0
175 cssutils==2.6.0
175 cssutils==2.6.0
176 lxml==5.3.0
176 lxml==5.3.0
177 requests==2.28.2
177 requests==2.32.3
178 certifi==2022.12.7
178 certifi==2024.8.30
179 charset-normalizer==3.1.0
179 charset-normalizer==3.4.0
180 idna==3.4
180 idna==3.10
181 urllib3==1.26.14
181 urllib3==1.26.20
182 psutil==5.9.8
182 psutil==5.9.8
183 psycopg2==2.9.9
183 psycopg2==2.9.10
184 py-bcrypt==0.4
184 py-bcrypt==0.4
185 pycmarkgfm==1.2.0
185 pycmarkgfm==1.2.0
186 cffi==1.16.0
186 cffi==1.17.1
187 pycparser==2.21
187 pycparser==2.22
188 pycryptodome==3.17
188 pycryptodome==3.21.0
189 pycurl==7.45.3
189 pycurl==7.45.3
190 pymysql==1.0.3
190 pymysql==1.1.1
191 pyotp==2.8.0
191 pyotp==2.8.0
192 pyparsing==3.1.1
192 pyparsing==3.1.1
193 pyramid-mailer==0.15.1
193 pyramid-mailer==0.15.1
@@ -198,19 +198,19 pyramid-mailer==0.15.1
198 pastedeploy==3.1.0
198 pastedeploy==3.1.0
199 plaster==1.1.2
199 plaster==1.1.2
200 translationstring==1.4
200 translationstring==1.4
201 venusian==3.0.0
201 venusian==3.1.0
202 webob==1.8.7
202 webob==1.8.9
203 zope.deprecation==5.0.0
203 zope.deprecation==5.0.0
204 zope.interface==7.0.3
204 zope.interface==7.1.1
205 repoze.sendmail==4.4.1
205 repoze.sendmail==4.4.1
206 transaction==5.0.0
206 transaction==5.0.0
207 zope.interface==7.0.3
207 zope.interface==7.1.1
208 zope.interface==7.0.3
208 zope.interface==7.1.1
209 transaction==5.0.0
209 transaction==5.0.0
210 zope.interface==7.0.3
210 zope.interface==7.1.1
211 pyramid-mako==1.1.0
211 pyramid-mako==1.1.0
212 mako==1.2.4
212 mako==1.3.6
213 markupsafe==2.1.2
213 markupsafe==3.0.2
214 pyramid==2.0.2
214 pyramid==2.0.2
215 hupper==1.12
215 hupper==1.12
216 plaster==1.1.2
216 plaster==1.1.2
@@ -218,16 +218,15 pyramid-mako==1.1.0
218 pastedeploy==3.1.0
218 pastedeploy==3.1.0
219 plaster==1.1.2
219 plaster==1.1.2
220 translationstring==1.4
220 translationstring==1.4
221 venusian==3.0.0
221 venusian==3.1.0
222 webob==1.8.7
222 webob==1.8.9
223 zope.deprecation==5.0.0
223 zope.deprecation==5.0.0
224 zope.interface==7.0.3
224 zope.interface==7.1.1
225 python-ldap==3.4.3
225 python-ldap==3.4.3
226 pyasn1==0.4.8
226 pyasn1==0.4.8
227 pyasn1-modules==0.2.8
227 pyasn1-modules==0.2.8
228 pyasn1==0.4.8
228 pyasn1==0.4.8
229 python-memcached==1.59
229 python-memcached==1.62
230 six==1.16.0
231 python-pam==2.0.2
230 python-pam==2.0.2
232 python3-saml==1.16.0
231 python3-saml==1.16.0
233 isodate==0.6.1
232 isodate==0.6.1
@@ -236,60 +235,66 python3-saml==1.16.0
236 xmlsec==1.3.14
235 xmlsec==1.3.14
237 lxml==5.3.0
236 lxml==5.3.0
238 pyyaml==6.0.1
237 pyyaml==6.0.1
239 redis==5.1.0
238 redis==5.2.0
240 async-timeout==4.0.3
239 async-timeout==4.0.3
241 regex==2022.10.31
240 regex==2022.10.31
242 routes==2.5.1
241 routes==2.5.1
243 repoze.lru==0.7
242 repoze.lru==0.7
244 six==1.16.0
243 six==1.16.0
245 s3fs==2024.9.0
244 s3fs==2024.10.0
246 aiobotocore==2.13.0
245 aiobotocore==2.15.2
247 aiohttp==3.9.5
246 aiohttp==3.11.4
247 aiohappyeyeballs==2.4.3
248 aiosignal==1.3.1
248 aiosignal==1.3.1
249 frozenlist==1.4.1
249 frozenlist==1.5.0
250 attrs==22.2.0
250 attrs==24.2.0
251 frozenlist==1.4.1
251 frozenlist==1.5.0
252 multidict==6.0.5
252 multidict==6.1.0
253 yarl==1.9.4
253 propcache==0.2.0
254 idna==3.4
254 yarl==1.17.2
255 multidict==6.0.5
255 idna==3.10
256 aioitertools==0.11.0
256 multidict==6.1.0
257 botocore==1.34.106
257 propcache==0.2.0
258 aioitertools==0.12.0
259 botocore==1.35.36
258 jmespath==1.0.1
260 jmespath==1.0.1
259 python-dateutil==2.8.2
261 python-dateutil==2.9.0.post0
260 six==1.16.0
262 six==1.16.0
261 urllib3==1.26.14
263 urllib3==1.26.20
262 wrapt==1.16.0
264 wrapt==1.16.0
263 aiohttp==3.9.5
265 aiohttp==3.11.4
266 aiohappyeyeballs==2.4.3
264 aiosignal==1.3.1
267 aiosignal==1.3.1
265 frozenlist==1.4.1
268 frozenlist==1.5.0
266 attrs==22.2.0
269 attrs==24.2.0
267 frozenlist==1.4.1
270 frozenlist==1.5.0
268 multidict==6.0.5
271 multidict==6.1.0
269 yarl==1.9.4
272 propcache==0.2.0
270 idna==3.4
273 yarl==1.17.2
271 multidict==6.0.5
274 idna==3.10
272 fsspec==2024.9.0
275 multidict==6.1.0
276 propcache==0.2.0
277 fsspec==2024.10.0
273 simplejson==3.19.2
278 simplejson==3.19.2
274 sshpubkeys==3.3.1
279 sshpubkeys==3.3.1
275 cryptography==40.0.2
280 cryptography==43.0.3
276 cffi==1.16.0
281 cffi==1.17.1
277 pycparser==2.21
282 pycparser==2.22
278 ecdsa==0.18.0
283 ecdsa==0.19.0
279 six==1.16.0
284 six==1.16.0
280 sqlalchemy==1.4.52
285 sqlalchemy==1.4.52
281 greenlet==3.0.3
286 greenlet==3.1.1
282 typing_extensions==4.12.2
287 typing_extensions==4.12.2
283 supervisor==4.2.5
288 supervisor==4.2.5
284 tzlocal==4.3
289 tzlocal==4.3
285 pytz-deprecation-shim==0.1.0.post0
290 pytz-deprecation-shim==0.1.0.post0
286 tzdata==2024.1
291 tzdata==2024.2
287 tempita==0.5.2
292 tempita==0.5.2
288 unidecode==1.3.6
293 unidecode==1.3.8
289 urlobject==2.4.3
294 urlobject==2.4.3
290 waitress==3.0.0
295 waitress==3.0.1
291 webhelpers2==2.1
296 webhelpers2==2.1
292 markupsafe==2.1.2
297 markupsafe==3.0.2
293 six==1.16.0
298 six==1.16.0
294 whoosh==2.7.4
299 whoosh==2.7.4
295 zope.cachedescriptors==5.0.0
300 zope.cachedescriptors==5.0.0
@@ -15,10 +15,8 pyramid-debugtoolbar
15 flake8
15 flake8
16 ruff
16 ruff
17
17
18 pipdeptree==2.7.1
18 pipdeptree
19 invoke==2.0.0
19 invoke==2.0.0
20 bumpversion==0.6.0
21 bump2version==1.0.1
22
20
23 docutils-stubs
21 docutils-stubs
24 types-redis
22 types-redis
@@ -1,3 +1,3
1 ## rhodecode-tools, special case, use file://PATH.tar.gz#egg=rhodecode-tools==X.Y.Z, to test local version
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
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==3.0.0
3 rhodecode-tools==4.1.0
@@ -1,47 +1,54
1 # test related requirements
1 # test related requirements
2
2 mock==5.1.0
3 mock==5.1.0
3 pytest-cov==4.1.0
4
4 coverage==7.4.3
5 pytest==8.3.3
5 pytest==8.1.1
6 iniconfig==2.0.0
6 iniconfig==2.0.0
7 packaging==24.1
7 packaging==24.1
8 pluggy==1.5.0
8 pluggy==1.4.0
9 pytest-cov==5.0.0
9 pytest-env==1.1.3
10 coverage==7.6.4
10 pytest==8.1.1
11 pytest==8.3.3
11 iniconfig==2.0.0
12 iniconfig==2.0.0
12 packaging==24.1
13 packaging==24.1
13 pluggy==1.4.0
14 pluggy==1.5.0
14 pytest-profiling==1.7.0
15 pytest-env==1.1.5
15 gprof2dot==2022.7.29
16 pytest==8.3.3
16 pytest==8.1.1
17 iniconfig==2.0.0
17 iniconfig==2.0.0
18 packaging==24.1
18 packaging==24.1
19 pluggy==1.4.0
19 pluggy==1.5.0
20 six==1.16.0
20 pytest-profiling==1.7.0
21 pytest-rerunfailures==13.0
21 gprof2dot==2024.6.6
22 packaging==24.1
22 pytest==8.3.3
23 pytest==8.1.1
24 iniconfig==2.0.0
23 iniconfig==2.0.0
25 packaging==24.1
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 pytest-runner==6.0.1
33 pytest-runner==6.0.1
28 pytest-sugar==1.0.0
34 pytest-sugar==1.0.0
29 packaging==24.1
35 packaging==24.1
30 pytest==8.1.1
36 pytest==8.3.3
31 iniconfig==2.0.0
37 iniconfig==2.0.0
32 packaging==24.1
38 packaging==24.1
33 pluggy==1.4.0
39 pluggy==1.5.0
34 termcolor==2.4.0
40 termcolor==2.5.0
35 pytest-timeout==2.3.1
41 pytest-timeout==2.3.1
36 pytest==8.1.1
42 pytest==8.3.3
37 iniconfig==2.0.0
43 iniconfig==2.0.0
38 packaging==24.1
44 packaging==24.1
39 pluggy==1.4.0
45 pluggy==1.5.0
40 webtest==3.0.0
46
47 webtest==3.0.1
41 beautifulsoup4==4.12.3
48 beautifulsoup4==4.12.3
42 soupsieve==2.5
49 soupsieve==2.5
43 waitress==3.0.0
50 waitress==3.0.1
44 webob==1.8.7
51 webob==1.8.9
45
52
46 # RhodeCode test-data
53 # RhodeCode test-data
47 rc_testdata @ https://code.rhodecode.com/upstream/rc-testdata-dist/raw/77378e9097f700b4c1b9391b56199fe63566b5c9/rc_testdata-0.11.0.tar.gz#egg=rc_testdata
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -45,13 +45,20 CELERY_EAGER = False
45 # link to config for pyramid
45 # link to config for pyramid
46 CONFIG = {}
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 class ConfigGet:
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 if key not in CONFIG:
60 if key not in CONFIG:
54 if missing == self.NotGiven:
61 if missing != NotGiven:
55 return missing
62 return missing
56 # we don't get key, we don't get missing value, return nothing similar as config.get(key)
63 # we don't get key, we don't get missing value, return nothing similar as config.get(key)
57 return None
64 return None
@@ -74,6 +81,12 class ConfigGet:
74 val = self._get_val_or_missing(key, missing)
81 val = self._get_val_or_missing(key, missing)
75 return str2bool(val)
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 # Populated with the settings dictionary from application init in
90 # Populated with the settings dictionary from application init in
78 # rhodecode.conf.environment.load_pyramid_environment
91 # rhodecode.conf.environment.load_pyramid_environment
79 PYRAMID_SETTINGS = {}
92 PYRAMID_SETTINGS = {}
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,12 +21,12 import pytest
21 from rhodecode.model.meta import Session
21 from rhodecode.model.meta import Session
22 from rhodecode.model.user import UserModel
22 from rhodecode.model.user import UserModel
23 from rhodecode.model.auth_token import AuthTokenModel
23 from rhodecode.model.auth_token import AuthTokenModel
24 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
25
24
26
25
27 @pytest.fixture(scope="class")
26 @pytest.fixture(scope="class")
28 def testuser_api(request, baseapp):
27 def testuser_api(request, baseapp):
29 cls = request.cls
28 cls = request.cls
29 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
30
30
31 # ADMIN USER
31 # ADMIN USER
32 cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
32 cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.model.gist import GistModel
23 from rhodecode.model.gist import GistModel
25 from rhodecode.api.tests.utils import (
24 from rhodecode.api.tests.utils import (
26 build_data, api_call, assert_error, assert_ok, crash)
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 @pytest.mark.usefixtures("testuser_api", "app")
29 @pytest.mark.usefixtures("testuser_api", "app")
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
28 from rhodecode.api.tests.utils import (
27 from rhodecode.api.tests.utils import (
29 build_data, api_call, assert_ok, assert_error, crash)
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 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.str_utils import safe_str
31 from rhodecode.lib.str_utils import safe_str
33
32
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
27 from rhodecode.api.tests.utils import (
26 from rhodecode.api.tests.utils import (
28 build_data, api_call, assert_ok, assert_error, crash)
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 fixture = Fixture()
31 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,7 +25,7 from rhodecode.tests import (
26 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL)
25 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL)
27 from rhodecode.api.tests.utils import (
26 from rhodecode.api.tests.utils import (
28 build_data, api_call, assert_ok, assert_error, jsonify, crash)
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 from rhodecode.model.db import RepoGroup
29 from rhodecode.model.db import RepoGroup
31
30
32
31
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.model.user_group import UserGroupModel
24 from rhodecode.model.user_group import UserGroupModel
26 from rhodecode.api.tests.utils import (
25 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok, crash, jsonify)
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 @pytest.mark.usefixtures("testuser_api", "app")
30 @pytest.mark.usefixtures("testuser_api", "app")
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
29 from rhodecode.api.tests.utils import (
28 from rhodecode.api.tests.utils import (
30 build_data, api_call, assert_error, assert_ok, crash)
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 fixture = Fixture()
33 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,6 +19,7
20
19
21 import pytest
20 import pytest
22
21
22 from rhodecode.lib.str_utils import safe_str
23 from rhodecode.model.db import User, ChangesetComment
23 from rhodecode.model.db import User, ChangesetComment
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.comment import CommentsModel
25 from rhodecode.model.comment import CommentsModel
@@ -37,7 +37,7 def make_repo_comments_factory(request):
37 commit = repo.scm_instance()[0]
37 commit = repo.scm_instance()[0]
38
38
39 commit_id = commit.raw_id
39 commit_id = commit.raw_id
40 file_0 = commit.affected_files[0]
40 file_0 = safe_str(commit.affected_files[0])
41 comments = []
41 comments = []
42
42
43 # general
43 # general
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +19,6
20 import mock
19 import mock
21 import pytest
20 import pytest
22
21
23 from rhodecode.model.scm import ScmModel
24 from rhodecode.api.tests.utils import (
22 from rhodecode.api.tests.utils import (
25 build_data, api_call, assert_ok, assert_error, crash)
23 build_data, api_call, assert_ok, assert_error, crash)
26
24
@@ -31,13 +29,14 class TestRescanRepos(object):
31 id_, params = build_data(self.apikey, 'rescan_repos')
29 id_, params = build_data(self.apikey, 'rescan_repos')
32 response = api_call(self.app, params)
30 response = api_call(self.app, params)
33
31
34 expected = {'added': [], 'removed': []}
32 expected = {'added': [], 'errors': []}
35 assert_ok(id_, expected, given=response.body)
33 assert_ok(id_, expected, given=response.body)
36
34
37 @mock.patch.object(ScmModel, 'repo_scan', crash)
35 def test_api_rescan_repos_error(self):
38 def test_api_rescann_error(self):
39 id_, params = build_data(self.apikey, 'rescan_repos', )
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 expected = 'Error occurred during rescan repositories action'
41 expected = 'Error occurred during rescan repositories action'
43 assert_error(id_, expected, given=response.body)
42 assert_error(id_, expected, given=response.body)
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
24 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
26 from rhodecode.api.tests.utils import (
25 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok, crash, jsonify)
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 from rhodecode.tests.fixture_mods.fixture_utils import plain_http_host_only_stub
28 from rhodecode.tests.fixtures.fixture_utils import plain_http_host_only_stub
30
29
31 fixture = Fixture()
30 fixture = Fixture()
32
31
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 ','.join(_changes_details_types)))
317 ','.join(_changes_details_types)))
318
318
319 vcs_repo = repo.scm_instance()
319 vcs_repo = repo.scm_instance()
320 pre_load = ['author', 'branch', 'date', 'message', 'parents',
320 pre_load = ['author', 'branch', 'date', 'message', 'parents', 'status', '_commit']
321 'status', '_commit', '_file_paths']
322
321
323 try:
322 try:
324 commit = repo.get_commit(commit_id=revision, pre_load=pre_load)
323 commit = repo.get_commit(commit_id=revision, pre_load=pre_load)
@@ -376,8 +375,7 def get_repo_changesets(request, apiuser
376 ','.join(_changes_details_types)))
375 ','.join(_changes_details_types)))
377
376
378 limit = int(limit)
377 limit = int(limit)
379 pre_load = ['author', 'branch', 'date', 'message', 'parents',
378 pre_load = ['author', 'branch', 'date', 'message', 'parents', 'status', '_commit']
380 'status', '_commit', '_file_paths']
381
379
382 vcs_repo = repo.scm_instance()
380 vcs_repo = repo.scm_instance()
383 # SVN needs a special case to distinguish its index and commit id
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -18,14 +18,13
18
18
19 import logging
19 import logging
20 import itertools
20 import itertools
21 import base64
22
21
23 from rhodecode.api import (
22 from rhodecode.api import (
24 jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods)
23 jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods)
25
24
26 from rhodecode.api.utils import (
25 from rhodecode.api.utils import (
27 Optional, OAttr, has_superadmin_permission, get_user_or_error)
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 from rhodecode.lib import system_info
28 from rhodecode.lib import system_info
30 from rhodecode.lib import user_sessions
29 from rhodecode.lib import user_sessions
31 from rhodecode.lib import exc_tracking
30 from rhodecode.lib import exc_tracking
@@ -33,9 +32,7 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.utils2 import safe_int
32 from rhodecode.lib.utils2 import safe_int
34 from rhodecode.model.db import UserIpMap
33 from rhodecode.model.db import UserIpMap
35 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
36 from rhodecode.apps.file_store import utils as store_utils
35
37 from rhodecode.apps.file_store.exceptions import FileNotAllowedException, \
38 FileOverSizeException
39
36
40 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
41
38
@@ -158,13 +155,10 def get_ip(request, apiuser, userid=Opti
158
155
159
156
160 @jsonrpc_method()
157 @jsonrpc_method()
161 def rescan_repos(request, apiuser, remove_obsolete=Optional(False)):
158 def rescan_repos(request, apiuser):
162 """
159 """
163 Triggers a rescan of the specified repositories.
160 Triggers a rescan of the specified repositories.
164
161 It returns list of added repositories, and errors during scan.
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".
168
162
169 This command can only be run using an |authtoken| with admin rights to
163 This command can only be run using an |authtoken| with admin rights to
170 the specified repository.
164 the specified repository.
@@ -173,9 +167,6 def rescan_repos(request, apiuser, remov
173
167
174 :param apiuser: This is filled automatically from the |authtoken|.
168 :param apiuser: This is filled automatically from the |authtoken|.
175 :type apiuser: AuthUser
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 Example output:
171 Example output:
181
172
@@ -184,7 +175,7 def rescan_repos(request, apiuser, remov
184 id : <id_given_in_input>
175 id : <id_given_in_input>
185 result : {
176 result : {
186 'added': [<added repository name>,...]
177 'added': [<added repository name>,...]
187 'removed': [<removed repository name>,...]
178 'errors': [<error_list>,...]
188 }
179 }
189 error : null
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 if not has_superadmin_permission(apiuser):
195 if not has_superadmin_permission(apiuser):
203 raise JSONRPCForbidden()
196 raise JSONRPCForbidden()
204
197
205 try:
198 try:
206 rm_obsolete = Optional.extract(remove_obsolete)
199 added, errors = repo2db_mapper(ScmModel().repo_scan(), force_hooks_rebuild=True)
207 added, removed = repo2db_mapper(ScmModel().repo_scan(),
200 return {'added': added, 'errors': errors}
208 remove_obsolete=rm_obsolete, force_hooks_rebuild=True)
209 return {'added': added, 'removed': removed}
210 except Exception:
201 except Exception:
211 log.exception('Failed to run repo rescann')
202 log.exception('Failed to run repo rescan')
212 raise JSONRPCError(
203 raise JSONRPCError(
213 'Error occurred during rescan repositories action'
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 @jsonrpc_method()
257 @jsonrpc_method()
218 def cleanup_sessions(request, apiuser, older_then=Optional(60)):
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -395,7 +395,7 class RepoAppView(BaseAppView):
395 settings = settings_model.get_repo_settings_inherited()
395 settings = settings_model.get_repo_settings_inherited()
396 return settings.get(settings_key, default)
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 log.debug("Looking for README file at path %s", path)
399 log.debug("Looking for README file at path %s", path)
400 if commit_id:
400 if commit_id:
401 landing_commit_id = commit_id
401 landing_commit_id = commit_id
@@ -413,16 +413,14 class RepoAppView(BaseAppView):
413
413
414 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
414 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
415 def generate_repo_readme(
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
418 _readme_data = None
419 readme_filename = None
419 _readme_filename = None
420
420
421 commit = db_repo.get_commit(_commit_id)
421 commit = db_repo.get_commit(_commit_id)
422 log.debug("Searching for a README file at commit %s.", _commit_id)
422 log.debug("Searching for a README file at commit %s.", _commit_id)
423 readme_node = ReadmeFinder(_renderer_type).search(
423 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path, nodes=nodes)
424 commit, path=_readme_search_path
425 )
426
424
427 if readme_node:
425 if readme_node:
428 log.debug("Found README node: %s", readme_node)
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 commit, readme_node, relative_urls
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 readme_data, readme_filename = generate_repo_readme(
450 readme_data, readme_filename = generate_repo_readme(
453 db_repo.repo_id,
451 db_repo.repo_id,
454 landing_commit_id,
452 landing_commit_id,
455 db_repo.repo_name,
453 db_repo.repo_name,
456 path,
454 path,
457 renderer_type,
455 renderer_type
458 )
456 )
459
457
460 compute_time = time.time() - start
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,5
1
1
2 import dataclasses
2 import dataclasses# Copyright (C) 2016-2024 RhodeCode GmbH
3 # Copyright (C) 2016-2023 RhodeCode GmbH
4 #
3 #
5 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -361,12 +361,21 def admin_routes(config):
361 renderer='rhodecode:templates/admin/settings/settings.mako')
361 renderer='rhodecode:templates/admin/settings/settings.mako')
362
362
363 config.add_route(
363 config.add_route(
364 name='admin_settings_mapping_update',
364 name='admin_settings_mapping_create',
365 pattern='/settings/mapping/update')
365 pattern='/settings/mapping/create')
366 config.add_view(
366 config.add_view(
367 AdminSettingsView,
367 AdminSettingsView,
368 attr='settings_mapping_update',
368 attr='settings_mapping_create',
369 route_name='admin_settings_mapping_update', request_method='POST',
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 renderer='rhodecode:templates/admin/settings/settings.mako')
379 renderer='rhodecode:templates/admin/settings/settings.mako')
371
380
372 config.add_route(
381 config.add_route(
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -26,7 +25,7 import pytest
26 from rhodecode.lib.str_utils import safe_str
25 from rhodecode.lib.str_utils import safe_str
27 from rhodecode.tests import *
26 from rhodecode.tests import *
28 from rhodecode.tests.routes import route_path
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 from rhodecode.model.db import UserLog
29 from rhodecode.model.db import UserLog
31 from rhodecode.model.meta import Session
30 from rhodecode.model.meta import Session
32
31
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +19,7
20 import pytest
19 import pytest
21
20
22 from rhodecode.tests import TestController
21 from rhodecode.tests import TestController
23 from rhodecode.tests.fixture import Fixture
22 from rhodecode.tests.fixtures.rc_fixture import Fixture
24 from rhodecode.tests.routes import route_path
23 from rhodecode.tests.routes import route_path
25
24
26 fixture = Fixture()
25 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import (
36 from rhodecode.tests import (
38 login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
37 login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
39 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
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 from rhodecode.tests.utils import repo_on_filesystem
40 from rhodecode.tests.utils import repo_on_filesystem
42 from rhodecode.tests.routes import route_path
41 from rhodecode.tests.routes import route_path
43
42
@@ -111,9 +110,11 class TestAdminRepos(object):
111 repo_type=backend.alias,
110 repo_type=backend.alias,
112 repo_description=description,
111 repo_description=description,
113 csrf_token=csrf_token))
112 csrf_token=csrf_token))
114
113 try:
115 self.assert_repository_is_created_correctly(
114 self.assert_repository_is_created_correctly(repo_name, description, backend)
116 repo_name, description, backend)
115 finally:
116 RepoModel().delete(numeric_repo)
117 Session().commit()
117
118
118 @pytest.mark.parametrize("suffix", ['', '_ąćę'], ids=['', 'non-ascii'])
119 @pytest.mark.parametrize("suffix", ['', '_ąćę'], ids=['', 'non-ascii'])
119 def test_create_in_group(
120 def test_create_in_group(
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.model.repo_group import RepoGroupModel
26 from rhodecode.model.repo_group import RepoGroupModel
28 from rhodecode.tests import (
27 from rhodecode.tests import (
29 assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH)
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 from rhodecode.tests.routes import route_path
30 from rhodecode.tests.routes import route_path
32
31
33
32
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import (
24 from rhodecode.tests import (
26 TestController, assert_session_flash)
25 TestController, assert_session_flash)
27 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixtures.rc_fixture import Fixture
28 from rhodecode.tests.routes import route_path
27 from rhodecode.tests.routes import route_path
29
28
30 fixture = Fixture()
29 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import (
28 from rhodecode.tests import (
30 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
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 from rhodecode.tests.routes import route_path
31 from rhodecode.tests.routes import route_path
33
32
34 fixture = Fixture()
33 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +21,7 import pytest
22 from rhodecode.model.db import User, UserSshKeys
21 from rhodecode.model.db import User, UserSshKeys
23
22
24 from rhodecode.tests import TestController, assert_session_flash
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 from rhodecode.tests.routes import route_path
25 from rhodecode.tests.routes import route_path
27
26
28 fixture = Fixture()
27 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -38,7 +38,7 from rhodecode.lib.auth import (
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
39 from rhodecode.lib.celerylib import tasks, run_task
39 from rhodecode.lib.celerylib import tasks, run_task
40 from rhodecode.lib.str_utils import safe_str
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 from rhodecode.lib.utils2 import str2bool, AttributeDict
42 from rhodecode.lib.utils2 import str2bool, AttributeDict
43 from rhodecode.lib.index import searcher_from_config
43 from rhodecode.lib.index import searcher_from_config
44
44
@@ -77,13 +77,16 class AdminSettingsView(BaseAppView):
77 raise Exception('Could not get application ui settings !')
77 raise Exception('Could not get application ui settings !')
78 settings = {
78 settings = {
79 # legacy param that needs to be kept
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 for each in ret:
83 for each in ret:
83 k = each.ui_key
84 k = each.ui_key
84 v = each.ui_value
85 v = each.ui_value
86 section = each.ui_section
87
85 # skip some options if they are defined
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 continue
90 continue
88
91
89 if k == '/':
92 if k == '/':
@@ -98,7 +101,7 class AdminSettingsView(BaseAppView):
98 if each.ui_section in ['hooks', 'extensions']:
101 if each.ui_section in ['hooks', 'extensions']:
99 v = each.ui_active
102 v = each.ui_active
100
103
101 settings[each.ui_section + '_' + k] = v
104 settings[section + '_' + k] = v
102
105
103 return settings
106 return settings
104
107
@@ -125,8 +128,6 class AdminSettingsView(BaseAppView):
125 c.svn_config_path = rhodecode.ConfigGet().get_str(config_keys.config_file_path)
128 c.svn_config_path = rhodecode.ConfigGet().get_str(config_keys.config_file_path)
126 defaults = self._form_defaults()
129 defaults = self._form_defaults()
127
130
128 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
129
130 data = render('rhodecode:templates/admin/settings/settings.mako',
131 data = render('rhodecode:templates/admin/settings/settings.mako',
131 self._get_template_context(c), self.request)
132 self._get_template_context(c), self.request)
132 html = formencode.htmlfill.render(
133 html = formencode.htmlfill.render(
@@ -232,13 +233,12 class AdminSettingsView(BaseAppView):
232 @LoginRequired()
233 @LoginRequired()
233 @HasPermissionAllDecorator('hg.admin')
234 @HasPermissionAllDecorator('hg.admin')
234 @CSRFRequired()
235 @CSRFRequired()
235 def settings_mapping_update(self):
236 def settings_mapping_create(self):
236 _ = self.request.translate
237 _ = self.request.translate
237 c = self.load_default_context()
238 c = self.load_default_context()
238 c.active = 'mapping'
239 c.active = 'mapping'
239 rm_obsolete = self.request.POST.get('destroy', False)
240 invalidate_cache = self.request.POST.get('invalidate', False)
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 if invalidate_cache:
243 if invalidate_cache:
244 log.debug('invalidating all repositories cache')
244 log.debug('invalidating all repositories cache')
@@ -246,16 +246,34 class AdminSettingsView(BaseAppView):
246 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
246 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
247
247
248 filesystem_repos = ScmModel().repo_scan()
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 PermissionModel().trigger_permission_flush()
250 PermissionModel().trigger_permission_flush()
251
251
252 def _repr(rm_repo):
252 def _repr(rm_repo):
253 return ', '.join(map(safe_str, rm_repo)) or '-'
253 return ', '.join(map(safe_str, rm_repo)) or '-'
254
254
255 h.flash(_('Repositories successfully '
255 if errors:
256 'rescanned added: %s ; removed: %s') %
256 h.flash(_('Errors during scan: {}').format(_repr(errors), ), category='error')
257 (_repr(added), _repr(removed)),
257
258 category='success')
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 raise HTTPFound(h.route_path('admin_settings_mapping'))
277 raise HTTPFound(h.route_path('admin_settings_mapping'))
260
278
261 @LoginRequired()
279 @LoginRequired()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
4 #
2 #
5 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
4 #
2 #
5 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
4 #
2 #
5 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,6 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2
3 # Copyright (C) 2016-2023 RhodeCode GmbH
4 #
2 #
5 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.model.db import Session, Repository, RepoGroup
27 from rhodecode.model.db import Session, Repository, RepoGroup
28
28
29 from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN
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 from rhodecode.tests.routes import route_path
31 from rhodecode.tests.routes import route_path
32
32
33 fixture = Fixture()
33 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.lib.ext_json import json
22 from rhodecode.lib.ext_json import json
23
23
24 from rhodecode.tests import TestController
24 from rhodecode.tests import TestController
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 from rhodecode.tests.routes import route_path
26 from rhodecode.tests.routes import route_path
27
27
28 fixture = Fixture()
28 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +20,7 import pytest
20 from rhodecode.lib.ext_json import json
20 from rhodecode.lib.ext_json import json
21
21
22 from rhodecode.tests import TestController
22 from rhodecode.tests import TestController
23 from rhodecode.tests.fixture import Fixture
23 from rhodecode.tests.fixtures.rc_fixture import Fixture
24 from rhodecode.tests.routes import route_path
24 from rhodecode.tests.routes import route_path
25
25
26 fixture = Fixture()
26 fixture = Fixture()
@@ -1,23 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-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
20 # Copyright (C) 2016-2023 RhodeCode GmbH
21 #
2 #
22 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
23 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -40,7 +21,7 import pytest
40 from rhodecode.lib.ext_json import json
21 from rhodecode.lib.ext_json import json
41
22
42 from rhodecode.tests import TestController
23 from rhodecode.tests import TestController
43 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixtures.rc_fixture import Fixture
44 from rhodecode.tests.routes import route_path
25 from rhodecode.tests.routes import route_path
45
26
46 fixture = Fixture()
27 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.settings import SettingsModel
25 from rhodecode.model.settings import SettingsModel
26 from rhodecode.tests import TestController
26 from rhodecode.tests import TestController
27 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixtures.rc_fixture import Fixture
28 from rhodecode.tests.routes import route_path
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -3,7 +3,7 import mock
3
3
4 from rhodecode.lib.type_utils import AttributeDict
4 from rhodecode.lib.type_utils import AttributeDict
5 from rhodecode.model.meta import Session
5 from rhodecode.model.meta import Session
6 from rhodecode.tests.fixture import Fixture
6 from rhodecode.tests.fixtures.rc_fixture import Fixture
7 from rhodecode.tests.routes import route_path
7 from rhodecode.tests.routes import route_path
8 from rhodecode.model.settings import SettingsModel
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.tests import (
31 from rhodecode.tests import (
32 assert_session_flash, HG_REPO, TEST_USER_ADMIN_LOGIN,
32 assert_session_flash, HG_REPO, TEST_USER_ADMIN_LOGIN,
33 no_newline_id_generator)
33 no_newline_id_generator)
34 from rhodecode.tests.fixture import Fixture
34 from rhodecode.tests.fixtures.rc_fixture import Fixture
35 from rhodecode.tests.routes import route_path
35 from rhodecode.tests.routes import route_path
36
36
37 fixture = Fixture()
37 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.tests import (
22 from rhodecode.tests import (
23 TestController, clear_cache_regions,
23 TestController, clear_cache_regions,
24 TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
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 from rhodecode.tests.utils import AssertResponse
26 from rhodecode.tests.utils import AssertResponse
27 from rhodecode.tests.routes import route_path
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.model.db import User
22 from rhodecode.model.db import User
23 from rhodecode.tests import (
23 from rhodecode.tests import (
24 TestController, assert_session_flash)
24 TestController, assert_session_flash)
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 from rhodecode.tests.routes import route_path
26 from rhodecode.tests.routes import route_path
27
27
28
28
@@ -1,23 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-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
20 # Copyright (C) 2016-2023 RhodeCode GmbH
21 #
2 #
22 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
23 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.tests import (
23 from rhodecode.tests import (
24 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
24 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
25 assert_session_flash, TEST_USER_REGULAR_PASS)
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 from rhodecode.tests.routes import route_path
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +21,7 import pytest
21 from rhodecode.tests import (
21 from rhodecode.tests import (
22 TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
22 TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
23 TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
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 from rhodecode.tests.routes import route_path
25 from rhodecode.tests.routes import route_path
26
26
27 from rhodecode.model.db import Notification, User
27 from rhodecode.model.db import Notification, User
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.model.meta import Session
23 from rhodecode.model.meta import Session
25 from rhodecode.model.user import UserModel
24 from rhodecode.model.user import UserModel
26 from rhodecode.tests import assert_session_flash, TestController
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 from rhodecode.tests.routes import route_path
27 from rhodecode.tests.routes import route_path
29
28
30 fixture = Fixture()
29 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +19,7
20 from rhodecode.tests import (
19 from rhodecode.tests import (
21 TestController, TEST_USER_ADMIN_LOGIN,
20 TestController, TEST_USER_ADMIN_LOGIN,
22 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
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 from rhodecode.tests.routes import route_path
23 from rhodecode.tests.routes import route_path
25
24
26 fixture = Fixture()
25 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,7 +18,7
19
18
20 from rhodecode.model.db import User, Repository, UserFollowing
19 from rhodecode.model.db import User, Repository, UserFollowing
21 from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN
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 from rhodecode.tests.routes import route_path
22 from rhodecode.tests.routes import route_path
24
23
25 fixture = Fixture()
24 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +20,7
21 from rhodecode.model.db import User, UserSshKeys
20 from rhodecode.model.db import User, UserSshKeys
22
21
23 from rhodecode.tests import TestController, assert_session_flash
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 from rhodecode.tests.routes import route_path
24 from rhodecode.tests.routes import route_path
26
25
27 fixture = Fixture()
26 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +21,7 import pytest
22 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
21 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
23 from rhodecode.lib.vcs import nodes
22 from rhodecode.lib.vcs import nodes
24 from rhodecode.lib.vcs.backends.base import EmptyCommit
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 from rhodecode.tests.utils import commit_change
25 from rhodecode.tests.utils import commit_change
27 from rhodecode.tests.routes import route_path
26 from rhodecode.tests.routes import route_path
28
27
@@ -166,14 +165,15 class TestSideBySideDiff(object):
166 response.mustcontain('Collapse 2 commits')
165 response.mustcontain('Collapse 2 commits')
167 response.mustcontain('123 file changed')
166 response.mustcontain('123 file changed')
168
167
169 response.mustcontain(
168 response.mustcontain(f'r{commit1.idx}:{commit1.short_id}...r{commit2.idx}:{commit2.short_id}')
170 'r%s:%s...r%s:%s' % (
171 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
172
169
173 response.mustcontain(f_path)
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 def test_diff_side_by_side_from_0_commit_with_file_filter(self, app, backend, backend_stub):
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 f_path = b'test_sidebyside_file.py'
177 f_path = b'test_sidebyside_file.py'
178 commit1_content = b'content-25d7e49c18b159446c\n'
178 commit1_content = b'content-25d7e49c18b159446c\n'
179 commit2_content = b'content-603d6c72c46d953420\n'
179 commit2_content = b'content-603d6c72c46d953420\n'
@@ -200,9 +200,7 class TestSideBySideDiff(object):
200 response.mustcontain('Collapse 2 commits')
200 response.mustcontain('Collapse 2 commits')
201 response.mustcontain('1 file changed')
201 response.mustcontain('1 file changed')
202
202
203 response.mustcontain(
203 response.mustcontain(f'r{commit1.idx}:{commit1.short_id}...r{commit2.idx}:{commit2.short_id}')
204 'r%s:%s...r%s:%s' % (
205 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
206
204
207 response.mustcontain(f_path)
205 response.mustcontain(f_path)
208
206
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.model.db import Session, Repository
32 from rhodecode.model.db import Session, Repository
34
33
35 from rhodecode.tests import assert_session_flash
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 from rhodecode.tests.routes import route_path
36 from rhodecode.tests.routes import route_path
38
37
39
38
@@ -42,207 +41,171 fixture = Fixture()
42
41
43 def get_node_history(backend_type):
42 def get_node_history(backend_type):
44 return {
43 return {
45 'hg': json.loads(fixture.load_resource('hg_node_history_response.json')),
44 "hg": json.loads(fixture.load_resource("hg_node_history_response.json")),
46 'git': json.loads(fixture.load_resource('git_node_history_response.json')),
45 "git": json.loads(fixture.load_resource("git_node_history_response.json")),
47 'svn': json.loads(fixture.load_resource('svn_node_history_response.json')),
46 "svn": json.loads(fixture.load_resource("svn_node_history_response.json")),
48 }[backend_type]
47 }[backend_type]
49
48
50
49
51 def assert_files_in_response(response, files, params):
50 def assert_files_in_response(response, files, params):
52 template = (
51 template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"'
53 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
54 _assert_items_in_response(response, files, template, params)
52 _assert_items_in_response(response, files, template, params)
55
53
56
54
57 def assert_dirs_in_response(response, dirs, params):
55 def assert_dirs_in_response(response, dirs, params):
58 template = (
56 template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"'
59 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
60 _assert_items_in_response(response, dirs, template, params)
57 _assert_items_in_response(response, dirs, template, params)
61
58
62
59
63 def _assert_items_in_response(response, items, template, params):
60 def _assert_items_in_response(response, items, template, params):
64 for item in items:
61 for item in items:
65 item_params = {'name': item}
62 item_params = {"name": item}
66 item_params.update(params)
63 item_params.update(params)
67 response.mustcontain(template % item_params)
64 response.mustcontain(template % item_params)
68
65
69
66
70 def assert_timeago_in_response(response, items, params):
67 def assert_timeago_in_response(response, items, params):
71 for item in items:
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 @pytest.mark.usefixtures("app")
72 @pytest.mark.usefixtures("app")
76 class TestFilesViews(object):
73 class TestFilesViews(object):
77
78 def test_show_files(self, backend):
74 def test_show_files(self, backend):
79 response = self.app.get(
75 response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=""))
80 route_path('repo_files',
81 repo_name=backend.repo_name,
82 commit_id='tip', f_path='/'))
83 commit = backend.repo.get_commit()
76 commit = backend.repo.get_commit()
84
77
85 params = {
78 params = {"repo_name": backend.repo_name, "commit_id": commit.raw_id, "date": commit.date}
86 'repo_name': backend.repo_name,
79 assert_dirs_in_response(response, ["docs", "vcs"], params)
87 'commit_id': commit.raw_id,
88 'date': commit.date
89 }
90 assert_dirs_in_response(response, ['docs', 'vcs'], params)
91 files = [
80 files = [
92 '.gitignore',
81 ".gitignore",
93 '.hgignore',
82 ".hgignore",
94 '.hgtags',
83 ".hgtags",
95 # TODO: missing in Git
84 # TODO: missing in Git
96 # '.travis.yml',
85 # '.travis.yml',
97 'MANIFEST.in',
86 "MANIFEST.in",
98 'README.rst',
87 "README.rst",
99 # TODO: File is missing in svn repository
88 # TODO: File is missing in svn repository
100 # 'run_test_and_report.sh',
89 # 'run_test_and_report.sh',
101 'setup.cfg',
90 "setup.cfg",
102 'setup.py',
91 "setup.py",
103 'test_and_report.sh',
92 "test_and_report.sh",
104 'tox.ini',
93 "tox.ini",
105 ]
94 ]
106 assert_files_in_response(response, files, params)
95 assert_files_in_response(response, files, params)
107 assert_timeago_in_response(response, files, params)
96 assert_timeago_in_response(response, files, params)
108
97
109 def test_show_files_links_submodules_with_absolute_url(self, backend_hg):
98 def test_show_files_links_submodules_with_absolute_url(self, backend_hg):
110 repo = backend_hg['subrepos']
99 repo = backend_hg["subrepos"]
111 response = self.app.get(
100 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
112 route_path('repo_files',
113 repo_name=repo.repo_name,
114 commit_id='tip', f_path='/'))
115 assert_response = response.assert_response()
101 assert_response = response.assert_response()
116 assert_response.contains_one_link(
102 assert_response.contains_one_link("absolute-path @ 000000000000", "http://example.com/absolute-path")
117 'absolute-path @ 000000000000', 'http://example.com/absolute-path')
118
103
119 def test_show_files_links_submodules_with_absolute_url_subpaths(
104 def test_show_files_links_submodules_with_absolute_url_subpaths(self, backend_hg):
120 self, backend_hg):
105 repo = backend_hg["subrepos"]
121 repo = backend_hg['subrepos']
106 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
122 response = self.app.get(
123 route_path('repo_files',
124 repo_name=repo.repo_name,
125 commit_id='tip', f_path='/'))
126 assert_response = response.assert_response()
107 assert_response = response.assert_response()
127 assert_response.contains_one_link(
108 assert_response.contains_one_link("subpaths-path @ 000000000000", "http://sub-base.example.com/subpaths-path")
128 'subpaths-path @ 000000000000',
129 'http://sub-base.example.com/subpaths-path')
130
109
131 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
110 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
132 def test_files_menu(self, backend):
111 def test_files_menu(self, backend):
133 new_branch = "temp_branch_name"
112 new_branch = "temp_branch_name"
134 commits = [
113 commits = [{"message": "a"}, {"message": "b", "branch": new_branch}]
135 {'message': 'a'},
136 {'message': 'b', 'branch': new_branch}
137 ]
138 backend.create_repo(commits)
114 backend.create_repo(commits)
139 backend.repo.landing_rev = f"branch:{new_branch}"
115 backend.repo.landing_rev = f"branch:{new_branch}"
140 Session().commit()
116 Session().commit()
141
117
142 # get response based on tip and not new commit
118 # get response based on tip and not new commit
143 response = self.app.get(
119 response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=""))
144 route_path('repo_files',
145 repo_name=backend.repo_name,
146 commit_id='tip', f_path='/'))
147
120
148 # make sure Files menu url is not tip but new commit
121 # make sure Files menu url is not tip but new commit
149 landing_rev = backend.repo.landing_ref_name
122 landing_rev = backend.repo.landing_ref_name
150 files_url = route_path('repo_files:default_path',
123 files_url = route_path(
151 repo_name=backend.repo_name,
124 "repo_files:default_path", repo_name=backend.repo_name, commit_id=landing_rev, params={"at": landing_rev}
152 commit_id=landing_rev, params={'at': landing_rev})
125 )
153
126
154 assert landing_rev != 'tip'
127 assert landing_rev != "tip"
155 response.mustcontain(f'<li class="active"><a class="menulink" href="{files_url}">')
128 response.mustcontain(f'<li class="active"><a class="menulink" href="{files_url}">')
156
129
157 def test_show_files_commit(self, backend):
130 def test_show_files_commit(self, backend):
158 commit = backend.repo.get_commit(commit_idx=32)
131 commit = backend.repo.get_commit(commit_idx=32)
159
132
160 response = self.app.get(
133 response = self.app.get(
161 route_path('repo_files',
134 route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="")
162 repo_name=backend.repo_name,
135 )
163 commit_id=commit.raw_id, f_path='/'))
164
136
165 dirs = ['docs', 'tests']
137 dirs = ["docs", "tests"]
166 files = ['README.rst']
138 files = ["README.rst"]
167 params = {
139 params = {
168 'repo_name': backend.repo_name,
140 "repo_name": backend.repo_name,
169 'commit_id': commit.raw_id,
141 "commit_id": commit.raw_id,
170 }
142 }
171 assert_dirs_in_response(response, dirs, params)
143 assert_dirs_in_response(response, dirs, params)
172 assert_files_in_response(response, files, params)
144 assert_files_in_response(response, files, params)
173
145
174 def test_show_files_different_branch(self, backend):
146 def test_show_files_different_branch(self, backend):
175 branches = dict(
147 branches = dict(
176 hg=(150, ['git']),
148 hg=(150, ["git"]),
177 # TODO: Git test repository does not contain other branches
149 # TODO: Git test repository does not contain other branches
178 git=(633, ['master']),
150 git=(633, ["master"]),
179 # TODO: Branch support in Subversion
151 # TODO: Branch support in Subversion
180 svn=(150, [])
152 svn=(150, []),
181 )
153 )
182 idx, branches = branches[backend.alias]
154 idx, branches = branches[backend.alias]
183 commit = backend.repo.get_commit(commit_idx=idx)
155 commit = backend.repo.get_commit(commit_idx=idx)
184 response = self.app.get(
156 response = self.app.get(
185 route_path('repo_files',
157 route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="")
186 repo_name=backend.repo_name,
158 )
187 commit_id=commit.raw_id, f_path='/'))
188
159
189 assert_response = response.assert_response()
160 assert_response = response.assert_response()
190 for branch in branches:
161 for branch in branches:
191 assert_response.element_contains('.tags .branchtag', branch)
162 assert_response.element_contains(".tags .branchtag", branch)
192
163
193 def test_show_files_paging(self, backend):
164 def test_show_files_paging(self, backend):
194 repo = backend.repo
165 repo = backend.repo
195 indexes = [73, 92, 109, 1, 0]
166 indexes = [73, 92, 109, 1, 0]
196 idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id)
167 idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id) for rev in indexes]
197 for rev in indexes]
198
168
199 for idx in idx_map:
169 for idx in idx_map:
200 response = self.app.get(
170 response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id=idx[1], f_path=""))
201 route_path('repo_files',
202 repo_name=backend.repo_name,
203 commit_id=idx[1], f_path='/'))
204
171
205 response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8]))
172 response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8]))
206
173
207 def test_file_source(self, backend):
174 def test_file_source(self, backend):
208 commit = backend.repo.get_commit(commit_idx=167)
175 commit = backend.repo.get_commit(commit_idx=167)
209 response = self.app.get(
176 response = self.app.get(
210 route_path('repo_files',
177 route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py")
211 repo_name=backend.repo_name,
178 )
212 commit_id=commit.raw_id, f_path='vcs/nodes.py'))
213
179
214 msgbox = """<div class="commit">%s</div>"""
180 msgbox = """<div class="commit">%s</div>"""
215 response.mustcontain(msgbox % (commit.message, ))
181 response.mustcontain(msgbox % (commit.message,))
216
182
217 assert_response = response.assert_response()
183 assert_response = response.assert_response()
218 if commit.branch:
184 if commit.branch:
219 assert_response.element_contains(
185 assert_response.element_contains(".tags.tags-main .branchtag", commit.branch)
220 '.tags.tags-main .branchtag', commit.branch)
221 if commit.tags:
186 if commit.tags:
222 for tag in commit.tags:
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 def test_file_source_annotated(self, backend):
190 def test_file_source_annotated(self, backend):
226 response = self.app.get(
191 response = self.app.get(
227 route_path('repo_files:annotated',
192 route_path("repo_files:annotated", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py")
228 repo_name=backend.repo_name,
193 )
229 commit_id='tip', f_path='vcs/nodes.py'))
230 expected_commits = {
194 expected_commits = {
231 'hg': 'r356',
195 "hg": "r356",
232 'git': 'r345',
196 "git": "r345",
233 'svn': 'r208',
197 "svn": "r208",
234 }
198 }
235 response.mustcontain(expected_commits[backend.alias])
199 response.mustcontain(expected_commits[backend.alias])
236
200
237 def test_file_source_authors(self, backend):
201 def test_file_source_authors(self, backend):
238 response = self.app.get(
202 response = self.app.get(
239 route_path('repo_file_authors',
203 route_path("repo_file_authors", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py")
240 repo_name=backend.repo_name,
204 )
241 commit_id='tip', f_path='vcs/nodes.py'))
242 expected_authors = {
205 expected_authors = {
243 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
206 "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"),
244 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
207 "git": ("Marcin Kuzminski", "Lukasz Balcerzak"),
245 'svn': ('marcin', 'lukasz'),
208 "svn": ("marcin", "lukasz"),
246 }
209 }
247
210
248 for author in expected_authors[backend.alias]:
211 for author in expected_authors[backend.alias]:
@@ -250,14 +213,18 class TestFilesViews(object):
250
213
251 def test_file_source_authors_with_annotation(self, backend):
214 def test_file_source_authors_with_annotation(self, backend):
252 response = self.app.get(
215 response = self.app.get(
253 route_path('repo_file_authors',
216 route_path(
254 repo_name=backend.repo_name,
217 "repo_file_authors",
255 commit_id='tip', f_path='vcs/nodes.py',
218 repo_name=backend.repo_name,
256 params=dict(annotate=1)))
219 commit_id="tip",
220 f_path="vcs/nodes.py",
221 params=dict(annotate=1),
222 )
223 )
257 expected_authors = {
224 expected_authors = {
258 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
225 "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"),
259 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
226 "git": ("Marcin Kuzminski", "Lukasz Balcerzak"),
260 'svn': ('marcin', 'lukasz'),
227 "svn": ("marcin", "lukasz"),
261 }
228 }
262
229
263 for author in expected_authors[backend.alias]:
230 for author in expected_authors[backend.alias]:
@@ -265,94 +232,88 class TestFilesViews(object):
265
232
266 def test_file_source_history(self, backend, xhr_header):
233 def test_file_source_history(self, backend, xhr_header):
267 response = self.app.get(
234 response = self.app.get(
268 route_path('repo_file_history',
235 route_path("repo_file_history", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py"),
269 repo_name=backend.repo_name,
236 extra_environ=xhr_header,
270 commit_id='tip', f_path='vcs/nodes.py'),
237 )
271 extra_environ=xhr_header)
272 assert get_node_history(backend.alias) == json.loads(response.body)
238 assert get_node_history(backend.alias) == json.loads(response.body)
273
239
274 def test_file_source_history_svn(self, backend_svn, xhr_header):
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 response = self.app.get(
242 response = self.app.get(
277 route_path('repo_file_history',
243 route_path(
278 repo_name=simple_repo.repo_name,
244 "repo_file_history", repo_name=simple_repo.repo_name, commit_id="tip", f_path="trunk/example.py"
279 commit_id='tip', f_path='trunk/example.py'),
245 ),
280 extra_environ=xhr_header)
246 extra_environ=xhr_header,
247 )
281
248
282 expected_data = json.loads(
249 expected_data = json.loads(fixture.load_resource("svn_node_history_branches.json"))
283 fixture.load_resource('svn_node_history_branches.json'))
284
250
285 assert expected_data == response.json
251 assert expected_data == response.json
286
252
287 def test_file_source_history_with_annotation(self, backend, xhr_header):
253 def test_file_source_history_with_annotation(self, backend, xhr_header):
288 response = self.app.get(
254 response = self.app.get(
289 route_path('repo_file_history',
255 route_path(
290 repo_name=backend.repo_name,
256 "repo_file_history",
291 commit_id='tip', f_path='vcs/nodes.py',
257 repo_name=backend.repo_name,
292 params=dict(annotate=1)),
258 commit_id="tip",
293
259 f_path="vcs/nodes.py",
294 extra_environ=xhr_header)
260 params=dict(annotate=1),
261 ),
262 extra_environ=xhr_header,
263 )
295 assert get_node_history(backend.alias) == json.loads(response.body)
264 assert get_node_history(backend.alias) == json.loads(response.body)
296
265
297 def test_tree_search_top_level(self, backend, xhr_header):
266 def test_tree_search_top_level(self, backend, xhr_header):
298 commit = backend.repo.get_commit(commit_idx=173)
267 commit = backend.repo.get_commit(commit_idx=173)
299 response = self.app.get(
268 response = self.app.get(
300 route_path('repo_files_nodelist',
269 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""),
301 repo_name=backend.repo_name,
270 extra_environ=xhr_header,
302 commit_id=commit.raw_id, f_path='/'),
271 )
303 extra_environ=xhr_header)
272 assert "nodes" in response.json
304 assert 'nodes' in response.json
273 assert {"name": "docs", "type": "dir"} in response.json["nodes"]
305 assert {'name': 'docs', 'type': 'dir'} in response.json['nodes']
306
274
307 def test_tree_search_missing_xhr(self, backend):
275 def test_tree_search_missing_xhr(self, backend):
308 self.app.get(
276 self.app.get(
309 route_path('repo_files_nodelist',
277 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404
310 repo_name=backend.repo_name,
278 )
311 commit_id='tip', f_path='/'),
312 status=404)
313
279
314 def test_tree_search_at_path(self, backend, xhr_header):
280 def test_tree_search_at_path(self, backend, xhr_header):
315 commit = backend.repo.get_commit(commit_idx=173)
281 commit = backend.repo.get_commit(commit_idx=173)
316 response = self.app.get(
282 response = self.app.get(
317 route_path('repo_files_nodelist',
283 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs"),
318 repo_name=backend.repo_name,
284 extra_environ=xhr_header,
319 commit_id=commit.raw_id, f_path='/docs'),
285 )
320 extra_environ=xhr_header)
286 assert "nodes" in response.json
321 assert 'nodes' in response.json
287 nodes = response.json["nodes"]
322 nodes = response.json['nodes']
288 assert {"name": "docs/api", "type": "dir"} in nodes
323 assert {'name': 'docs/api', 'type': 'dir'} in nodes
289 assert {"name": "docs/index.rst", "type": "file"} in nodes
324 assert {'name': 'docs/index.rst', 'type': 'file'} in nodes
325
290
326 def test_tree_search_at_path_2nd_level(self, backend, xhr_header):
291 def test_tree_search_at_path_2nd_level(self, backend, xhr_header):
327 commit = backend.repo.get_commit(commit_idx=173)
292 commit = backend.repo.get_commit(commit_idx=173)
328 response = self.app.get(
293 response = self.app.get(
329 route_path('repo_files_nodelist',
294 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs/api"),
330 repo_name=backend.repo_name,
295 extra_environ=xhr_header,
331 commit_id=commit.raw_id, f_path='/docs/api'),
296 )
332 extra_environ=xhr_header)
297 assert "nodes" in response.json
333 assert 'nodes' in response.json
298 nodes = response.json["nodes"]
334 nodes = response.json['nodes']
299 assert {"name": "docs/api/index.rst", "type": "file"} in nodes
335 assert {'name': 'docs/api/index.rst', 'type': 'file'} in nodes
336
300
337 def test_tree_search_at_path_missing_xhr(self, backend):
301 def test_tree_search_at_path_missing_xhr(self, backend):
338 self.app.get(
302 self.app.get(
339 route_path('repo_files_nodelist',
303 route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path="/docs"), status=404
340 repo_name=backend.repo_name,
304 )
341 commit_id='tip', f_path='/docs'),
342 status=404)
343
305
344 def test_nodetree(self, backend, xhr_header):
306 def test_nodetree(self, backend, xhr_header):
345 commit = backend.repo.get_commit(commit_idx=173)
307 commit = backend.repo.get_commit(commit_idx=173)
346 response = self.app.get(
308 response = self.app.get(
347 route_path('repo_nodetree_full',
309 route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""),
348 repo_name=backend.repo_name,
310 extra_environ=xhr_header,
349 commit_id=commit.raw_id, f_path='/'),
311 )
350 extra_environ=xhr_header)
351
312
352 assert_response = response.assert_response()
313 assert_response = response.assert_response()
353
314
354 for attr in ['data-commit-id', 'data-date', 'data-author']:
315 for attr in ["data-commit-id", "data-date", "data-author"]:
355 elements = assert_response.get_elements('[{}]'.format(attr))
316 elements = assert_response.get_elements("[{}]".format(attr))
356 assert len(elements) > 1
317 assert len(elements) > 1
357
318
358 for element in elements:
319 for element in elements:
@@ -361,124 +322,113 class TestFilesViews(object):
361 def test_nodetree_if_file(self, backend, xhr_header):
322 def test_nodetree_if_file(self, backend, xhr_header):
362 commit = backend.repo.get_commit(commit_idx=173)
323 commit = backend.repo.get_commit(commit_idx=173)
363 response = self.app.get(
324 response = self.app.get(
364 route_path('repo_nodetree_full',
325 route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="README.rst"),
365 repo_name=backend.repo_name,
326 extra_environ=xhr_header,
366 commit_id=commit.raw_id, f_path='README.rst'),
327 )
367 extra_environ=xhr_header)
328 assert response.text == ""
368 assert response.text == ''
369
329
370 def test_nodetree_wrong_path(self, backend, xhr_header):
330 def test_nodetree_wrong_path(self, backend, xhr_header):
371 commit = backend.repo.get_commit(commit_idx=173)
331 commit = backend.repo.get_commit(commit_idx=173)
372 response = self.app.get(
332 response = self.app.get(
373 route_path('repo_nodetree_full',
333 route_path(
374 repo_name=backend.repo_name,
334 "repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/dont-exist"
375 commit_id=commit.raw_id, f_path='/dont-exist'),
335 ),
376 extra_environ=xhr_header)
336 extra_environ=xhr_header,
337 )
377
338
378 err = 'error: There is no file nor ' \
339 err = "error: There is no file nor " "directory at the given path"
379 'directory at the given path'
380 assert err in response.text
340 assert err in response.text
381
341
382 def test_nodetree_missing_xhr(self, backend):
342 def test_nodetree_missing_xhr(self, backend):
383 self.app.get(
343 self.app.get(
384 route_path('repo_nodetree_full',
344 route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404
385 repo_name=backend.repo_name,
345 )
386 commit_id='tip', f_path='/'),
387 status=404)
388
346
389
347
390 @pytest.mark.usefixtures("app", "autologin_user")
348 @pytest.mark.usefixtures("app", "autologin_user")
391 class TestRawFileHandling(object):
349 class TestRawFileHandling(object):
392
393 def test_download_file(self, backend):
350 def test_download_file(self, backend):
394 commit = backend.repo.get_commit(commit_idx=173)
351 commit = backend.repo.get_commit(commit_idx=173)
395 response = self.app.get(
352 response = self.app.get(
396 route_path('repo_file_download',
353 route_path(
397 repo_name=backend.repo_name,
354 "repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py"
398 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 assert response.content_type == "text/x-python"
359 assert response.content_type == "text/x-python"
402
360
403 def test_download_file_wrong_cs(self, backend):
361 def test_download_file_wrong_cs(self, backend):
404 raw_id = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
362 raw_id = "ERRORce30c96924232dffcd24178a07ffeb5dfc"
405
363
406 response = self.app.get(
364 response = self.app.get(
407 route_path('repo_file_download',
365 route_path("repo_file_download", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"),
408 repo_name=backend.repo_name,
366 status=404,
409 commit_id=raw_id, f_path='vcs/nodes.svg'),
367 )
410 status=404)
411
368
412 msg = """No such commit exists for this repository"""
369 msg = """No such commit exists for this repository"""
413 response.mustcontain(msg)
370 response.mustcontain(msg)
414
371
415 def test_download_file_wrong_f_path(self, backend):
372 def test_download_file_wrong_f_path(self, backend):
416 commit = backend.repo.get_commit(commit_idx=173)
373 commit = backend.repo.get_commit(commit_idx=173)
417 f_path = 'vcs/ERRORnodes.py'
374 f_path = "vcs/ERRORnodes.py"
418
375
419 response = self.app.get(
376 response = self.app.get(
420 route_path('repo_file_download',
377 route_path("repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path),
421 repo_name=backend.repo_name,
378 status=404,
422 commit_id=commit.raw_id, f_path=f_path),
379 )
423 status=404)
424
380
425 msg = (
381 msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id)
426 "There is no file nor directory at the given path: "
427 "`%s` at commit %s" % (f_path, commit.short_id))
428 response.mustcontain(msg)
382 response.mustcontain(msg)
429
383
430 def test_file_raw(self, backend):
384 def test_file_raw(self, backend):
431 commit = backend.repo.get_commit(commit_idx=173)
385 commit = backend.repo.get_commit(commit_idx=173)
432 response = self.app.get(
386 response = self.app.get(
433 route_path('repo_file_raw',
387 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py"),
434 repo_name=backend.repo_name,
388 )
435 commit_id=commit.raw_id, f_path='vcs/nodes.py'),)
436
389
437 assert response.content_type == "text/plain"
390 assert response.content_type == "text/plain"
438
391
439 def test_file_raw_binary(self, backend):
392 def test_file_raw_binary(self, backend):
440 commit = backend.repo.get_commit()
393 commit = backend.repo.get_commit()
441 response = self.app.get(
394 response = self.app.get(
442 route_path('repo_file_raw',
395 route_path(
443 repo_name=backend.repo_name,
396 "repo_file_raw",
444 commit_id=commit.raw_id,
397 repo_name=backend.repo_name,
445 f_path='docs/theme/ADC/static/breadcrumb_background.png'),)
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 def test_raw_file_wrong_cs(self, backend):
405 def test_raw_file_wrong_cs(self, backend):
450 raw_id = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
406 raw_id = "ERRORcce30c96924232dffcd24178a07ffeb5dfc"
451
407
452 response = self.app.get(
408 response = self.app.get(
453 route_path('repo_file_raw',
409 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"),
454 repo_name=backend.repo_name,
410 status=404,
455 commit_id=raw_id, f_path='vcs/nodes.svg'),
411 )
456 status=404)
457
412
458 msg = """No such commit exists for this repository"""
413 msg = """No such commit exists for this repository"""
459 response.mustcontain(msg)
414 response.mustcontain(msg)
460
415
461 def test_raw_wrong_f_path(self, backend):
416 def test_raw_wrong_f_path(self, backend):
462 commit = backend.repo.get_commit(commit_idx=173)
417 commit = backend.repo.get_commit(commit_idx=173)
463 f_path = 'vcs/ERRORnodes.py'
418 f_path = "vcs/ERRORnodes.py"
464 response = self.app.get(
419 response = self.app.get(
465 route_path('repo_file_raw',
420 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path), status=404
466 repo_name=backend.repo_name,
421 )
467 commit_id=commit.raw_id, f_path=f_path),
468 status=404)
469
422
470 msg = (
423 msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id)
471 "There is no file nor directory at the given path: "
472 "`%s` at commit %s" % (f_path, commit.short_id))
473 response.mustcontain(msg)
424 response.mustcontain(msg)
474
425
475 def test_raw_svg_should_not_be_rendered(self, backend):
426 def test_raw_svg_should_not_be_rendered(self, backend):
476 backend.create_repo()
427 backend.create_repo()
477 backend.ensure_file(b"xss.svg")
428 backend.ensure_file(b"xss.svg")
478 response = self.app.get(
429 response = self.app.get(
479 route_path('repo_file_raw',
430 route_path("repo_file_raw", repo_name=backend.repo_name, commit_id="tip", f_path="xss.svg"),
480 repo_name=backend.repo_name,
431 )
481 commit_id='tip', f_path='xss.svg'),)
482 # If the content type is image/svg+xml then it allows to render HTML
432 # If the content type is image/svg+xml then it allows to render HTML
483 # and malicious SVG.
433 # and malicious SVG.
484 assert response.content_type == "text/plain"
434 assert response.content_type == "text/plain"
@@ -486,25 +436,23 class TestRawFileHandling(object):
486
436
487 @pytest.mark.usefixtures("app")
437 @pytest.mark.usefixtures("app")
488 class TestRepositoryArchival(object):
438 class TestRepositoryArchival(object):
489
490 def test_archival(self, backend):
439 def test_archival(self, backend):
491 backend.enable_downloads()
440 backend.enable_downloads()
492 commit = backend.repo.get_commit(commit_idx=173)
441 commit = backend.repo.get_commit(commit_idx=173)
493
442
494 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
443 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
495 path_sha = get_path_sha('/')
444 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)
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 fname = commit.raw_id + extension
449 fname = commit.raw_id + extension
499 response = self.app.get(
450 response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname))
500 route_path('repo_archivefile',
501 repo_name=backend.repo_name,
502 fname=fname))
503
451
504 assert response.status == '200 OK'
452 assert response.status == "200 OK"
505 headers = [
453 headers = [
506 ('Content-Disposition', f'attachment; filename={filename}'),
454 ("Content-Disposition", f"attachment; filename={filename}"),
507 ('Content-Type', content_type),
455 ("Content-Type", content_type),
508 ]
456 ]
509
457
510 for header in headers:
458 for header in headers:
@@ -514,19 +462,25 class TestRepositoryArchival(object):
514 backend.enable_downloads()
462 backend.enable_downloads()
515 commit = backend.repo.get_commit(commit_idx=173)
463 commit = backend.repo.get_commit(commit_idx=173)
516 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
464 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
517 path_sha = get_path_sha('/')
465 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)
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 fname = commit.raw_id + extension
475 fname = commit.raw_id + extension
521 response = self.app.get(
476 response = self.app.get(
522 route_path('repo_archivefile',
477 route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"with_hash": 0})
523 repo_name=backend.repo_name,
478 )
524 fname=fname, params={'with_hash': 0}))
525
479
526 assert response.status == '200 OK'
480 assert response.status == "200 OK"
527 headers = [
481 headers = [
528 ('Content-Disposition', f'attachment; filename={filename}'),
482 ("Content-Disposition", f"attachment; filename={filename}"),
529 ('Content-Type', content_type),
483 ("Content-Type", content_type),
530 ]
484 ]
531
485
532 for header in headers:
486 for header in headers:
@@ -535,359 +489,332 class TestRepositoryArchival(object):
535 def test_archival_at_path(self, backend):
489 def test_archival_at_path(self, backend):
536 backend.enable_downloads()
490 backend.enable_downloads()
537 commit = backend.repo.get_commit(commit_idx=190)
491 commit = backend.repo.get_commit(commit_idx=190)
538 at_path = 'vcs'
492 at_path = "vcs"
539
493
540 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
494 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
541 path_sha = get_path_sha(at_path)
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 fname = commit.raw_id + extension
500 fname = commit.raw_id + extension
545 response = self.app.get(
501 response = self.app.get(
546 route_path('repo_archivefile',
502 route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"at_path": at_path})
547 repo_name=backend.repo_name,
503 )
548 fname=fname, params={'at_path': at_path}))
549
504
550 assert response.status == '200 OK'
505 assert response.status == "200 OK"
551 headers = [
506 headers = [
552 ('Content-Disposition', f'attachment; filename={filename}'),
507 ("Content-Disposition", f"attachment; filename={filename}"),
553 ('Content-Type', content_type),
508 ("Content-Type", content_type),
554 ]
509 ]
555
510
556 for header in headers:
511 for header in headers:
557 assert header in list(response.headers.items())
512 assert header in list(response.headers.items())
558
513
559 @pytest.mark.parametrize('arch_ext',[
514 @pytest.mark.parametrize("arch_ext", ["tar", "rar", "x", "..ax", ".zipz", "tar.gz.tar"])
560 'tar', 'rar', 'x', '..ax', '.zipz', 'tar.gz.tar'])
561 def test_archival_wrong_ext(self, backend, arch_ext):
515 def test_archival_wrong_ext(self, backend, arch_ext):
562 backend.enable_downloads()
516 backend.enable_downloads()
563 commit = backend.repo.get_commit(commit_idx=173)
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(
521 response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname))
568 route_path('repo_archivefile',
522 response.mustcontain("Unknown archive type for: `{}`".format(fname))
569 repo_name=backend.repo_name,
570 fname=fname))
571 response.mustcontain(
572 'Unknown archive type for: `{}`'.format(fname))
573
523
574 @pytest.mark.parametrize('commit_id', [
524 @pytest.mark.parametrize("commit_id", ["00x000000", "tar", "wrong", "@$@$42413232", "232dffcd"])
575 '00x000000', 'tar', 'wrong', '@$@$42413232', '232dffcd'])
576 def test_archival_wrong_commit_id(self, backend, commit_id):
525 def test_archival_wrong_commit_id(self, backend, commit_id):
577 backend.enable_downloads()
526 backend.enable_downloads()
578 fname = f'{commit_id}.zip'
527 fname = f"{commit_id}.zip"
579
528
580 response = self.app.get(
529 response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname))
581 route_path('repo_archivefile',
530 response.mustcontain("Unknown commit_id")
582 repo_name=backend.repo_name,
583 fname=fname))
584 response.mustcontain('Unknown commit_id')
585
531
586
532
587 @pytest.mark.usefixtures("app")
533 @pytest.mark.usefixtures("app")
588 class TestFilesDiff(object):
534 class TestFilesDiff(object):
589
535 @pytest.mark.parametrize("diff", ["diff", "download", "raw"])
590 @pytest.mark.parametrize("diff", ['diff', 'download', 'raw'])
591 def test_file_full_diff(self, backend, diff):
536 def test_file_full_diff(self, backend, diff):
592 commit1 = backend.repo.get_commit(commit_idx=-1)
537 commit1 = backend.repo.get_commit(commit_idx=-1)
593 commit2 = backend.repo.get_commit(commit_idx=-2)
538 commit2 = backend.repo.get_commit(commit_idx=-2)
594
539
595 response = self.app.get(
540 response = self.app.get(
596 route_path('repo_files_diff',
541 route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README"),
597 repo_name=backend.repo_name,
598 f_path='README'),
599 params={
542 params={
600 'diff1': commit2.raw_id,
543 "diff1": commit2.raw_id,
601 'diff2': commit1.raw_id,
544 "diff2": commit1.raw_id,
602 'fulldiff': '1',
545 "fulldiff": "1",
603 'diff': diff,
546 "diff": diff,
604 })
547 },
548 )
605
549
606 if diff == 'diff':
550 if diff == "diff":
607 # use redirect since this is OLD view redirecting to compare page
551 # use redirect since this is OLD view redirecting to compare page
608 response = response.follow()
552 response = response.follow()
609
553
610 # It's a symlink to README.rst
554 # It's a symlink to README.rst
611 response.mustcontain('README.rst')
555 response.mustcontain("README.rst")
612 response.mustcontain('No newline at end of file')
556 response.mustcontain("No newline at end of file")
613
557
614 def test_file_binary_diff(self, backend):
558 def test_file_binary_diff(self, backend):
615 commits = [
559 commits = [
616 {'message': 'First commit'},
560 {"message": "First commit"},
617 {'message': 'Commit with binary',
561 {"message": "Commit with binary", "added": [nodes.FileNode(b"file.bin", content="\0BINARY\0")]},
618 'added': [nodes.FileNode(b'file.bin', content='\0BINARY\0')]},
619 ]
562 ]
620 repo = backend.create_repo(commits=commits)
563 repo = backend.create_repo(commits=commits)
621
564
622 response = self.app.get(
565 response = self.app.get(
623 route_path('repo_files_diff',
566 route_path("repo_files_diff", repo_name=backend.repo_name, f_path="file.bin"),
624 repo_name=backend.repo_name,
625 f_path='file.bin'),
626 params={
567 params={
627 'diff1': repo.get_commit(commit_idx=0).raw_id,
568 "diff1": repo.get_commit(commit_idx=0).raw_id,
628 'diff2': repo.get_commit(commit_idx=1).raw_id,
569 "diff2": repo.get_commit(commit_idx=1).raw_id,
629 'fulldiff': '1',
570 "fulldiff": "1",
630 'diff': 'diff',
571 "diff": "diff",
631 })
572 },
573 )
632 # use redirect since this is OLD view redirecting to compare page
574 # use redirect since this is OLD view redirecting to compare page
633 response = response.follow()
575 response = response.follow()
634 response.mustcontain('Collapse 1 commit')
576 response.mustcontain("Collapse 1 commit")
635 file_changes = (1, 0, 0)
577 file_changes = (1, 0, 0)
636
578
637 compare_page = ComparePage(response)
579 compare_page = ComparePage(response)
638 compare_page.contains_change_summary(*file_changes)
580 compare_page.contains_change_summary(*file_changes)
639
581
640 if backend.alias == 'svn':
582 if backend.alias == "svn":
641 response.mustcontain('new file 10644')
583 response.mustcontain("new file 10644")
642 # TODO(marcink): SVN doesn't yet detect binary changes
584 # TODO(marcink): SVN doesn't yet detect binary changes
643 else:
585 else:
644 response.mustcontain('new file 100644')
586 response.mustcontain("new file 100644")
645 response.mustcontain('binary diff hidden')
587 response.mustcontain("binary diff hidden")
646
588
647 def test_diff_2way(self, backend):
589 def test_diff_2way(self, backend):
648 commit1 = backend.repo.get_commit(commit_idx=-1)
590 commit1 = backend.repo.get_commit(commit_idx=-1)
649 commit2 = backend.repo.get_commit(commit_idx=-2)
591 commit2 = backend.repo.get_commit(commit_idx=-2)
650 response = self.app.get(
592 response = self.app.get(
651 route_path('repo_files_diff_2way_redirect',
593 route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"),
652 repo_name=backend.repo_name,
653 f_path='README'),
654 params={
594 params={
655 'diff1': commit2.raw_id,
595 "diff1": commit2.raw_id,
656 'diff2': commit1.raw_id,
596 "diff2": commit1.raw_id,
657 })
597 },
598 )
658 # use redirect since this is OLD view redirecting to compare page
599 # use redirect since this is OLD view redirecting to compare page
659 response = response.follow()
600 response = response.follow()
660
601
661 # It's a symlink to README.rst
602 # It's a symlink to README.rst
662 response.mustcontain('README.rst')
603 response.mustcontain("README.rst")
663 response.mustcontain('No newline at end of file')
604 response.mustcontain("No newline at end of file")
664
605
665 def test_requires_one_commit_id(self, backend, autologin_user):
606 def test_requires_one_commit_id(self, backend, autologin_user):
666 response = self.app.get(
607 response = self.app.get(
667 route_path('repo_files_diff',
608 route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README.rst"), status=400
668 repo_name=backend.repo_name,
609 )
669 f_path='README.rst'),
610 response.mustcontain("Need query parameter", "diff1", "diff2", "to generate a diff.")
670 status=400)
671 response.mustcontain(
672 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
673
611
674 def test_returns_no_files_if_file_does_not_exist(self, vcsbackend):
612 def test_returns_no_files_if_file_does_not_exist(self, vcsbackend):
675 repo = vcsbackend.repo
613 repo = vcsbackend.repo
676 response = self.app.get(
614 response = self.app.get(
677 route_path('repo_files_diff',
615 route_path("repo_files_diff", repo_name=repo.name, f_path="does-not-exist-in-any-commit"),
678 repo_name=repo.name,
616 params={"diff1": repo[0].raw_id, "diff2": repo[1].raw_id},
679 f_path='does-not-exist-in-any-commit'),
617 )
680 params={
681 'diff1': repo[0].raw_id,
682 'diff2': repo[1].raw_id
683 })
684
618
685 response = response.follow()
619 response = response.follow()
686 response.mustcontain('No files')
620 response.mustcontain("No files")
687
621
688 def test_returns_redirect_if_file_not_changed(self, backend):
622 def test_returns_redirect_if_file_not_changed(self, backend):
689 commit = backend.repo.get_commit(commit_idx=-1)
623 commit = backend.repo.get_commit(commit_idx=-1)
690 response = self.app.get(
624 response = self.app.get(
691 route_path('repo_files_diff_2way_redirect',
625 route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"),
692 repo_name=backend.repo_name,
693 f_path='README'),
694 params={
626 params={
695 'diff1': commit.raw_id,
627 "diff1": commit.raw_id,
696 'diff2': commit.raw_id,
628 "diff2": commit.raw_id,
697 })
629 },
630 )
698
631
699 response = response.follow()
632 response = response.follow()
700 response.mustcontain('No files')
633 response.mustcontain("No files")
701 response.mustcontain('No commits in this compare')
634 response.mustcontain("No commits in this compare")
702
635
703 def test_supports_diff_to_different_path_svn(self, backend_svn):
636 def test_supports_diff_to_different_path_svn(self, backend_svn):
704 #TODO: check this case
637 # TODO: check this case
705 return
638 return
706
639
707 repo = backend_svn['svn-simple-layout'].scm_instance()
640 repo = backend_svn["svn-simple-layout"].scm_instance()
708 commit_id_1 = '24'
641 commit_id_1 = "24"
709 commit_id_2 = '26'
642 commit_id_2 = "26"
710
643
711 response = self.app.get(
644 response = self.app.get(
712 route_path('repo_files_diff',
645 route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"),
713 repo_name=backend_svn.repo_name,
714 f_path='trunk/example.py'),
715 params={
646 params={
716 'diff1': 'tags/v0.2/example.py@' + commit_id_1,
647 "diff1": "tags/v0.2/example.py@" + commit_id_1,
717 'diff2': commit_id_2,
648 "diff2": commit_id_2,
718 })
649 },
650 )
719
651
720 response = response.follow()
652 response = response.follow()
721 response.mustcontain(
653 response.mustcontain(
722 # diff contains this
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 # Note: Expecting that we indicate the user what's being compared
658 # Note: Expecting that we indicate the user what's being compared
726 response.mustcontain("trunk/example.py")
659 response.mustcontain("trunk/example.py")
727 response.mustcontain("tags/v0.2/example.py")
660 response.mustcontain("tags/v0.2/example.py")
728
661
729 def test_show_rev_redirects_to_svn_path(self, backend_svn):
662 def test_show_rev_redirects_to_svn_path(self, backend_svn):
730 #TODO: check this case
663 # TODO: check this case
731 return
664 return
732
665
733 repo = backend_svn['svn-simple-layout'].scm_instance()
666 repo = backend_svn["svn-simple-layout"].scm_instance()
734 commit_id = repo[-1].raw_id
667 commit_id = repo[-1].raw_id
735
668
736 response = self.app.get(
669 response = self.app.get(
737 route_path('repo_files_diff',
670 route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"),
738 repo_name=backend_svn.repo_name,
739 f_path='trunk/example.py'),
740 params={
671 params={
741 'diff1': 'branches/argparse/example.py@' + commit_id,
672 "diff1": "branches/argparse/example.py@" + commit_id,
742 'diff2': commit_id,
673 "diff2": commit_id,
743 },
674 },
744 status=302)
675 status=302,
676 )
745 response = response.follow()
677 response = response.follow()
746 assert response.headers['Location'].endswith(
678 assert response.headers["Location"].endswith("svn-svn-simple-layout/files/26/branches/argparse/example.py")
747 'svn-svn-simple-layout/files/26/branches/argparse/example.py')
748
679
749 def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
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 return
682 return
752
683
753 repo = backend_svn['svn-simple-layout'].scm_instance()
684 repo = backend_svn["svn-simple-layout"].scm_instance()
754 commit_id = repo[-1].raw_id
685 commit_id = repo[-1].raw_id
755 response = self.app.get(
686 response = self.app.get(
756 route_path('repo_files_diff',
687 route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"),
757 repo_name=backend_svn.repo_name,
758 f_path='trunk/example.py'),
759 params={
688 params={
760 'diff1': 'branches/argparse/example.py@' + commit_id,
689 "diff1": "branches/argparse/example.py@" + commit_id,
761 'diff2': commit_id,
690 "diff2": commit_id,
762 'show_rev': 'Show at Revision',
691 "show_rev": "Show at Revision",
763 'annotate': 'true',
692 "annotate": "true",
764 },
693 },
765 status=302)
694 status=302,
695 )
766 response = response.follow()
696 response = response.follow()
767 assert response.headers['Location'].endswith(
697 assert response.headers["Location"].endswith("svn-svn-simple-layout/annotate/26/branches/argparse/example.py")
768 'svn-svn-simple-layout/annotate/26/branches/argparse/example.py')
769
698
770
699
771 @pytest.mark.usefixtures("app", "autologin_user")
700 @pytest.mark.usefixtures("app", "autologin_user")
772 class TestModifyFilesWithWebInterface(object):
701 class TestModifyFilesWithWebInterface(object):
773
774 def test_add_file_view(self, backend):
702 def test_add_file_view(self, backend):
775 self.app.get(
703 self.app.get(route_path("repo_files_add_file", repo_name=backend.repo_name, commit_id="tip", f_path=""))
776 route_path('repo_files_add_file',
777 repo_name=backend.repo_name,
778 commit_id='tip', f_path='/')
779 )
780
704
781 @pytest.mark.xfail_backends("svn", reason="Depends on online editing")
705 @pytest.mark.xfail_backends("svn", reason="Depends on online editing")
782 def test_add_file_into_repo_missing_content(self, backend, csrf_token):
706 def test_add_file_into_repo_missing_content(self, backend, csrf_token):
783 backend.create_repo()
707 backend.create_repo()
784 filename = 'init.py'
708 filename = "init.py"
785 response = self.app.post(
709 response = self.app.post(
786 route_path('repo_files_create_file',
710 route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id="tip", f_path=""),
787 repo_name=backend.repo_name,
788 commit_id='tip', f_path='/'),
789 params={
711 params={
790 'content': "",
712 "content": "",
791 'filename': filename,
713 "filename": filename,
792 'csrf_token': csrf_token,
714 "csrf_token": csrf_token,
793 },
715 },
794 status=302)
716 status=302,
795 expected_msg = 'Successfully committed new file `{}`'.format(os.path.join(filename))
717 )
718 expected_msg = "Successfully committed new file `{}`".format(os.path.join(filename))
796 assert_session_flash(response, expected_msg)
719 assert_session_flash(response, expected_msg)
797
720
798 def test_add_file_into_repo_missing_filename(self, backend, csrf_token):
721 def test_add_file_into_repo_missing_filename(self, backend, csrf_token):
799 commit_id = backend.repo.get_commit().raw_id
722 commit_id = backend.repo.get_commit().raw_id
800 response = self.app.post(
723 response = self.app.post(
801 route_path('repo_files_create_file',
724 route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id=commit_id, f_path=""),
802 repo_name=backend.repo_name,
803 commit_id=commit_id, f_path='/'),
804 params={
725 params={
805 'content': "foo",
726 "content": "foo",
806 'csrf_token': csrf_token,
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(
734 def test_add_file_into_repo_errors_and_no_commits(self, backend, csrf_token):
813 self, backend, csrf_token):
814 repo = backend.create_repo()
735 repo = backend.create_repo()
815 # Create a file with no filename, it will display an error but
736 # Create a file with no filename, it will display an error but
816 # the repo has no commits yet
737 # the repo has no commits yet
817 response = self.app.post(
738 response = self.app.post(
818 route_path('repo_files_create_file',
739 route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id="tip", f_path=""),
819 repo_name=repo.repo_name,
820 commit_id='tip', f_path='/'),
821 params={
740 params={
822 'content': "foo",
741 "content": "foo",
823 'csrf_token': csrf_token,
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 # Not allowed, redirect to the summary
749 # Not allowed, redirect to the summary
830 redirected = response.follow()
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 # As there are no commits, displays the summary page with the error of
753 # As there are no commits, displays the summary page with the error of
834 # creating a file with no filename
754 # creating a file with no filename
835
755
836 assert redirected.request.path == summary_url
756 assert redirected.request.path == summary_url
837
757
838 @pytest.mark.parametrize("filename, clean_filename", [
758 @pytest.mark.parametrize(
839 ('/abs/foo', 'abs/foo'),
759 "filename, clean_filename",
840 ('../rel/foo', 'rel/foo'),
760 [
841 ('file/../foo/foo', 'file/foo/foo'),
761 ("/abs/foo", "abs/foo"),
842 ])
762 ("../rel/foo", "rel/foo"),
763 ("file/../foo/foo", "file/foo/foo"),
764 ],
765 )
843 def test_add_file_into_repo_bad_filenames(self, filename, clean_filename, backend, csrf_token):
766 def test_add_file_into_repo_bad_filenames(self, filename, clean_filename, backend, csrf_token):
844 repo = backend.create_repo()
767 repo = backend.create_repo()
845 commit_id = repo.get_commit().raw_id
768 commit_id = repo.get_commit().raw_id
846
769
847 response = self.app.post(
770 response = self.app.post(
848 route_path('repo_files_create_file',
771 route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""),
849 repo_name=repo.repo_name,
850 commit_id=commit_id, f_path='/'),
851 params={
772 params={
852 'content': "foo",
773 "content": "foo",
853 'filename': filename,
774 "filename": filename,
854 'csrf_token': csrf_token,
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 assert_session_flash(response, expected_msg)
781 assert_session_flash(response, expected_msg)
860
782
861 @pytest.mark.parametrize("cnt, filename, content", [
783 @pytest.mark.parametrize(
862 (1, 'foo.txt', "Content"),
784 "cnt, filename, content",
863 (2, 'dir/foo.rst', "Content"),
785 [
864 (3, 'dir/foo-second.rst', "Content"),
786 (1, "foo.txt", "Content"),
865 (4, 'rel/dir/foo.bar', "Content"),
787 (2, "dir/foo.rst", "Content"),
866 ])
788 (3, "dir/foo-second.rst", "Content"),
789 (4, "rel/dir/foo.bar", "Content"),
790 ],
791 )
867 def test_add_file_into_empty_repo(self, cnt, filename, content, backend, csrf_token):
792 def test_add_file_into_empty_repo(self, cnt, filename, content, backend, csrf_token):
868 repo = backend.create_repo()
793 repo = backend.create_repo()
869 commit_id = repo.get_commit().raw_id
794 commit_id = repo.get_commit().raw_id
870 response = self.app.post(
795 response = self.app.post(
871 route_path('repo_files_create_file',
796 route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""),
872 repo_name=repo.repo_name,
873 commit_id=commit_id, f_path='/'),
874 params={
797 params={
875 'content': content,
798 "content": content,
876 'filename': filename,
799 "filename": filename,
877 'csrf_token': csrf_token,
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 assert_session_flash(response, expected_msg)
806 assert_session_flash(response, expected_msg)
883
807
884 def test_edit_file_view(self, backend):
808 def test_edit_file_view(self, backend):
885 response = self.app.get(
809 response = self.app.get(
886 route_path('repo_files_edit_file',
810 route_path(
887 repo_name=backend.repo_name,
811 "repo_files_edit_file",
888 commit_id=backend.default_head_id,
812 repo_name=backend.repo_name,
889 f_path='vcs/nodes.py'),
813 commit_id=backend.default_head_id,
890 status=200)
814 f_path="vcs/nodes.py",
815 ),
816 status=200,
817 )
891 response.mustcontain("Module holding everything related to vcs nodes.")
818 response.mustcontain("Module holding everything related to vcs nodes.")
892
819
893 def test_edit_file_view_not_on_branch(self, backend):
820 def test_edit_file_view_not_on_branch(self, backend):
@@ -895,229 +822,221 class TestModifyFilesWithWebInterface(ob
895 backend.ensure_file(b"vcs/nodes.py")
822 backend.ensure_file(b"vcs/nodes.py")
896
823
897 response = self.app.get(
824 response = self.app.get(
898 route_path('repo_files_edit_file',
825 route_path("repo_files_edit_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"),
899 repo_name=repo.repo_name,
826 status=302,
900 commit_id='tip',
827 )
901 f_path='vcs/nodes.py'),
828 assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.")
902 status=302)
903 assert_session_flash(
904 response, 'Cannot modify file. Given commit `tip` is not head of a branch.')
905
829
906 def test_edit_file_view_commit_changes(self, backend, csrf_token):
830 def test_edit_file_view_commit_changes(self, backend, csrf_token):
907 repo = backend.create_repo()
831 repo = backend.create_repo()
908 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
832 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
909
833
910 response = self.app.post(
834 response = self.app.post(
911 route_path('repo_files_update_file',
835 route_path(
912 repo_name=repo.repo_name,
836 "repo_files_update_file",
913 commit_id=backend.default_head_id,
837 repo_name=repo.repo_name,
914 f_path='vcs/nodes.py'),
838 commit_id=backend.default_head_id,
839 f_path="vcs/nodes.py",
840 ),
915 params={
841 params={
916 'content': "print 'hello world'",
842 "content": "print 'hello world'",
917 'message': 'I committed',
843 "message": "I committed",
918 'filename': "vcs/nodes.py",
844 "filename": "vcs/nodes.py",
919 'csrf_token': csrf_token,
845 "csrf_token": csrf_token,
920 },
846 },
921 status=302)
847 status=302,
922 assert_session_flash(
848 )
923 response, 'Successfully committed changes to file `vcs/nodes.py`')
849 assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`")
924 tip = repo.get_commit(commit_idx=-1)
850 tip = repo.get_commit(commit_idx=-1)
925 assert tip.message == 'I committed'
851 assert tip.message == "I committed"
926
852
927 def test_replace_binary_file_view_commit_changes(self, backend, csrf_token):
853 def test_replace_binary_file_view_commit_changes(self, backend, csrf_token):
928 repo = backend.create_repo()
854 repo = backend.create_repo()
929 backend.ensure_file(b"vcs/nodes.docx", content=b"PREVIOUS CONTENT'")
855 backend.ensure_file(b"vcs/nodes.docx", content=b"PREVIOUS CONTENT'")
930
856
931 response = self.app.post(
857 response = self.app.post(
932 route_path('repo_files_replace_binary',
858 route_path(
933 repo_name=repo.repo_name,
859 "repo_files_replace_binary",
934 commit_id=backend.default_head_id,
860 repo_name=repo.repo_name,
935 f_path='vcs/nodes.docx'),
861 commit_id=backend.default_head_id,
862 f_path="vcs/nodes.docx",
863 ),
936 params={
864 params={
937 'message': 'I committed',
865 "message": "I committed",
938 'csrf_token': csrf_token,
866 "csrf_token": csrf_token,
939 },
867 },
940 upload_files=[('files_upload', 'vcs/nodes.docx', b'SOME CONTENT')],
868 upload_files=[("files_upload", "vcs/nodes.docx", b"SOME CONTENT")],
941 status=200)
869 status=200,
942 assert_session_flash(
870 )
943 response, 'Successfully committed 1 new file')
871 assert_session_flash(response, "Successfully committed 1 new file")
944 tip = repo.get_commit(commit_idx=-1)
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,
875 def test_edit_file_view_commit_changes_default_message(self, backend, csrf_token):
948 csrf_token):
949 repo = backend.create_repo()
876 repo = backend.create_repo()
950 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
877 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
951
878
952 commit_id = (
879 commit_id = backend.default_branch_name or backend.repo.scm_instance().commit_ids[-1]
953 backend.default_branch_name or
954 backend.repo.scm_instance().commit_ids[-1])
955
880
956 response = self.app.post(
881 response = self.app.post(
957 route_path('repo_files_update_file',
882 route_path("repo_files_update_file", repo_name=repo.repo_name, commit_id=commit_id, f_path="vcs/nodes.py"),
958 repo_name=repo.repo_name,
959 commit_id=commit_id,
960 f_path='vcs/nodes.py'),
961 params={
883 params={
962 'content': "print 'hello world'",
884 "content": "print 'hello world'",
963 'message': '',
885 "message": "",
964 'filename': "vcs/nodes.py",
886 "filename": "vcs/nodes.py",
965 'csrf_token': csrf_token,
887 "csrf_token": csrf_token,
966 },
888 },
967 status=302)
889 status=302,
968 assert_session_flash(
890 )
969 response, 'Successfully committed changes to file `vcs/nodes.py`')
891 assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`")
970 tip = repo.get_commit(commit_idx=-1)
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 def test_replace_binary_file_content_with_content_that_not_belong_to_original_type(self, backend, csrf_token):
895 def test_replace_binary_file_content_with_content_that_not_belong_to_original_type(self, backend, csrf_token):
974 repo = backend.create_repo()
896 repo = backend.create_repo()
975 backend.ensure_file(b"vcs/sheet.xlsx", content=b"PREVIOUS CONTENT'")
897 backend.ensure_file(b"vcs/sheet.xlsx", content=b"PREVIOUS CONTENT'")
976
898
977 response = self.app.post(
899 response = self.app.post(
978 route_path('repo_files_replace_binary',
900 route_path(
979 repo_name=repo.repo_name,
901 "repo_files_replace_binary",
980 commit_id=backend.default_head_id,
902 repo_name=repo.repo_name,
981 f_path='vcs/sheet.xlsx'),
903 commit_id=backend.default_head_id,
904 f_path="vcs/sheet.xlsx",
905 ),
982 params={
906 params={
983 'message': 'I committed',
907 "message": "I committed",
984 'csrf_token': csrf_token,
908 "csrf_token": csrf_token,
985 },
909 },
986 upload_files=[('files_upload', 'vcs/sheet.docx', b'SOME CONTENT')],
910 upload_files=[("files_upload", "vcs/sheet.docx", b"SOME CONTENT")],
987 status=200)
911 status=200,
988 assert response.json['error'] == "file extension of uploaded file doesn't match an original file's extension"
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", [
915 @pytest.mark.parametrize(
991 ([], 'missing files'),
916 "replacement_files, expected_error",
992 (
917 [
993 [('files_upload', 'vcs/node1.docx', b'SOME CONTENT'),
918 ([], "missing files"),
994 ('files_upload', 'vcs/node2.docx', b'SOME CONTENT')],
919 (
995 'too many files for replacement'),
920 [
996 ])
921 ("files_upload", "vcs/node1.docx", b"SOME CONTENT"),
997 def test_replace_binary_with_wrong_amount_of_content_sources(self, replacement_files, expected_error, backend,
922 ("files_upload", "vcs/node2.docx", b"SOME CONTENT"),
998 csrf_token):
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 repo = backend.create_repo()
931 repo = backend.create_repo()
1000 backend.ensure_file(b"vcs/node.docx", content=b"PREVIOUS CONTENT'")
932 backend.ensure_file(b"vcs/node.docx", content=b"PREVIOUS CONTENT'")
1001
933
1002 response = self.app.post(
934 response = self.app.post(
1003 route_path('repo_files_replace_binary',
935 route_path(
1004 repo_name=repo.repo_name,
936 "repo_files_replace_binary",
1005 commit_id=backend.default_head_id,
937 repo_name=repo.repo_name,
1006 f_path='vcs/node.docx'),
938 commit_id=backend.default_head_id,
939 f_path="vcs/node.docx",
940 ),
1007 params={
941 params={
1008 'message': 'I committed',
942 "message": "I committed",
1009 'csrf_token': csrf_token,
943 "csrf_token": csrf_token,
1010 },
944 },
1011 upload_files=replacement_files,
945 upload_files=replacement_files,
1012 status=200)
946 status=200,
1013 assert response.json['error'] == expected_error
947 )
948 assert response.json["error"] == expected_error
1014
949
1015 def test_delete_file_view(self, backend):
950 def test_delete_file_view(self, backend):
1016 self.app.get(
951 self.app.get(
1017 route_path('repo_files_remove_file',
952 route_path(
1018 repo_name=backend.repo_name,
953 "repo_files_remove_file",
1019 commit_id=backend.default_head_id,
954 repo_name=backend.repo_name,
1020 f_path='vcs/nodes.py'),
955 commit_id=backend.default_head_id,
1021 status=200)
956 f_path="vcs/nodes.py",
957 ),
958 status=200,
959 )
1022
960
1023 def test_delete_file_view_not_on_branch(self, backend):
961 def test_delete_file_view_not_on_branch(self, backend):
1024 repo = backend.create_repo()
962 repo = backend.create_repo()
1025 backend.ensure_file(b'vcs/nodes.py')
963 backend.ensure_file(b"vcs/nodes.py")
1026
964
1027 response = self.app.get(
965 response = self.app.get(
1028 route_path('repo_files_remove_file',
966 route_path("repo_files_remove_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"),
1029 repo_name=repo.repo_name,
967 status=302,
1030 commit_id='tip',
968 )
1031 f_path='vcs/nodes.py'),
969 assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.")
1032 status=302)
1033 assert_session_flash(
1034 response, 'Cannot modify file. Given commit `tip` is not head of a branch.')
1035
970
1036 def test_delete_file_view_commit_changes(self, backend, csrf_token):
971 def test_delete_file_view_commit_changes(self, backend, csrf_token):
1037 repo = backend.create_repo()
972 repo = backend.create_repo()
1038 backend.ensure_file(b"vcs/nodes.py")
973 backend.ensure_file(b"vcs/nodes.py")
1039
974
1040 response = self.app.post(
975 response = self.app.post(
1041 route_path('repo_files_delete_file',
976 route_path(
1042 repo_name=repo.repo_name,
977 "repo_files_delete_file",
1043 commit_id=backend.default_head_id,
978 repo_name=repo.repo_name,
1044 f_path='vcs/nodes.py'),
979 commit_id=backend.default_head_id,
980 f_path="vcs/nodes.py",
981 ),
1045 params={
982 params={
1046 'message': 'i committed',
983 "message": "i committed",
1047 'csrf_token': csrf_token,
984 "csrf_token": csrf_token,
1048 },
985 },
1049 status=302)
986 status=302,
1050 assert_session_flash(
987 )
1051 response, 'Successfully deleted file `vcs/nodes.py`')
988 assert_session_flash(response, "Successfully deleted file `vcs/nodes.py`")
1052
989
1053
990
1054 @pytest.mark.usefixtures("app")
991 @pytest.mark.usefixtures("app")
1055 class TestFilesViewOtherCases(object):
992 class TestFilesViewOtherCases(object):
1056
1057 def test_access_empty_repo_redirect_to_summary_with_alert_write_perms(
993 def test_access_empty_repo_redirect_to_summary_with_alert_write_perms(
1058 self, backend_stub, autologin_regular_user, user_regular,
994 self, backend_stub, autologin_regular_user, user_regular, user_util
1059 user_util):
995 ):
1060
1061 repo = backend_stub.create_repo()
996 repo = backend_stub.create_repo()
1062 user_util.grant_user_permission_to_repo(
997 user_util.grant_user_permission_to_repo(repo, user_regular, "repository.write")
1063 repo, user_regular, 'repository.write')
998 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
1064 response = self.app.get(
1065 route_path('repo_files',
1066 repo_name=repo.repo_name,
1067 commit_id='tip', f_path='/'))
1068
999
1069 repo_file_add_url = route_path(
1000 repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="")
1070 'repo_files_add_file',
1071 repo_name=repo.repo_name,
1072 commit_id=0, f_path='')
1073 add_new = f'<a class="alert-link" href="{repo_file_add_url}">add a new file</a>'
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(
1003 repo_file_upload_url = route_path("repo_files_upload_file", repo_name=repo.repo_name, commit_id=0, f_path="")
1076 'repo_files_upload_file',
1077 repo_name=repo.repo_name,
1078 commit_id=0, f_path='')
1079 upload_new = f'<a class="alert-link" href="{repo_file_upload_url}">upload a new file</a>'
1004 upload_new = f'<a class="alert-link" href="{repo_file_upload_url}">upload a new file</a>'
1080
1005
1081 assert_session_flash(
1006 assert_session_flash(response, "There are no files yet. Click here to %s or %s." % (add_new, upload_new))
1082 response,
1083 'There are no files yet. Click here to %s or %s.' % (add_new, upload_new)
1084 )
1085
1007
1086 def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms(
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 repo = backend_stub.create_repo()
1011 repo = backend_stub.create_repo()
1089 # init session for anon user
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(
1015 repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="")
1093 'repo_files_add_file',
1094 repo_name=repo.repo_name,
1095 commit_id=0, f_path='')
1096
1016
1097 response = self.app.get(
1017 response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path=""))
1098 route_path('repo_files',
1099 repo_name=repo.repo_name,
1100 commit_id='tip', f_path='/'))
1101
1018
1102 assert_session_flash(response, no_=repo_file_add_url)
1019 assert_session_flash(response, no_=repo_file_add_url)
1103
1020
1104 @pytest.mark.parametrize('file_node', [
1021 @pytest.mark.parametrize(
1105 b'archive/file.zip',
1022 "file_node",
1106 b'diff/my-file.txt',
1023 [
1107 b'render.py',
1024 b"archive/file.zip",
1108 b'render',
1025 b"diff/my-file.txt",
1109 b'remove_file',
1026 b"render.py",
1110 b'remove_file/to-delete.txt',
1027 b"render",
1111 ])
1028 b"remove_file",
1029 b"remove_file/to-delete.txt",
1030 ],
1031 )
1112 def test_file_names_equal_to_routes_parts(self, backend, file_node):
1032 def test_file_names_equal_to_routes_parts(self, backend, file_node):
1113 backend.create_repo()
1033 backend.create_repo()
1114 backend.ensure_file(file_node)
1034 backend.ensure_file(file_node)
1115
1035
1116 self.app.get(
1036 self.app.get(
1117 route_path('repo_files',
1037 route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=safe_str(file_node)),
1118 repo_name=backend.repo_name,
1038 status=200,
1119 commit_id='tip', f_path=safe_str(file_node)),
1039 )
1120 status=200)
1121
1040
1122
1041
1123 class TestAdjustFilePathForSvn(object):
1042 class TestAdjustFilePathForSvn(object):
@@ -1126,20 +1045,20 class TestAdjustFilePathForSvn(object):
1126 """
1045 """
1127
1046
1128 def test_returns_path_relative_to_matched_reference(self):
1047 def test_returns_path_relative_to_matched_reference(self):
1129 repo = self._repo(branches=['trunk'])
1048 repo = self._repo(branches=["trunk"])
1130 self.assert_file_adjustment('trunk/file', 'file', repo)
1049 self.assert_file_adjustment("trunk/file", "file", repo)
1131
1050
1132 def test_does_not_modify_file_if_no_reference_matches(self):
1051 def test_does_not_modify_file_if_no_reference_matches(self):
1133 repo = self._repo(branches=['trunk'])
1052 repo = self._repo(branches=["trunk"])
1134 self.assert_file_adjustment('notes/file', 'notes/file', repo)
1053 self.assert_file_adjustment("notes/file", "notes/file", repo)
1135
1054
1136 def test_does_not_adjust_partial_directory_names(self):
1055 def test_does_not_adjust_partial_directory_names(self):
1137 repo = self._repo(branches=['trun'])
1056 repo = self._repo(branches=["trun"])
1138 self.assert_file_adjustment('trunk/file', 'trunk/file', repo)
1057 self.assert_file_adjustment("trunk/file", "trunk/file", repo)
1139
1058
1140 def test_is_robust_to_patterns_which_prefix_other_patterns(self):
1059 def test_is_robust_to_patterns_which_prefix_other_patterns(self):
1141 repo = self._repo(branches=['trunk', 'trunk/new', 'trunk/old'])
1060 repo = self._repo(branches=["trunk", "trunk/new", "trunk/old"])
1142 self.assert_file_adjustment('trunk/new/file', 'file', repo)
1061 self.assert_file_adjustment("trunk/new/file", "file", repo)
1143
1062
1144 def assert_file_adjustment(self, f_path, expected, repo):
1063 def assert_file_adjustment(self, f_path, expected, repo):
1145 result = RepoFilesView.adjust_file_path_for_svn(f_path, repo)
1064 result = RepoFilesView.adjust_file_path_for_svn(f_path, repo)
@@ -1147,6 +1066,6 class TestAdjustFilePathForSvn(object):
1147
1066
1148 def _repo(self, branches=None):
1067 def _repo(self, branches=None):
1149 repo = mock.Mock()
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 repo.tags = {}
1070 repo.tags = {}
1152 return repo
1071 return repo
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +20,7 import pytest
21
20
22 from rhodecode.tests import TestController, assert_session_flash, HG_FORK, GIT_FORK
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 from rhodecode.lib import helpers as h
24 from rhodecode.lib import helpers as h
26
25
27 from rhodecode.model.db import Repository
26 from rhodecode.model.db import Repository
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -21,7 +20,7 import pytest
21
20
22 from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
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 from rhodecode.tests.routes import route_path
24 from rhodecode.tests.routes import route_path
26
25
27 fixture = Fixture()
26 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -15,6 +15,9
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import logging
19 import os
20
18 import mock
21 import mock
19 import pytest
22 import pytest
20
23
@@ -41,7 +44,7 from rhodecode.tests import (
41 TEST_USER_ADMIN_LOGIN,
44 TEST_USER_ADMIN_LOGIN,
42 TEST_USER_REGULAR_LOGIN,
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 from rhodecode.tests.routes import route_path
48 from rhodecode.tests.routes import route_path
46
49
47
50
@@ -1050,7 +1053,6 class TestPullrequestsView(object):
1050 )
1053 )
1051 assert len(notifications.all()) == 2
1054 assert len(notifications.all()) == 2
1052
1055
1053 @pytest.mark.xfail(reason="unable to fix this test after python3 migration")
1054 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
1056 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
1055 commits = [
1057 commits = [
1056 {
1058 {
@@ -1125,20 +1127,38 class TestPullrequestsView(object):
1125 response.mustcontain(no=["content_of_ancestor-child"])
1127 response.mustcontain(no=["content_of_ancestor-child"])
1126 response.mustcontain("content_of_change")
1128 response.mustcontain("content_of_change")
1127
1129
1128 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
1130 def test_merge_pull_request_enabled(self, pr_util, csrf_token, rcextensions_modification):
1129 # Clear any previous calls to rcextensions
1130 rhodecode.EXTENSIONS.calls.clear()
1131
1131
1132 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
1132 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
1133 pull_request_id = pull_request.pull_request_id
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 url = route_path(
1136 url = route_path(
1137 "pullrequest_merge",
1137 "pullrequest_merge",
1138 repo_name=str(repo_name[0]),
1138 repo_name=repo_name,
1139 pull_request_id=pull_request_id,
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 pull_request = PullRequest.get(pull_request_id)
1163 pull_request = PullRequest.get(pull_request_id)
1144
1164
@@ -1162,12 +1182,39 class TestPullrequestsView(object):
1162 assert actions[-1].action == "user.push"
1182 assert actions[-1].action == "user.push"
1163 assert actions[-1].action_data["commit_ids"] == pr_commit_ids
1183 assert actions[-1].action_data["commit_ids"] == pr_commit_ids
1164
1184
1165 # Check post_push rcextension was really executed
1185 with open(rc_ext_location) as f:
1166 push_calls = rhodecode.EXTENSIONS.calls["_push_hook"]
1186 f_data = f.read()
1167 assert len(push_calls) == 1
1187 assert 'test-execution' in f_data
1168 unused_last_call_args, last_call_kwargs = push_calls[0]
1188 for commit_id in pr_commit_ids:
1169 assert last_call_kwargs["action"] == "push"
1189 assert f'{commit_id}' in f_data
1170 assert last_call_kwargs["commit_ids"] == pr_commit_ids
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 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
1219 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
1173 pull_request = pr_util.create_pull_request(mergeable=False)
1220 pull_request = pr_util.create_pull_request(mergeable=False)
@@ -1523,7 +1570,6 class TestPullrequestsView(object):
1523
1570
1524 assert pull_request.revisions == [commit_ids["change-rebased"]]
1571 assert pull_request.revisions == [commit_ids["change-rebased"]]
1525
1572
1526
1527 def test_remove_pull_request_branch(self, backend_git, csrf_token):
1573 def test_remove_pull_request_branch(self, backend_git, csrf_token):
1528 branch_name = "development"
1574 branch_name = "development"
1529 commits = [
1575 commits = [
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.model.meta import Session
25 from rhodecode.model.meta import Session
27 from rhodecode.tests import (
26 from rhodecode.tests import (
28 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, assert_session_flash)
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 from rhodecode.tests.routes import route_path
29 from rhodecode.tests.routes import route_path
31
30
32 fixture = Fixture()
31 fixture = Fixture()
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.model.repo import RepoModel
23 from rhodecode.model.repo import RepoModel
25 from rhodecode.tests import (
24 from rhodecode.tests import (
26 HG_REPO, GIT_REPO, assert_session_flash, no_newline_id_generator)
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 from rhodecode.tests.utils import repo_on_filesystem
27 from rhodecode.tests.utils import repo_on_filesystem
29 from rhodecode.tests.routes import route_path
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.scm import ScmModel
32 from rhodecode.model.scm import ScmModel
33 from rhodecode.tests import assert_session_flash
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 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
35 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
36 from rhodecode.tests.routes import route_path
36 from rhodecode.tests.routes import route_path
37
37
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 from rhodecode.tests import (
29 from rhodecode.tests import (
31 login_user_session, logout_user_session,
30 login_user_session, logout_user_session,
32 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
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 from rhodecode.tests.utils import AssertResponse
33 from rhodecode.tests.utils import AssertResponse
35 from rhodecode.tests.routes import route_path
34 from rhodecode.tests.routes import route_path
36
35
@@ -48,6 +47,7 class TestVcsSettings(object):
48 'extensions_evolve': False,
47 'extensions_evolve': False,
49 'phases_publish': 'False',
48 'phases_publish': 'False',
50 'rhodecode_pr_merge_enabled': False,
49 'rhodecode_pr_merge_enabled': False,
50 'rhodecode_auto_merge_enabled': False,
51 'rhodecode_use_outdated_comments': False,
51 'rhodecode_use_outdated_comments': False,
52 'new_svn_branch': '',
52 'new_svn_branch': '',
53 'new_svn_tag': ''
53 'new_svn_tag': ''
@@ -59,7 +59,7 class TestVcsSettings(object):
59 response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name))
59 response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name))
60
60
61 expected_settings = (
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 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
63 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
64 'hooks_outgoing_pull_logger'
64 'hooks_outgoing_pull_logger'
65 )
65 )
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -32,16 +31,13 class TestAdminRepoVcsSettings(object):
32 @pytest.mark.parametrize('setting_name, setting_backends', [
31 @pytest.mark.parametrize('setting_name, setting_backends', [
33 ('hg_use_rebase_for_merging', ['hg']),
32 ('hg_use_rebase_for_merging', ['hg']),
34 ])
33 ])
35 def test_labs_settings_visible_if_enabled(
34 def test_labs_settings_visible_if_enabled(self, setting_name, setting_backends, backend):
36 self, setting_name, setting_backends, backend):
37 if backend.alias not in setting_backends:
35 if backend.alias not in setting_backends:
38 pytest.skip('Setting not available for backend {}'.format(backend))
36 pytest.skip('Setting not available for backend {}'.format(backend))
39
37
40 vcs_settings_url = route_path(
38 vcs_settings_url = route_path('edit_repo_vcs', repo_name=backend.repo.repo_name)
41 'edit_repo_vcs', repo_name=backend.repo.repo_name)
42
39
43 with mock.patch.dict(
40 with mock.patch.dict(rhodecode.CONFIG, {'labs_settings_active': 'true'}):
44 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
45 response = self.app.get(vcs_settings_url)
41 response = self.app.get(vcs_settings_url)
46
42
47 assertr = response.assert_response()
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -33,7 +33,7 from rhodecode.lib.auth import (
33 from rhodecode.lib.graphmod import _colored, _dagwalker
33 from rhodecode.lib.graphmod import _colored, _dagwalker
34 from rhodecode.lib.helpers import RepoPage
34 from rhodecode.lib.helpers import RepoPage
35 from rhodecode.lib.utils2 import str2bool
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 from rhodecode.lib.vcs.exceptions import (
37 from rhodecode.lib.vcs.exceptions import (
38 RepositoryError, CommitDoesNotExistError,
38 RepositoryError, CommitDoesNotExistError,
39 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
39 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
@@ -204,10 +204,9 class RepoChangelogView(RepoAppView):
204 log.debug('generating changelog for path %s', f_path)
204 log.debug('generating changelog for path %s', f_path)
205 # get the history for the file !
205 # get the history for the file !
206 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
206 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
207
207 bytes_path = safe_bytes(f_path)
208 try:
208 try:
209 collection = base_commit.get_path_history(
209 collection = base_commit.get_path_history(bytes_path, limit=hist_limit, pre_load=pre_load)
210 f_path, limit=hist_limit, pre_load=pre_load)
211 if collection and partial_xhr:
210 if collection and partial_xhr:
212 # for ajax call we remove first one since we're looking
211 # for ajax call we remove first one since we're looking
213 # at it right now in the context of a file commit
212 # at it right now in the context of a file commit
@@ -216,7 +215,7 class RepoChangelogView(RepoAppView):
216 # this node is not present at tip!
215 # this node is not present at tip!
217 try:
216 try:
218 commit = self._get_commit_or_redirect(commit_id)
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 except RepositoryError as e:
219 except RepositoryError as e:
221 h.flash(safe_str(e), category='warning')
220 h.flash(safe_str(e), category='warning')
222 redirect_url = h.route_path(
221 redirect_url = h.route_path(
@@ -310,9 +309,8 class RepoChangelogView(RepoAppView):
310 log.exception(safe_str(e))
309 log.exception(safe_str(e))
311 raise HTTPFound(
310 raise HTTPFound(
312 h.route_path('repo_commits', repo_name=self.db_repo_name))
311 h.route_path('repo_commits', repo_name=self.db_repo_name))
313
312 bytes_path = safe_bytes(f_path)
314 collection = base_commit.get_path_history(
313 collection = base_commit.get_path_history(bytes_path, limit=hist_limit, pre_load=pre_load)
315 f_path, limit=hist_limit, pre_load=pre_load)
316 collection = list(reversed(collection))
314 collection = list(reversed(collection))
317 else:
315 else:
318 collection = self.rhodecode_vcs_repo.get_commits(
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -89,8 +89,7 class RepoCommitsView(RepoAppView):
89 commit_range = commit_id_range.split('...')[:2]
89 commit_range = commit_id_range.split('...')[:2]
90
90
91 try:
91 try:
92 pre_load = ['affected_files', 'author', 'branch', 'date',
92 pre_load = ['author', 'branch', 'date', 'message', 'parents']
93 'message', 'parents']
94 if self.rhodecode_vcs_repo.alias == 'hg':
93 if self.rhodecode_vcs_repo.alias == 'hg':
95 pre_load += ['hidden', 'obsolete', 'phase']
94 pre_load += ['hidden', 'obsolete', 'phase']
96
95
@@ -100,8 +99,7 class RepoCommitsView(RepoAppView):
100 pre_load=pre_load, translate_tags=False)
99 pre_load=pre_load, translate_tags=False)
101 commits = list(commits)
100 commits = list(commits)
102 else:
101 else:
103 commits = [self.rhodecode_vcs_repo.get_commit(
102 commits = [self.rhodecode_vcs_repo.get_commit(commit_id=commit_id_range, pre_load=pre_load)]
104 commit_id=commit_id_range, pre_load=pre_load)]
105
103
106 c.commit_ranges = commits
104 c.commit_ranges = commits
107 if not c.commit_ranges:
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.lib import audit_logger
40 from rhodecode.lib import audit_logger
41 from rhodecode.lib.hash_utils import sha1_safe
41 from rhodecode.lib.hash_utils import sha1_safe
42 from rhodecode.lib.archive_cache import (
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 from rhodecode.lib.str_utils import safe_bytes, convert_special_chars
48 from rhodecode.lib.str_utils import safe_bytes, convert_special_chars
45 from rhodecode.lib.view_utils import parse_path_ref
49 from rhodecode.lib.view_utils import parse_path_ref
46 from rhodecode.lib.exceptions import NonRelativePathError
50 from rhodecode.lib.exceptions import NonRelativePathError
47 from rhodecode.lib.codeblocks import (
51 from rhodecode.lib.codeblocks import filenode_as_lines_tokens, filenode_as_annotated_lines_tokens
48 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
49 from rhodecode.lib.utils2 import convert_line_endings, detect_mode
52 from rhodecode.lib.utils2 import convert_line_endings, detect_mode
50 from rhodecode.lib.type_utils import str2bool
53 from rhodecode.lib.type_utils import str2bool
51 from rhodecode.lib.str_utils import safe_str, safe_int, header_safe_str
54 from rhodecode.lib.str_utils import safe_str, safe_int, header_safe_str
52 from rhodecode.lib.auth import (
55 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired
53 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
54 from rhodecode.lib.vcs import path as vcspath
56 from rhodecode.lib.vcs import path as vcspath
55 from rhodecode.lib.vcs.backends.base import EmptyCommit
57 from rhodecode.lib.vcs.backends.base import EmptyCommit
56 from rhodecode.lib.vcs.conf import settings
58 from rhodecode.lib.vcs.conf import settings
57 from rhodecode.lib.vcs.nodes import FileNode
59 from rhodecode.lib.vcs.nodes import FileNode
58 from rhodecode.lib.vcs.exceptions import (
60 from rhodecode.lib.vcs.exceptions import (
59 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
61 RepositoryError,
60 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
62 CommitDoesNotExistError,
61 NodeDoesNotExistError, CommitError, NodeError)
63 EmptyRepositoryError,
64 ImproperArchiveTypeError,
65 VCSError,
66 NodeAlreadyExistsError,
67 NodeDoesNotExistError,
68 CommitError,
69 NodeError,
70 )
62
71
63 from rhodecode.model.scm import ScmModel
72 from rhodecode.model.scm import ScmModel
64 from rhodecode.model.db import Repository
73 from rhodecode.model.db import Repository
@@ -66,17 +75,17 from rhodecode.model.db import Repositor
66 log = logging.getLogger(__name__)
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 # original backward compat name of archive
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 # e.g vcsserver-id-abcd-sub-1-abcfdef-archive-all.zip
82 # e.g vcsserver-id-abcd-sub-1-abcfdef-archive-all.zip
74 # vcsserver-id-abcd-sub-0-abcfdef-COMMIT_SHA-PATH_SHA.zip
83 # vcsserver-id-abcd-sub-0-abcfdef-COMMIT_SHA-PATH_SHA.zip
75 id_sha = sha1_safe(str(db_repo_id))[:4]
84 id_sha = sha1_safe(str(db_repo_id))[:4]
76 sub_repo = 'sub-1' if subrepos else 'sub-0'
85 sub_repo = "sub-1" if subrepos else "sub-0"
77 commit = commit_sha if with_hash else 'archive'
86 commit = commit_sha if with_hash else "archive"
78 path_marker = (path_sha if with_hash else '') or 'all'
87 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}'
88 archive_name = f"{clean_name}-id-{id_sha}-{sub_repo}-{commit}-{path_marker}{ext}"
80
89
81 return archive_name
90 return archive_name
82
91
@@ -86,16 +95,15 def get_path_sha(at_path):
86
95
87
96
88 def _get_archive_spec(fname):
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 fileformat = None
100 fileformat = None
92 ext = None
101 ext = None
93 content_type = None
102 content_type = None
94 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
103 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
95
96 if fname.endswith(extension):
104 if fname.endswith(extension):
97 fileformat = a_type
105 fileformat = a_type
98 log.debug('archive is of type: %s', fileformat)
106 log.debug("archive is of type: %s", fileformat)
99 ext = extension
107 ext = extension
100 break
108 break
101
109
@@ -103,13 +111,12 def _get_archive_spec(fname):
103 raise ValueError()
111 raise ValueError()
104
112
105 # left over part of whole fname is the commit
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 return commit_id, ext, fileformat, content_type
116 return commit_id, ext, fileformat, content_type
109
117
110
118
111 class RepoFilesView(RepoAppView):
119 class RepoFilesView(RepoAppView):
112
113 @staticmethod
120 @staticmethod
114 def adjust_file_path_for_svn(f_path, repo):
121 def adjust_file_path_for_svn(f_path, repo):
115 """
122 """
@@ -118,13 +125,11 class RepoFilesView(RepoAppView):
118 This is mainly based on prefix matching of the recognized tags and
125 This is mainly based on prefix matching of the recognized tags and
119 branches in the underlying repository.
126 branches in the underlying repository.
120 """
127 """
121 tags_and_branches = itertools.chain(
128 tags_and_branches = itertools.chain(repo.branches.keys(), repo.tags.keys())
122 repo.branches.keys(),
123 repo.tags.keys())
124 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
129 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
125
130
126 for name in tags_and_branches:
131 for name in tags_and_branches:
127 if f_path.startswith(f'{name}/'):
132 if f_path.startswith(f"{name}/"):
128 f_path = vcspath.relpath(f_path, name)
133 f_path = vcspath.relpath(f_path, name)
129 break
134 break
130 return f_path
135 return f_path
@@ -135,66 +140,61 class RepoFilesView(RepoAppView):
135 c.enable_downloads = self.db_repo.enable_downloads
140 c.enable_downloads = self.db_repo.enable_downloads
136 return c
141 return c
137
142
138 def _ensure_not_locked(self, commit_id='tip'):
143 def _ensure_not_locked(self, commit_id="tip"):
139 _ = self.request.translate
144 _ = self.request.translate
140
145
141 repo = self.db_repo
146 repo = self.db_repo
142 if repo.enable_locking and repo.locked[0]:
147 if repo.enable_locking and repo.locked[0]:
143 h.flash(_('This repository has been locked by %s on %s')
148 h.flash(
144 % (h.person_by_id(repo.locked[0]),
149 _("This repository has been locked by %s on %s")
145 h.format_date(h.time_to_datetime(repo.locked[1]))),
150 % (h.person_by_id(repo.locked[0]), h.format_date(h.time_to_datetime(repo.locked[1]))),
146 'warning')
151 "warning",
147 files_url = h.route_path(
152 )
148 'repo_files:default_path',
153 files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id)
149 repo_name=self.db_repo_name, commit_id=commit_id)
150 raise HTTPFound(files_url)
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 _ = self.request.translate
157 _ = self.request.translate
154
158
155 if not is_head:
159 if not is_head:
156 message = _('Cannot modify file. '
160 message = _("Cannot modify file. " "Given commit `{}` is not head of a branch.").format(commit_id)
157 'Given commit `{}` is not head of a branch.').format(commit_id)
161 h.flash(message, category="warning")
158 h.flash(message, category='warning')
159
162
160 if json_mode:
163 if json_mode:
161 return message
164 return message
162
165
163 files_url = h.route_path(
166 files_url = h.route_path("repo_files", repo_name=self.db_repo_name, commit_id=commit_id, f_path=f_path)
164 'repo_files', repo_name=self.db_repo_name, commit_id=commit_id,
165 f_path=f_path)
166 raise HTTPFound(files_url)
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 _ = self.request.translate
170 _ = self.request.translate
170
171
171 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(
172 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(self.db_repo_name, branch_name)
172 self.db_repo_name, branch_name)
173 if branch_perm and branch_perm not in ["branch.push", "branch.push_force"]:
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))
174 message = _('Branch `{}` changes forbidden by rule {}.').format(
175 h.flash(message, "warning")
175 h.escape(branch_name), h.escape(rule))
176 h.flash(message, 'warning')
177
176
178 if json_mode:
177 if json_mode:
179 return message
178 return message
180
179
181 files_url = h.route_path(
180 files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id)
182 'repo_files:default_path', repo_name=self.db_repo_name, commit_id=commit_id)
183
181
184 raise HTTPFound(files_url)
182 raise HTTPFound(files_url)
185
183
186 def _get_commit_and_path(self):
184 def _get_commit_and_path(self):
187 default_commit_id = self.db_repo.landing_ref_name
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(
188 commit_id = self.request.matchdict.get("commit_id", default_commit_id)
191 'commit_id', default_commit_id)
192 f_path = self._get_f_path(self.request.matchdict, default_f_path)
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):
194 @classmethod
196 enc_list = getattr(c, 'default_encodings', [])
195 def _get_default_encoding(cls, c):
197 return enc_list[0] if enc_list else 'UTF-8'
196 enc_list = getattr(c, "default_encodings", [])
197 return enc_list[0] if enc_list else "UTF-8"
198
198
199 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
199 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
200 """
200 """
@@ -213,31 +213,25 class RepoFilesView(RepoAppView):
213 return None
213 return None
214
214
215 add_new = upload_new = ""
215 add_new = upload_new = ""
216 if h.HasRepoPermissionAny(
216 if h.HasRepoPermissionAny("repository.write", "repository.admin")(self.db_repo_name):
217 '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 _url = h.route_path(
218 add_new = h.link_to(_("add a new file"), _url, class_="alert-link")
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")
223
219
224 _url_upld = h.route_path(
220 _url_upld = h.route_path("repo_files_upload_file", repo_name=self.db_repo_name, commit_id=0, f_path="")
225 'repo_files_upload_file',
221 upload_new = h.link_to(_("upload a new file"), _url_upld, class_="alert-link")
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")
229
222
230 h.flash(h.literal(
223 h.flash(
231 _('There are no files yet. Click here to %s or %s.') % (add_new, upload_new)), category='warning')
224 h.literal(_("There are no files yet. Click here to %s or %s.") % (add_new, upload_new)),
232 raise HTTPFound(
225 category="warning",
233 h.route_path('repo_summary', repo_name=self.db_repo_name))
226 )
227 raise HTTPFound(h.route_path("repo_summary", repo_name=self.db_repo_name))
234
228
235 except (CommitDoesNotExistError, LookupError) as e:
229 except (CommitDoesNotExistError, LookupError) as e:
236 msg = _('No such commit exists for this repository. Commit: {}').format(commit_id)
230 msg = _("No such commit exists for this repository. Commit: {}").format(commit_id)
237 h.flash(msg, category='error')
231 h.flash(msg, category="error")
238 raise HTTPNotFound()
232 raise HTTPNotFound()
239 except RepositoryError as e:
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 raise HTTPNotFound()
235 raise HTTPNotFound()
242
236
243 def _get_filenode_or_redirect(self, commit_obj, path, pre_load=None):
237 def _get_filenode_or_redirect(self, commit_obj, path, pre_load=None):
@@ -250,22 +244,22 class RepoFilesView(RepoAppView):
250 try:
244 try:
251 file_node = commit_obj.get_node(path, pre_load=pre_load)
245 file_node = commit_obj.get_node(path, pre_load=pre_load)
252 if file_node.is_dir():
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 except CommitDoesNotExistError:
248 except CommitDoesNotExistError:
255 log.exception('No such commit exists for this repository')
249 log.exception("No such commit exists for this repository")
256 h.flash(_('No such commit exists for this repository'), category='error')
250 h.flash(_("No such commit exists for this repository"), category="error")
257 raise HTTPNotFound()
251 raise HTTPNotFound()
258 except RepositoryError as e:
252 except RepositoryError as e:
259 log.warning('Repository error while fetching filenode `%s`. Err:%s', path, e)
253 log.warning("Repository error while fetching filenode `%s`. Err:%s", path, e)
260 h.flash(h.escape(safe_str(e)), category='error')
254 h.flash(h.escape(safe_str(e)), category="error")
261 raise HTTPNotFound()
255 raise HTTPNotFound()
262
256
263 return file_node
257 return file_node
264
258
265 def _is_valid_head(self, commit_id, repo, landing_ref):
259 def _is_valid_head(self, commit_id, repo, landing_ref):
266 branch_name = sha_commit_id = ''
260 branch_name = sha_commit_id = ""
267 is_head = False
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 for _branch_name, branch_commit_id in repo.branches.items():
264 for _branch_name, branch_commit_id in repo.branches.items():
271 # simple case we pass in branch name, it's a HEAD
265 # simple case we pass in branch name, it's a HEAD
@@ -301,39 +295,39 class RepoFilesView(RepoAppView):
301 return branch_name, sha_commit_id, is_head
295 return branch_name, sha_commit_id, is_head
302
296
303 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None):
297 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None):
304
305 repo_id = self.db_repo.repo_id
298 repo_id = self.db_repo.repo_id
306 force_recache = self.get_recache_flag()
299 force_recache = self.get_recache_flag()
307
300
308 cache_seconds = safe_int(
301 cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time")
309 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
310 cache_on = not force_recache and cache_seconds > 0
302 cache_on = not force_recache and cache_seconds > 0
303
311 log.debug(
304 log.debug(
312 'Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`'
305 "Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`"
313 'with caching: %s[TTL: %ss]' % (
306 "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0)
314 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}'
309 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)
310 region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid)
318
311
319 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
312 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
320 def compute_file_tree(_name_hash, _repo_id, _commit_id, _f_path, _full_load, _at_rev):
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',
314 log.debug("Generating cached file tree at for repo_id: %s, %s, %s", _repo_id, _commit_id, _f_path)
322 _repo_id, _commit_id, _f_path)
323
315
324 c.full_load = _full_load
316 c.full_load = _full_load
325 return render(
317 return render(
326 'rhodecode:templates/files/files_browser_tree.mako',
318 "rhodecode:templates/files/files_browser_tree.mako",
327 self._get_template_context(c), self.request, _at_rev)
319 self._get_template_context(c),
320 self.request,
321 _at_rev,
322 )
328
323
329 return compute_file_tree(
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 def create_pure_path(self, *parts):
328 def create_pure_path(self, *parts):
333 # Split paths and sanitize them, removing any ../ etc
329 # Split paths and sanitize them, removing any ../ etc
334 sanitized_path = [
330 sanitized_path = [x for x in pathlib.PurePath(*parts).parts if x not in [".", ".."]]
335 x for x in pathlib.PurePath(*parts).parts
336 if x not in ['.', '..']]
337
331
338 pure_path = pathlib.PurePath(*sanitized_path)
332 pure_path = pathlib.PurePath(*sanitized_path)
339 return pure_path
333 return pure_path
@@ -341,10 +335,7 class RepoFilesView(RepoAppView):
341 def _is_lf_enabled(self, target_repo):
335 def _is_lf_enabled(self, target_repo):
342 lf_enabled = False
336 lf_enabled = False
343
337
344 lf_key_for_vcs_map = {
338 lf_key_for_vcs_map = {"hg": "extensions_largefiles", "git": "vcs_git_lfs_enabled"}
345 'hg': 'extensions_largefiles',
346 'git': 'vcs_git_lfs_enabled'
347 }
348
339
349 lf_key_for_vcs = lf_key_for_vcs_map.get(target_repo.repo_type)
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 return lf_enabled
345 return lf_enabled
355
346
356 @LoginRequired()
347 @LoginRequired()
357 @HasRepoPermissionAnyDecorator(
348 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
358 'repository.read', 'repository.write', 'repository.admin')
359 def repo_archivefile(self):
349 def repo_archivefile(self):
360 # archive cache config
350 # archive cache config
361 from rhodecode import CONFIG
351 from rhodecode import CONFIG
352
362 _ = self.request.translate
353 _ = self.request.translate
363 self.load_default_context()
354 self.load_default_context()
364 default_at_path = '/'
355
365 fname = self.request.matchdict['fname']
356 subrepos = self.request.GET.get("subrepos") == "true"
366 subrepos = self.request.GET.get('subrepos') == 'true'
357 with_hash = str2bool(self.request.GET.get("with_hash", "1"))
367 with_hash = str2bool(self.request.GET.get('with_hash', '1'))
358
368 at_path = self.request.GET.get('at_path') or default_at_path
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 if not self.db_repo.enable_downloads:
363 if not self.db_repo.enable_downloads:
371 return Response(_('Downloads disabled'))
364 return Response(_("Downloads disabled"))
372
365
373 try:
366 try:
374 commit_id, ext, fileformat, content_type = \
367 commit_id, ext, file_format, content_type = _get_archive_spec(fname)
375 _get_archive_spec(fname)
376 except ValueError:
368 except ValueError:
377 return Response(_('Unknown archive type for: `{}`').format(
369 return Response(_("Unknown archive type for: `{}`").format(h.escape(fname)))
378 h.escape(fname)))
379
370
380 try:
371 try:
381 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
372 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
382 except CommitDoesNotExistError:
373 except CommitDoesNotExistError:
383 return Response(_('Unknown commit_id {}').format(
374 return Response(_("Unknown commit_id {}").format(h.escape(commit_id)))
384 h.escape(commit_id)))
385 except EmptyRepositoryError:
375 except EmptyRepositoryError:
386 return Response(_('Empty repository'))
376 return Response(_("Empty repository"))
387
377
388 # we used a ref, or a shorter version, lets redirect client ot use explicit hash
378 # we used a ref, or a shorter version, lets redirect client ot use explicit hash
389 if commit_id != commit.raw_id:
379 if commit_id != commit.raw_id:
390 fname=f'{commit.raw_id}{ext}'
380 fname = f"{commit.raw_id}{ext}"
391 raise HTTPFound(self.request.current_route_path(fname=fname))
381 raise HTTPFound(self.request.current_route_path(fname=fname))
392
382
393 try:
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 except Exception:
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 path_sha = get_path_sha(at_path)
388 path_sha = get_path_sha(at_path)
399
389
400 # used for cache etc, consistent unique archive name
390 # used for cache etc, consistent unique archive name
401 archive_name_key = get_archive_name(
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,
392 self.db_repo.repo_id,
403 path_sha=path_sha, with_hash=True)
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 if not with_hash:
401 if not with_hash:
406 path_sha = ''
402 path_sha = ""
407
403
408 # what end client gets served
404 # what end client gets served
409 response_archive_name = get_archive_name(
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,
406 self.db_repo.repo_id,
411 path_sha=path_sha, with_hash=with_hash)
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 # remove extension from our archive directory name
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 d_cache = get_archival_cache_store(config=CONFIG)
420 d_cache = get_archival_cache_store(config=CONFIG)
419
421
@@ -421,29 +423,38 class RepoFilesView(RepoAppView):
421 d_cache_conf = get_archival_config(config=CONFIG)
423 d_cache_conf = get_archival_config(config=CONFIG)
422
424
423 # This is also a cache key, and lock key
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 use_cached_archive = False
428 use_cached_archive = False
427 if not archive_cache_disable and archive_name_key in d_cache:
429 if not archive_cache_disable and archive_name_key in d_cache:
428 reader, metadata = d_cache.fetch(archive_name_key)
430 reader, metadata = d_cache.fetch(archive_name_key)
429
431
430 use_cached_archive = True
432 use_cached_archive = True
431 log.debug('Found cached archive as key=%s tag=%s, serving archive from cache reader=%s',
433 log.debug(
432 archive_name_key, metadata, reader.name)
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 else:
439 else:
434 reader = None
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 if not reader:
443 if not reader:
438 # generate new archive, as previous was not found in the cache
444 # generate new archive, as previous was not found in the cache
439 try:
445 try:
440 with d_cache.get_lock(reentrant_lock_key):
446 with d_cache.get_lock(reentrant_lock_key):
441 try:
447 try:
442 commit.archive_repo(archive_name_key, archive_dir_name=archive_dir_name,
448 commit.archive_repo(
443 kind=fileformat, subrepos=subrepos,
449 archive_name_key,
444 archive_at_path=at_path, cache_config=d_cache_conf)
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 except ImproperArchiveTypeError:
456 except ImproperArchiveTypeError:
446 return _('Unknown archive type')
457 return _("Unknown archive type")
447
458
448 except ArchiveCacheGenerationLock:
459 except ArchiveCacheGenerationLock:
449 retry_after = round(random.uniform(0.3, 3.0), 1)
460 retry_after = round(random.uniform(0.3, 3.0), 1)
@@ -462,7 +473,7 class RepoFilesView(RepoAppView):
462 reader, metadata = d_cache.fetch(archive_name_key, retry=True, retry_attempts=30)
473 reader, metadata = d_cache.fetch(archive_name_key, retry=True, retry_attempts=30)
463
474
464 response = Response(app_iter=archive_iterator(reader))
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 response.content_type = str(content_type)
477 response.content_type = str(content_type)
467
478
468 try:
479 try:
@@ -470,23 +481,25 class RepoFilesView(RepoAppView):
470 finally:
481 finally:
471 # store download action
482 # store download action
472 audit_logger.store_web(
483 audit_logger.store_web(
473 'repo.archive.download', action_data={
484 "repo.archive.download",
474 'user_agent': self.request.user_agent,
485 action_data={
475 'archive_name': archive_name_key,
486 "user_agent": self.request.user_agent,
476 'archive_spec': fname,
487 "archive_name": archive_name_key,
477 'archive_cached': use_cached_archive},
488 "archive_spec": fname,
489 "archive_cached": use_cached_archive,
490 },
478 user=self._rhodecode_user,
491 user=self._rhodecode_user,
479 repo=self.db_repo,
492 repo=self.db_repo,
480 commit=True
493 commit=True,
481 )
494 )
482
495
483 def _get_file_node(self, commit_id, f_path):
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 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
498 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
486 try:
499 try:
487 node = commit.get_node(f_path)
500 node = commit.get_node(safe_bytes(f_path))
488 if node.is_dir():
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 except NodeDoesNotExistError:
503 except NodeDoesNotExistError:
491 commit = EmptyCommit(
504 commit = EmptyCommit(
492 commit_id=commit_id,
505 commit_id=commit_id,
@@ -495,46 +508,43 class RepoFilesView(RepoAppView):
495 alias=commit.repository.alias,
508 alias=commit.repository.alias,
496 message=commit.message,
509 message=commit.message,
497 author=commit.author,
510 author=commit.author,
498 date=commit.date)
511 date=commit.date,
499 node = FileNode(safe_bytes(f_path), b'', commit=commit)
512 )
513 node = FileNode(safe_bytes(f_path), b"", commit=commit)
500 else:
514 else:
501 commit = EmptyCommit(
515 commit = EmptyCommit(repo=self.rhodecode_vcs_repo, alias=self.rhodecode_vcs_repo.alias)
502 repo=self.rhodecode_vcs_repo,
516 node = FileNode(safe_bytes(f_path), b"", commit=commit)
503 alias=self.rhodecode_vcs_repo.alias)
504 node = FileNode(safe_bytes(f_path), b'', commit=commit)
505 return node
517 return node
506
518
507 @LoginRequired()
519 @LoginRequired()
508 @HasRepoPermissionAnyDecorator(
520 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
509 'repository.read', 'repository.write', 'repository.admin')
510 def repo_files_diff(self):
521 def repo_files_diff(self):
511 c = self.load_default_context()
522 c = self.load_default_context()
512 f_path = self._get_f_path(self.request.matchdict)
523 f_path = self._get_f_path(self.request.matchdict)
513 diff1 = self.request.GET.get('diff1', '')
524 diff1 = self.request.GET.get("diff1", "")
514 diff2 = self.request.GET.get('diff2', '')
525 diff2 = self.request.GET.get("diff2", "")
515
526
516 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
527 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
517
528
518 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
529 ignore_whitespace = str2bool(self.request.GET.get("ignorews"))
519 line_context = self.request.GET.get('context', 3)
530 line_context = self.request.GET.get("context", 3)
520
531
521 if not any((diff1, diff2)):
532 if not any((diff1, diff2)):
522 h.flash(
533 h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error")
523 'Need query parameter "diff1" or "diff2" to generate a diff.',
524 category='error')
525 raise HTTPBadRequest()
534 raise HTTPBadRequest()
526
535
527 c.action = self.request.GET.get('diff')
536 c.action = self.request.GET.get("diff")
528 if c.action not in ['download', 'raw']:
537 if c.action not in ["download", "raw"]:
529 compare_url = h.route_path(
538 compare_url = h.route_path(
530 'repo_compare',
539 "repo_compare",
531 repo_name=self.db_repo_name,
540 repo_name=self.db_repo_name,
532 source_ref_type='rev',
541 source_ref_type="rev",
533 source_ref=diff1,
542 source_ref=diff1,
534 target_repo=self.db_repo_name,
543 target_repo=self.db_repo_name,
535 target_ref_type='rev',
544 target_ref_type="rev",
536 target_ref=diff2,
545 target_ref=diff2,
537 _query=dict(f_path=f_path))
546 _query=dict(f_path=f_path),
547 )
538 # redirect to new view if we render diff
548 # redirect to new view if we render diff
539 raise HTTPFound(compare_url)
549 raise HTTPFound(compare_url)
540
550
@@ -543,43 +553,34 class RepoFilesView(RepoAppView):
543 node2 = self._get_file_node(diff2, f_path)
553 node2 = self._get_file_node(diff2, f_path)
544 except (RepositoryError, NodeError):
554 except (RepositoryError, NodeError):
545 log.exception("Exception while trying to get node from repository")
555 log.exception("Exception while trying to get node from repository")
546 raise HTTPFound(
556 raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path=f_path))
547 h.route_path('repo_files', repo_name=self.db_repo_name,
548 commit_id='tip', f_path=f_path))
549
557
550 if all(isinstance(node.commit, EmptyCommit)
558 if all(isinstance(node.commit, EmptyCommit) for node in (node1, node2)):
551 for node in (node1, node2)):
552 raise HTTPNotFound()
559 raise HTTPNotFound()
553
560
554 c.commit_1 = node1.commit
561 c.commit_1 = node1.commit
555 c.commit_2 = node2.commit
562 c.commit_2 = node2.commit
556
563
557 if c.action == 'download':
564 if c.action == "download":
558 _diff = diffs.get_gitdiff(node1, node2,
565 _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context)
559 ignore_whitespace=ignore_whitespace,
560 context=line_context)
561 # NOTE: this was using diff_format='gitdiff'
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 response = Response(self.path_filter.get_raw_patch(diff))
569 response = Response(self.path_filter.get_raw_patch(diff))
565 response.content_type = 'text/plain'
570 response.content_type = "text/plain"
566 response.content_disposition = (
571 response.content_disposition = f"attachment; filename={f_path}_{diff1}_vs_{diff2}.diff"
567 f'attachment; filename={f_path}_{diff1}_vs_{diff2}.diff'
568 )
569 charset = self._get_default_encoding(c)
572 charset = self._get_default_encoding(c)
570 if charset:
573 if charset:
571 response.charset = charset
574 response.charset = charset
572 return response
575 return response
573
576
574 elif c.action == 'raw':
577 elif c.action == "raw":
575 _diff = diffs.get_gitdiff(node1, node2,
578 _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context)
576 ignore_whitespace=ignore_whitespace,
577 context=line_context)
578 # NOTE: this was using diff_format='gitdiff'
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 response = Response(self.path_filter.get_raw_patch(diff))
582 response = Response(self.path_filter.get_raw_patch(diff))
582 response.content_type = 'text/plain'
583 response.content_type = "text/plain"
583 charset = self._get_default_encoding(c)
584 charset = self._get_default_encoding(c)
584 if charset:
585 if charset:
585 response.charset = charset
586 response.charset = charset
@@ -589,31 +590,32 class RepoFilesView(RepoAppView):
589 raise HTTPNotFound()
590 raise HTTPNotFound()
590
591
591 @LoginRequired()
592 @LoginRequired()
592 @HasRepoPermissionAnyDecorator(
593 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
593 'repository.read', 'repository.write', 'repository.admin')
594 def repo_files_diff_2way_redirect(self):
594 def repo_files_diff_2way_redirect(self):
595 """
595 """
596 Kept only to make OLD links work
596 Kept only to make OLD links work
597 """
597 """
598 f_path = self._get_f_path_unchecked(self.request.matchdict)
598 f_path = self._get_f_path_unchecked(self.request.matchdict)
599 diff1 = self.request.GET.get('diff1', '')
599 diff1 = self.request.GET.get("diff1", "")
600 diff2 = self.request.GET.get('diff2', '')
600 diff2 = self.request.GET.get("diff2", "")
601
601
602 if not any((diff1, diff2)):
602 if not any((diff1, diff2)):
603 h.flash(
603 h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error")
604 'Need query parameter "diff1" or "diff2" to generate a diff.',
605 category='error')
606 raise HTTPBadRequest()
604 raise HTTPBadRequest()
607
605
608 compare_url = h.route_path(
606 compare_url = h.route_path(
609 'repo_compare',
607 "repo_compare",
610 repo_name=self.db_repo_name,
608 repo_name=self.db_repo_name,
611 source_ref_type='rev',
609 source_ref_type="rev",
612 source_ref=diff1,
610 source_ref=diff1,
613 target_ref_type='rev',
611 target_ref_type="rev",
614 target_ref=diff2,
612 target_ref=diff2,
615 _query=dict(f_path=f_path, diffmode='sideside',
613 _query=dict(
616 target_repo=self.db_repo_name,))
614 f_path=f_path,
615 diffmode="sideside",
616 target_repo=self.db_repo_name,
617 ),
618 )
617 raise HTTPFound(compare_url)
619 raise HTTPFound(compare_url)
618
620
619 @LoginRequired()
621 @LoginRequired()
@@ -627,52 +629,51 class RepoFilesView(RepoAppView):
627 landing_url = h.repo_files_by_ref_url(
629 landing_url = h.repo_files_by_ref_url(
628 c.rhodecode_db_repo.repo_name,
630 c.rhodecode_db_repo.repo_name,
629 c.rhodecode_db_repo.repo_type,
631 c.rhodecode_db_repo.repo_type,
630 f_path='',
632 f_path="",
631 ref_name=ref_name,
633 ref_name=ref_name,
632 commit_id='tip',
634 commit_id="tip",
633 query=dict(at=ref_name)
635 query=dict(at=ref_name),
634 )
636 )
635
637
636 raise HTTPFound(landing_url)
638 raise HTTPFound(landing_url)
637
639
638 @LoginRequired()
640 @LoginRequired()
639 @HasRepoPermissionAnyDecorator(
641 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
640 'repository.read', 'repository.write', 'repository.admin')
641 def repo_files(self):
642 def repo_files(self):
642 c = self.load_default_context()
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 # default is false, but .rst/.md files later are auto rendered, we can
648 # default is false, but .rst/.md files later are auto rendered, we can
648 # overwrite auto rendering by setting this GET flag
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 c.commit = self._get_commit_or_redirect(commit_id)
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 c.f_path = f_path
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 # files or dirs
659 # files or dirs
659 try:
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 c.file_author = True
663 c.file_author = True
663 c.file_tree = ''
664 c.file_tree = ""
664
665
665 # prev link
666 # prev link
666 try:
667 try:
667 prev_commit = c.commit.prev(c.branch)
668 prev_commit = c.commit.prev(c.branch)
668 c.prev_commit = prev_commit
669 c.prev_commit = prev_commit
669 c.url_prev = h.route_path(
670 c.url_prev = h.route_path(
670 'repo_files', repo_name=self.db_repo_name,
671 "repo_files", repo_name=self.db_repo_name, commit_id=prev_commit.raw_id, f_path=f_path
671 commit_id=prev_commit.raw_id, f_path=f_path)
672 )
672 if c.branch:
673 if c.branch:
673 c.url_prev += '?branch=%s' % c.branch
674 c.url_prev += f"?branch={c.branch}"
674 except (CommitDoesNotExistError, VCSError):
675 except (CommitDoesNotExistError, VCSError):
675 c.url_prev = '#'
676 c.url_prev = "#"
676 c.prev_commit = EmptyCommit()
677 c.prev_commit = EmptyCommit()
677
678
678 # next link
679 # next link
@@ -680,110 +681,101 class RepoFilesView(RepoAppView):
680 next_commit = c.commit.next(c.branch)
681 next_commit = c.commit.next(c.branch)
681 c.next_commit = next_commit
682 c.next_commit = next_commit
682 c.url_next = h.route_path(
683 c.url_next = h.route_path(
683 'repo_files', repo_name=self.db_repo_name,
684 "repo_files", repo_name=self.db_repo_name, commit_id=next_commit.raw_id, f_path=f_path
684 commit_id=next_commit.raw_id, f_path=f_path)
685 )
685 if c.branch:
686 if c.branch:
686 c.url_next += '?branch=%s' % c.branch
687 c.url_next += f"?branch={c.branch}"
687 except (CommitDoesNotExistError, VCSError):
688 except (CommitDoesNotExistError, VCSError):
688 c.url_next = '#'
689 c.url_next = "#"
689 c.next_commit = EmptyCommit()
690 c.next_commit = EmptyCommit()
690
691
691 # load file content
692 # load file content
692 if c.file.is_file():
693 if c.file.is_file():
693
694 c.lf_node = {}
694 c.lf_node = {}
695
695
696 has_lf_enabled = self._is_lf_enabled(self.db_repo)
696 has_lf_enabled = self._is_lf_enabled(self.db_repo)
697 if has_lf_enabled:
697 if has_lf_enabled:
698 c.lf_node = c.file.get_largefile_node()
698 c.lf_node = c.file.get_largefile_node()
699
699
700 c.file_source_page = 'true'
700 c.file_source_page = "true"
701 c.file_last_commit = c.file.last_commit
701 c.file_last_commit = c.file.last_commit
702
702
703 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
703 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
704
704
705 if not (c.file_size_too_big or c.file.is_binary):
705 if not (c.file_size_too_big or c.file.is_binary):
706 if c.annotate: # annotation has precedence over renderer
706 if c.annotate: # annotation has precedence over renderer
707 c.annotated_lines = filenode_as_annotated_lines_tokens(
707 c.annotated_lines = filenode_as_annotated_lines_tokens(c.file)
708 c.file
709 )
710 else:
708 else:
711 c.renderer = (
709 c.renderer = c.renderer and h.renderer_from_filename(c.file.path)
712 c.renderer and h.renderer_from_filename(c.file.path)
713 )
714 if not c.renderer:
710 if not c.renderer:
715 c.lines = filenode_as_lines_tokens(c.file)
711 c.lines = filenode_as_lines_tokens(c.file)
716
712
717 _branch_name, _sha_commit_id, is_head = \
713 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
718 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
714 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
719 landing_ref=self.db_repo.landing_ref_name)
715 )
720 c.on_branch_head = is_head
716 c.on_branch_head = is_head
721
717
722 branch = c.commit.branch if (
718 branch = c.commit.branch if (c.commit.branch and "/" not in c.commit.branch) else None
723 c.commit.branch and '/' not in c.commit.branch) else None
724 c.branch_or_raw_id = branch or c.commit.raw_id
719 c.branch_or_raw_id = branch or c.commit.raw_id
725 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
720 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
726
721
727 author = c.file_last_commit.author
722 author = c.file_last_commit.author
728 c.authors = [[
723 c.authors = [[h.email(author), h.person(author, "username_or_name_or_email"), 1]]
729 h.email(author),
730 h.person(author, 'username_or_name_or_email'),
731 1
732 ]]
733
724
734 else: # load tree content at path
725 else: # load tree content (dir content) at path
735 c.file_source_page = 'false'
726 c.file_source_page = "false"
736 c.authors = []
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 # this loads a simple tree without metadata to speed things up
731 # this loads a simple tree without metadata to speed things up
738 # later via ajax we call repo_nodetree_full and fetch whole
732 # later via ajax we call repo_nodetree_full and fetch whole
739 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev)
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 = \
735 c.readme_data, c.readme_file = self._get_readme_data(
742 self._get_readme_data(self.db_repo, c.visual.default_renderer,
736 self.db_repo, c.visual.default_renderer, c.commit.raw_id, bytes_path, nodes=c.file_nodes
743 c.commit.raw_id, f_path)
737 )
744
738
745 except RepositoryError as e:
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 raise HTTPNotFound()
741 raise HTTPNotFound()
748
742
749 if self.request.environ.get('HTTP_X_PJAX'):
743 if self.request.environ.get("HTTP_X_PJAX"):
750 html = render('rhodecode:templates/files/files_pjax.mako',
744 html = render("rhodecode:templates/files/files_pjax.mako", self._get_template_context(c), self.request)
751 self._get_template_context(c), self.request)
752 else:
745 else:
753 html = render('rhodecode:templates/files/files.mako',
746 html = render("rhodecode:templates/files/files.mako", self._get_template_context(c), self.request)
754 self._get_template_context(c), self.request)
755 return Response(html)
747 return Response(html)
756
748
757 @HasRepoPermissionAnyDecorator(
749 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
758 'repository.read', 'repository.write', 'repository.admin')
759 def repo_files_annotated_previous(self):
750 def repo_files_annotated_previous(self):
760 self.load_default_context()
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 commit = self._get_commit_or_redirect(commit_id)
754 commit = self._get_commit_or_redirect(commit_id)
764 prev_commit_id = commit.raw_id
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 is_file = False
757 is_file = False
767 try:
758 try:
768 _file = commit.get_node(f_path)
759 _file = commit.get_node(bytes_path)
769 is_file = _file.is_file()
760 is_file = _file.is_file()
770 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
761 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
771 pass
762 pass
772
763
773 if is_file:
764 if is_file:
774 history = commit.get_path_history(f_path)
765 history = commit.get_path_history(bytes_path)
775 prev_commit_id = history[1].raw_id \
766 prev_commit_id = history[1].raw_id if len(history) > 1 else prev_commit_id
776 if len(history) > 1 else prev_commit_id
777 prev_url = h.route_path(
767 prev_url = h.route_path(
778 'repo_files:annotated', repo_name=self.db_repo_name,
768 "repo_files:annotated",
779 commit_id=prev_commit_id, f_path=f_path,
769 repo_name=self.db_repo_name,
780 _anchor=f'L{line_anchor}')
770 commit_id=prev_commit_id,
771 f_path=bytes_path,
772 _anchor=f"L{line_anchor}",
773 )
781
774
782 raise HTTPFound(prev_url)
775 raise HTTPFound(prev_url)
783
776
784 @LoginRequired()
777 @LoginRequired()
785 @HasRepoPermissionAnyDecorator(
778 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
786 'repository.read', 'repository.write', 'repository.admin')
787 def repo_nodetree_full(self):
779 def repo_nodetree_full(self):
788 """
780 """
789 Returns rendered html of file tree that contains commit date,
781 Returns rendered html of file tree that contains commit date,
@@ -792,22 +784,22 class RepoFilesView(RepoAppView):
792 """
784 """
793 c = self.load_default_context()
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 commit = self._get_commit_or_redirect(commit_id)
788 commit = self._get_commit_or_redirect(commit_id)
797 try:
789 try:
798 dir_node = commit.get_node(f_path)
790 dir_node = commit.get_node(bytes_path)
799 except RepositoryError as e:
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 if dir_node.is_file():
794 if dir_node.is_file():
803 return Response('')
795 return Response("")
804
796
805 c.file = dir_node
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 c.commit = commit
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(
802 html = self._get_tree_at_commit(c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
810 c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
811
803
812 return Response(html)
804 return Response(html)
813
805
@@ -816,15 +808,12 class RepoFilesView(RepoAppView):
816 safe_path = f_name.replace('"', '\\"')
808 safe_path = f_name.replace('"', '\\"')
817 encoded_path = urllib.parse.quote(f_name)
809 encoded_path = urllib.parse.quote(f_name)
818
810
819 headers = "attachment; " \
811 headers = f"attachment; " f'filename="{safe_path}"; ' f"filename*=UTF-8''{encoded_path}"
820 "filename=\"{}\"; " \
821 "filename*=UTF-8\'\'{}".format(safe_path, encoded_path)
822
812
823 return header_safe_str(headers)
813 return header_safe_str(headers)
824
814
825 @LoginRequired()
815 @LoginRequired()
826 @HasRepoPermissionAnyDecorator(
816 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
827 'repository.read', 'repository.write', 'repository.admin')
828 def repo_file_raw(self):
817 def repo_file_raw(self):
829 """
818 """
830 Action for show as raw, some mimetypes are "rendered",
819 Action for show as raw, some mimetypes are "rendered",
@@ -832,25 +821,24 class RepoFilesView(RepoAppView):
832 """
821 """
833 c = self.load_default_context()
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 commit = self._get_commit_or_redirect(commit_id)
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 raw_mimetype_mapping = {
828 raw_mimetype_mapping = {
840 # map original mimetype to a mimetype used for "show as raw"
829 # map original mimetype to a mimetype used for "show as raw"
841 # you can also provide a content-disposition to override the
830 # you can also provide a content-disposition to override the
842 # default "attachment" disposition.
831 # default "attachment" disposition.
843 # orig_type: (new_type, new_dispo)
832 # orig_type: (new_type, new_dispo)
844
845 # show images inline:
833 # show images inline:
846 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
834 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
847 # for example render an SVG with javascript inside or even render
835 # for example render an SVG with javascript inside or even render
848 # HTML.
836 # HTML.
849 'image/x-icon': ('image/x-icon', 'inline'),
837 "image/x-icon": ("image/x-icon", "inline"),
850 'image/png': ('image/png', 'inline'),
838 "image/png": ("image/png", "inline"),
851 'image/gif': ('image/gif', 'inline'),
839 "image/gif": ("image/gif", "inline"),
852 'image/jpeg': ('image/jpeg', 'inline'),
840 "image/jpeg": ("image/jpeg", "inline"),
853 'application/pdf': ('application/pdf', 'inline'),
841 "application/pdf": ("application/pdf", "inline"),
854 }
842 }
855
843
856 mimetype = file_node.mimetype
844 mimetype = file_node.mimetype
@@ -860,7 +848,7 class RepoFilesView(RepoAppView):
860 # we don't know anything special about this, handle it safely
848 # we don't know anything special about this, handle it safely
861 if file_node.is_binary:
849 if file_node.is_binary:
862 # do same as download raw for binary files
850 # do same as download raw for binary files
863 mimetype, disposition = 'application/octet-stream', 'attachment'
851 mimetype, disposition = "application/octet-stream", "attachment"
864 else:
852 else:
865 # do not just use the original mimetype, but force text/plain,
853 # do not just use the original mimetype, but force text/plain,
866 # otherwise it would serve text/html and that might be unsafe.
854 # otherwise it would serve text/html and that might be unsafe.
@@ -869,9 +857,9 class RepoFilesView(RepoAppView):
869 # binary.This might lead to erroneous text display in some
857 # binary.This might lead to erroneous text display in some
870 # cases, but helps in other cases, like with text files
858 # cases, but helps in other cases, like with text files
871 # without extension.
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 disposition = self._get_attachement_headers(f_path)
863 disposition = self._get_attachement_headers(f_path)
876
864
877 stream_content = file_node.stream_bytes()
865 stream_content = file_node.stream_bytes()
@@ -887,16 +875,15 class RepoFilesView(RepoAppView):
887 return response
875 return response
888
876
889 @LoginRequired()
877 @LoginRequired()
890 @HasRepoPermissionAnyDecorator(
878 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
891 'repository.read', 'repository.write', 'repository.admin')
892 def repo_file_download(self):
879 def repo_file_download(self):
893 c = self.load_default_context()
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 commit = self._get_commit_or_redirect(commit_id)
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 # only if lf get flag is passed, we download this file
887 # only if lf get flag is passed, we download this file
901 # as LFS/Largefile
888 # as LFS/Largefile
902 lf_node = file_node.get_largefile_node()
889 lf_node = file_node.get_largefile_node()
@@ -919,49 +906,41 class RepoFilesView(RepoAppView):
919 return response
906 return response
920
907
921 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
908 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
922
909 cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time")
923 cache_seconds = safe_int(
924 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
925 cache_on = cache_seconds > 0
910 cache_on = cache_seconds > 0
926 log.debug(
911 log.debug(
927 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`'
912 "Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`"
928 'with caching: %s[TTL: %ss]' % (
913 "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0)
929 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
914 )
930
915
931 cache_namespace_uid = f'repo.{repo_id}'
916 cache_namespace_uid = f"repo.{repo_id}"
932 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
917 region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid)
933
918
934 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
919 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
935 def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path):
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',
921 log.debug("Generating cached nodelist for repo_id:%s, %s, %s", _repo_id, commit_id, f_path)
937 _repo_id, commit_id, f_path)
938 try:
922 try:
939 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path)
923 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path)
940 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
924 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
941 log.exception(safe_str(e))
925 log.exception(safe_str(e))
942 h.flash(h.escape(safe_str(e)), category='error')
926 h.flash(h.escape(safe_str(e)), category="error")
943 raise HTTPFound(h.route_path(
927 raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path="/"))
944 'repo_files', repo_name=self.db_repo_name,
945 commit_id='tip', f_path='/'))
946
928
947 return _d + _f
929 return _d + _f
948
930
949 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id,
931 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path)
950 commit_id, f_path)
932 return filter(lambda n: self.path_filter.path_access_allowed(n["name"]), result)
951 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
952
933
953 @LoginRequired()
934 @LoginRequired()
954 @HasRepoPermissionAnyDecorator(
935 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
955 'repository.read', 'repository.write', 'repository.admin')
956 def repo_nodelist(self):
936 def repo_nodelist(self):
957 self.load_default_context()
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 commit = self._get_commit_or_redirect(commit_id)
940 commit = self._get_commit_or_redirect(commit_id)
961
941
962 metadata = self._get_nodelist_at_commit(
942 metadata = self._get_nodelist_at_commit(self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
963 self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
943 return {"nodes": [x for x in metadata]}
964 return {'nodes': [x for x in metadata]}
965
944
966 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
945 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
967 items = []
946 items = []
@@ -978,7 +957,7 class RepoFilesView(RepoAppView):
978
957
979 # NOTE(dan): old code we used in "diff" mode compare
958 # NOTE(dan): old code we used in "diff" mode compare
980 new_f_path = vcspath.join(name, f_path)
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 def _get_node_history(self, commit_obj, f_path, commits=None):
962 def _get_node_history(self, commit_obj, f_path, commits=None):
984 """
963 """
@@ -996,37 +975,34 class RepoFilesView(RepoAppView):
996 if commits is None:
975 if commits is None:
997 pre_load = ["author", "branch"]
976 pre_load = ["author", "branch"]
998 try:
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 except (NodeDoesNotExistError, CommitError):
979 except (NodeDoesNotExistError, CommitError):
1001 # this node is not present at tip!
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 history = []
983 history = []
1005 commits_group = ([], _("Changesets"))
984 commits_group = ([], _("Changesets"))
1006 for commit in commits:
985 for commit in commits:
1007 branch = ' (%s)' % commit.branch if commit.branch else ''
986 branch = " (%s)" % commit.branch if commit.branch else ""
1008 n_desc = f'r{commit.idx}:{commit.short_id}{branch}'
987 n_desc = f"r{commit.idx}:{commit.short_id}{branch}"
1009 commits_group[0].append((commit.raw_id, n_desc, 'sha'))
988 commits_group[0].append((commit.raw_id, n_desc, "sha"))
1010 history.append(commits_group)
989 history.append(commits_group)
1011
990
1012 symbolic_reference = self._symbolic_reference
991 symbolic_reference = self._symbolic_reference
1013
992
1014 if self.rhodecode_vcs_repo.alias == 'svn':
993 if self.rhodecode_vcs_repo.alias == "svn":
1015 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
994 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(f_path, self.rhodecode_vcs_repo)
1016 f_path, self.rhodecode_vcs_repo)
1017 if adjusted_f_path != f_path:
995 if adjusted_f_path != f_path:
1018 log.debug(
996 log.debug(
1019 'Recognized svn tag or branch in file "%s", using svn '
997 'Recognized svn tag or branch in file "%s", using svn ' "specific symbolic references", f_path
1020 'specific symbolic references', f_path)
998 )
1021 f_path = adjusted_f_path
999 f_path = adjusted_f_path
1022 symbolic_reference = self._symbolic_reference_svn
1000 symbolic_reference = self._symbolic_reference_svn
1023
1001
1024 branches = self._create_references(
1002 branches = self._create_references(self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, "branch")
1025 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch')
1026 branches_group = (branches, _("Branches"))
1003 branches_group = (branches, _("Branches"))
1027
1004
1028 tags = self._create_references(
1005 tags = self._create_references(self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, "tag")
1029 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag')
1030 tags_group = (tags, _("Tags"))
1006 tags_group = (tags, _("Tags"))
1031
1007
1032 history.append(branches_group)
1008 history.append(branches_group)
@@ -1035,14 +1011,13 class RepoFilesView(RepoAppView):
1035 return history, commits
1011 return history, commits
1036
1012
1037 @LoginRequired()
1013 @LoginRequired()
1038 @HasRepoPermissionAnyDecorator(
1014 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
1039 'repository.read', 'repository.write', 'repository.admin')
1040 def repo_file_history(self):
1015 def repo_file_history(self):
1041 self.load_default_context()
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 commit = self._get_commit_or_redirect(commit_id)
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 if file_node.is_file():
1022 if file_node.is_file():
1048 file_history, _hist = self._get_node_history(commit, f_path)
1023 file_history, _hist = self._get_node_history(commit, f_path)
@@ -1051,52 +1026,38 class RepoFilesView(RepoAppView):
1051 for section_items, section in file_history:
1026 for section_items, section in file_history:
1052 items = []
1027 items = []
1053 for obj_id, obj_text, obj_type in section_items:
1028 for obj_id, obj_text, obj_type in section_items:
1054 at_rev = ''
1029 at_rev = ""
1055 if obj_type in ['branch', 'bookmark', 'tag']:
1030 if obj_type in ["branch", "bookmark", "tag"]:
1056 at_rev = obj_text
1031 at_rev = obj_text
1057 entry = {
1032 entry = {"id": obj_id, "text": obj_text, "type": obj_type, "at_rev": at_rev}
1058 'id': obj_id,
1059 'text': obj_text,
1060 'type': obj_type,
1061 'at_rev': at_rev
1062 }
1063
1033
1064 items.append(entry)
1034 items.append(entry)
1065
1035
1066 res.append({
1036 res.append({"text": section, "children": items})
1067 'text': section,
1068 'children': items
1069 })
1070
1037
1071 data = {
1038 data = {"more": False, "results": res}
1072 'more': False,
1073 'results': res
1074 }
1075 return data
1039 return data
1076
1040
1077 log.warning('Cannot fetch history for directory')
1041 log.warning("Cannot fetch history for directory")
1078 raise HTTPBadRequest()
1042 raise HTTPBadRequest()
1079
1043
1080 @LoginRequired()
1044 @LoginRequired()
1081 @HasRepoPermissionAnyDecorator(
1045 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
1082 'repository.read', 'repository.write', 'repository.admin')
1083 def repo_file_authors(self):
1046 def repo_file_authors(self):
1084 c = self.load_default_context()
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 commit = self._get_commit_or_redirect(commit_id)
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 if not file_node.is_file():
1053 if not file_node.is_file():
1091 raise HTTPBadRequest()
1054 raise HTTPBadRequest()
1092
1055
1093 c.file_last_commit = file_node.last_commit
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 # use _hist from annotation if annotation mode is on
1058 # use _hist from annotation if annotation mode is on
1096 commit_ids = {x[1] for x in file_node.annotate}
1059 commit_ids = {x[1] for x in file_node.annotate}
1097 _hist = (
1060 _hist = (self.rhodecode_vcs_repo.get_commit(commit_id) for commit_id in commit_ids)
1098 self.rhodecode_vcs_repo.get_commit(commit_id)
1099 for commit_id in commit_ids)
1100 else:
1061 else:
1101 _f_history, _hist = self._get_node_history(commit, f_path)
1062 _f_history, _hist = self._get_node_history(commit, f_path)
1102 c.file_author = False
1063 c.file_author = False
@@ -1107,8 +1068,8 class RepoFilesView(RepoAppView):
1107 if author not in unique:
1068 if author not in unique:
1108 unique[commit.author] = [
1069 unique[commit.author] = [
1109 h.email(author),
1070 h.email(author),
1110 h.person(author, 'username_or_name_or_email'),
1071 h.person(author, "username_or_name_or_email"),
1111 1 # counter
1072 1, # counter
1112 ]
1073 ]
1113
1074
1114 else:
1075 else:
@@ -1120,204 +1081,186 class RepoFilesView(RepoAppView):
1120 return self._get_template_context(c)
1081 return self._get_template_context(c)
1121
1082
1122 @LoginRequired()
1083 @LoginRequired()
1123 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1084 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1124 def repo_files_check_head(self):
1085 def repo_files_check_head(self):
1125 self.load_default_context()
1086 self.load_default_context()
1126
1087
1127 commit_id, f_path = self._get_commit_and_path()
1088 commit_id, f_path, bytes_path = self._get_commit_and_path()
1128 _branch_name, _sha_commit_id, is_head = \
1089 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1129 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1090 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1130 landing_ref=self.db_repo.landing_ref_name)
1091 )
1131
1092
1132 new_path = self.request.POST.get('path')
1093 new_path = self.request.POST.get("path")
1133 operation = self.request.POST.get('operation')
1094 operation = self.request.POST.get("operation")
1134 path_exist = ''
1095 path_exist = ""
1135
1096
1136 if new_path and operation in ['create', 'upload']:
1097 if new_path and operation in ["create", "upload"]:
1137 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1098 new_f_path = os.path.join(f_path.lstrip("/"), new_path)
1138 try:
1099 try:
1139 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1100 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1140 # NOTE(dan): construct whole path without leading /
1101 # NOTE(dan): construct whole path without leading /
1141 file_node = commit_obj.get_node(new_f_path)
1102 file_node = commit_obj.get_node(safe_bytes(new_f_path))
1142 if file_node is not None:
1103 if file_node:
1143 path_exist = new_f_path
1104 path_exist = new_f_path
1144 except EmptyRepositoryError:
1105 except (EmptyRepositoryError, NodeDoesNotExistError):
1145 pass
1146 except Exception:
1147 pass
1106 pass
1148
1107
1149 return {
1108 return {"branch": _branch_name, "sha": _sha_commit_id, "is_head": is_head, "path_exists": path_exist}
1150 'branch': _branch_name,
1151 'sha': _sha_commit_id,
1152 'is_head': is_head,
1153 'path_exists': path_exist
1154 }
1155
1109
1156 @LoginRequired()
1110 @LoginRequired()
1157 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1111 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1158 def repo_files_remove_file(self):
1112 def repo_files_remove_file(self):
1159 _ = self.request.translate
1113 _ = self.request.translate
1160 c = self.load_default_context()
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 self._ensure_not_locked()
1117 self._ensure_not_locked()
1164 _branch_name, _sha_commit_id, is_head = \
1118 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1165 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1119 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1166 landing_ref=self.db_repo.landing_ref_name)
1120 )
1167
1121
1168 self.forbid_non_head(is_head, f_path)
1122 self.forbid_non_head(is_head, f_path)
1169 self.check_branch_permission(_branch_name)
1123 self.check_branch_permission(_branch_name)
1170
1124
1171 c.commit = self._get_commit_or_redirect(commit_id)
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 = _(
1128 c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path)
1175 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1176 c.f_path = f_path
1129 c.f_path = f_path
1177
1130
1178 return self._get_template_context(c)
1131 return self._get_template_context(c)
1179
1132
1180 @LoginRequired()
1133 @LoginRequired()
1181 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1134 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1182 @CSRFRequired()
1135 @CSRFRequired()
1183 def repo_files_delete_file(self):
1136 def repo_files_delete_file(self):
1184 _ = self.request.translate
1137 _ = self.request.translate
1185
1138
1186 c = self.load_default_context()
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 self._ensure_not_locked()
1142 self._ensure_not_locked()
1190 _branch_name, _sha_commit_id, is_head = \
1143 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1191 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1144 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1192 landing_ref=self.db_repo.landing_ref_name)
1145 )
1193
1146
1194 self.forbid_non_head(is_head, f_path)
1147 self.forbid_non_head(is_head, f_path)
1195 self.check_branch_permission(_branch_name)
1148 self.check_branch_permission(_branch_name)
1196
1149
1197 c.commit = self._get_commit_or_redirect(commit_id)
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 = _(
1153 c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path)
1201 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1202 c.f_path = f_path
1154 c.f_path = f_path
1203 node_path = f_path
1155 node_path = f_path
1204 author = self._rhodecode_db_user.full_contact
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 try:
1158 try:
1207 nodes = {
1159 nodes = {safe_bytes(node_path): {"content": b""}}
1208 safe_bytes(node_path): {
1209 'content': b''
1210 }
1211 }
1212 ScmModel().delete_nodes(
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 message=message,
1163 message=message,
1215 nodes=nodes,
1164 nodes=nodes,
1216 parent_commit=c.commit,
1165 parent_commit=c.commit,
1217 author=author,
1166 author=author,
1218 )
1167 )
1219
1168
1220 h.flash(
1169 h.flash(_("Successfully deleted file `{}`").format(h.escape(f_path)), category="success")
1221 _('Successfully deleted file `{}`').format(
1222 h.escape(f_path)), category='success')
1223 except Exception:
1170 except Exception:
1224 log.exception('Error during commit operation')
1171 log.exception("Error during commit operation")
1225 h.flash(_('Error occurred during commit'), category='error')
1172 h.flash(_("Error occurred during commit"), category="error")
1226 raise HTTPFound(
1173 raise HTTPFound(h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip"))
1227 h.route_path('repo_commit', repo_name=self.db_repo_name,
1228 commit_id='tip'))
1229
1174
1230 @LoginRequired()
1175 @LoginRequired()
1231 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1176 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1232 def repo_files_edit_file(self):
1177 def repo_files_edit_file(self):
1233 _ = self.request.translate
1178 _ = self.request.translate
1234 c = self.load_default_context()
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 self._ensure_not_locked()
1182 self._ensure_not_locked()
1238 _branch_name, _sha_commit_id, is_head = \
1183 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1239 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1184 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1240 landing_ref=self.db_repo.landing_ref_name)
1185 )
1241
1186
1242 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1187 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1243 self.check_branch_permission(_branch_name, commit_id=commit_id)
1188 self.check_branch_permission(_branch_name, commit_id=commit_id)
1244
1189
1245 c.commit = self._get_commit_or_redirect(commit_id)
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 if c.file.is_binary:
1193 if c.file.is_binary:
1249 files_url = h.route_path(
1194 files_url = h.route_path(
1250 'repo_files',
1195 "repo_files", repo_name=self.db_repo_name, commit_id=c.commit.raw_id, f_path=f_path
1251 repo_name=self.db_repo_name,
1196 )
1252 commit_id=c.commit.raw_id, f_path=f_path)
1253 raise HTTPFound(files_url)
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 c.f_path = f_path
1200 c.f_path = f_path
1257
1201
1258 return self._get_template_context(c)
1202 return self._get_template_context(c)
1259
1203
1260 @LoginRequired()
1204 @LoginRequired()
1261 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1205 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1262 @CSRFRequired()
1206 @CSRFRequired()
1263 def repo_files_update_file(self):
1207 def repo_files_update_file(self):
1264 _ = self.request.translate
1208 _ = self.request.translate
1265 c = self.load_default_context()
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 self._ensure_not_locked()
1212 self._ensure_not_locked()
1269
1213
1270 c.commit = self._get_commit_or_redirect(commit_id)
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 if c.file.is_binary:
1217 if c.file.is_binary:
1274 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1218 raise HTTPFound(
1275 commit_id=c.commit.raw_id, f_path=f_path))
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 = \
1222 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1278 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1223 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1279 landing_ref=self.db_repo.landing_ref_name)
1224 )
1280
1225
1281 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1226 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1282 self.check_branch_permission(_branch_name, commit_id=commit_id)
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 c.f_path = f_path
1230 c.f_path = f_path
1286
1231
1287 old_content = c.file.str_content
1232 old_content = c.file.str_content
1288 sl = old_content.splitlines(1)
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 r_post = self.request.POST
1236 r_post = self.request.POST
1292 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1237 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1293 line_ending_mode = detect_mode(first_line, 0)
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 org_node_path = c.file.str_path
1243 org_node_path = c.file.str_path
1299 filename = r_post['filename']
1244 filename = r_post["filename"]
1300
1245
1301 root_path = c.file.dir_path
1246 root_path = c.file.dir_path
1302 pure_path = self.create_pure_path(root_path, filename)
1247 pure_path = self.create_pure_path(root_path, filename)
1303 node_path = pure_path.as_posix()
1248 node_path = pure_path.as_posix()
1304
1249
1305 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1250 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit_id)
1306 commit_id=commit_id)
1307 if content == old_content and node_path == org_node_path:
1251 if content == old_content and node_path == org_node_path:
1308 h.flash(_('No changes detected on {}').format(h.escape(org_node_path)),
1252 h.flash(_("No changes detected on {}").format(h.escape(org_node_path)), category="warning")
1309 category='warning')
1310 raise HTTPFound(default_redirect_url)
1253 raise HTTPFound(default_redirect_url)
1311
1254
1312 try:
1255 try:
1313 mapping = {
1256 mapping = {
1314 c.file.bytes_path: {
1257 c.file.bytes_path: {
1315 'org_filename': org_node_path,
1258 "org_filename": org_node_path,
1316 'filename': safe_bytes(node_path),
1259 "filename": safe_bytes(node_path),
1317 'content': safe_bytes(content),
1260 "content": safe_bytes(content),
1318 'lexer': '',
1261 "lexer": "",
1319 'op': 'mod',
1262 "op": "mod",
1320 'mode': c.file.mode
1263 "mode": c.file.mode,
1321 }
1264 }
1322 }
1265 }
1323
1266
@@ -1329,28 +1272,26 class RepoFilesView(RepoAppView):
1329 parent_commit=c.commit,
1272 parent_commit=c.commit,
1330 )
1273 )
1331
1274
1332 h.flash(_('Successfully committed changes to file `{}`').format(
1275 h.flash(_("Successfully committed changes to file `{}`").format(h.escape(f_path)), category="success")
1333 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)
1334 default_redirect_url = h.route_path(
1335 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1336
1277
1337 except Exception:
1278 except Exception:
1338 log.exception('Error occurred during commit')
1279 log.exception("Error occurred during commit")
1339 h.flash(_('Error occurred during commit'), category='error')
1280 h.flash(_("Error occurred during commit"), category="error")
1340
1281
1341 raise HTTPFound(default_redirect_url)
1282 raise HTTPFound(default_redirect_url)
1342
1283
1343 @LoginRequired()
1284 @LoginRequired()
1344 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1285 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1345 def repo_files_add_file(self):
1286 def repo_files_add_file(self):
1346 _ = self.request.translate
1287 _ = self.request.translate
1347 c = self.load_default_context()
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 self._ensure_not_locked()
1291 self._ensure_not_locked()
1351
1292
1352 # Check if we need to use this page to upload binary
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 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1296 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1356 if c.commit is None:
1297 if c.commit is None:
@@ -1359,29 +1300,32 class RepoFilesView(RepoAppView):
1359 if self.rhodecode_vcs_repo.is_empty():
1300 if self.rhodecode_vcs_repo.is_empty():
1360 # for empty repository we cannot check for current branch, we rely on
1301 # for empty repository we cannot check for current branch, we rely on
1361 # c.commit.branch instead
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 else:
1304 else:
1364 _branch_name, _sha_commit_id, is_head = \
1305 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1365 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1306 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1366 landing_ref=self.db_repo.landing_ref_name)
1307 )
1367
1308
1368 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1309 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1369 self.check_branch_permission(_branch_name, commit_id=commit_id)
1310 self.check_branch_permission(_branch_name, commit_id=commit_id)
1370
1311
1371 c.default_message = (_('Added file via RhodeCode Enterprise')) \
1312 c.default_message = (
1372 if not upload_binary else (_('Edited file {} via RhodeCode Enterprise').format(f_path))
1313 (_("Added file via RhodeCode Enterprise"))
1373 c.f_path = f_path.lstrip('/') # ensure not relative path
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 c.replace_binary = upload_binary
1318 c.replace_binary = upload_binary
1375
1319
1376 return self._get_template_context(c)
1320 return self._get_template_context(c)
1377
1321
1378 @LoginRequired()
1322 @LoginRequired()
1379 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1323 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1380 @CSRFRequired()
1324 @CSRFRequired()
1381 def repo_files_create_file(self):
1325 def repo_files_create_file(self):
1382 _ = self.request.translate
1326 _ = self.request.translate
1383 c = self.load_default_context()
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 self._ensure_not_locked()
1330 self._ensure_not_locked()
1387
1331
@@ -1391,56 +1335,48 class RepoFilesView(RepoAppView):
1391
1335
1392 # calculate redirect URL
1336 # calculate redirect URL
1393 if self.rhodecode_vcs_repo.is_empty():
1337 if self.rhodecode_vcs_repo.is_empty():
1394 default_redirect_url = h.route_path(
1338 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1395 'repo_summary', repo_name=self.db_repo_name)
1396 else:
1339 else:
1397 default_redirect_url = h.route_path(
1340 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1398 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1399
1341
1400 if self.rhodecode_vcs_repo.is_empty():
1342 if self.rhodecode_vcs_repo.is_empty():
1401 # for empty repository we cannot check for current branch, we rely on
1343 # for empty repository we cannot check for current branch, we rely on
1402 # c.commit.branch instead
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 else:
1346 else:
1405 _branch_name, _sha_commit_id, is_head = \
1347 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1406 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1348 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1407 landing_ref=self.db_repo.landing_ref_name)
1349 )
1408
1350
1409 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1351 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1410 self.check_branch_permission(_branch_name, commit_id=commit_id)
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 c.f_path = f_path
1355 c.f_path = f_path
1414
1356
1415 r_post = self.request.POST
1357 r_post = self.request.POST
1416 message = r_post.get('message') or c.default_message
1358 message = r_post.get("message") or c.default_message
1417 filename = r_post.get('filename')
1359 filename = r_post.get("filename")
1418 unix_mode = 0
1360 unix_mode = 0
1419
1361
1420 if not filename:
1362 if not filename:
1421 # If there's no commit, redirect to repo summary
1363 # If there's no commit, redirect to repo summary
1422 if type(c.commit) is EmptyCommit:
1364 if type(c.commit) is EmptyCommit:
1423 redirect_url = h.route_path(
1365 redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1424 'repo_summary', repo_name=self.db_repo_name)
1425 else:
1366 else:
1426 redirect_url = default_redirect_url
1367 redirect_url = default_redirect_url
1427 h.flash(_('No filename specified'), category='warning')
1368 h.flash(_("No filename specified"), category="warning")
1428 raise HTTPFound(redirect_url)
1369 raise HTTPFound(redirect_url)
1429
1370
1430 root_path = f_path
1371 root_path = f_path
1431 pure_path = self.create_pure_path(root_path, filename)
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 author = self._rhodecode_db_user.full_contact
1375 author = self._rhodecode_db_user.full_contact
1435 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1376 content = convert_line_endings(r_post.get("content", ""), unix_mode)
1436 nodes = {
1377 nodes = {safe_bytes(node_path): {"content": safe_bytes(content)}}
1437 safe_bytes(node_path): {
1438 'content': safe_bytes(content)
1439 }
1440 }
1441
1378
1442 try:
1379 try:
1443
1444 commit = ScmModel().create_nodes(
1380 commit = ScmModel().create_nodes(
1445 user=self._rhodecode_db_user.user_id,
1381 user=self._rhodecode_db_user.user_id,
1446 repo=self.db_repo,
1382 repo=self.db_repo,
@@ -1450,32 +1386,32 class RepoFilesView(RepoAppView):
1450 author=author,
1386 author=author,
1451 )
1387 )
1452
1388
1453 h.flash(_('Successfully committed new file `{}`').format(
1389 h.flash(_("Successfully committed new file `{}`").format(h.escape(node_path)), category="success")
1454 h.escape(node_path)), category='success')
1455
1390
1456 default_redirect_url = h.route_path(
1391 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1457 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1458
1392
1459 except NonRelativePathError:
1393 except NonRelativePathError:
1460 log.exception('Non Relative path found')
1394 log.exception("Non Relative path found")
1461 h.flash(_('The location specified must be a relative path and must not '
1395 h.flash(
1462 'contain .. in the path'), category='warning')
1396 _("The location specified must be a relative path and must not " "contain .. in the path"),
1397 category="warning",
1398 )
1463 raise HTTPFound(default_redirect_url)
1399 raise HTTPFound(default_redirect_url)
1464 except (NodeError, NodeAlreadyExistsError) as e:
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 except Exception:
1402 except Exception:
1467 log.exception('Error occurred during commit')
1403 log.exception("Error occurred during commit")
1468 h.flash(_('Error occurred during commit'), category='error')
1404 h.flash(_("Error occurred during commit"), category="error")
1469
1405
1470 raise HTTPFound(default_redirect_url)
1406 raise HTTPFound(default_redirect_url)
1471
1407
1472 @LoginRequired()
1408 @LoginRequired()
1473 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1409 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1474 @CSRFRequired()
1410 @CSRFRequired()
1475 def repo_files_upload_file(self):
1411 def repo_files_upload_file(self):
1476 _ = self.request.translate
1412 _ = self.request.translate
1477 c = self.load_default_context()
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 self._ensure_not_locked()
1416 self._ensure_not_locked()
1481
1417
@@ -1485,65 +1421,52 class RepoFilesView(RepoAppView):
1485
1421
1486 # calculate redirect URL
1422 # calculate redirect URL
1487 if self.rhodecode_vcs_repo.is_empty():
1423 if self.rhodecode_vcs_repo.is_empty():
1488 default_redirect_url = h.route_path(
1424 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1489 'repo_summary', repo_name=self.db_repo_name)
1490 else:
1425 else:
1491 default_redirect_url = h.route_path(
1426 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1492 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1493
1427
1494 if self.rhodecode_vcs_repo.is_empty():
1428 if self.rhodecode_vcs_repo.is_empty():
1495 # for empty repository we cannot check for current branch, we rely on
1429 # for empty repository we cannot check for current branch, we rely on
1496 # c.commit.branch instead
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 else:
1432 else:
1499 _branch_name, _sha_commit_id, is_head = \
1433 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1500 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1434 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1501 landing_ref=self.db_repo.landing_ref_name)
1435 )
1502
1436
1503 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1437 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1504 if error:
1438 if error:
1505 return {
1439 return {"error": error, "redirect_url": default_redirect_url}
1506 'error': error,
1507 'redirect_url': default_redirect_url
1508 }
1509 error = self.check_branch_permission(_branch_name, json_mode=True)
1440 error = self.check_branch_permission(_branch_name, json_mode=True)
1510 if error:
1441 if error:
1511 return {
1442 return {"error": error, "redirect_url": default_redirect_url}
1512 'error': error,
1513 'redirect_url': default_redirect_url
1514 }
1515
1443
1516 c.default_message = (_('Added file via RhodeCode Enterprise'))
1444 c.default_message = _("Added file via RhodeCode Enterprise")
1517 c.f_path = f_path
1445 c.f_path = f_path
1518
1446
1519 r_post = self.request.POST
1447 r_post = self.request.POST
1520
1448
1521 message = c.default_message
1449 message = c.default_message
1522 user_message = r_post.getall('message')
1450 user_message = r_post.getall("message")
1523 if isinstance(user_message, list) and user_message:
1451 if isinstance(user_message, list) and user_message:
1524 # we take the first from duplicated results if it's not empty
1452 # we take the first from duplicated results if it's not empty
1525 message = user_message[0] if user_message[0] else message
1453 message = user_message[0] if user_message[0] else message
1526
1454
1527 nodes = {}
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 content = file_obj.file
1458 content = file_obj.file
1531 filename = file_obj.filename
1459 filename = file_obj.filename
1532
1460
1533 root_path = f_path
1461 root_path = f_path
1534 pure_path = self.create_pure_path(root_path, filename)
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)] = {
1465 nodes[safe_bytes(node_path)] = {"content": content}
1538 'content': content
1539 }
1540
1466
1541 if not nodes:
1467 if not nodes:
1542 error = 'missing files'
1468 error = "missing files"
1543 return {
1469 return {"error": error, "redirect_url": default_redirect_url}
1544 'error': error,
1545 'redirect_url': default_redirect_url
1546 }
1547
1470
1548 author = self._rhodecode_db_user.full_contact
1471 author = self._rhodecode_db_user.full_contact
1549
1472
@@ -1557,54 +1480,40 class RepoFilesView(RepoAppView):
1557 author=author,
1480 author=author,
1558 )
1481 )
1559 if len(nodes) == 1:
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 else:
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(
1489 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1567 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1568
1490
1569 except NonRelativePathError:
1491 except NonRelativePathError:
1570 log.exception('Non Relative path found')
1492 log.exception("Non Relative path found")
1571 error = _('The location specified must be a relative path and must not '
1493 error = _("The location specified must be a relative path and must not " "contain .. in the path")
1572 'contain .. in the path')
1494 h.flash(error, category="warning")
1573 h.flash(error, category='warning')
1574
1495
1575 return {
1496 return {"error": error, "redirect_url": default_redirect_url}
1576 'error': error,
1577 'redirect_url': default_redirect_url
1578 }
1579 except (NodeError, NodeAlreadyExistsError) as e:
1497 except (NodeError, NodeAlreadyExistsError) as e:
1580 error = h.escape(e)
1498 error = h.escape(e)
1581 h.flash(error, category='error')
1499 h.flash(error, category="error")
1582
1500
1583 return {
1501 return {"error": error, "redirect_url": default_redirect_url}
1584 'error': error,
1585 'redirect_url': default_redirect_url
1586 }
1587 except Exception:
1502 except Exception:
1588 log.exception('Error occurred during commit')
1503 log.exception("Error occurred during commit")
1589 error = _('Error occurred during commit')
1504 error = _("Error occurred during commit")
1590 h.flash(error, category='error')
1505 h.flash(error, category="error")
1591 return {
1506 return {"error": error, "redirect_url": default_redirect_url}
1592 'error': error,
1593 'redirect_url': default_redirect_url
1594 }
1595
1507
1596 return {
1508 return {"error": None, "redirect_url": default_redirect_url}
1597 'error': None,
1598 'redirect_url': default_redirect_url
1599 }
1600
1509
1601 @LoginRequired()
1510 @LoginRequired()
1602 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1511 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1603 @CSRFRequired()
1512 @CSRFRequired()
1604 def repo_files_replace_file(self):
1513 def repo_files_replace_file(self):
1605 _ = self.request.translate
1514 _ = self.request.translate
1606 c = self.load_default_context()
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 self._ensure_not_locked()
1518 self._ensure_not_locked()
1610
1519
@@ -1613,64 +1522,50 class RepoFilesView(RepoAppView):
1613 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1522 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1614
1523
1615 if self.rhodecode_vcs_repo.is_empty():
1524 if self.rhodecode_vcs_repo.is_empty():
1616 default_redirect_url = h.route_path(
1525 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1617 'repo_summary', repo_name=self.db_repo_name)
1618 else:
1526 else:
1619 default_redirect_url = h.route_path(
1527 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1620 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1621
1528
1622 if self.rhodecode_vcs_repo.is_empty():
1529 if self.rhodecode_vcs_repo.is_empty():
1623 # for empty repository we cannot check for current branch, we rely on
1530 # for empty repository we cannot check for current branch, we rely on
1624 # c.commit.branch instead
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 else:
1533 else:
1627 _branch_name, _sha_commit_id, is_head = \
1534 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1628 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1535 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1629 landing_ref=self.db_repo.landing_ref_name)
1536 )
1630
1537
1631 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1538 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1632 if error:
1539 if error:
1633 return {
1540 return {"error": error, "redirect_url": default_redirect_url}
1634 'error': error,
1635 'redirect_url': default_redirect_url
1636 }
1637 error = self.check_branch_permission(_branch_name, json_mode=True)
1541 error = self.check_branch_permission(_branch_name, json_mode=True)
1638 if error:
1542 if error:
1639 return {
1543 return {"error": error, "redirect_url": default_redirect_url}
1640 'error': error,
1641 'redirect_url': default_redirect_url
1642 }
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 c.f_path = f_path
1546 c.f_path = f_path
1646
1547
1647 r_post = self.request.POST
1548 r_post = self.request.POST
1648
1549
1649 message = c.default_message
1550 message = c.default_message
1650 user_message = r_post.getall('message')
1551 user_message = r_post.getall("message")
1651 if isinstance(user_message, list) and user_message:
1552 if isinstance(user_message, list) and user_message:
1652 # we take the first from duplicated results if it's not empty
1553 # we take the first from duplicated results if it's not empty
1653 message = user_message[0] if user_message[0] else message
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 if (objects_count := len(data_for_replacement)) > 1:
1557 if (objects_count := len(data_for_replacement)) > 1:
1657 return {
1558 return {"error": "too many files for replacement", "redirect_url": default_redirect_url}
1658 'error': 'too many files for replacement',
1659 'redirect_url': default_redirect_url
1660 }
1661 elif not objects_count:
1559 elif not objects_count:
1662 return {
1560 return {"error": "missing files", "redirect_url": default_redirect_url}
1663 'error': 'missing files',
1664 'redirect_url': default_redirect_url
1665 }
1666
1561
1667 content = data_for_replacement[0].file
1562 content = data_for_replacement[0].file
1668 retrieved_filename = data_for_replacement[0].filename
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 return {
1566 return {
1672 'error': 'file extension of uploaded file doesn\'t match an original file\'s extension',
1567 "error": "file extension of uploaded file doesn't match an original file's extension",
1673 'redirect_url': default_redirect_url
1568 "redirect_url": default_redirect_url,
1674 }
1569 }
1675
1570
1676 author = self._rhodecode_db_user.full_contact
1571 author = self._rhodecode_db_user.full_contact
@@ -1681,36 +1576,26 class RepoFilesView(RepoAppView):
1681 repo=self.db_repo,
1576 repo=self.db_repo,
1682 message=message,
1577 message=message,
1683 node={
1578 node={
1684 'content': content,
1579 "content": content,
1685 'file_path': f_path.encode(),
1580 "file_path": f_path.encode(),
1686 },
1581 },
1687 parent_commit=c.commit,
1582 parent_commit=c.commit,
1688 author=author,
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(
1588 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1694 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1695
1589
1696 except (NodeError, NodeAlreadyExistsError) as e:
1590 except (NodeError, NodeAlreadyExistsError) as e:
1697 error = h.escape(e)
1591 error = h.escape(e)
1698 h.flash(error, category='error')
1592 h.flash(error, category="error")
1699
1593
1700 return {
1594 return {"error": error, "redirect_url": default_redirect_url}
1701 'error': error,
1702 'redirect_url': default_redirect_url
1703 }
1704 except Exception:
1595 except Exception:
1705 log.exception('Error occurred during commit')
1596 log.exception("Error occurred during commit")
1706 error = _('Error occurred during commit')
1597 error = _("Error occurred during commit")
1707 h.flash(error, category='error')
1598 h.flash(error, category="error")
1708 return {
1599 return {"error": error, "redirect_url": default_redirect_url}
1709 'error': error,
1710 'redirect_url': default_redirect_url
1711 }
1712
1600
1713 return {
1601 return {"error": None, "redirect_url": default_redirect_url}
1714 'error': None,
1715 'redirect_url': default_redirect_url
1716 }
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -487,7 +487,9 class RepoPullRequestsView(RepoAppView,
487 c.pull_request_set_reviewers_data_json = ext_json.str_json(c.pull_request_set_reviewers_data_json)
487 c.pull_request_set_reviewers_data_json = ext_json.str_json(c.pull_request_set_reviewers_data_json)
488
488
489 # observers
489 # observers
490 observer_ids = []
490 for observer_obj, member in pull_request_at_ver.observers():
491 for observer_obj, member in pull_request_at_ver.observers():
492 observer_ids.append(observer_obj.user_id)
491 member_observer = h.reviewer_as_json(
493 member_observer = h.reviewer_as_json(
492 member, reasons=[], mandatory=False,
494 member, reasons=[], mandatory=False,
493 role=observer_obj.role,
495 role=observer_obj.role,
@@ -497,6 +499,7 class RepoPullRequestsView(RepoAppView,
497 c.pull_request_set_observers_data_json['observers'].append(member_observer)
499 c.pull_request_set_observers_data_json['observers'].append(member_observer)
498
500
499 c.pull_request_set_observers_data_json = ext_json.str_json(c.pull_request_set_observers_data_json)
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 general_comments, inline_comments = \
504 general_comments, inline_comments = \
502 self.register_comments_vars(c, pull_request_latest, versions)
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,7 +20,7 import os
20 import sys
20 import sys
21 import logging
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 from rhodecode.lib.ext_json import sjson as json
24 from rhodecode.lib.ext_json import sjson as json
25 from rhodecode.lib.vcs.conf import settings as vcs_settings
25 from rhodecode.lib.vcs.conf import settings as vcs_settings
26 from rhodecode.lib.api_utils import call_service_api
26 from rhodecode.lib.api_utils import call_service_api
@@ -162,9 +162,7 class SshVcsServer(object):
162 extras = {}
162 extras = {}
163 extras.update(tunnel_extras)
163 extras.update(tunnel_extras)
164
164
165 callback_daemon, extras = prepare_callback_daemon(
165 callback_daemon, extras = prepare_callback_daemon(extras, protocol=self.hooks_protocol)
166 extras, protocol=self.hooks_protocol,
167 host=vcs_settings.HOOKS_HOST)
168
166
169 with callback_daemon:
167 with callback_daemon:
170 try:
168 try:
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -25,7 +25,7 import tempfile
25 from subprocess import Popen, PIPE
25 from subprocess import Popen, PIPE
26 import urllib.parse
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 from .base import SshVcsServer
29 from .base import SshVcsServer
30
30
31 log = logging.getLogger(__name__)
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -33,19 +33,24 class GitServerCreator(object):
33 'app:main': {
33 'app:main': {
34 'ssh.executable.git': git_path,
34 'ssh.executable.git': git_path,
35 'vcs.hooks.protocol.v2': 'celery',
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 repo_name = 'test_git'
41 repo_name = 'test_git'
39 repo_mode = 'receive-pack'
42 repo_mode = 'receive-pack'
40 user = plain_dummy_user()
43 user = plain_dummy_user()
41
44
42 def __init__(self):
45 def __init__(self, service_api_url, ini_file):
43 pass
46 self.service_api_url = service_api_url
47 self.ini_file = ini_file
44
48
45 def create(self, **kwargs):
49 def create(self, **kwargs):
50 self.config_data['app:main']['app.service_api.host'] = self.service_api_url
46 parameters = {
51 parameters = {
47 'store': self.root,
52 'store': self.root,
48 'ini_path': '',
53 'ini_path': self.ini_file,
49 'user': self.user,
54 'user': self.user,
50 'repo_name': self.repo_name,
55 'repo_name': self.repo_name,
51 'repo_mode': self.repo_mode,
56 'repo_mode': self.repo_mode,
@@ -60,12 +65,30 class GitServerCreator(object):
60 return server
65 return server
61
66
62
67
63 @pytest.fixture()
68 @pytest.fixture(scope='module')
64 def git_server(app):
69 def git_server(request, module_app, rhodecode_factory, available_port_factory):
65 return GitServerCreator()
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 def test_command(self, git_server):
93 def test_command(self, git_server):
71 server = git_server.create()
94 server = git_server.create()
@@ -102,14 +125,14 class TestGitServer(object):
102 assert result is value
125 assert result is value
103
126
104 def test_run_returns_executes_command(self, git_server):
127 def test_run_returns_executes_command(self, git_server):
128 from rhodecode.apps.ssh_support.lib.backends.git import GitTunnelWrapper
105 server = git_server.create()
129 server = git_server.create()
106 from rhodecode.apps.ssh_support.lib.backends.git import GitTunnelWrapper
107
130
108 os.environ['SSH_CLIENT'] = '127.0.0.1'
131 os.environ['SSH_CLIENT'] = '127.0.0.1'
109 with mock.patch.object(GitTunnelWrapper, 'create_hooks_env') as _patch:
132 with mock.patch.object(GitTunnelWrapper, 'create_hooks_env') as _patch:
110 _patch.return_value = 0
133 _patch.return_value = 0
111 with mock.patch.object(GitTunnelWrapper, 'command', return_value='date'):
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 assert exit_code == (0, False)
137 assert exit_code == (0, False)
115
138
@@ -135,7 +158,7 class TestGitServer(object):
135 'action': action,
158 'action': action,
136 'ip': '10.10.10.10',
159 'ip': '10.10.10.10',
137 'locked_by': [None, None],
160 'locked_by': [None, None],
138 'config': '',
161 'config': git_server.ini_file,
139 'repo_store': store,
162 'repo_store': store,
140 'server_url': None,
163 'server_url': None,
141 'hooks': ['push', 'pull'],
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -17,6 +17,7
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import os
19 import os
20
20 import mock
21 import mock
21 import pytest
22 import pytest
22
23
@@ -32,22 +33,27 class MercurialServerCreator(object):
32 'app:main': {
33 'app:main': {
33 'ssh.executable.hg': hg_path,
34 'ssh.executable.hg': hg_path,
34 'vcs.hooks.protocol.v2': 'celery',
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 repo_name = 'test_hg'
41 repo_name = 'test_hg'
38 user = plain_dummy_user()
42 user = plain_dummy_user()
39
43
40 def __init__(self):
44 def __init__(self, service_api_url, ini_file):
41 pass
45 self.service_api_url = service_api_url
46 self.ini_file = ini_file
42
47
43 def create(self, **kwargs):
48 def create(self, **kwargs):
49 self.config_data['app:main']['app.service_api.host'] = self.service_api_url
44 parameters = {
50 parameters = {
45 'store': self.root,
51 'store': self.root,
46 'ini_path': '',
52 'ini_path': self.ini_file,
47 'user': self.user,
53 'user': self.user,
48 'repo_name': self.repo_name,
54 'repo_name': self.repo_name,
49 'user_permissions': {
55 'user_permissions': {
50 'test_hg': 'repository.admin'
56 self.repo_name: 'repository.admin'
51 },
57 },
52 'settings': self.config_data['app:main'],
58 'settings': self.config_data['app:main'],
53 'env': plain_dummy_env()
59 'env': plain_dummy_env()
@@ -57,12 +63,30 class MercurialServerCreator(object):
57 return server
63 return server
58
64
59
65
60 @pytest.fixture()
66 @pytest.fixture(scope='module')
61 def hg_server(app):
67 def hg_server(request, module_app, rhodecode_factory, available_port_factory):
62 return MercurialServerCreator()
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 def test_command(self, hg_server, tmpdir):
91 def test_command(self, hg_server, tmpdir):
68 server = hg_server.create()
92 server = hg_server.create()
@@ -107,7 +131,7 class TestMercurialServer(object):
107 with mock.patch.object(MercurialTunnelWrapper, 'create_hooks_env') as _patch:
131 with mock.patch.object(MercurialTunnelWrapper, 'create_hooks_env') as _patch:
108 _patch.return_value = 0
132 _patch.return_value = 0
109 with mock.patch.object(MercurialTunnelWrapper, 'command', return_value='date'):
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 assert exit_code == (0, False)
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -15,7 +15,9
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18 import os
19 import os
20
19 import mock
21 import mock
20 import pytest
22 import pytest
21
23
@@ -26,39 +28,62 from rhodecode.apps.ssh_support.tests.co
26 class SubversionServerCreator(object):
28 class SubversionServerCreator(object):
27 root = '/tmp/repo/path/'
29 root = '/tmp/repo/path/'
28 svn_path = '/usr/local/bin/svnserve'
30 svn_path = '/usr/local/bin/svnserve'
31
29 config_data = {
32 config_data = {
30 'app:main': {
33 'app:main': {
31 'ssh.executable.svn': svn_path,
34 'ssh.executable.svn': svn_path,
32 'vcs.hooks.protocol.v2': 'celery',
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 repo_name = 'test-svn'
41 repo_name = 'test-svn'
36 user = plain_dummy_user()
42 user = plain_dummy_user()
37
43
38 def __init__(self):
44 def __init__(self, service_api_url, ini_file):
39 pass
45 self.service_api_url = service_api_url
46 self.ini_file = ini_file
40
47
41 def create(self, **kwargs):
48 def create(self, **kwargs):
49 self.config_data['app:main']['app.service_api.host'] = self.service_api_url
42 parameters = {
50 parameters = {
43 'store': self.root,
51 'store': self.root,
52 'ini_path': self.ini_file,
53 'user': self.user,
44 'repo_name': self.repo_name,
54 'repo_name': self.repo_name,
45 'ini_path': '',
46 'user': self.user,
47 'user_permissions': {
55 'user_permissions': {
48 self.repo_name: 'repository.admin'
56 self.repo_name: 'repository.admin'
49 },
57 },
50 'settings': self.config_data['app:main'],
58 'settings': self.config_data['app:main'],
51 'env': plain_dummy_env()
59 'env': plain_dummy_env()
52 }
60 }
53
54 parameters.update(kwargs)
61 parameters.update(kwargs)
55 server = SubversionServer(**parameters)
62 server = SubversionServer(**parameters)
56 return server
63 return server
57
64
58
65
59 @pytest.fixture()
66 @pytest.fixture(scope='module')
60 def svn_server(app):
67 def svn_server(request, module_app, rhodecode_factory, available_port_factory):
61 return SubversionServerCreator()
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 class TestSubversionServer(object):
89 class TestSubversionServer(object):
@@ -168,8 +193,9 class TestSubversionServer(object):
168 assert repo_name == expected_match
193 assert repo_name == expected_match
169
194
170 def test_run_returns_executes_command(self, svn_server):
195 def test_run_returns_executes_command(self, svn_server):
196 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
197
171 server = svn_server.create()
198 server = svn_server.create()
172 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
173 os.environ['SSH_CLIENT'] = '127.0.0.1'
199 os.environ['SSH_CLIENT'] = '127.0.0.1'
174 with mock.patch.object(
200 with mock.patch.object(
175 SubversionTunnelWrapper, 'get_first_client_response',
201 SubversionTunnelWrapper, 'get_first_client_response',
@@ -184,20 +210,18 class TestSubversionServer(object):
184 SubversionTunnelWrapper, 'command',
210 SubversionTunnelWrapper, 'command',
185 return_value=['date']):
211 return_value=['date']):
186
212
187 exit_code = server.run()
213 exit_code = server.run(tunnel_extras={'config': server.ini_path})
188 # SVN has this differently configured, and we get in our mock env
214 # SVN has this differently configured, and we get in our mock env
189 # None as return code
215 # None as return code
190 assert exit_code == (None, False)
216 assert exit_code == (None, False)
191
217
192 def test_run_returns_executes_command_that_cannot_extract_repo_name(self, svn_server):
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 server = svn_server.create()
221 server = svn_server.create()
194 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
222 with mock.patch.object(SubversionTunnelWrapper, 'command', return_value=['date']):
195 with mock.patch.object(
223 with mock.patch.object(SubversionTunnelWrapper, 'get_first_client_response',
196 SubversionTunnelWrapper, 'command',
197 return_value=['date']):
198 with mock.patch.object(
199 SubversionTunnelWrapper, 'get_first_client_response',
200 return_value=None):
224 return_value=None):
201 exit_code = server.run()
225 exit_code = server.run(tunnel_extras={'config': server.ini_path})
202
226
203 assert exit_code == (1, False)
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -22,7 +22,7 from rhodecode.tests import (
22 TestController, assert_session_flash, TEST_USER_ADMIN_LOGIN)
22 TestController, assert_session_flash, TEST_USER_ADMIN_LOGIN)
23 from rhodecode.model.db import UserGroup
23 from rhodecode.model.db import UserGroup
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixtures.rc_fixture import Fixture
26 from rhodecode.tests.routes import route_path
26 from rhodecode.tests.routes import route_path
27
27
28 fixture = Fixture()
28 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -18,7 +18,7
18 from rhodecode.model.user_group import UserGroupModel
18 from rhodecode.model.user_group import UserGroupModel
19 from rhodecode.tests import (
19 from rhodecode.tests import (
20 TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
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 from rhodecode.tests.routes import route_path
22 from rhodecode.tests.routes import route_path
23
23
24 fixture = Fixture()
24 fixture = Fixture()
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.tests import (
22 from rhodecode.tests import (
23 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
23 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
24 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
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 from rhodecode.tests.utils import AssertResponse
26 from rhodecode.tests.utils import AssertResponse
27 from rhodecode.tests.routes import route_path
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
122 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
123 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
123 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
124 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
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 settings_maker.make_setting('vcs.hg.largefiles.storage_location',
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 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
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 # celery
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 settings_maker.make_setting('celery.result_backend', broker_url)
169 settings_maker.make_setting('celery.result_backend', broker_url)
167
170
168 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
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 # This program is free software: you can redistribute it and/or modify
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
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 log = logging.getLogger(__name__)
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 # Store the settings to make them available to other modules.
34 # Store the settings to make them available to other modules.
35 settings_merged = global_config.copy()
35 settings_merged = global_config.copy()
36 settings_merged.update(settings)
36 settings_merged.update(settings)
@@ -40,7 +40,7 def propagate_rhodecode_config(global_co
40 rhodecode.PYRAMID_SETTINGS = settings_merged
40 rhodecode.PYRAMID_SETTINGS = settings_merged
41 rhodecode.CONFIG = settings_merged
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 rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id()
44 rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id()
45 log.debug('set rhodecode.CONFIG data')
45 log.debug('set rhodecode.CONFIG data')
46
46
@@ -93,6 +93,7 def load_pyramid_environment(global_conf
93 # first run, to store data...
93 # first run, to store data...
94 propagate_rhodecode_config(global_config, settings, {})
94 propagate_rhodecode_config(global_config, settings, {})
95
95
96
96 if vcs_server_enabled:
97 if vcs_server_enabled:
97 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
98 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
98 else:
99 else:
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.lib.utils2 import AttributeDict
49 from rhodecode.lib.utils2 import AttributeDict
50 from rhodecode.lib.exc_tracking import store_exception, format_exc
50 from rhodecode.lib.exc_tracking import store_exception, format_exc
51 from rhodecode.subscribers import (
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 write_metadata_if_needed, write_usage_data, import_license_if_present)
53 write_metadata_if_needed, write_usage_data, import_license_if_present)
54 from rhodecode.lib.statsd_client import StatsdClient
54 from rhodecode.lib.statsd_client import StatsdClient
55
55
@@ -101,6 +101,9 def make_pyramid_app(global_config, **se
101 patches.inspect_getargspec()
101 patches.inspect_getargspec()
102 patches.repoze_sendmail_lf_fix()
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 load_pyramid_environment(global_config, settings)
107 load_pyramid_environment(global_config, settings)
105
108
106 # Static file view comes first
109 # Static file view comes first
@@ -392,6 +395,7 def includeme(config, auth_resources=Non
392 # Add subscribers.
395 # Add subscribers.
393 if load_all:
396 if load_all:
394 log.debug('Adding subscribers...')
397 log.debug('Adding subscribers...')
398 config.add_subscriber(auto_merge_pr_if_needed, rhodecode.events.PullRequestReviewEvent)
395 config.add_subscriber(scan_repositories_if_enabled,
399 config.add_subscriber(scan_repositories_if_enabled,
396 pyramid.events.ApplicationCreated)
400 pyramid.events.ApplicationCreated)
397 config.add_subscriber(write_metadata_if_needed,
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -17,7 +17,7
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
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 from .hooks import (
23 from .hooks import (
@@ -85,7 +85,7 def _pre_push_hook(*args, **kwargs):
85
85
86 # check files names
86 # check files names
87 if forbidden_files:
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 for forbidden_pattern in forbid_files:
89 for forbidden_pattern in forbid_files:
90 # here we can also filter for operation, e.g if check for only ADDED files
90 # here we can also filter for operation, e.g if check for only ADDED files
91 # if operation == 'A':
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 # Copyright (C) 2016-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -55,7 +54,7 def run(*args, **kwargs):
55 return fields
54 return fields
56
55
57
56
58 class _Undefined(object):
57 class _Undefined:
59 pass
58 pass
60
59
61
60
@@ -67,7 +66,7 def get_field(extra_fields_data, key, de
67
66
68 if key not in extra_fields_data:
67 if key not in extra_fields_data:
69 if isinstance(default, _Undefined):
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 return default
70 return default
72
71
73 # NOTE(dan): from metadata we get field_label, field_value, field_desc, field_type
72 # NOTE(dan): from metadata we get field_label, field_value, field_desc, field_type
@@ -1,5 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 # Copyright (C) 2016-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 # Copyright (C) 2016-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 cmd = [
51 cmd = [
53 'log',
52 'log',
54 '--pretty=format:{"commit_id": "%H", "author": "%aN <%aE>", "date": "%ad", "message": "%s"}',
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 stdout, stderr = repo.run_git_command(cmd, extra_env=git_env)
57 stdout, stderr = repo.run_git_command(cmd, extra_env=git_env)
@@ -80,12 +79,12 def run(*args, **kwargs):
80
79
81 if vcs_type == 'git':
80 if vcs_type == 'git':
82 for rev_data in kwargs['commit_ids']:
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 commits = get_git_commits(vcs_repo, kwargs['commit_ids'])
83 commits = get_git_commits(vcs_repo, kwargs['commit_ids'])
85
84
86 if vcs_type == 'hg':
85 if vcs_type == 'hg':
87 for rev_data in kwargs['commit_ids']:
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 commits = get_hg_commits(vcs_repo, kwargs['commit_ids'])
88 commits = get_hg_commits(vcs_repo, kwargs['commit_ids'])
90
89
91 return commits
90 return commits
@@ -1,5 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 # Copyright (C) 2016-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 if vcs_type == 'git':
133 if vcs_type == 'git':
135 for rev_data in kwargs['commit_ids']:
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 files = get_git_files(repo, vcs_repo, kwargs['commit_ids'])
136 files = get_git_files(repo, vcs_repo, kwargs['commit_ids'])
138
137
139 if vcs_type == 'hg':
138 if vcs_type == 'hg':
140 for rev_data in kwargs['commit_ids']:
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 files = get_hg_files(repo, vcs_repo, kwargs['commit_ids'])
141 files = get_hg_files(repo, vcs_repo, kwargs['commit_ids'])
143
142
144 if vcs_type == 'svn':
143 if vcs_type == 'svn':
@@ -1,5 +1,4
1
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 # Copyright (C) 2016-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -28,7 +28,7 import urllib.error
28 log = logging.getLogger('rhodecode.' + __name__)
28 log = logging.getLogger('rhodecode.' + __name__)
29
29
30
30
31 class HookResponse(object):
31 class HookResponse:
32 def __init__(self, status, output):
32 def __init__(self, status, output):
33 self.status = status
33 self.status = status
34 self.output = output
34 self.output = output
@@ -44,6 +44,11 class HookResponse(object):
44 def __bool__(self):
44 def __bool__(self):
45 return self.status == 0
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 class DotDict(dict):
53 class DotDict(dict):
49
54
@@ -91,8 +96,8 class DotDict(dict):
91 def __repr__(self):
96 def __repr__(self):
92 keys = list(self.keys())
97 keys = list(self.keys())
93 keys.sort()
98 keys.sort()
94 args = ', '.join(['%s=%r' % (key, self[key]) for key in keys])
99 args = ', '.join(['{}={!r}'.format(key, self[key]) for key in keys])
95 return '%s(%s)' % (self.__class__.__name__, args)
100 return '{}({})'.format(self.__class__.__name__, args)
96
101
97 @staticmethod
102 @staticmethod
98 def fromDict(d):
103 def fromDict(d):
@@ -110,7 +115,7 def serialize(x):
110
115
111 def unserialize(x):
116 def unserialize(x):
112 if isinstance(x, dict):
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 elif isinstance(x, (list, tuple)):
119 elif isinstance(x, (list, tuple)):
115 return type(x)(unserialize(v) for v in x)
120 return type(x)(unserialize(v) for v in x)
116 else:
121 else:
@@ -161,7 +166,8 def str2bool(_str) -> bool:
161 string into boolean
166 string into boolean
162
167
163 :param _str: string value to translate into boolean
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 if _str is None:
172 if _str is None:
167 return False
173 return False
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -49,22 +48,22 link_config = [
49 {
48 {
50 "name": "enterprise_docs",
49 "name": "enterprise_docs",
51 "target": "https://rhodecode.com/r1/enterprise/docs/",
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 "name": "enterprise_log_file_locations",
54 "name": "enterprise_log_file_locations",
56 "target": "https://rhodecode.com/r1/enterprise/docs/admin-system-overview/",
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 "name": "enterprise_issue_tracker_settings",
59 "name": "enterprise_issue_tracker_settings",
61 "target": "https://rhodecode.com/r1/enterprise/docs/issue-trackers-overview/",
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 "name": "enterprise_svn_setup",
64 "name": "enterprise_svn_setup",
66 "target": "https://rhodecode.com/r1/enterprise/docs/svn-setup/",
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 "name": "enterprise_license_convert_from_old",
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,6 +19,8
19 import os
19 import os
20 import platform
20 import platform
21
21
22 from rhodecode.lib.type_utils import str2bool
23
22 DEFAULT_USER = 'default'
24 DEFAULT_USER = 'default'
23
25
24
26
@@ -37,7 +39,6 def configure_vcs(config):
37
39
38 conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol.v2']
40 conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol.v2']
39 conf.settings.HOOKS_HOST = config['vcs.hooks.host']
41 conf.settings.HOOKS_HOST = config['vcs.hooks.host']
40 conf.settings.DEFAULT_ENCODINGS = config['default_encoding']
41 conf.settings.ALIASES[:] = config['vcs.backends']
42 conf.settings.ALIASES[:] = config['vcs.backends']
42 conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version']
43 conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version']
43
44
@@ -48,28 +49,23 def initialize_database(config):
48 engine = engine_from_config(config, 'sqlalchemy.db1.')
49 engine = engine_from_config(config, 'sqlalchemy.db1.')
49 init_model(engine, encryption_key=get_encryption_key(config))
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):
57 repo_store_path = os.environ.get('RC_TEST_ENV_REPO_STORE') or settings['repo_store.path']
53 if test_env is None:
54 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
55
58
56 from rhodecode.lib.utils import (
59 from rhodecode.lib.utils import (
57 create_test_directory, create_test_database, create_test_repositories,
60 create_test_directory, create_test_database, create_test_repositories,
58 create_test_index)
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 # test repos
66 # test repos
64 if test_env:
67 create_test_repositories(repo_store_path, settings)
65 create_test_directory(TESTS_TMP_PATH)
68 create_test_index(repo_store_path, settings)
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)
73
69
74
70
75 def get_vcs_server_protocol(config):
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -18,13 +18,10
18
18
19 import logging
19 import logging
20
20
21 from rhodecode.events.base import (
21 from rhodecode.events.base import RhodeCodeIntegrationEvent, RhodecodeEvent
22 RhodeCodeIntegrationEvent,
23 RhodecodeEvent
24 )
25
22
26 from rhodecode.events.base import ( # pragma: no cover
23 from rhodecode.events.base import ( # pragma: no cover
27 FtsBuild
24 FtsBuild,
28 )
25 )
29
26
30 from rhodecode.events.user import ( # pragma: no cover
27 from rhodecode.events.user import ( # pragma: no cover
@@ -37,11 +34,16 from rhodecode.events.user import ( # p
37
34
38 from rhodecode.events.repo import ( # pragma: no cover
35 from rhodecode.events.repo import ( # pragma: no cover
39 RepoEvent,
36 RepoEvent,
40 RepoCommitCommentEvent, RepoCommitCommentEditEvent,
37 RepoCommitCommentEvent,
41 RepoPreCreateEvent, RepoCreateEvent,
38 RepoCommitCommentEditEvent,
42 RepoPreDeleteEvent, RepoDeleteEvent,
39 RepoPreCreateEvent,
43 RepoPrePushEvent, RepoPushEvent,
40 RepoCreateEvent,
44 RepoPrePullEvent, RepoPullEvent,
41 RepoPreDeleteEvent,
42 RepoDeleteEvent,
43 RepoPrePushEvent,
44 RepoPushEvent,
45 RepoPrePullEvent,
46 RepoPullEvent,
45 )
47 )
46
48
47 from rhodecode.events.repo_group import ( # pragma: no cover
49 from rhodecode.events.repo_group import ( # pragma: no cover
@@ -77,12 +79,13 def trigger(event, registry=None):
77 from pyramid.threadlocal import get_current_registry
79 from pyramid.threadlocal import get_current_registry
78
80
79 event_name = event.__class__
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 registry = registry or get_current_registry()
83 registry = registry or get_current_registry()
82 registry.notify(event)
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 # Send the events to integrations directly
87 # Send the events to integrations directly
86 from rhodecode.integrations import integrations_event_handler
88 from rhodecode.integrations import integrations_event_handler
89
87 if isinstance(event, RhodeCodeIntegrationEvent):
90 if isinstance(event, RhodeCodeIntegrationEvent):
88 integrations_event_handler(event)
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 # This program is free software: you can redistribute it and/or modify
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
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 # this is a user object to be used for events caused by the system (eg. shell)
28 # this is a user object to be used for events caused by the system (eg. shell)
29 SYSTEM_USER = AttributeDict(dict(
29 SYSTEM_USER = AttributeDict(dict(username="__SYSTEM__", user_id="__SYSTEM_ID__"))
30 username='__SYSTEM__',
31 user_id='__SYSTEM_ID__'
32 ))
33
30
34 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
35
32
@@ -38,16 +35,18 class RhodecodeEvent(object):
38 """
35 """
39 Base event class for all RhodeCode events
36 Base event class for all RhodeCode events
40 """
37 """
38
41 name = "RhodeCodeEvent"
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 self._request = request
43 self._request = request
46 self._actor = actor
44 self._actor = actor
45 self._context = context
47 self.utc_timestamp = datetime.datetime.utcnow()
46 self.utc_timestamp = datetime.datetime.utcnow()
48
47
49 def __repr__(self):
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 def get_request(self):
51 def get_request(self):
53 if self._request:
52 if self._request:
@@ -63,11 +62,11 class RhodecodeEvent(object):
63 if not self.request:
62 if not self.request:
64 return
63 return
65
64
66 user = getattr(self.request, 'user', None)
65 user = getattr(self.request, "user", None)
67 if user:
66 if user:
68 return user
67 return user
69
68
70 api_user = getattr(self.request, 'rpc_user', None)
69 api_user = getattr(self.request, "rpc_user", None)
71 if api_user:
70 if api_user:
72 return api_user
71 return api_user
73
72
@@ -80,15 +79,17 class RhodecodeEvent(object):
80 return self._actor
79 return self._actor
81
80
82 auth_user = self.auth_user
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 if isinstance(auth_user, AuthUser):
83 if isinstance(auth_user, AuthUser):
85 instance = auth_user.get_instance()
84 instance = auth_user.get_instance()
86 # we can't find this DB user...
85 # we can't find this DB user...
87 if not instance:
86 if not instance:
88 return AttributeDict(dict(
87 return AttributeDict(
89 username=auth_user.username,
88 dict(
90 user_id=auth_user.user_id,
89 username=auth_user.username,
91 ))
90 user_id=auth_user.user_id,
91 )
92 )
92 elif auth_user:
93 elif auth_user:
93 return auth_user
94 return auth_user
94 return SYSTEM_USER
95 return SYSTEM_USER
@@ -98,29 +99,26 class RhodecodeEvent(object):
98 auth_user = self.auth_user
99 auth_user = self.auth_user
99 if auth_user:
100 if auth_user:
100 return auth_user.ip_addr
101 return auth_user.ip_addr
101 return '<no ip available>'
102 return "<no ip available>"
102
103
103 @property
104 @property
104 def server_url(self):
105 def server_url(self):
105 if self.request:
106 if self.request:
106 try:
107 try:
107 return self.request.route_url('home')
108 return self.request.route_url("home")
108 except Exception:
109 except Exception:
109 log.exception('Failed to fetch URL for server')
110 log.exception("Failed to fetch URL for server")
110 return self.no_url_set
111 return self.no_url_set
111
112
112 return self.no_url_set
113 return self.no_url_set
113
114
114 def as_dict(self):
115 def as_dict(self):
115 data = {
116 data = {
116 'name': self.name,
117 "name": self.name,
117 'utc_timestamp': self.utc_timestamp,
118 "utc_timestamp": self.utc_timestamp,
118 'actor_ip': self.actor_ip,
119 "actor_ip": self.actor_ip,
119 'actor': {
120 "actor": {"username": self.actor.username, "user_id": self.actor.user_id},
120 'username': self.actor.username,
121 "server_url": self.server_url,
121 'user_id': self.actor.user_id
122 },
123 'server_url': self.server_url
124 }
122 }
125 return data
123 return data
126
124
@@ -129,13 +127,14 class RhodeCodeIntegrationEvent(Rhodecod
129 """
127 """
130 Special subclass for Integration events
128 Special subclass for Integration events
131 """
129 """
132 description = ''
130
131 description = ""
133
132
134
133
135 class FtsBuild(RhodecodeEvent):
134 class FtsBuild(RhodecodeEvent):
136 """
135 """
137 This event will be triggered when FTS Build is triggered
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -24,20 +24,23 class IUserRegistered(Interface):
24 An event type that is emitted whenever a new user registers a user
24 An event type that is emitted whenever a new user registers a user
25 account.
25 account.
26 """
26 """
27 user = Attribute('The user object.')
27
28 session = Attribute('The session while processing the register form post.')
28 user = Attribute("The user object.")
29 session = Attribute("The session while processing the register form post.")
29
30
30
31
31 class IUserPreCreate(Interface):
32 class IUserPreCreate(Interface):
32 """
33 """
33 An event type that is emitted before a new user object is created.
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 class IUserPreUpdate(Interface):
40 class IUserPreUpdate(Interface):
39 """
41 """
40 An event type that is emitted before a user object is updated.
42 An event type that is emitted before a user object is updated.
41 """
43 """
42 user = Attribute('The not yet updated user object')
44
43 user_data = Attribute('Data used to update the user')
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -19,7 +19,7
19 import logging
19 import logging
20
20
21 from rhodecode.translation import lazy_ugettext
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 log = logging.getLogger(__name__)
24 log = logging.getLogger(__name__)
25
25
@@ -30,45 +30,44 class PullRequestEvent(RepoEvent):
30
30
31 :param pullrequest: a :class:`PullRequest` instance
31 :param pullrequest: a :class:`PullRequest` instance
32 """
32 """
33 name = 'pullrequest-event'
33
34 display_name = lazy_ugettext('pullrequest generic event')
34 name = "pullrequest-event"
35 description = lazy_ugettext('All events within a context of a pull request')
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 def __init__(self, pullrequest, context=None):
38 super().__init__(pullrequest.target_repo)
39 super().__init__(pullrequest.target_repo, context=context)
39 self.pullrequest = pullrequest
40 self.pullrequest = pullrequest
41 self.context = self._context
40
42
41 def as_dict(self):
43 def as_dict(self):
42 from rhodecode.lib.utils2 import md5_safe
44 from rhodecode.lib.utils2 import md5_safe
43 from rhodecode.model.pull_request import PullRequestModel
45 from rhodecode.model.pull_request import PullRequestModel
46
44 data = super().as_dict()
47 data = super().as_dict()
45
48
46 commits = _commits_as_dict(
49 commits = _commits_as_dict(self, commit_ids=self.pullrequest.revisions, repos=[self.pullrequest.source_repo])
47 self,
48 commit_ids=self.pullrequest.revisions,
49 repos=[self.pullrequest.source_repo]
50 )
51 issues = _issues_as_dict(commits)
50 issues = _issues_as_dict(commits)
52 # calculate hashes of all commits for unique identifier of commits
51 # calculate hashes of all commits for unique identifier of commits
53 # inside that pull request
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({
55 data.update(
57 'pullrequest': {
56 {
58 'title': self.pullrequest.title,
57 "pullrequest": {
59 'issues': issues,
58 "title": self.pullrequest.title,
60 'pull_request_id': self.pullrequest.pull_request_id,
59 "issues": issues,
61 'url': PullRequestModel().get_url(
60 "pull_request_id": self.pullrequest.pull_request_id,
62 self.pullrequest, request=self.request),
61 "url": PullRequestModel().get_url(self.pullrequest, request=self.request),
63 'permalink_url': PullRequestModel().get_url(
62 "permalink_url": PullRequestModel().get_url(self.pullrequest, request=self.request, permalink=True),
64 self.pullrequest, request=self.request, permalink=True),
63 "shadow_url": PullRequestModel().get_shadow_clone_url(self.pullrequest, request=self.request),
65 'shadow_url': PullRequestModel().get_shadow_clone_url(
64 "status": self.pullrequest.calculated_review_status(),
66 self.pullrequest, request=self.request),
65 "commits_uid": commits_hash,
67 'status': self.pullrequest.calculated_review_status(),
66 "commits": commits,
68 'commits_uid': commits_hash,
67 },
69 'commits': commits,
68 "context": self.context,
70 }
69 }
71 })
70 )
72 return data
71 return data
73
72
74
73
@@ -77,9 +76,10 class PullRequestCreateEvent(PullRequest
77 An instance of this class is emitted as an :term:`event` after a pull
76 An instance of this class is emitted as an :term:`event` after a pull
78 request is created.
77 request is created.
79 """
78 """
80 name = 'pullrequest-create'
79
81 display_name = lazy_ugettext('pullrequest created')
80 name = "pullrequest-create"
82 description = lazy_ugettext('Event triggered after pull request was created')
81 display_name = lazy_ugettext("pullrequest created")
82 description = lazy_ugettext("Event triggered after pull request was created")
83
83
84
84
85 class PullRequestCloseEvent(PullRequestEvent):
85 class PullRequestCloseEvent(PullRequestEvent):
@@ -87,9 +87,10 class PullRequestCloseEvent(PullRequestE
87 An instance of this class is emitted as an :term:`event` after a pull
87 An instance of this class is emitted as an :term:`event` after a pull
88 request is closed.
88 request is closed.
89 """
89 """
90 name = 'pullrequest-close'
90
91 display_name = lazy_ugettext('pullrequest closed')
91 name = "pullrequest-close"
92 description = lazy_ugettext('Event triggered after pull request was closed')
92 display_name = lazy_ugettext("pullrequest closed")
93 description = lazy_ugettext("Event triggered after pull request was closed")
93
94
94
95
95 class PullRequestUpdateEvent(PullRequestEvent):
96 class PullRequestUpdateEvent(PullRequestEvent):
@@ -97,9 +98,10 class PullRequestUpdateEvent(PullRequest
97 An instance of this class is emitted as an :term:`event` after a pull
98 An instance of this class is emitted as an :term:`event` after a pull
98 request's commits have been updated.
99 request's commits have been updated.
99 """
100 """
100 name = 'pullrequest-update'
101
101 display_name = lazy_ugettext('pullrequest commits updated')
102 name = "pullrequest-update"
102 description = lazy_ugettext('Event triggered after pull requests was updated')
103 display_name = lazy_ugettext("pullrequest commits updated")
104 description = lazy_ugettext("Event triggered after pull requests was updated")
103
105
104
106
105 class PullRequestReviewEvent(PullRequestEvent):
107 class PullRequestReviewEvent(PullRequestEvent):
@@ -107,13 +109,13 class PullRequestReviewEvent(PullRequest
107 An instance of this class is emitted as an :term:`event` after a pull
109 An instance of this class is emitted as an :term:`event` after a pull
108 request review has changed. A status defines new status of review.
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):
113 name = "pullrequest-review"
116 super().__init__(pullrequest)
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 self.status = status
119 self.status = status
118
120
119
121
@@ -122,10 +124,10 class PullRequestMergeEvent(PullRequestE
122 An instance of this class is emitted as an :term:`event` after a pull
124 An instance of this class is emitted as an :term:`event` after a pull
123 request is merged.
125 request is merged.
124 """
126 """
125 name = 'pullrequest-merge'
127
126 display_name = lazy_ugettext('pullrequest merged')
128 name = "pullrequest-merge"
127 description = lazy_ugettext('Event triggered after a successful merge operation '
129 display_name = lazy_ugettext("pullrequest merged")
128 'was executed on a pull request')
130 description = lazy_ugettext("Event triggered after a successful merge operation was executed on a pull request")
129
131
130
132
131 class PullRequestCommentEvent(PullRequestEvent):
133 class PullRequestCommentEvent(PullRequestEvent):
@@ -133,37 +135,39 class PullRequestCommentEvent(PullReques
133 An instance of this class is emitted as an :term:`event` after a pull
135 An instance of this class is emitted as an :term:`event` after a pull
134 request comment is created.
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):
139 name = "pullrequest-comment"
142 super().__init__(pullrequest)
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 self.comment = comment
145 self.comment = comment
144
146
145 def as_dict(self):
147 def as_dict(self):
146 from rhodecode.model.comment import CommentsModel
148 from rhodecode.model.comment import CommentsModel
149
147 data = super().as_dict()
150 data = super().as_dict()
148
151
149 status = None
152 status = None
150 if self.comment.status_change:
153 if self.comment.status_change:
151 status = self.comment.review_status
154 status = self.comment.review_status
152
155
153 data.update({
156 data.update(
154 'comment': {
157 {
155 'status': status,
158 "comment": {
156 'text': self.comment.text,
159 "status": status,
157 'type': self.comment.comment_type,
160 "text": self.comment.text,
158 'file': self.comment.f_path,
161 "type": self.comment.comment_type,
159 'line': self.comment.line_no,
162 "file": self.comment.f_path,
160 'version': self.comment.last_version,
163 "line": self.comment.line_no,
161 'url': CommentsModel().get_url(
164 "version": self.comment.last_version,
162 self.comment, request=self.request),
165 "url": CommentsModel().get_url(self.comment, request=self.request),
163 'permalink_url': CommentsModel().get_url(
166 "permalink_url": CommentsModel().get_url(self.comment, request=self.request, permalink=True),
164 self.comment, request=self.request, permalink=True),
167 },
168 "context": self.context,
165 }
169 }
166 })
170 )
167 return data
171 return data
168
172
169
173
@@ -172,10 +176,10 class PullRequestCommentEditEvent(PullRe
172 An instance of this class is emitted as an :term:`event` after a pull
176 An instance of this class is emitted as an :term:`event` after a pull
173 request comment is edited.
177 request comment is edited.
174 """
178 """
175 name = 'pullrequest-comment-edit'
179
176 display_name = lazy_ugettext('pullrequest comment edited')
180 name = "pullrequest-comment-edit"
177 description = lazy_ugettext('Event triggered after a comment was edited on a code '
181 display_name = lazy_ugettext("pullrequest comment edited")
178 'in the pull request')
182 description = lazy_ugettext("Event triggered after a comment was edited on a code in the pull request")
179
183
180 def __init__(self, pullrequest, comment):
184 def __init__(self, pullrequest, comment):
181 super().__init__(pullrequest)
185 super().__init__(pullrequest)
@@ -183,24 +187,26 class PullRequestCommentEditEvent(PullRe
183
187
184 def as_dict(self):
188 def as_dict(self):
185 from rhodecode.model.comment import CommentsModel
189 from rhodecode.model.comment import CommentsModel
190
186 data = super().as_dict()
191 data = super().as_dict()
187
192
188 status = None
193 status = None
189 if self.comment.status_change:
194 if self.comment.status_change:
190 status = self.comment.review_status
195 status = self.comment.review_status
191
196
192 data.update({
197 data.update(
193 'comment': {
198 {
194 'status': status,
199 "comment": {
195 'text': self.comment.text,
200 "status": status,
196 'type': self.comment.comment_type,
201 "text": self.comment.text,
197 'file': self.comment.f_path,
202 "type": self.comment.comment_type,
198 'line': self.comment.line_no,
203 "file": self.comment.f_path,
199 'version': self.comment.last_version,
204 "line": self.comment.line_no,
200 'url': CommentsModel().get_url(
205 "version": self.comment.last_version,
201 self.comment, request=self.request),
206 "url": CommentsModel().get_url(self.comment, request=self.request),
202 'permalink_url': CommentsModel().get_url(
207 "permalink_url": CommentsModel().get_url(self.comment, request=self.request, permalink=True),
203 self.comment, request=self.request, permalink=True),
208 },
209 "context": self.context
204 }
210 }
205 })
211 )
206 return data
212 return data
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 :param repos: a list of repos to check
37 :param repos: a list of repos to check
38 """
38 """
39 from rhodecode.lib.utils2 import extract_mentioned_users
39 from rhodecode.lib.utils2 import extract_mentioned_users
40 from rhodecode.lib.helpers import (
40 from rhodecode.lib.helpers import urlify_commit_message, process_patterns, chop_at_smart
41 urlify_commit_message, process_patterns, chop_at_smart)
42 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo import RepoModel
43
42
44 if not repos:
43 if not repos:
45 raise Exception('no repo defined')
44 raise Exception("no repo defined")
46
45
47 if not isinstance(repos, (tuple, list)):
46 if not isinstance(repos, (tuple, list)):
48 repos = [repos]
47 repos = [repos]
@@ -63,37 +62,31 def _commits_as_dict(event, commit_ids,
63 try:
62 try:
64 # use copy of needed_commits since we modify it while iterating
63 # use copy of needed_commits since we modify it while iterating
65 for commit_id in list(needed_commits):
64 for commit_id in list(needed_commits):
66 if commit_id.startswith('tag=>'):
65 if commit_id.startswith("tag=>"):
67 raw_id = commit_id[5:]
66 raw_id = commit_id[5:]
68 cs_data = {
67 cs_data = {
69 'raw_id': commit_id, 'short_id': commit_id,
68 "raw_id": commit_id,
70 'branch': None,
69 "short_id": commit_id,
71 'git_ref_change': 'tag_add',
70 "branch": None,
72 'message': f'Added new tag {raw_id}',
71 "git_ref_change": "tag_add",
73 'author': event.actor.full_contact,
72 "message": f"Added new tag {raw_id}",
74 'date': datetime.datetime.now(),
73 "author": event.actor.full_contact,
75 'refs': {
74 "date": datetime.datetime.now(),
76 'branches': [],
75 "refs": {"branches": [], "bookmarks": [], "tags": []},
77 'bookmarks': [],
78 'tags': []
79 }
80 }
76 }
81 commits.append(cs_data)
77 commits.append(cs_data)
82
78
83 elif commit_id.startswith('delete_branch=>'):
79 elif commit_id.startswith("delete_branch=>"):
84 raw_id = commit_id[15:]
80 raw_id = commit_id[15:]
85 cs_data = {
81 cs_data = {
86 'raw_id': commit_id, 'short_id': commit_id,
82 "raw_id": commit_id,
87 'branch': None,
83 "short_id": commit_id,
88 'git_ref_change': 'branch_delete',
84 "branch": None,
89 'message': f'Deleted branch {raw_id}',
85 "git_ref_change": "branch_delete",
90 'author': event.actor.full_contact,
86 "message": f"Deleted branch {raw_id}",
91 'date': datetime.datetime.now(),
87 "author": event.actor.full_contact,
92 'refs': {
88 "date": datetime.datetime.now(),
93 'branches': [],
89 "refs": {"branches": [], "bookmarks": [], "tags": []},
94 'bookmarks': [],
95 'tags': []
96 }
97 }
90 }
98 commits.append(cs_data)
91 commits.append(cs_data)
99
92
@@ -104,50 +97,45 def _commits_as_dict(event, commit_ids,
104 continue # maybe its in next repo
97 continue # maybe its in next repo
105
98
106 cs_data = cs.__json__()
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'])
102 cs_data["mentions"] = extract_mentioned_users(cs_data["message"])
110 cs_data['reviewers'] = reviewers
103 cs_data["reviewers"] = reviewers
111 cs_data['url'] = RepoModel().get_commit_url(
104 cs_data["url"] = RepoModel().get_commit_url(repo, cs_data["raw_id"], request=event.request)
112 repo, cs_data['raw_id'], request=event.request)
105 cs_data["permalink_url"] = RepoModel().get_commit_url(
113 cs_data['permalink_url'] = RepoModel().get_commit_url(
106 repo, cs_data["raw_id"], request=event.request, permalink=True
114 repo, cs_data['raw_id'], request=event.request,
107 )
115 permalink=True)
108 urlified_message, issues_data, errors = process_patterns(cs_data["message"], repo.repo_name)
116 urlified_message, issues_data, errors = process_patterns(
109 cs_data["issues"] = issues_data
117 cs_data['message'], repo.repo_name)
110 cs_data["message_html"] = urlify_commit_message(cs_data["message"], repo.repo_name)
118 cs_data['issues'] = issues_data
111 cs_data["message_html_title"] = chop_at_smart(cs_data["message"], "\n", suffix_if_chopped="...")
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='...')
123 commits.append(cs_data)
112 commits.append(cs_data)
124
113
125 needed_commits.remove(commit_id)
114 needed_commits.remove(commit_id)
126
115
127 except Exception:
116 except Exception:
128 log.exception('Failed to extract commits data')
117 log.exception("Failed to extract commits data")
129 # we don't send any commits when crash happens, only full list
118 # we don't send any commits when crash happens, only full list
130 # matters we short circuit then.
119 # matters we short circuit then.
131 return []
120 return []
132
121
133 # we failed to remove all needed_commits from all repositories
122 # we failed to remove all needed_commits from all repositories
134 if needed_commits:
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 if missing_commits:
127 if missing_commits:
139 log.error('Inconsistent repository state. '
128 log.error("Inconsistent repository state. " "Missing commits: %s", ", ".join(missing_commits))
140 'Missing commits: %s', ', '.join(missing_commits))
141
129
142 return commits
130 return commits
143
131
144
132
145 def _issues_as_dict(commits):
133 def _issues_as_dict(commits):
146 """ Helper function to serialize issues from commits """
134 """Helper function to serialize issues from commits"""
147 issues = {}
135 issues = {}
148 for commit in commits:
136 for commit in commits:
149 for issue in commit['issues']:
137 for issue in commit["issues"]:
150 issues[issue['id']] = issue
138 issues[issue["id"]] = issue
151 return issues
139 return issues
152
140
153
141
@@ -156,33 +144,36 class RepoEvent(RhodeCodeIntegrationEven
156 Base class for events acting on a repository.
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 :param repo: a :class:`Repository` instance
149 :param repo: a :class:`Repository` instance
162 """
150 """
163 super().__init__(actor=actor)
151 super().__init__(actor=actor, context=context)
164 self.repo = repo
152 self.repo = repo
153 self.context = self._context
165
154
166 def as_dict(self):
155 def as_dict(self):
167 from rhodecode.model.repo import RepoModel
156 from rhodecode.model.repo import RepoModel
157
168 data = super().as_dict()
158 data = super().as_dict()
169
159
170 extra_fields = collections.OrderedDict()
160 extra_fields = collections.OrderedDict()
171 for field in self.repo.extra_fields:
161 for field in self.repo.extra_fields:
172 extra_fields[field.field_key] = field.field_value
162 extra_fields[field.field_key] = field.field_value
173
163
174 data.update({
164 data.update(
175 'repo': {
165 {
176 'repo_id': self.repo.repo_id,
166 "repo": {
177 'repo_name': self.repo.repo_name,
167 "repo_id": self.repo.repo_id,
178 'repo_type': self.repo.repo_type,
168 "repo_name": self.repo.repo_name,
179 'url': RepoModel().get_url(
169 "repo_type": self.repo.repo_type,
180 self.repo, request=self.request),
170 "url": RepoModel().get_url(self.repo, request=self.request),
181 'permalink_url': RepoModel().get_url(
171 "permalink_url": RepoModel().get_url(self.repo, request=self.request, permalink=True),
182 self.repo, request=self.request, permalink=True),
172 "extra_fields": extra_fields,
183 'extra_fields': extra_fields
173 },
174 "context": self.context,
184 }
175 }
185 })
176 )
186 return data
177 return data
187
178
188
179
@@ -192,32 +183,32 class RepoCommitCommentEvent(RepoEvent):
192 on repository commit.
183 on repository commit.
193 """
184 """
194
185
195 name = 'repo-commit-comment'
186 name = "repo-commit-comment"
196 display_name = lazy_ugettext('repository commit comment')
187 display_name = lazy_ugettext("repository commit comment")
197 description = lazy_ugettext('Event triggered after a comment was made '
188 description = lazy_ugettext("Event triggered after a comment was made on commit inside a repository")
198 'on commit inside a repository')
199
189
200 def __init__(self, repo, commit, comment):
190 def __init__(self, repo, commit, comment, context=None):
201 super().__init__(repo)
191 super().__init__(repo, context=context)
202 self.commit = commit
192 self.commit = commit
203 self.comment = comment
193 self.comment = comment
204
194
205 def as_dict(self):
195 def as_dict(self):
206 data = super().as_dict()
196 data = super().as_dict()
207 data['commit'] = {
197 data["commit"] = {
208 'commit_id': self.commit.raw_id,
198 "commit_id": self.commit.raw_id,
209 'commit_message': self.commit.message,
199 "commit_message": self.commit.message,
210 'commit_branch': self.commit.branch,
200 "commit_branch": self.commit.branch,
211 }
201 }
212
202
213 data['comment'] = {
203 data["comment"] = {
214 'comment_id': self.comment.comment_id,
204 "comment_id": self.comment.comment_id,
215 'comment_text': self.comment.text,
205 "comment_text": self.comment.text,
216 'comment_type': self.comment.comment_type,
206 "comment_type": self.comment.comment_type,
217 'comment_f_path': self.comment.f_path,
207 "comment_f_path": self.comment.f_path,
218 'comment_line_no': self.comment.line_no,
208 "comment_line_no": self.comment.line_no,
219 'comment_version': self.comment.last_version,
209 "comment_version": self.comment.last_version,
220 }
210 }
211 data["contex"] = self.context
221 return data
212 return data
222
213
223
214
@@ -227,32 +218,32 class RepoCommitCommentEditEvent(RepoEve
227 on repository commit.
218 on repository commit.
228 """
219 """
229
220
230 name = 'repo-commit-edit-comment'
221 name = "repo-commit-edit-comment"
231 display_name = lazy_ugettext('repository commit edit comment')
222 display_name = lazy_ugettext("repository commit edit comment")
232 description = lazy_ugettext('Event triggered after a comment was edited '
223 description = lazy_ugettext("Event triggered after a comment was edited on commit inside a repository")
233 'on commit inside a repository')
234
224
235 def __init__(self, repo, commit, comment):
225 def __init__(self, repo, commit, comment, context=None):
236 super().__init__(repo)
226 super().__init__(repo, context=context)
237 self.commit = commit
227 self.commit = commit
238 self.comment = comment
228 self.comment = comment
239
229
240 def as_dict(self):
230 def as_dict(self):
241 data = super().as_dict()
231 data = super().as_dict()
242 data['commit'] = {
232 data["commit"] = {
243 'commit_id': self.commit.raw_id,
233 "commit_id": self.commit.raw_id,
244 'commit_message': self.commit.message,
234 "commit_message": self.commit.message,
245 'commit_branch': self.commit.branch,
235 "commit_branch": self.commit.branch,
246 }
236 }
247
237
248 data['comment'] = {
238 data["comment"] = {
249 'comment_id': self.comment.comment_id,
239 "comment_id": self.comment.comment_id,
250 'comment_text': self.comment.text,
240 "comment_text": self.comment.text,
251 'comment_type': self.comment.comment_type,
241 "comment_type": self.comment.comment_type,
252 'comment_f_path': self.comment.f_path,
242 "comment_f_path": self.comment.f_path,
253 'comment_line_no': self.comment.line_no,
243 "comment_line_no": self.comment.line_no,
254 'comment_version': self.comment.last_version,
244 "comment_version": self.comment.last_version,
255 }
245 }
246 data["context"] = "context"
256 return data
247 return data
257
248
258
249
@@ -261,9 +252,10 class RepoPreCreateEvent(RepoEvent):
261 An instance of this class is emitted as an :term:`event` before a repo is
252 An instance of this class is emitted as an :term:`event` before a repo is
262 created.
253 created.
263 """
254 """
264 name = 'repo-pre-create'
255
265 display_name = lazy_ugettext('repository pre create')
256 name = "repo-pre-create"
266 description = lazy_ugettext('Event triggered before repository is created')
257 display_name = lazy_ugettext("repository pre create")
258 description = lazy_ugettext("Event triggered before repository is created")
267
259
268
260
269 class RepoCreateEvent(RepoEvent):
261 class RepoCreateEvent(RepoEvent):
@@ -271,9 +263,10 class RepoCreateEvent(RepoEvent):
271 An instance of this class is emitted as an :term:`event` whenever a repo is
263 An instance of this class is emitted as an :term:`event` whenever a repo is
272 created.
264 created.
273 """
265 """
274 name = 'repo-create'
266
275 display_name = lazy_ugettext('repository created')
267 name = "repo-create"
276 description = lazy_ugettext('Event triggered after repository was created')
268 display_name = lazy_ugettext("repository created")
269 description = lazy_ugettext("Event triggered after repository was created")
277
270
278
271
279 class RepoPreDeleteEvent(RepoEvent):
272 class RepoPreDeleteEvent(RepoEvent):
@@ -281,9 +274,10 class RepoPreDeleteEvent(RepoEvent):
281 An instance of this class is emitted as an :term:`event` whenever a repo is
274 An instance of this class is emitted as an :term:`event` whenever a repo is
282 created.
275 created.
283 """
276 """
284 name = 'repo-pre-delete'
277
285 display_name = lazy_ugettext('repository pre delete')
278 name = "repo-pre-delete"
286 description = lazy_ugettext('Event triggered before a repository is deleted')
279 display_name = lazy_ugettext("repository pre delete")
280 description = lazy_ugettext("Event triggered before a repository is deleted")
287
281
288
282
289 class RepoDeleteEvent(RepoEvent):
283 class RepoDeleteEvent(RepoEvent):
@@ -291,43 +285,45 class RepoDeleteEvent(RepoEvent):
291 An instance of this class is emitted as an :term:`event` whenever a repo is
285 An instance of this class is emitted as an :term:`event` whenever a repo is
292 created.
286 created.
293 """
287 """
294 name = 'repo-delete'
288
295 display_name = lazy_ugettext('repository deleted')
289 name = "repo-delete"
296 description = lazy_ugettext('Event triggered after repository was deleted')
290 display_name = lazy_ugettext("repository deleted")
291 description = lazy_ugettext("Event triggered after repository was deleted")
297
292
298
293
299 class RepoVCSEvent(RepoEvent):
294 class RepoVCSEvent(RepoEvent):
300 """
295 """
301 Base class for events triggered by the VCS
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 self.repo = Repository.get_by_repo_name(repo_name)
303 self.repo = Repository.get_by_repo_name(repo_name)
308 if not self.repo:
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 self.extras = extras
306 self.extras = extras
311 super().__init__(self.repo)
307 super().__init__(self.repo, context=context)
312
308
313 @property
309 @property
314 def actor(self):
310 def actor(self):
315 if self.extras.get('username'):
311 if self.extras.get("username"):
316 return User.get_by_username(self.extras['username'])
312 return User.get_by_username(self.extras["username"])
317
313
318 @property
314 @property
319 def actor_ip(self):
315 def actor_ip(self):
320 if self.extras.get('ip'):
316 if self.extras.get("ip"):
321 return self.extras['ip']
317 return self.extras["ip"]
322
318
323 @property
319 @property
324 def server_url(self):
320 def server_url(self):
325 if self.extras.get('server_url'):
321 if self.extras.get("server_url"):
326 return self.extras['server_url']
322 return self.extras["server_url"]
327
323
328 @property
324 @property
329 def request(self):
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 class RepoPrePullEvent(RepoVCSEvent):
329 class RepoPrePullEvent(RepoVCSEvent):
@@ -335,9 +331,10 class RepoPrePullEvent(RepoVCSEvent):
335 An instance of this class is emitted as an :term:`event` before commits
331 An instance of this class is emitted as an :term:`event` before commits
336 are pulled from a repo.
332 are pulled from a repo.
337 """
333 """
338 name = 'repo-pre-pull'
334
339 display_name = lazy_ugettext('repository pre pull')
335 name = "repo-pre-pull"
340 description = lazy_ugettext('Event triggered before repository code is pulled')
336 display_name = lazy_ugettext("repository pre pull")
337 description = lazy_ugettext("Event triggered before repository code is pulled")
341
338
342
339
343 class RepoPullEvent(RepoVCSEvent):
340 class RepoPullEvent(RepoVCSEvent):
@@ -345,9 +342,10 class RepoPullEvent(RepoVCSEvent):
345 An instance of this class is emitted as an :term:`event` after commits
342 An instance of this class is emitted as an :term:`event` after commits
346 are pulled from a repo.
343 are pulled from a repo.
347 """
344 """
348 name = 'repo-pull'
345
349 display_name = lazy_ugettext('repository pull')
346 name = "repo-pull"
350 description = lazy_ugettext('Event triggered after repository code was pulled')
347 display_name = lazy_ugettext("repository pull")
348 description = lazy_ugettext("Event triggered after repository code was pulled")
351
349
352
350
353 class RepoPrePushEvent(RepoVCSEvent):
351 class RepoPrePushEvent(RepoVCSEvent):
@@ -355,10 +353,10 class RepoPrePushEvent(RepoVCSEvent):
355 An instance of this class is emitted as an :term:`event` before commits
353 An instance of this class is emitted as an :term:`event` before commits
356 are pushed to a repo.
354 are pushed to a repo.
357 """
355 """
358 name = 'repo-pre-push'
356
359 display_name = lazy_ugettext('repository pre push')
357 name = "repo-pre-push"
360 description = lazy_ugettext('Event triggered before the code is '
358 display_name = lazy_ugettext("repository pre push")
361 'pushed to a repository')
359 description = lazy_ugettext("Event triggered before the code is pushed to a repository")
362
360
363
361
364 class RepoPushEvent(RepoVCSEvent):
362 class RepoPushEvent(RepoVCSEvent):
@@ -368,13 +366,13 class RepoPushEvent(RepoVCSEvent):
368
366
369 :param extras: (optional) dict of data from proxied VCS actions
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):
370 name = "repo-push"
377 super().__init__(repo_name, extras)
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 self.pushed_commit_ids = pushed_commit_ids
376 self.pushed_commit_ids = pushed_commit_ids
379 self.new_refs = extras.new_refs
377 self.new_refs = extras.new_refs
380
378
@@ -382,63 +380,48 class RepoPushEvent(RepoVCSEvent):
382 data = super().as_dict()
380 data = super().as_dict()
383
381
384 def branch_url(branch_name):
382 def branch_url(branch_name):
385 return '{}/changelog?branch={}'.format(
383 return "{}/changelog?branch={}".format(data["repo"]["url"], branch_name)
386 data['repo']['url'], branch_name)
387
384
388 def tag_url(tag_name):
385 def tag_url(tag_name):
389 return '{}/files/{}/'.format(
386 return "{}/files/{}/".format(data["repo"]["url"], tag_name)
390 data['repo']['url'], tag_name)
391
387
392 commits = _commits_as_dict(
388 commits = _commits_as_dict(self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
393 self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
394
389
395 last_branch = None
390 last_branch = None
396 for commit in reversed(commits):
391 for commit in reversed(commits):
397 commit['branch'] = commit['branch'] or last_branch
392 commit["branch"] = commit["branch"] or last_branch
398 last_branch = commit['branch']
393 last_branch = commit["branch"]
399 issues = _issues_as_dict(commits)
394 issues = _issues_as_dict(commits)
400
395
401 branches = set()
396 branches = set()
402 tags = set()
397 tags = set()
403 for commit in commits:
398 for commit in commits:
404 if commit['refs']['tags']:
399 if commit["refs"]["tags"]:
405 for tag in commit['refs']['tags']:
400 for tag in commit["refs"]["tags"]:
406 tags.add(tag)
401 tags.add(tag)
407 if commit['branch']:
402 if commit["branch"]:
408 branches.add(commit['branch'])
403 branches.add(commit["branch"])
409
404
410 # maybe we have branches in new_refs ?
405 # maybe we have branches in new_refs ?
411 try:
406 try:
412 branches = branches.union(set(self.new_refs['branches']))
407 branches = branches.union(set(self.new_refs["branches"]))
413 except Exception:
408 except Exception:
414 pass
409 pass
415
410
416 branches = [
411 branches = [{"name": branch, "url": branch_url(branch)} for branch in branches]
417 {
418 'name': branch,
419 'url': branch_url(branch)
420 }
421 for branch in branches
422 ]
423
412
424 # maybe we have branches in new_refs ?
413 # maybe we have branches in new_refs ?
425 try:
414 try:
426 tags = tags.union(set(self.new_refs['tags']))
415 tags = tags.union(set(self.new_refs["tags"]))
427 except Exception:
416 except Exception:
428 pass
417 pass
429
418
430 tags = [
419 tags = [{"name": tag, "url": tag_url(tag)} for tag in tags]
431 {
432 'name': tag,
433 'url': tag_url(tag)
434 }
435 for tag in tags
436 ]
437
420
438 data['push'] = {
421 data["push"] = {
439 'commits': commits,
422 "commits": commits,
440 'issues': issues,
423 "issues": issues,
441 'branches': branches,
424 "branches": branches,
442 'tags': tags,
425 "tags": tags,
443 }
426 }
444 return data
427 return data
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -29,27 +29,31 class RepoGroupEvent(RhodeCodeIntegratio
29 """
29 """
30 Base class for events acting on a repository group.
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):
35 def __init__(self, repo_group, context=None):
36 super().__init__()
36 super().__init__(context=context)
37 self.repo_group = repo_group
37 self.repo_group = repo_group
38 self.context = self._context
38
39
39 def as_dict(self):
40 def as_dict(self):
40 data = super().as_dict()
41 data = super().as_dict()
41 data.update({
42 data.update(
42 'repo_group': {
43 {
43 'group_id': self.repo_group.group_id,
44 "repo_group": {
44 'group_name': self.repo_group.group_name,
45 "group_id": self.repo_group.group_id,
45 'group_parent_id': self.repo_group.group_parent_id,
46 "group_name": self.repo_group.group_name,
46 'group_description': self.repo_group.group_description,
47 "group_parent_id": self.repo_group.group_parent_id,
47 'user_id': self.repo_group.user_id,
48 "group_description": self.repo_group.group_description,
48 'created_by': self.repo_group.user.username,
49 "user_id": self.repo_group.user_id,
49 'created_on': self.repo_group.created_on,
50 "created_by": self.repo_group.user.username,
50 'enable_locking': self.repo_group.enable_locking,
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 return data
57 return data
54
58
55
59
@@ -58,9 +62,10 class RepoGroupCreateEvent(RepoGroupEven
58 An instance of this class is emitted as an :term:`event` whenever a
62 An instance of this class is emitted as an :term:`event` whenever a
59 repository group is created.
63 repository group is created.
60 """
64 """
61 name = 'repo-group-create'
65
62 display_name = lazy_ugettext('repository group created')
66 name = "repo-group-create"
63 description = lazy_ugettext('Event triggered after a repository group was created')
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 class RepoGroupDeleteEvent(RepoGroupEvent):
71 class RepoGroupDeleteEvent(RepoGroupEvent):
@@ -68,9 +73,10 class RepoGroupDeleteEvent(RepoGroupEven
68 An instance of this class is emitted as an :term:`event` whenever a
73 An instance of this class is emitted as an :term:`event` whenever a
69 repository group is deleted.
74 repository group is deleted.
70 """
75 """
71 name = 'repo-group-delete'
76
72 display_name = lazy_ugettext('repository group deleted')
77 name = "repo-group-delete"
73 description = lazy_ugettext('Event triggered after a repository group was deleted')
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 class RepoGroupUpdateEvent(RepoGroupEvent):
82 class RepoGroupUpdateEvent(RepoGroupEvent):
@@ -78,6 +84,7 class RepoGroupUpdateEvent(RepoGroupEven
78 An instance of this class is emitted as an :term:`event` whenever a
84 An instance of this class is emitted as an :term:`event` whenever a
79 repository group is updated.
85 repository group is updated.
80 """
86 """
81 name = 'repo-group-update'
87
82 display_name = lazy_ugettext('repository group update')
88 name = "repo-group-update"
83 description = lazy_ugettext('Event triggered after a repository group was updated')
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 # This program is free software: you can redistribute it and/or modify
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
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 from rhodecode.translation import lazy_ugettext
22 from rhodecode.translation import lazy_ugettext
23 from rhodecode.events.base import RhodecodeEvent, RhodeCodeIntegrationEvent
23 from rhodecode.events.base import RhodecodeEvent, RhodeCodeIntegrationEvent
24 from rhodecode.events.interfaces import (
24 from rhodecode.events.interfaces import IUserRegistered, IUserPreCreate, IUserPreUpdate
25 IUserRegistered, IUserPreCreate, IUserPreUpdate)
26
25
27 log = logging.getLogger(__name__)
26 log = logging.getLogger(__name__)
28
27
@@ -33,8 +32,9 class UserRegistered(RhodeCodeIntegratio
33 An instance of this class is emitted as an :term:`event` whenever a user
32 An instance of this class is emitted as an :term:`event` whenever a user
34 account is registered.
33 account is registered.
35 """
34 """
36 name = 'user-register'
35
37 display_name = lazy_ugettext('user registered')
36 name = "user-register"
37 display_name = lazy_ugettext("user registered")
38
38
39 def __init__(self, user, session):
39 def __init__(self, user, session):
40 super().__init__()
40 super().__init__()
@@ -48,8 +48,9 class UserPreCreate(RhodeCodeIntegration
48 An instance of this class is emitted as an :term:`event` before a new user
48 An instance of this class is emitted as an :term:`event` before a new user
49 object is created.
49 object is created.
50 """
50 """
51 name = 'user-pre-create'
51
52 display_name = lazy_ugettext('user pre create')
52 name = "user-pre-create"
53 display_name = lazy_ugettext("user pre create")
53
54
54 def __init__(self, user_data):
55 def __init__(self, user_data):
55 super().__init__()
56 super().__init__()
@@ -62,8 +63,9 class UserPostCreate(RhodeCodeIntegratio
62 An instance of this class is emitted as an :term:`event` after a new user
63 An instance of this class is emitted as an :term:`event` after a new user
63 object is created.
64 object is created.
64 """
65 """
65 name = 'user-post-create'
66
66 display_name = lazy_ugettext('user post create')
67 name = "user-post-create"
68 display_name = lazy_ugettext("user post create")
67
69
68 def __init__(self, user_data):
70 def __init__(self, user_data):
69 super().__init__()
71 super().__init__()
@@ -76,8 +78,9 class UserPreUpdate(RhodeCodeIntegration
76 An instance of this class is emitted as an :term:`event` before a user
78 An instance of this class is emitted as an :term:`event` before a user
77 object is updated.
79 object is updated.
78 """
80 """
79 name = 'user-pre-update'
81
80 display_name = lazy_ugettext('user pre update')
82 name = "user-pre-update"
83 display_name = lazy_ugettext("user pre update")
81
84
82 def __init__(self, user, user_data):
85 def __init__(self, user, user_data):
83 super().__init__()
86 super().__init__()
@@ -87,8 +90,8 class UserPreUpdate(RhodeCodeIntegration
87
90
88 class UserPermissionsChange(RhodecodeEvent):
91 class UserPermissionsChange(RhodecodeEvent):
89 """
92 """
90 This event should be triggered on an event that permissions of user might changed.
93 This event should be triggered on an event that permissions of user might be changed.
91 Currently this should be triggered on:
94 Currently, this should be triggered on:
92
95
93 - user added/removed from user group
96 - user added/removed from user group
94 - repo permissions changed
97 - repo permissions changed
@@ -96,9 +99,11 class UserPermissionsChange(RhodecodeEve
96 - user group permissions changed
99 - user group permissions changed
97
100
98 """
101 """
99 name = 'user-permissions-change'
102
100 display_name = lazy_ugettext('user permissions change')
103 name = "user-permissions-change"
104 display_name = lazy_ugettext("user permissions change")
101
105
102 def __init__(self, user_ids):
106 def __init__(self, user_ids, context=None):
103 super().__init__()
107 super().__init__(context=context)
104 self.user_ids = user_ids
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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"""
2 """Utilities for writing code that runs on Python 2 and 3"""# Copyright (C) 2010-2015 Benjamin Peterson
3
4 # Copyright (c) 2010-2015 Benjamin Peterson
5 #
3 #
6 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 # of this software and associated documentation files (the "Software"), to deal
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -478,7 +478,9 class DiffSet(object):
478 log.debug('rendering diff for %r', patch['filename'])
478 log.debug('rendering diff for %r', patch['filename'])
479
479
480 source_filename = patch['original_filename']
480 source_filename = patch['original_filename']
481 source_filename_bytes = patch['original_filename_bytes']
481 target_filename = patch['filename']
482 target_filename = patch['filename']
483 target_filename_bytes = patch['filename_bytes']
482
484
483 source_lexer = plain_text_lexer
485 source_lexer = plain_text_lexer
484 target_lexer = plain_text_lexer
486 target_lexer = plain_text_lexer
@@ -491,12 +493,12 class DiffSet(object):
491 if (source_filename and patch['operation'] in ('D', 'M')
493 if (source_filename and patch['operation'] in ('D', 'M')
492 and source_filename not in self.source_nodes):
494 and source_filename not in self.source_nodes):
493 self.source_nodes[source_filename] = (
495 self.source_nodes[source_filename] = (
494 self.source_node_getter(source_filename))
496 self.source_node_getter(source_filename_bytes))
495
497
496 if (target_filename and patch['operation'] in ('A', 'M')
498 if (target_filename and patch['operation'] in ('A', 'M')
497 and target_filename not in self.target_nodes):
499 and target_filename not in self.target_nodes):
498 self.target_nodes[target_filename] = (
500 self.target_nodes[target_filename] = (
499 self.target_node_getter(target_filename))
501 self.target_node_getter(target_filename_bytes))
500
502
501 elif hl_mode == self.HL_FAST:
503 elif hl_mode == self.HL_FAST:
502 source_lexer = self._get_lexer_for_filename(source_filename)
504 source_lexer = self._get_lexer_for_filename(source_filename)
@@ -558,6 +560,7 class DiffSet(object):
558 })
560 })
559
561
560 file_chunks = patch['chunks'][1:]
562 file_chunks = patch['chunks'][1:]
563
561 for i, hunk in enumerate(file_chunks, 1):
564 for i, hunk in enumerate(file_chunks, 1):
562 hunkbit = self.parse_hunk(hunk, source_file, target_file)
565 hunkbit = self.parse_hunk(hunk, source_file, target_file)
563 hunkbit.source_file_path = source_file_path
566 hunkbit.source_file_path = source_file_path
@@ -593,12 +596,13 class DiffSet(object):
593 return filediff
596 return filediff
594
597
595 def parse_hunk(self, hunk, source_file, target_file):
598 def parse_hunk(self, hunk, source_file, target_file):
599
596 result = AttributeDict(dict(
600 result = AttributeDict(dict(
597 source_start=hunk['source_start'],
601 source_start=hunk['source_start'],
598 source_length=hunk['source_length'],
602 source_length=hunk['source_length'],
599 target_start=hunk['target_start'],
603 target_start=hunk['target_start'],
600 target_length=hunk['target_length'],
604 target_length=hunk['target_length'],
601 section_header=hunk['section_header'],
605 section_header=safe_str(hunk['section_header']),
602 lines=[],
606 lines=[],
603 ))
607 ))
604 before, after = [], []
608 before, after = [], []
@@ -1,4 +1,4
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -307,7 +307,7 class DbManage(object):
307
307
308 def create_test_admin_and_users(self):
308 def create_test_admin_and_users(self):
309 log.info('creating admin and regular test users')
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 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
311 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
312 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
312 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
313 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
313 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
@@ -328,8 +328,6 class DbManage(object):
328 and disables dotencode
328 and disables dotencode
329 """
329 """
330 settings_model = SettingsModel(sa=self.sa)
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 # Build HOOKS
332 # Build HOOKS
335 hooks = [
333 hooks = [
@@ -360,24 +358,6 class DbManage(object):
360 largefiles.ui_value = ''
358 largefiles.ui_value = ''
361 self.sa.add(largefiles)
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 # enable hgevolve disabled by default
361 # enable hgevolve disabled by default
382 hgevolve = RhodeCodeUi()
362 hgevolve = RhodeCodeUi()
383 hgevolve.ui_section = 'extensions'
363 hgevolve.ui_section = 'extensions'
@@ -1,6 +1,4
1
1 # Copyright (C) 2012-2024 RhodeCode GmbH
2
3 # Copyright (C) 2012-2023 RhodeCode GmbH
4 #
2 #
5 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -1,5 +1,4
1
1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
2 #
4 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -455,7 +455,9 class DiffProcessor(object):
455 return arg
455 return arg
456
456
457 for chunk in self._diff.chunks():
457 for chunk in self._diff.chunks():
458 bytes_head = chunk.header
458 head = chunk.header_as_str
459 head = chunk.header_as_str
460
459 log.debug('parsing diff chunk %r', chunk)
461 log.debug('parsing diff chunk %r', chunk)
460
462
461 raw_diff = chunk.raw
463 raw_diff = chunk.raw
@@ -598,10 +600,17 class DiffProcessor(object):
598
600
599 chunks.insert(0, frag)
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 _files.append({
609 _files.append({
603 'original_filename': original_filename,
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 'old_revision': head['a_blob_id'],
614 'old_revision': head['a_blob_id'],
606 'new_revision': head['b_blob_id'],
615 'new_revision': head['b_blob_id'],
607 'chunks': chunks,
616 'chunks': chunks,
@@ -1,4 +1,4
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
4 # it under the terms of the GNU Affero General Public License, version 3
@@ -20,8 +20,7
20 Set of custom exceptions used in RhodeCode
20 Set of custom exceptions used in RhodeCode
21 """
21 """
22
22
23 from webob.exc import HTTPClientError
23 from pyramid.httpexceptions import HTTPBadGateway, HTTPClientError
24 from pyramid.httpexceptions import HTTPBadGateway
25
24
26
25
27 class LdapUsernameError(Exception):
26 class LdapUsernameError(Exception):
@@ -102,12 +101,7 class HTTPRequirementError(HTTPClientErr
102 self.args = (message, )
101 self.args = (message, )
103
102
104
103
105 class ClientNotSupportedError(HTTPRequirementError):
104 class HTTPLockedRepo(HTTPClientError):
106 title = explanation = 'Client Not Supported'
107 reason = None
108
109
110 class HTTPLockedRC(HTTPClientError):
111 """
105 """
112 Special Exception For locked Repos in RhodeCode, the return code can
106 Special Exception For locked Repos in RhodeCode, the return code can
113 be overwritten by _code keyword argument passed into constructors
107 be overwritten by _code keyword argument passed into constructors
@@ -131,14 +125,13 class HTTPBranchProtected(HTTPClientErro
131 Special Exception For Indicating that branch is protected in RhodeCode, the
125 Special Exception For Indicating that branch is protected in RhodeCode, the
132 return code can be overwritten by _code keyword argument passed into constructors
126 return code can be overwritten by _code keyword argument passed into constructors
133 """
127 """
134 code = 403
135 title = explanation = 'Branch Protected'
128 title = explanation = 'Branch Protected'
136 reason = None
129 reason = None
137
130
138 def __init__(self, message, *args, **kwargs):
131
139 self.title = self.explanation = message
132 class ClientNotSupported(HTTPRequirementError):
140 super().__init__(*args, **kwargs)
133 title = explanation = 'Client Not Supported'
141 self.args = (message, )
134 reason = None
142
135
143
136
144 class IMCCommitError(Exception):
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 # This program is free software: you can redistribute it and/or modify
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
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 from pygments.lexers import (
51 from pygments.lexers import (
52 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
52 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
53
53
54 from pyramid.threadlocal import get_current_request
55 from tempita import looper
54 from tempita import looper
56 from webhelpers2.html import literal, HTML, escape
55 from webhelpers2.html import literal, HTML, escape
57 from webhelpers2.html._autolink import _auto_link_urls
56 from webhelpers2.html._autolink import _auto_link_urls
@@ -89,6 +88,7 from rhodecode.lib.utils2 import (
89 str2bool,
88 str2bool,
90 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime,
89 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime,
91 AttributeDict, safe_int, md5, md5_safe, get_host_info)
90 AttributeDict, safe_int, md5, md5_safe, get_host_info)
91 from rhodecode.lib.pyramid_utils import get_current_request
92 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
92 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
93 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
93 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
94 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
94 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
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
NO CONTENT: file was removed
1 NO CONTENT: file was removed
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
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
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
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
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