##// END OF EJS Templates
fix(hooks): improve the code that manages hooks dir and files to be more prone to permissions issues
super-admin -
r1179:4e9d64b6 default
parent child Browse files
Show More
@@ -1,202 +1,208 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2023 RhodeCode GmbH
2 # Copyright (C) 2014-2023 RhodeCode GmbH
3 #
3 #
4 # 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
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
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
17
18 import re
18 import re
19 import os
19 import os
20 import sys
20 import sys
21 import datetime
21 import datetime
22 import logging
22 import logging
23 import pkg_resources
23 import pkg_resources
24
24
25 import vcsserver
25 import vcsserver
26 from vcsserver.str_utils import safe_bytes
26 from vcsserver.str_utils import safe_bytes
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 def get_git_hooks_path(repo_path, bare):
31 def get_git_hooks_path(repo_path, bare):
32 hooks_path = os.path.join(repo_path, 'hooks')
32 hooks_path = os.path.join(repo_path, 'hooks')
33 if not bare:
33 if not bare:
34 hooks_path = os.path.join(repo_path, '.git', 'hooks')
34 hooks_path = os.path.join(repo_path, '.git', 'hooks')
35
35
36 return hooks_path
36 return hooks_path
37
37
38
38
39 def install_git_hooks(repo_path, bare, executable=None, force_create=False):
39 def install_git_hooks(repo_path, bare, executable=None, force_create=False):
40 """
40 """
41 Creates a RhodeCode hook inside a git repository
41 Creates a RhodeCode hook inside a git repository
42
42
43 :param repo_path: path to repository
43 :param repo_path: path to repository
44 :param bare: defines if repository is considered a bare git repo
44 :param executable: binary executable to put in the hooks
45 :param executable: binary executable to put in the hooks
45 :param force_create: Create even if same name hook exists
46 :param force_create: Creates even if the same name hook exists
46 """
47 """
47 executable = executable or sys.executable
48 executable = executable or sys.executable
48 hooks_path = get_git_hooks_path(repo_path, bare)
49 hooks_path = get_git_hooks_path(repo_path, bare)
49
50
50 if not os.path.isdir(hooks_path):
51 # we always call it to ensure dir exists and it has a proper mode
52 if not os.path.exists(hooks_path):
53 # If it doesn't exist, create a new directory with the specified mode
51 os.makedirs(hooks_path, mode=0o777, exist_ok=True)
54 os.makedirs(hooks_path, mode=0o777, exist_ok=True)
55 else:
56 # If it exists, change the directory's mode to the specified mode
57 os.chmod(hooks_path, mode=0o777)
52
58
53 tmpl_post = pkg_resources.resource_string(
59 tmpl_post = pkg_resources.resource_string(
54 'vcsserver', '/'.join(
60 'vcsserver', '/'.join(
55 ('hook_utils', 'hook_templates', 'git_post_receive.py.tmpl')))
61 ('hook_utils', 'hook_templates', 'git_post_receive.py.tmpl')))
56 tmpl_pre = pkg_resources.resource_string(
62 tmpl_pre = pkg_resources.resource_string(
57 'vcsserver', '/'.join(
63 'vcsserver', '/'.join(
58 ('hook_utils', 'hook_templates', 'git_pre_receive.py.tmpl')))
64 ('hook_utils', 'hook_templates', 'git_pre_receive.py.tmpl')))
59
65
60 path = '' # not used for now
66 path = '' # not used for now
61 timestamp = datetime.datetime.utcnow().isoformat()
67 timestamp = datetime.datetime.utcnow().isoformat()
62
68
63 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
69 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
64 log.debug('Installing git hook in repo %s', repo_path)
70 log.debug('Installing git hook in repo %s', repo_path)
65 _hook_file = os.path.join(hooks_path, f'{h_type}-receive')
71 _hook_file = os.path.join(hooks_path, f'{h_type}-receive')
66 _rhodecode_hook = check_rhodecode_hook(_hook_file)
72 _rhodecode_hook = check_rhodecode_hook(_hook_file)
67
73
68 if _rhodecode_hook or force_create:
74 if _rhodecode_hook or force_create:
69 log.debug('writing git %s hook file at %s !', h_type, _hook_file)
75 log.debug('writing git %s hook file at %s !', h_type, _hook_file)
70 try:
76 try:
71 with open(_hook_file, 'wb') as f:
77 with open(_hook_file, 'wb') as f:
72 template = template.replace(b'_TMPL_', safe_bytes(vcsserver.__version__))
78 template = template.replace(b'_TMPL_', safe_bytes(vcsserver.__version__))
73 template = template.replace(b'_DATE_', safe_bytes(timestamp))
79 template = template.replace(b'_DATE_', safe_bytes(timestamp))
74 template = template.replace(b'_ENV_', safe_bytes(executable))
80 template = template.replace(b'_ENV_', safe_bytes(executable))
75 template = template.replace(b'_PATH_', safe_bytes(path))
81 template = template.replace(b'_PATH_', safe_bytes(path))
76 f.write(template)
82 f.write(template)
77 os.chmod(_hook_file, 0o755)
83 os.chmod(_hook_file, 0o755)
78 except OSError:
84 except OSError:
79 log.exception('error writing hook file %s', _hook_file)
85 log.exception('error writing hook file %s', _hook_file)
80 else:
86 else:
81 log.debug('skipping writing hook file')
87 log.debug('skipping writing hook file')
82
88
83 return True
89 return True
84
90
85
91
86 def get_svn_hooks_path(repo_path):
92 def get_svn_hooks_path(repo_path):
87 hooks_path = os.path.join(repo_path, 'hooks')
93 hooks_path = os.path.join(repo_path, 'hooks')
88
94
89 return hooks_path
95 return hooks_path
90
96
91
97
92 def install_svn_hooks(repo_path, executable=None, force_create=False):
98 def install_svn_hooks(repo_path, executable=None, force_create=False):
93 """
99 """
94 Creates RhodeCode hooks inside a svn repository
100 Creates RhodeCode hooks inside a svn repository
95
101
96 :param repo_path: path to repository
102 :param repo_path: path to repository
97 :param executable: binary executable to put in the hooks
103 :param executable: binary executable to put in the hooks
98 :param force_create: Create even if same name hook exists
104 :param force_create: Create even if same name hook exists
99 """
105 """
100 executable = executable or sys.executable
106 executable = executable or sys.executable
101 hooks_path = get_svn_hooks_path(repo_path)
107 hooks_path = get_svn_hooks_path(repo_path)
102 if not os.path.isdir(hooks_path):
108 if not os.path.isdir(hooks_path):
103 os.makedirs(hooks_path, mode=0o777, exist_ok=True)
109 os.makedirs(hooks_path, mode=0o777, exist_ok=True)
104
110
105 tmpl_post = pkg_resources.resource_string(
111 tmpl_post = pkg_resources.resource_string(
106 'vcsserver', '/'.join(
112 'vcsserver', '/'.join(
107 ('hook_utils', 'hook_templates', 'svn_post_commit_hook.py.tmpl')))
113 ('hook_utils', 'hook_templates', 'svn_post_commit_hook.py.tmpl')))
108 tmpl_pre = pkg_resources.resource_string(
114 tmpl_pre = pkg_resources.resource_string(
109 'vcsserver', '/'.join(
115 'vcsserver', '/'.join(
110 ('hook_utils', 'hook_templates', 'svn_pre_commit_hook.py.tmpl')))
116 ('hook_utils', 'hook_templates', 'svn_pre_commit_hook.py.tmpl')))
111
117
112 path = '' # not used for now
118 path = '' # not used for now
113 timestamp = datetime.datetime.utcnow().isoformat()
119 timestamp = datetime.datetime.utcnow().isoformat()
114
120
115 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
121 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
116 log.debug('Installing svn hook in repo %s', repo_path)
122 log.debug('Installing svn hook in repo %s', repo_path)
117 _hook_file = os.path.join(hooks_path, f'{h_type}-commit')
123 _hook_file = os.path.join(hooks_path, f'{h_type}-commit')
118 _rhodecode_hook = check_rhodecode_hook(_hook_file)
124 _rhodecode_hook = check_rhodecode_hook(_hook_file)
119
125
120 if _rhodecode_hook or force_create:
126 if _rhodecode_hook or force_create:
121 log.debug('writing svn %s hook file at %s !', h_type, _hook_file)
127 log.debug('writing svn %s hook file at %s !', h_type, _hook_file)
122
128
123 try:
129 try:
124 with open(_hook_file, 'wb') as f:
130 with open(_hook_file, 'wb') as f:
125 template = template.replace(b'_TMPL_', safe_bytes(vcsserver.__version__))
131 template = template.replace(b'_TMPL_', safe_bytes(vcsserver.__version__))
126 template = template.replace(b'_DATE_', safe_bytes(timestamp))
132 template = template.replace(b'_DATE_', safe_bytes(timestamp))
127 template = template.replace(b'_ENV_', safe_bytes(executable))
133 template = template.replace(b'_ENV_', safe_bytes(executable))
128 template = template.replace(b'_PATH_', safe_bytes(path))
134 template = template.replace(b'_PATH_', safe_bytes(path))
129
135
130 f.write(template)
136 f.write(template)
131 os.chmod(_hook_file, 0o755)
137 os.chmod(_hook_file, 0o755)
132 except OSError:
138 except OSError:
133 log.exception('error writing hook file %s', _hook_file)
139 log.exception('error writing hook file %s', _hook_file)
134 else:
140 else:
135 log.debug('skipping writing hook file')
141 log.debug('skipping writing hook file')
136
142
137 return True
143 return True
138
144
139
145
140 def get_version_from_hook(hook_path):
146 def get_version_from_hook(hook_path):
141 version = b''
147 version = b''
142 hook_content = read_hook_content(hook_path)
148 hook_content = read_hook_content(hook_path)
143 matches = re.search(rb'RC_HOOK_VER\s*=\s*(.*)', hook_content)
149 matches = re.search(rb'RC_HOOK_VER\s*=\s*(.*)', hook_content)
144 if matches:
150 if matches:
145 try:
151 try:
146 version = matches.groups()[0]
152 version = matches.groups()[0]
147 log.debug('got version %s from hooks.', version)
153 log.debug('got version %s from hooks.', version)
148 except Exception:
154 except Exception:
149 log.exception("Exception while reading the hook version.")
155 log.exception("Exception while reading the hook version.")
150 return version.replace(b"'", b"")
156 return version.replace(b"'", b"")
151
157
152
158
153 def check_rhodecode_hook(hook_path):
159 def check_rhodecode_hook(hook_path):
154 """
160 """
155 Check if the hook was created by RhodeCode
161 Check if the hook was created by RhodeCode
156 """
162 """
157 if not os.path.exists(hook_path):
163 if not os.path.exists(hook_path):
158 return True
164 return True
159
165
160 log.debug('hook exists, checking if it is from RhodeCode')
166 log.debug('hook exists, checking if it is from RhodeCode')
161
167
162 version = get_version_from_hook(hook_path)
168 version = get_version_from_hook(hook_path)
163 if version:
169 if version:
164 return True
170 return True
165
171
166 return False
172 return False
167
173
168
174
169 def read_hook_content(hook_path) -> bytes:
175 def read_hook_content(hook_path) -> bytes:
170 content = b''
176 content = b''
171 if os.path.isfile(hook_path):
177 if os.path.isfile(hook_path):
172 with open(hook_path, 'rb') as f:
178 with open(hook_path, 'rb') as f:
173 content = f.read()
179 content = f.read()
174 return content
180 return content
175
181
176
182
177 def get_git_pre_hook_version(repo_path, bare):
183 def get_git_pre_hook_version(repo_path, bare):
178 hooks_path = get_git_hooks_path(repo_path, bare)
184 hooks_path = get_git_hooks_path(repo_path, bare)
179 _hook_file = os.path.join(hooks_path, 'pre-receive')
185 _hook_file = os.path.join(hooks_path, 'pre-receive')
180 version = get_version_from_hook(_hook_file)
186 version = get_version_from_hook(_hook_file)
181 return version
187 return version
182
188
183
189
184 def get_git_post_hook_version(repo_path, bare):
190 def get_git_post_hook_version(repo_path, bare):
185 hooks_path = get_git_hooks_path(repo_path, bare)
191 hooks_path = get_git_hooks_path(repo_path, bare)
186 _hook_file = os.path.join(hooks_path, 'post-receive')
192 _hook_file = os.path.join(hooks_path, 'post-receive')
187 version = get_version_from_hook(_hook_file)
193 version = get_version_from_hook(_hook_file)
188 return version
194 return version
189
195
190
196
191 def get_svn_pre_hook_version(repo_path):
197 def get_svn_pre_hook_version(repo_path):
192 hooks_path = get_svn_hooks_path(repo_path)
198 hooks_path = get_svn_hooks_path(repo_path)
193 _hook_file = os.path.join(hooks_path, 'pre-commit')
199 _hook_file = os.path.join(hooks_path, 'pre-commit')
194 version = get_version_from_hook(_hook_file)
200 version = get_version_from_hook(_hook_file)
195 return version
201 return version
196
202
197
203
198 def get_svn_post_hook_version(repo_path):
204 def get_svn_post_hook_version(repo_path):
199 hooks_path = get_svn_hooks_path(repo_path)
205 hooks_path = get_svn_hooks_path(repo_path)
200 _hook_file = os.path.join(hooks_path, 'post-commit')
206 _hook_file = os.path.join(hooks_path, 'post-commit')
201 version = get_version_from_hook(_hook_file)
207 version = get_version_from_hook(_hook_file)
202 return version
208 return version
General Comments 0
You need to be logged in to leave comments. Login now