##// END OF EJS Templates
forks: fixed forking with new landing rev....
marcink -
r3969:f86c0abc default
parent child Browse files
Show More
@@ -1,336 +1,333 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22
23 23 from rhodecode.tests import TestController, assert_session_flash, HG_FORK, GIT_FORK
24 24
25 25 from rhodecode.tests.fixture import Fixture
26 26 from rhodecode.lib import helpers as h
27 27
28 28 from rhodecode.model.db import Repository
29 29 from rhodecode.model.repo import RepoModel
30 30 from rhodecode.model.user import UserModel
31 31 from rhodecode.model.meta import Session
32 32
33 33 fixture = Fixture()
34 34
35 35
36 36 def route_path(name, params=None, **kwargs):
37 37 import urllib
38 38
39 39 base_url = {
40 40 'repo_summary': '/{repo_name}',
41 41 'repo_creating_check': '/{repo_name}/repo_creating_check',
42 42 'repo_fork_new': '/{repo_name}/fork',
43 43 'repo_fork_create': '/{repo_name}/fork/create',
44 44 'repo_forks_show_all': '/{repo_name}/forks',
45 45 'repo_forks_data': '/{repo_name}/forks/data',
46 46 }[name].format(**kwargs)
47 47
48 48 if params:
49 49 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
50 50 return base_url
51 51
52 52
53 53 FORK_NAME = {
54 54 'hg': HG_FORK,
55 55 'git': GIT_FORK
56 56 }
57 57
58 58
59 59 @pytest.mark.skip_backends('svn')
60 60 class TestRepoForkViewTests(TestController):
61 61
62 62 def test_show_forks(self, backend, xhr_header):
63 63 self.log_user()
64 64 response = self.app.get(
65 65 route_path('repo_forks_data', repo_name=backend.repo_name),
66 66 extra_environ=xhr_header)
67 67
68 68 assert response.json == {u'data': [], u'draw': None,
69 69 u'recordsFiltered': 0, u'recordsTotal': 0}
70 70
71 71 def test_no_permissions_to_fork_page(self, backend, user_util):
72 72 user = user_util.create_user(password='qweqwe')
73 73 user_id = user.user_id
74 74 self.log_user(user.username, 'qweqwe')
75 75
76 76 user_model = UserModel()
77 77 user_model.revoke_perm(user_id, 'hg.fork.repository')
78 78 user_model.grant_perm(user_id, 'hg.fork.none')
79 79 u = UserModel().get(user_id)
80 80 u.inherit_default_permissions = False
81 81 Session().commit()
82 82 # try create a fork
83 83 self.app.get(
84 84 route_path('repo_fork_new', repo_name=backend.repo_name),
85 85 status=404)
86 86
87 87 def test_no_permissions_to_fork_submit(self, backend, csrf_token, user_util):
88 88 user = user_util.create_user(password='qweqwe')
89 89 user_id = user.user_id
90 90 self.log_user(user.username, 'qweqwe')
91 91
92 92 user_model = UserModel()
93 93 user_model.revoke_perm(user_id, 'hg.fork.repository')
94 94 user_model.grant_perm(user_id, 'hg.fork.none')
95 95 u = UserModel().get(user_id)
96 96 u.inherit_default_permissions = False
97 97 Session().commit()
98 98 # try create a fork
99 99 self.app.post(
100 100 route_path('repo_fork_create', repo_name=backend.repo_name),
101 101 {'csrf_token': csrf_token},
102 102 status=404)
103 103
104 104 def test_fork_missing_data(self, autologin_user, backend, csrf_token):
105 105 # try create a fork
106 106 response = self.app.post(
107 107 route_path('repo_fork_create', repo_name=backend.repo_name),
108 108 {'csrf_token': csrf_token},
109 109 status=200)
110 110 # test if html fill works fine
111 111 response.mustcontain('Missing value')
112 112
113 113 def test_create_fork_page(self, autologin_user, backend):
114 114 self.app.get(
115 115 route_path('repo_fork_new', repo_name=backend.repo_name),
116 116 status=200)
117 117
118 118 def test_create_and_show_fork(
119 119 self, autologin_user, backend, csrf_token, xhr_header):
120 120
121 121 # create a fork
122 122 fork_name = FORK_NAME[backend.alias]
123 123 description = 'fork of vcs test'
124 124 repo_name = backend.repo_name
125 125 source_repo = Repository.get_by_repo_name(repo_name)
126 126 creation_args = {
127 127 'repo_name': fork_name,
128 128 'repo_group': '',
129 129 'fork_parent_id': source_repo.repo_id,
130 130 'repo_type': backend.alias,
131 131 'description': description,
132 132 'private': 'False',
133 'landing_rev': 'rev:tip',
134 133 'csrf_token': csrf_token,
135 134 }
136 135
137 136 self.app.post(
138 137 route_path('repo_fork_create', repo_name=repo_name), creation_args)
139 138
140 139 response = self.app.get(
141 140 route_path('repo_forks_data', repo_name=repo_name),
142 141 extra_environ=xhr_header)
143 142
144 143 assert response.json['data'][0]['fork_name'] == \
145 144 """<a href="/%s">%s</a>""" % (fork_name, fork_name)
146 145
147 146 # remove this fork
148 147 fixture.destroy_repo(fork_name)
149 148
150 149 def test_fork_create(self, autologin_user, backend, csrf_token):
151 150 fork_name = FORK_NAME[backend.alias]
152 151 description = 'fork of vcs test'
153 152 repo_name = backend.repo_name
154 153 source_repo = Repository.get_by_repo_name(repo_name)
155 154 creation_args = {
156 155 'repo_name': fork_name,
157 156 'repo_group': '',
158 157 'fork_parent_id': source_repo.repo_id,
159 158 'repo_type': backend.alias,
160 159 'description': description,
161 160 'private': 'False',
162 'landing_rev': 'rev:tip',
163 161 'csrf_token': csrf_token,
164 162 }
165 163 self.app.post(
166 164 route_path('repo_fork_create', repo_name=repo_name), creation_args)
167 165 repo = Repository.get_by_repo_name(FORK_NAME[backend.alias])
168 166 assert repo.fork.repo_name == backend.repo_name
169 167
170 168 # run the check page that triggers the flash message
171 169 response = self.app.get(
172 170 route_path('repo_creating_check', repo_name=fork_name))
173 171 # test if we have a message that fork is ok
174 172 assert_session_flash(response,
175 'Forked repository %s as <a href="/%s">%s</a>'
176 % (repo_name, fork_name, fork_name))
173 'Forked repository %s as <a href="/%s">%s</a>' % (
174 repo_name, fork_name, fork_name))
177 175
178 176 # test if the fork was created in the database
179 177 fork_repo = Session().query(Repository)\
180 178 .filter(Repository.repo_name == fork_name).one()
181 179
182 180 assert fork_repo.repo_name == fork_name
183 181 assert fork_repo.fork.repo_name == repo_name
184 182
185 183 # test if the repository is visible in the list ?
186 184 response = self.app.get(
187 185 h.route_path('repo_summary', repo_name=fork_name))
188 186 response.mustcontain(fork_name)
189 187 response.mustcontain(backend.alias)
190 188 response.mustcontain('Fork of')
191 189 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
192 190
193 191 def test_fork_create_into_group(self, autologin_user, backend, csrf_token):
194 192 group = fixture.create_repo_group('vc')
195 193 group_id = group.group_id
196 194 fork_name = FORK_NAME[backend.alias]
197 195 fork_name_full = 'vc/%s' % fork_name
198 196 description = 'fork of vcs test'
199 197 repo_name = backend.repo_name
200 198 source_repo = Repository.get_by_repo_name(repo_name)
201 199 creation_args = {
202 200 'repo_name': fork_name,
203 201 'repo_group': group_id,
204 202 'fork_parent_id': source_repo.repo_id,
205 203 'repo_type': backend.alias,
206 204 'description': description,
207 205 'private': 'False',
208 'landing_rev': 'rev:tip',
209 206 'csrf_token': csrf_token,
210 207 }
211 208 self.app.post(
212 209 route_path('repo_fork_create', repo_name=repo_name), creation_args)
213 210 repo = Repository.get_by_repo_name(fork_name_full)
214 211 assert repo.fork.repo_name == backend.repo_name
215 212
216 213 # run the check page that triggers the flash message
217 214 response = self.app.get(
218 215 route_path('repo_creating_check', repo_name=fork_name_full))
219 216 # test if we have a message that fork is ok
220 217 assert_session_flash(response,
221 'Forked repository %s as <a href="/%s">%s</a>'
222 % (repo_name, fork_name_full, fork_name_full))
218 'Forked repository %s as <a href="/%s">%s</a>' % (
219 repo_name, fork_name_full, fork_name_full))
223 220
224 221 # test if the fork was created in the database
225 222 fork_repo = Session().query(Repository)\
226 223 .filter(Repository.repo_name == fork_name_full).one()
227 224
228 225 assert fork_repo.repo_name == fork_name_full
229 226 assert fork_repo.fork.repo_name == repo_name
230 227
231 228 # test if the repository is visible in the list ?
232 229 response = self.app.get(
233 230 h.route_path('repo_summary', repo_name=fork_name_full))
234 231 response.mustcontain(fork_name_full)
235 232 response.mustcontain(backend.alias)
236 233
237 234 response.mustcontain('Fork of')
238 235 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
239 236
240 237 fixture.destroy_repo(fork_name_full)
241 238 fixture.destroy_repo_group(group_id)
242 239
243 240 def test_fork_read_permission(self, backend, xhr_header, user_util):
244 241 user = user_util.create_user(password='qweqwe')
245 242 user_id = user.user_id
246 243 self.log_user(user.username, 'qweqwe')
247 244
248 245 # create a fake fork
249 246 fork = user_util.create_repo(repo_type=backend.alias)
250 247 source = user_util.create_repo(repo_type=backend.alias)
251 248 repo_name = source.repo_name
252 249
253 250 fork.fork_id = source.repo_id
254 251 fork_name = fork.repo_name
255 252 Session().commit()
256 253
257 254 forks = Repository.query()\
258 255 .filter(Repository.repo_type == backend.alias)\
259 256 .filter(Repository.fork_id == source.repo_id).all()
260 257 assert 1 == len(forks)
261 258
262 259 # set read permissions for this
263 260 RepoModel().grant_user_permission(
264 261 repo=forks[0], user=user_id, perm='repository.read')
265 262 Session().commit()
266 263
267 264 response = self.app.get(
268 265 route_path('repo_forks_data', repo_name=repo_name),
269 266 extra_environ=xhr_header)
270 267
271 268 assert response.json['data'][0]['fork_name'] == \
272 269 """<a href="/%s">%s</a>""" % (fork_name, fork_name)
273 270
274 271 def test_fork_none_permission(self, backend, xhr_header, user_util):
275 272 user = user_util.create_user(password='qweqwe')
276 273 user_id = user.user_id
277 274 self.log_user(user.username, 'qweqwe')
278 275
279 276 # create a fake fork
280 277 fork = user_util.create_repo(repo_type=backend.alias)
281 278 source = user_util.create_repo(repo_type=backend.alias)
282 279 repo_name = source.repo_name
283 280
284 281 fork.fork_id = source.repo_id
285 282
286 283 Session().commit()
287 284
288 285 forks = Repository.query()\
289 286 .filter(Repository.repo_type == backend.alias)\
290 287 .filter(Repository.fork_id == source.repo_id).all()
291 288 assert 1 == len(forks)
292 289
293 290 # set none
294 291 RepoModel().grant_user_permission(
295 292 repo=forks[0], user=user_id, perm='repository.none')
296 293 Session().commit()
297 294
298 295 # fork shouldn't be there
299 296 response = self.app.get(
300 297 route_path('repo_forks_data', repo_name=repo_name),
301 298 extra_environ=xhr_header)
302 299
303 300 assert response.json == {u'data': [], u'draw': None,
304 301 u'recordsFiltered': 0, u'recordsTotal': 0}
305 302
306 303 @pytest.mark.parametrize('url_type', [
307 304 'repo_fork_new',
308 305 'repo_fork_create'
309 306 ])
310 307 def test_fork_is_forbidden_on_archived_repo(self, backend, xhr_header, user_util, url_type):
311 308 user = user_util.create_user(password='qweqwe')
312 309 self.log_user(user.username, 'qweqwe')
313 310
314 311 # create a temporary repo
315 312 source = user_util.create_repo(repo_type=backend.alias)
316 313 repo_name = source.repo_name
317 314 repo = Repository.get_by_repo_name(repo_name)
318 315 repo.archived = True
319 316 Session().commit()
320 317
321 318 response = self.app.get(
322 319 route_path(url_type, repo_name=repo_name), status=302)
323 320
324 321 msg = 'Action not supported for archived repository.'
325 322 assert_session_flash(response, msg)
326 323
327 324
328 325 class TestSVNFork(TestController):
329 326 @pytest.mark.parametrize('route_name', [
330 327 'repo_fork_create', 'repo_fork_new'
331 328 ])
332 329 def test_fork_redirects(self, autologin_user, backend_svn, route_name):
333 330
334 331 self.app.get(route_path(
335 332 route_name, repo_name=backend_svn.repo_name),
336 333 status=404)
@@ -1,265 +1,266 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import formencode
24 24 import formencode.htmlfill
25 25
26 26 from pyramid.httpexceptions import HTTPFound
27 27 from pyramid.view import view_config
28 28 from pyramid.renderers import render
29 29 from pyramid.response import Response
30 30
31 31 from rhodecode import events
32 32 from rhodecode.apps._base import RepoAppView, DataGridAppView
33 33 from rhodecode.lib.auth import (
34 34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
35 35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
36 36 import rhodecode.lib.helpers as h
37 37 from rhodecode.lib.celerylib.utils import get_task_id
38 38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
39 39 from rhodecode.model.permission import PermissionModel
40 40 from rhodecode.model.repo import RepoModel
41 41 from rhodecode.model.forms import RepoForkForm
42 42 from rhodecode.model.scm import ScmModel, RepoGroupList
43 43 from rhodecode.lib.utils2 import safe_int, safe_unicode
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class RepoForksView(RepoAppView, DataGridAppView):
49 49
50 50 def load_default_context(self):
51 51 c = self._get_local_tmpl_context(include_app_defaults=True)
52 52 c.rhodecode_repo = self.rhodecode_vcs_repo
53 53
54 54 acl_groups = RepoGroupList(
55 55 RepoGroup.query().all(),
56 56 perm_set=['group.write', 'group.admin'])
57 57 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
58 58 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
59 59
60 60 c.personal_repo_group = c.rhodecode_user.personal_repo_group
61 61
62 62 return c
63 63
64 64 @LoginRequired()
65 65 @HasRepoPermissionAnyDecorator(
66 66 'repository.read', 'repository.write', 'repository.admin')
67 67 @view_config(
68 68 route_name='repo_forks_show_all', request_method='GET',
69 69 renderer='rhodecode:templates/forks/forks.mako')
70 70 def repo_forks_show_all(self):
71 71 c = self.load_default_context()
72 72 return self._get_template_context(c)
73 73
74 74 @LoginRequired()
75 75 @HasRepoPermissionAnyDecorator(
76 76 'repository.read', 'repository.write', 'repository.admin')
77 77 @view_config(
78 78 route_name='repo_forks_data', request_method='GET',
79 79 renderer='json_ext', xhr=True)
80 80 def repo_forks_data(self):
81 81 _ = self.request.translate
82 82 self.load_default_context()
83 83 column_map = {
84 84 'fork_name': 'repo_name',
85 85 'fork_date': 'created_on',
86 86 'last_activity': 'updated_on'
87 87 }
88 88 draw, start, limit = self._extract_chunk(self.request)
89 89 search_q, order_by, order_dir = self._extract_ordering(
90 90 self.request, column_map=column_map)
91 91
92 92 acl_check = HasRepoPermissionAny(
93 93 'repository.read', 'repository.write', 'repository.admin')
94 94 repo_id = self.db_repo.repo_id
95 95 allowed_ids = [-1]
96 96 for f in Repository.query().filter(Repository.fork_id == repo_id):
97 97 if acl_check(f.repo_name, 'get forks check'):
98 98 allowed_ids.append(f.repo_id)
99 99
100 100 forks_data_total_count = Repository.query()\
101 101 .filter(Repository.fork_id == repo_id)\
102 102 .filter(Repository.repo_id.in_(allowed_ids))\
103 103 .count()
104 104
105 105 # json generate
106 106 base_q = Repository.query()\
107 107 .filter(Repository.fork_id == repo_id)\
108 108 .filter(Repository.repo_id.in_(allowed_ids))\
109 109
110 110 if search_q:
111 111 like_expression = u'%{}%'.format(safe_unicode(search_q))
112 112 base_q = base_q.filter(or_(
113 113 Repository.repo_name.ilike(like_expression),
114 114 Repository.description.ilike(like_expression),
115 115 ))
116 116
117 117 forks_data_total_filtered_count = base_q.count()
118 118
119 119 sort_col = getattr(Repository, order_by, None)
120 120 if sort_col:
121 121 if order_dir == 'asc':
122 122 # handle null values properly to order by NULL last
123 123 if order_by in ['last_activity']:
124 124 sort_col = coalesce(sort_col, datetime.date.max)
125 125 sort_col = sort_col.asc()
126 126 else:
127 127 # handle null values properly to order by NULL last
128 128 if order_by in ['last_activity']:
129 129 sort_col = coalesce(sort_col, datetime.date.min)
130 130 sort_col = sort_col.desc()
131 131
132 132 base_q = base_q.order_by(sort_col)
133 133 base_q = base_q.offset(start).limit(limit)
134 134
135 135 fork_list = base_q.all()
136 136
137 137 def fork_actions(fork):
138 138 url_link = h.route_path(
139 139 'repo_compare',
140 140 repo_name=fork.repo_name,
141 141 source_ref_type=self.db_repo.landing_rev[0],
142 142 source_ref=self.db_repo.landing_rev[1],
143 143 target_ref_type=self.db_repo.landing_rev[0],
144 144 target_ref=self.db_repo.landing_rev[1],
145 145 _query=dict(merge=1, target_repo=f.repo_name))
146 146 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
147 147
148 148 def fork_name(fork):
149 149 return h.link_to(fork.repo_name,
150 150 h.route_path('repo_summary', repo_name=fork.repo_name))
151 151
152 152 forks_data = []
153 153 for fork in fork_list:
154 154 forks_data.append({
155 155 "username": h.gravatar_with_user(self.request, fork.user.username),
156 156 "fork_name": fork_name(fork),
157 157 "description": fork.description_safe,
158 158 "fork_date": h.age_component(fork.created_on, time_is_local=True),
159 159 "last_activity": h.format_date(fork.updated_on),
160 160 "action": fork_actions(fork),
161 161 })
162 162
163 163 data = ({
164 164 'draw': draw,
165 165 'data': forks_data,
166 166 'recordsTotal': forks_data_total_count,
167 167 'recordsFiltered': forks_data_total_filtered_count,
168 168 })
169 169
170 170 return data
171 171
172 172 @LoginRequired()
173 173 @NotAnonymous()
174 174 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
175 175 @HasRepoPermissionAnyDecorator(
176 176 'repository.read', 'repository.write', 'repository.admin')
177 177 @view_config(
178 178 route_name='repo_fork_new', request_method='GET',
179 179 renderer='rhodecode:templates/forks/forks.mako')
180 180 def repo_fork_new(self):
181 181 c = self.load_default_context()
182 182
183 183 defaults = RepoModel()._get_defaults(self.db_repo_name)
184 184 # alter the description to indicate a fork
185 185 defaults['description'] = (
186 186 'fork of repository: %s \n%s' % (
187 187 defaults['repo_name'], defaults['description']))
188 188 # add suffix to fork
189 189 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
190 190
191 191 data = render('rhodecode:templates/forks/fork.mako',
192 192 self._get_template_context(c), self.request)
193 193 html = formencode.htmlfill.render(
194 194 data,
195 195 defaults=defaults,
196 196 encoding="UTF-8",
197 197 force_defaults=False
198 198 )
199 199 return Response(html)
200 200
201 201 @LoginRequired()
202 202 @NotAnonymous()
203 203 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
204 204 @HasRepoPermissionAnyDecorator(
205 205 'repository.read', 'repository.write', 'repository.admin')
206 206 @CSRFRequired()
207 207 @view_config(
208 208 route_name='repo_fork_create', request_method='POST',
209 209 renderer='rhodecode:templates/forks/fork.mako')
210 210 def repo_fork_create(self):
211 211 _ = self.request.translate
212 212 c = self.load_default_context()
213 213
214 214 _form = RepoForkForm(self.request.translate,
215 215 old_data={'repo_type': self.db_repo.repo_type},
216 216 repo_groups=c.repo_groups_choices)()
217 217 post_data = dict(self.request.POST)
218 218
219 219 # forbid injecting other repo by forging a request
220 220 post_data['fork_parent_id'] = self.db_repo.repo_id
221 post_data['landing_rev'] = self.db_repo._landing_revision
221 222
222 223 form_result = {}
223 224 task_id = None
224 225 try:
225 226 form_result = _form.to_python(post_data)
226 227 copy_permissions = form_result.get('copy_permissions')
227 228 # create fork is done sometimes async on celery, db transaction
228 229 # management is handled there.
229 230 task = RepoModel().create_fork(
230 231 form_result, c.rhodecode_user.user_id)
231 232
232 233 task_id = get_task_id(task)
233 234 except formencode.Invalid as errors:
234 235 c.rhodecode_db_repo = self.db_repo
235 236
236 237 data = render('rhodecode:templates/forks/fork.mako',
237 238 self._get_template_context(c), self.request)
238 239 html = formencode.htmlfill.render(
239 240 data,
240 241 defaults=errors.value,
241 242 errors=errors.error_dict or {},
242 243 prefix_error=False,
243 244 encoding="UTF-8",
244 245 force_defaults=False
245 246 )
246 247 return Response(html)
247 248 except Exception:
248 249 log.exception(
249 250 u'Exception while trying to fork the repository %s', self.db_repo_name)
250 251 msg = _('An error occurred during repository forking %s') % (self.db_repo_name, )
251 252 h.flash(msg, category='error')
252 253 raise HTTPFound(h.route_path('home'))
253 254
254 255 repo_name = form_result.get('repo_name_full', self.db_repo_name)
255 256
256 257 affected_user_ids = [self._rhodecode_user.user_id]
257 258 if copy_permissions:
258 259 # permission flush is done in repo creating
259 260 pass
260 261
261 262 PermissionModel().trigger_permission_flush(affected_user_ids)
262 263
263 264 raise HTTPFound(
264 265 h.route_path('repo_creating', repo_name=repo_name,
265 266 _query=dict(task_id=task_id)))
@@ -1,343 +1,343 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 RhodeCode task modules, containing all task that suppose to be run
23 23 by celery daemon
24 24 """
25 25
26 26 import os
27 27 import time
28 28
29 29 from pyramid import compat
30 30 from pyramid_mailer.mailer import Mailer
31 31 from pyramid_mailer.message import Message
32 32
33 33 import rhodecode
34 34 from rhodecode.lib import audit_logger
35 35 from rhodecode.lib.celerylib import get_logger, async_task, RequestContextTask
36 36 from rhodecode.lib.hooks_base import log_create_repository
37 37 from rhodecode.lib.utils2 import safe_int, str2bool
38 38 from rhodecode.model.db import Session, IntegrityError, Repository, User, true
39 39
40 40
41 41 @async_task(ignore_result=True, base=RequestContextTask)
42 42 def send_email(recipients, subject, body='', html_body='', email_config=None):
43 43 """
44 44 Sends an email with defined parameters from the .ini files.
45 45
46 46 :param recipients: list of recipients, it this is empty the defined email
47 47 address from field 'email_to' is used instead
48 48 :param subject: subject of the mail
49 49 :param body: body of the mail
50 50 :param html_body: html version of body
51 51 """
52 52 log = get_logger(send_email)
53 53
54 54 email_config = email_config or rhodecode.CONFIG
55 55
56 56 mail_server = email_config.get('smtp_server') or None
57 57 if mail_server is None:
58 58 log.error("SMTP server information missing. Sending email failed. "
59 59 "Make sure that `smtp_server` variable is configured "
60 60 "inside the .ini file")
61 61 return False
62 62
63 63 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
64 64
65 65 if recipients:
66 66 if isinstance(recipients, compat.string_types):
67 67 recipients = recipients.split(',')
68 68 else:
69 69 # if recipients are not defined we send to email_config + all admins
70 70 admins = []
71 71 for u in User.query().filter(User.admin == true()).all():
72 72 if u.email:
73 73 admins.append(u.email)
74 74 recipients = []
75 75 config_email = email_config.get('email_to')
76 76 if config_email:
77 77 recipients += [config_email]
78 78 recipients += admins
79 79
80 80 # translate our LEGACY config into the one that pyramid_mailer supports
81 81 email_conf = dict(
82 82 host=mail_server,
83 83 port=email_config.get('smtp_port', 25),
84 84 username=email_config.get('smtp_username'),
85 85 password=email_config.get('smtp_password'),
86 86
87 87 tls=str2bool(email_config.get('smtp_use_tls')),
88 88 ssl=str2bool(email_config.get('smtp_use_ssl')),
89 89
90 90 # SSL key file
91 91 # keyfile='',
92 92
93 93 # SSL certificate file
94 94 # certfile='',
95 95
96 96 # Location of maildir
97 97 # queue_path='',
98 98
99 99 default_sender=email_config.get('app_email_from', 'RhodeCode'),
100 100
101 101 debug=str2bool(email_config.get('smtp_debug')),
102 102 # /usr/sbin/sendmail Sendmail executable
103 103 # sendmail_app='',
104 104
105 105 # {sendmail_app} -t -i -f {sender} Template for sendmail execution
106 106 # sendmail_template='',
107 107 )
108 108
109 109 try:
110 110 mailer = Mailer(**email_conf)
111 111
112 112 message = Message(subject=subject,
113 113 sender=email_conf['default_sender'],
114 114 recipients=recipients,
115 115 body=body, html=html_body)
116 116 mailer.send_immediately(message)
117 117
118 118 except Exception:
119 119 log.exception('Mail sending failed')
120 120 return False
121 121 return True
122 122
123 123
124 124 @async_task(ignore_result=True, base=RequestContextTask)
125 125 def create_repo(form_data, cur_user):
126 126 from rhodecode.model.repo import RepoModel
127 127 from rhodecode.model.user import UserModel
128 128 from rhodecode.model.scm import ScmModel
129 129 from rhodecode.model.settings import SettingsModel
130 130
131 131 log = get_logger(create_repo)
132 132
133 133 cur_user = UserModel()._get_user(cur_user)
134 134 owner = cur_user
135 135
136 136 repo_name = form_data['repo_name']
137 137 repo_name_full = form_data['repo_name_full']
138 138 repo_type = form_data['repo_type']
139 139 description = form_data['repo_description']
140 140 private = form_data['repo_private']
141 141 clone_uri = form_data.get('clone_uri')
142 142 repo_group = safe_int(form_data['repo_group'])
143 143 copy_fork_permissions = form_data.get('copy_permissions')
144 144 copy_group_permissions = form_data.get('repo_copy_permissions')
145 145 fork_of = form_data.get('fork_parent_id')
146 146 state = form_data.get('repo_state', Repository.STATE_PENDING)
147 147
148 148 # repo creation defaults, private and repo_type are filled in form
149 149 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
150 150 enable_statistics = form_data.get(
151 151 'enable_statistics', defs.get('repo_enable_statistics'))
152 152 enable_locking = form_data.get(
153 153 'enable_locking', defs.get('repo_enable_locking'))
154 154 enable_downloads = form_data.get(
155 155 'enable_downloads', defs.get('repo_enable_downloads'))
156 156
157 157 # set landing rev based on default branches for SCM
158 158 landing_ref, _label = ScmModel.backend_landing_ref(repo_type)
159 159
160 160 try:
161 161 RepoModel()._create_repo(
162 162 repo_name=repo_name_full,
163 163 repo_type=repo_type,
164 164 description=description,
165 165 owner=owner,
166 166 private=private,
167 167 clone_uri=clone_uri,
168 168 repo_group=repo_group,
169 169 landing_rev=landing_ref,
170 170 fork_of=fork_of,
171 171 copy_fork_permissions=copy_fork_permissions,
172 172 copy_group_permissions=copy_group_permissions,
173 173 enable_statistics=enable_statistics,
174 174 enable_locking=enable_locking,
175 175 enable_downloads=enable_downloads,
176 176 state=state
177 177 )
178 178 Session().commit()
179 179
180 180 # now create this repo on Filesystem
181 181 RepoModel()._create_filesystem_repo(
182 182 repo_name=repo_name,
183 183 repo_type=repo_type,
184 184 repo_group=RepoModel()._get_repo_group(repo_group),
185 185 clone_uri=clone_uri,
186 186 )
187 187 repo = Repository.get_by_repo_name(repo_name_full)
188 188 log_create_repository(created_by=owner.username, **repo.get_dict())
189 189
190 190 # update repo commit caches initially
191 191 repo.update_commit_cache()
192 192
193 193 # set new created state
194 194 repo.set_state(Repository.STATE_CREATED)
195 195 repo_id = repo.repo_id
196 196 repo_data = repo.get_api_data()
197 197
198 198 audit_logger.store(
199 199 'repo.create', action_data={'data': repo_data},
200 200 user=cur_user,
201 201 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
202 202
203 203 Session().commit()
204 204 except Exception as e:
205 205 log.warning('Exception occurred when creating repository, '
206 206 'doing cleanup...', exc_info=True)
207 207 if isinstance(e, IntegrityError):
208 208 Session().rollback()
209 209
210 210 # rollback things manually !
211 211 repo = Repository.get_by_repo_name(repo_name_full)
212 212 if repo:
213 213 Repository.delete(repo.repo_id)
214 214 Session().commit()
215 215 RepoModel()._delete_filesystem_repo(repo)
216 216 log.info('Cleanup of repo %s finished', repo_name_full)
217 217 raise
218 218
219 219 return True
220 220
221 221
222 222 @async_task(ignore_result=True, base=RequestContextTask)
223 223 def create_repo_fork(form_data, cur_user):
224 224 """
225 225 Creates a fork of repository using internal VCS methods
226 226 """
227 227 from rhodecode.model.repo import RepoModel
228 228 from rhodecode.model.user import UserModel
229 229
230 230 log = get_logger(create_repo_fork)
231 231
232 232 cur_user = UserModel()._get_user(cur_user)
233 233 owner = cur_user
234 234
235 235 repo_name = form_data['repo_name'] # fork in this case
236 236 repo_name_full = form_data['repo_name_full']
237 237 repo_type = form_data['repo_type']
238 238 description = form_data['description']
239 239 private = form_data['private']
240 240 clone_uri = form_data.get('clone_uri')
241 241 repo_group = safe_int(form_data['repo_group'])
242 landing_rev = form_data['landing_rev']
242 landing_ref = form_data['landing_rev']
243 243 copy_fork_permissions = form_data.get('copy_permissions')
244 244 fork_id = safe_int(form_data.get('fork_parent_id'))
245 245
246 246 try:
247 247 fork_of = RepoModel()._get_repo(fork_id)
248 248 RepoModel()._create_repo(
249 249 repo_name=repo_name_full,
250 250 repo_type=repo_type,
251 251 description=description,
252 252 owner=owner,
253 253 private=private,
254 254 clone_uri=clone_uri,
255 255 repo_group=repo_group,
256 landing_rev=landing_rev,
256 landing_rev=landing_ref,
257 257 fork_of=fork_of,
258 258 copy_fork_permissions=copy_fork_permissions
259 259 )
260 260
261 261 Session().commit()
262 262
263 263 base_path = Repository.base_path()
264 264 source_repo_path = os.path.join(base_path, fork_of.repo_name)
265 265
266 266 # now create this repo on Filesystem
267 267 RepoModel()._create_filesystem_repo(
268 268 repo_name=repo_name,
269 269 repo_type=repo_type,
270 270 repo_group=RepoModel()._get_repo_group(repo_group),
271 271 clone_uri=source_repo_path,
272 272 )
273 273 repo = Repository.get_by_repo_name(repo_name_full)
274 274 log_create_repository(created_by=owner.username, **repo.get_dict())
275 275
276 276 # update repo commit caches initially
277 277 config = repo._config
278 278 config.set('extensions', 'largefiles', '')
279 279 repo.update_commit_cache(config=config)
280 280
281 281 # set new created state
282 282 repo.set_state(Repository.STATE_CREATED)
283 283
284 284 repo_id = repo.repo_id
285 285 repo_data = repo.get_api_data()
286 286 audit_logger.store(
287 287 'repo.fork', action_data={'data': repo_data},
288 288 user=cur_user,
289 289 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
290 290
291 291 Session().commit()
292 292 except Exception as e:
293 293 log.warning('Exception occurred when forking repository, '
294 294 'doing cleanup...', exc_info=True)
295 295 if isinstance(e, IntegrityError):
296 296 Session().rollback()
297 297
298 298 # rollback things manually !
299 299 repo = Repository.get_by_repo_name(repo_name_full)
300 300 if repo:
301 301 Repository.delete(repo.repo_id)
302 302 Session().commit()
303 303 RepoModel()._delete_filesystem_repo(repo)
304 304 log.info('Cleanup of repo %s finished', repo_name_full)
305 305 raise
306 306
307 307 return True
308 308
309 309
310 310 @async_task(ignore_result=True)
311 311 def repo_maintenance(repoid):
312 312 from rhodecode.lib import repo_maintenance as repo_maintenance_lib
313 313 log = get_logger(repo_maintenance)
314 314 repo = Repository.get_by_id_or_repo_name(repoid)
315 315 if repo:
316 316 maintenance = repo_maintenance_lib.RepoMaintenance()
317 317 tasks = maintenance.get_tasks_for_repo(repo)
318 318 log.debug('Executing %s tasks on repo `%s`', tasks, repoid)
319 319 executed_types = maintenance.execute(repo)
320 320 log.debug('Got execution results %s', executed_types)
321 321 else:
322 322 log.debug('Repo `%s` not found or without a clone_url', repoid)
323 323
324 324
325 325 @async_task(ignore_result=True)
326 326 def check_for_update():
327 327 from rhodecode.model.update import UpdateModel
328 328 update_url = UpdateModel().get_update_url()
329 329 cur_ver = rhodecode.__version__
330 330
331 331 try:
332 332 data = UpdateModel().get_update_data(update_url)
333 333 latest = data['versions'][0]
334 334 UpdateModel().store_version(latest['version'])
335 335 except Exception:
336 336 pass
337 337
338 338
339 339 @async_task(ignore_result=False)
340 340 def beat_check(*args, **kwargs):
341 341 log = get_logger(beat_check)
342 342 log.info('Got args: %r and kwargs %r', args, kwargs)
343 343 return time.time()
General Comments 0
You need to be logged in to leave comments. Login now