##// END OF EJS Templates
fix(svn): fixed problems with svn hooks binary dir not beeing propagates in mod_dav_svn
super-admin -
r1229:fe30068d v5.0.1 stable
parent child Browse files
Show More
@@ -0,0 +1,40 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import os
19
20
21 def get_config(ini_path, **kwargs):
22 import configparser
23 parser = configparser.ConfigParser(**kwargs)
24 parser.read(ini_path)
25 return parser
26
27
28 def get_app_config_lightweight(ini_path):
29 parser = get_config(ini_path)
30 parser.set('app:main', 'here', os.getcwd())
31 parser.set('app:main', '__file__', ini_path)
32 return dict(parser.items('app:main'))
33
34
35 def get_app_config(ini_path):
36 """
37 This loads the app context and provides a heavy type iniliaziation of config
38 """
39 from paste.deploy.loadwsgi import appconfig
40 return appconfig(f'config:{ini_path}', relative_to=os.getcwd())
@@ -28,6 +28,7 b' from vcsserver.type_utils import str2boo'
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31
31 # skip keys, that are set here, so we don't double process those
32 # skip keys, that are set here, so we don't double process those
32 set_keys = {
33 set_keys = {
33 '__file__': ''
34 '__file__': ''
@@ -51,6 +52,10 b' class SettingsMaker:'
51 return int(input_val)
52 return int(input_val)
52
53
53 @classmethod
54 @classmethod
55 def _float_func(cls, input_val):
56 return float(input_val)
57
58 @classmethod
54 def _list_func(cls, input_val, sep=','):
59 def _list_func(cls, input_val, sep=','):
55 return aslist(input_val, sep=sep)
60 return aslist(input_val, sep=sep)
56
61
@@ -61,8 +66,18 b' class SettingsMaker:'
61 return input_val
66 return input_val
62
67
63 @classmethod
68 @classmethod
64 def _float_func(cls, input_val):
69 def _string_no_quote_func(cls, input_val, lower=True):
65 return float(input_val)
70 """
71 Special case string function that detects if value is set to empty quote string
72 e.g.
73
74 core.binary_dir = ""
75 """
76
77 input_val = cls._string_func(input_val, lower=lower)
78 if input_val in ['""', "''"]:
79 return ''
80 return input_val
66
81
67 @classmethod
82 @classmethod
68 def _dir_func(cls, input_val, ensure_dir=False, mode=0o755):
83 def _dir_func(cls, input_val, ensure_dir=False, mode=0o755):
@@ -148,10 +163,12 b' class SettingsMaker:'
148 parser_func = {
163 parser_func = {
149 'bool': self._bool_func,
164 'bool': self._bool_func,
150 'int': self._int_func,
165 'int': self._int_func,
166 'float': self._float_func,
151 'list': self._list_func,
167 'list': self._list_func,
152 'list:newline': functools.partial(self._list_func, sep='/n'),
168 'list:newline': functools.partial(self._list_func, sep='/n'),
153 'list:spacesep': functools.partial(self._list_func, sep=' '),
169 'list:spacesep': functools.partial(self._list_func, sep=' '),
154 'string': functools.partial(self._string_func, lower=lower),
170 'string': functools.partial(self._string_func, lower=lower),
171 'string:noquote': functools.partial(self._string_no_quote_func, lower=lower),
155 'dir': self._dir_func,
172 'dir': self._dir_func,
156 'dir:ensured': functools.partial(self._dir_func, ensure_dir=True),
173 'dir:ensured': functools.partial(self._dir_func, ensure_dir=True),
157 'file': self._file_path_func,
174 'file': self._file_path_func,
@@ -142,9 +142,9 b' def install_svn_hooks(repo_path, executa'
142
142
143 env_expand = str([
143 env_expand = str([
144 ('RC_CORE_BINARY_DIR', vcsserver.settings.BINARY_DIR),
144 ('RC_CORE_BINARY_DIR', vcsserver.settings.BINARY_DIR),
145 ('RC_GIT_EXECUTABLE', vcsserver.settings.GIT_EXECUTABLE),
145 ('RC_GIT_EXECUTABLE', vcsserver.settings.GIT_EXECUTABLE()),
146 ('RC_SVN_EXECUTABLE', vcsserver.settings.SVN_EXECUTABLE),
146 ('RC_SVN_EXECUTABLE', vcsserver.settings.SVN_EXECUTABLE()),
147 ('RC_SVNLOOK_EXECUTABLE', vcsserver.settings.SVNLOOK_EXECUTABLE),
147 ('RC_SVNLOOK_EXECUTABLE', vcsserver.settings.SVNLOOK_EXECUTABLE()),
148
148
149 ])
149 ])
150 try:
150 try:
@@ -31,7 +31,6 b' from celery import Celery'
31 import mercurial.scmutil
31 import mercurial.scmutil
32 import mercurial.node
32 import mercurial.node
33
33
34 import vcsserver.settings
35 from vcsserver.lib.rc_json import json
34 from vcsserver.lib.rc_json import json
36 from vcsserver import exceptions, subprocessio, settings
35 from vcsserver import exceptions, subprocessio, settings
37 from vcsserver.str_utils import ascii_str, safe_str
36 from vcsserver.str_utils import ascii_str, safe_str
@@ -294,20 +293,23 b' def _get_hg_env(old_rev, new_rev, txnid,'
294 return [(k, v) for k, v in env.items()]
293 return [(k, v) for k, v in env.items()]
295
294
296
295
297 def _fix_hooks_executables():
296 def _fix_hooks_executables(ini_path=''):
298 """
297 """
299 This is a trick to set proper settings.EXECUTABLE paths for certain execution patterns
298 This is a trick to set proper settings.EXECUTABLE paths for certain execution patterns
300 especially for subversion where hooks strip entire env, and calling just 'svn' command will most likely fail
299 especially for subversion where hooks strip entire env, and calling just 'svn' command will most likely fail
301 because svn is not on PATH
300 because svn is not on PATH
302 """
301 """
303 vcsserver.settings.BINARY_DIR = (
302 from vcsserver.http_main import sanitize_settings_and_apply_defaults
304 os.environ.get('RC_BINARY_DIR') or vcsserver.settings.BINARY_DIR)
303 from vcsserver.lib.config_utils import get_app_config_lightweight
305 vcsserver.settings.GIT_EXECUTABLE = (
304
306 os.environ.get('RC_GIT_EXECUTABLE') or vcsserver.settings.GIT_EXECUTABLE)
305 core_binary_dir = settings.BINARY_DIR or '/usr/local/bin/rhodecode_bin/vcs_bin'
307 vcsserver.settings.SVN_EXECUTABLE = (
306 if ini_path:
308 os.environ.get('RC_SVN_EXECUTABLE') or vcsserver.settings.SVN_EXECUTABLE)
307
309 vcsserver.settings.SVNLOOK_EXECUTABLE = (
308 ini_settings = get_app_config_lightweight(ini_path)
310 os.environ.get('RC_SVNLOOK_EXECUTABLE') or vcsserver.settings.SVNLOOK_EXECUTABLE)
309 ini_settings = sanitize_settings_and_apply_defaults({'__file__': ini_path}, ini_settings)
310 core_binary_dir = ini_settings['core.binary_dir']
311
312 settings.BINARY_DIR = core_binary_dir
311
313
312
314
313 def repo_size(ui, repo, **kwargs):
315 def repo_size(ui, repo, **kwargs):
@@ -568,10 +570,12 b' def git_pre_receive(unused_repo_path, re'
568 rev_data = _parse_git_ref_lines(revision_lines)
570 rev_data = _parse_git_ref_lines(revision_lines)
569 if 'push' not in extras['hooks']:
571 if 'push' not in extras['hooks']:
570 return 0
572 return 0
573 _fix_hooks_executables()
574
571 empty_commit_id = '0' * 40
575 empty_commit_id = '0' * 40
572
576
573 detect_force_push = extras.get('detect_force_push')
577 detect_force_push = extras.get('detect_force_push')
574 _fix_hooks_executables()
578
575 for push_ref in rev_data:
579 for push_ref in rev_data:
576 # store our git-env which holds the temp store
580 # store our git-env which holds the temp store
577 push_ref['git_env'] = _get_git_env()
581 push_ref['git_env'] = _get_git_env()
@@ -586,7 +590,7 b' def git_pre_receive(unused_repo_path, re'
586 if type_ == 'heads' and not (new_branch or delete_branch):
590 if type_ == 'heads' and not (new_branch or delete_branch):
587 old_rev = push_ref['old_rev']
591 old_rev = push_ref['old_rev']
588 new_rev = push_ref['new_rev']
592 new_rev = push_ref['new_rev']
589 cmd = [settings.GIT_EXECUTABLE, 'rev-list', old_rev, f'^{new_rev}']
593 cmd = [settings.GIT_EXECUTABLE(), 'rev-list', old_rev, f'^{new_rev}']
590 stdout, stderr = subprocessio.run_command(
594 stdout, stderr = subprocessio.run_command(
591 cmd, env=os.environ.copy())
595 cmd, env=os.environ.copy())
592 # means we're having some non-reachable objects, this forced push was used
596 # means we're having some non-reachable objects, this forced push was used
@@ -611,6 +615,7 b' def git_post_receive(unused_repo_path, r'
611 extras = json.loads(env['RC_SCM_DATA'])
615 extras = json.loads(env['RC_SCM_DATA'])
612 if 'push' not in extras['hooks']:
616 if 'push' not in extras['hooks']:
613 return 0
617 return 0
618
614 _fix_hooks_executables()
619 _fix_hooks_executables()
615
620
616 rev_data = _parse_git_ref_lines(revision_lines)
621 rev_data = _parse_git_ref_lines(revision_lines)
@@ -645,14 +650,14 b' def git_post_receive(unused_repo_path, r'
645 repo.set_head(need_head_set)
650 repo.set_head(need_head_set)
646 print(f"Setting default branch to {push_ref_name}")
651 print(f"Setting default branch to {push_ref_name}")
647
652
648 cmd = [settings.GIT_EXECUTABLE, 'for-each-ref', '--format=%(refname)', 'refs/heads/*']
653 cmd = [settings.GIT_EXECUTABLE(), 'for-each-ref', '--format=%(refname)', 'refs/heads/*']
649 stdout, stderr = subprocessio.run_command(
654 stdout, stderr = subprocessio.run_command(
650 cmd, env=os.environ.copy())
655 cmd, env=os.environ.copy())
651 heads = safe_str(stdout)
656 heads = safe_str(stdout)
652 heads = heads.replace(push_ref['ref'], '')
657 heads = heads.replace(push_ref['ref'], '')
653 heads = ' '.join(head for head
658 heads = ' '.join(head for head
654 in heads.splitlines() if head) or '.'
659 in heads.splitlines() if head) or '.'
655 cmd = [settings.GIT_EXECUTABLE, 'log', '--reverse',
660 cmd = [settings.GIT_EXECUTABLE(), 'log', '--reverse',
656 '--pretty=format:%H', '--', push_ref['new_rev'],
661 '--pretty=format:%H', '--', push_ref['new_rev'],
657 '--not', heads]
662 '--not', heads]
658 stdout, stderr = subprocessio.run_command(
663 stdout, stderr = subprocessio.run_command(
@@ -666,7 +671,7 b' def git_post_receive(unused_repo_path, r'
666 if push_ref['name'] not in branches:
671 if push_ref['name'] not in branches:
667 branches.append(push_ref['name'])
672 branches.append(push_ref['name'])
668
673
669 cmd = [settings.GIT_EXECUTABLE, 'log',
674 cmd = [settings.GIT_EXECUTABLE(), 'log',
670 f'{push_ref["old_rev"]}..{push_ref["new_rev"]}',
675 f'{push_ref["old_rev"]}..{push_ref["new_rev"]}',
671 '--reverse', '--pretty=format:%H']
676 '--reverse', '--pretty=format:%H']
672 stdout, stderr = subprocessio.run_command(
677 stdout, stderr = subprocessio.run_command(
@@ -716,9 +721,11 b' def git_post_receive(unused_repo_path, r'
716
721
717
722
718 def _get_extras_from_txn_id(path, txn_id):
723 def _get_extras_from_txn_id(path, txn_id):
724 _fix_hooks_executables()
725
719 extras = {}
726 extras = {}
720 try:
727 try:
721 cmd = [settings.SVNLOOK_EXECUTABLE, 'pget',
728 cmd = [settings.SVNLOOK_EXECUTABLE(), 'pget',
722 '-t', txn_id,
729 '-t', txn_id,
723 '--revprop', path, 'rc-scm-extras']
730 '--revprop', path, 'rc-scm-extras']
724 stdout, stderr = subprocessio.run_command(
731 stdout, stderr = subprocessio.run_command(
@@ -731,9 +738,11 b' def _get_extras_from_txn_id(path, txn_id'
731
738
732
739
733 def _get_extras_from_commit_id(commit_id, path):
740 def _get_extras_from_commit_id(commit_id, path):
741 _fix_hooks_executables()
742
734 extras = {}
743 extras = {}
735 try:
744 try:
736 cmd = [settings.SVNLOOK_EXECUTABLE, 'pget',
745 cmd = [settings.SVNLOOK_EXECUTABLE(), 'pget',
737 '-r', commit_id,
746 '-r', commit_id,
738 '--revprop', path, 'rc-scm-extras']
747 '--revprop', path, 'rc-scm-extras']
739 stdout, stderr = subprocessio.run_command(
748 stdout, stderr = subprocessio.run_command(
@@ -746,11 +755,11 b' def _get_extras_from_commit_id(commit_id'
746
755
747
756
748 def svn_pre_commit(repo_path, commit_data, env):
757 def svn_pre_commit(repo_path, commit_data, env):
758
749 path, txn_id = commit_data
759 path, txn_id = commit_data
750 branches = []
760 branches = []
751 tags = []
761 tags = []
752
762
753 _fix_hooks_executables()
754 if env.get('RC_SCM_DATA'):
763 if env.get('RC_SCM_DATA'):
755 extras = json.loads(env['RC_SCM_DATA'])
764 extras = json.loads(env['RC_SCM_DATA'])
756 else:
765 else:
@@ -790,7 +799,6 b' def svn_post_commit(repo_path, commit_da'
790 branches = []
799 branches = []
791 tags = []
800 tags = []
792
801
793 _fix_hooks_executables()
794 if env.get('RC_SCM_DATA'):
802 if env.get('RC_SCM_DATA'):
795 extras = json.loads(env['RC_SCM_DATA'])
803 extras = json.loads(env['RC_SCM_DATA'])
796 else:
804 else:
@@ -263,11 +263,6 b' class HTTPApplication:'
263
263
264 settings.BINARY_DIR = binary_dir
264 settings.BINARY_DIR = binary_dir
265
265
266 # from core.binary dir we set executable paths
267 settings.GIT_EXECUTABLE = os.path.join(binary_dir, settings.GIT_EXECUTABLE)
268 settings.SVN_EXECUTABLE = os.path.join(binary_dir, settings.SVN_EXECUTABLE)
269 settings.SVNLOOK_EXECUTABLE = os.path.join(binary_dir, settings.SVNLOOK_EXECUTABLE)
270
271 # Store the settings to make them available to other modules.
266 # Store the settings to make them available to other modules.
272 vcsserver.PYRAMID_SETTINGS = settings_merged
267 vcsserver.PYRAMID_SETTINGS = settings_merged
273 vcsserver.CONFIG = settings_merged
268 vcsserver.CONFIG = settings_merged
@@ -716,7 +711,9 b' def sanitize_settings_and_apply_defaults'
716 settings_maker.make_setting('pyramid.default_locale_name', 'en')
711 settings_maker.make_setting('pyramid.default_locale_name', 'en')
717 settings_maker.make_setting('locale', 'en_US.UTF-8')
712 settings_maker.make_setting('locale', 'en_US.UTF-8')
718
713
719 settings_maker.make_setting('core.binary_dir', '/usr/local/bin/rhodecode_bin/vcs_bin')
714 settings_maker.make_setting(
715 'core.binary_dir', '/usr/local/bin/rhodecode_bin/vcs_bin',
716 default_when_empty=True, parser='string:noquote')
720
717
721 temp_store = tempfile.gettempdir()
718 temp_store = tempfile.gettempdir()
722 default_cache_dir = os.path.join(temp_store, 'rc_cache')
719 default_cache_dir = os.path.join(temp_store, 'rc_cache')
@@ -40,7 +40,7 b' from dulwich.repo import Repo as Dulwich'
40
40
41 import rhodecode
41 import rhodecode
42 from vcsserver import exceptions, settings, subprocessio
42 from vcsserver import exceptions, settings, subprocessio
43 from vcsserver.str_utils import safe_str, safe_int, safe_bytes, ascii_bytes, convert_to_str
43 from vcsserver.str_utils import safe_str, safe_int, safe_bytes, ascii_bytes, convert_to_str, splitnewlines
44 from vcsserver.base import RepoFactory, obfuscate_qs, ArchiveNode, store_archive_in_cache, BytesEnvelope, BinaryEnvelope
44 from vcsserver.base import RepoFactory, obfuscate_qs, ArchiveNode, store_archive_in_cache, BytesEnvelope, BinaryEnvelope
45 from vcsserver.hgcompat import (
45 from vcsserver.hgcompat import (
46 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
46 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
@@ -1347,7 +1347,8 b' class GitRemote(RemoteBase):'
1347 with repo_init as repo:
1347 with repo_init as repo:
1348 commit = repo[commit_id]
1348 commit = repo[commit_id]
1349 blame_obj = repo.blame(path, newest_commit=commit_id)
1349 blame_obj = repo.blame(path, newest_commit=commit_id)
1350 for i, line in enumerate(commit.tree[path].data.splitlines()):
1350 file_content = commit.tree[path].data
1351 for i, line in enumerate(splitnewlines(file_content)):
1351 line_no = i + 1
1352 line_no = i + 1
1352 hunk = blame_obj.for_line(line_no)
1353 hunk = blame_obj.for_line(line_no)
1353 blame_commit_id = hunk.final_commit_id.hex
1354 blame_commit_id = hunk.final_commit_id.hex
@@ -1423,7 +1424,7 b' class GitRemote(RemoteBase):'
1423 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
1424 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
1424 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
1425 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
1425
1426
1426 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
1427 cmd = [settings.GIT_EXECUTABLE()] + _copts + cmd
1427 _opts = {'env': gitenv, 'shell': False}
1428 _opts = {'env': gitenv, 'shell': False}
1428
1429
1429 proc = None
1430 proc = None
@@ -214,7 +214,7 b' def create_git_wsgi_app(repo_path, repo_'
214
214
215 :param config: is a dictionary holding the extras.
215 :param config: is a dictionary holding the extras.
216 """
216 """
217 git_path = settings.GIT_EXECUTABLE
217 git_path = settings.GIT_EXECUTABLE()
218 update_server_info = config.pop('git_update_server_info')
218 update_server_info = config.pop('git_update_server_info')
219 app = GitHandler(
219 app = GitHandler(
220 repo_path, repo_name, git_path, update_server_info, config)
220 repo_path, repo_name, git_path, update_server_info, config)
@@ -244,7 +244,7 b' class GitLFSHandler:'
244
244
245
245
246 def create_git_lfs_wsgi_app(repo_path, repo_name, config):
246 def create_git_lfs_wsgi_app(repo_path, repo_name, config):
247 git_path = settings.GIT_EXECUTABLE
247 git_path = settings.GIT_EXECUTABLE()
248 update_server_info = config.pop('git_update_server_info')
248 update_server_info = config.pop('git_update_server_info')
249 git_lfs_enabled = config.pop('git_lfs_enabled')
249 git_lfs_enabled = config.pop('git_lfs_enabled')
250 git_lfs_store_path = config.pop('git_lfs_store_path')
250 git_lfs_store_path = config.pop('git_lfs_store_path')
@@ -14,9 +14,18 b''
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 import os
17
18
18 WIRE_ENCODING = 'UTF-8'
19 WIRE_ENCODING = 'UTF-8'
19 GIT_EXECUTABLE = 'git'
20
20 SVN_EXECUTABLE = 'svn'
21 # Path where we can find binary dir
21 SVNLOOK_EXECUTABLE = 'svnlook'
22 BINARY_DIR = ''
22 BINARY_DIR = ''
23
24 def GIT_EXECUTABLE() -> str:
25 return os.environ.get('RC_GIT_EXECUTABLE') or os.path.join(BINARY_DIR, 'git')
26
27 def SVN_EXECUTABLE() -> str:
28 return os.environ.get('RC_SVN_EXECUTABLE') or os.path.join(BINARY_DIR, 'svn')
29
30 def SVNLOOK_EXECUTABLE() -> str:
31 return os.environ.get('RC_SVNLOOK_EXECUTABLE') or os.path.join(BINARY_DIR, 'svnlook')
@@ -142,3 +142,17 b' def convert_to_str(data):'
142 return list(convert_to_str(item) for item in data)
142 return list(convert_to_str(item) for item in data)
143 else:
143 else:
144 return data
144 return data
145
146
147 def splitnewlines(text: bytes):
148 """
149 like splitlines, but only split on newlines.
150 """
151
152 lines = [_l + b'\n' for _l in text.split(b'\n')]
153 if lines:
154 if lines[-1] == b'\n':
155 lines.pop()
156 else:
157 lines[-1] = lines[-1][:-1]
158 return lines No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now