##// END OF EJS Templates
update: add new async task to check for updates via scheduler....
marcink -
r2431:61a0f874 default
parent child Browse files
Show More
@@ -0,0 +1,71 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2013-2017 RhodeCode GmbH
4 #
5 # 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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22 import urllib2
23 from packaging import version
24
25 import rhodecode
26 from rhodecode.lib.ext_json import json
27 from rhodecode.model import BaseModel
28 from rhodecode.model.meta import Session
29 from rhodecode.model.settings import SettingsModel
30
31
32 log = logging.getLogger(__name__)
33
34
35 class UpdateModel(BaseModel):
36 UPDATE_SETTINGS_KEY = 'update_version'
37 UPDATE_URL_SETTINGS_KEY = 'rhodecode_update_url'
38
39 @staticmethod
40 def get_update_data(update_url):
41 """Return the JSON update data."""
42 ver = rhodecode.__version__
43 log.debug('Checking for upgrade on `%s` server', update_url)
44 opener = urllib2.build_opener()
45 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
46 response = opener.open(update_url)
47 response_data = response.read()
48 data = json.loads(response_data)
49 log.debug('update server returned data')
50 return data
51
52 def get_update_url(self):
53 settings = SettingsModel().get_all_settings()
54 return settings.get(self.UPDATE_URL_SETTINGS_KEY)
55
56 def store_version(self, version):
57 log.debug('Storing version %s into settings', version)
58 setting = SettingsModel().create_or_update_setting(
59 self.UPDATE_SETTINGS_KEY, version)
60 Session().add(setting)
61 Session().commit()
62
63 def get_stored_version(self):
64 obj = SettingsModel().get_setting_by_name(self.UPDATE_SETTINGS_KEY)
65 if obj:
66 return obj.app_settings_value
67 return '0.0.0'
68
69 def is_outdated(self, cur_version, latest_version=None):
70 latest_version = latest_version or self.get_stored_version()
71 return version.Version(latest_version) > version.Version(cur_version)
@@ -1,730 +1,729 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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 mock
22 22 import pytest
23 23
24 24 import rhodecode
25 25 from rhodecode.apps._base import ADMIN_PREFIX
26 26 from rhodecode.lib.utils2 import md5
27 27 from rhodecode.model.db import RhodeCodeUi
28 28 from rhodecode.model.meta import Session
29 29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
30 30 from rhodecode.tests import assert_session_flash
31 31 from rhodecode.tests.utils import AssertResponse
32 32
33 33
34 UPDATE_DATA_QUALNAME = (
35 'rhodecode.apps.admin.views.system_info.AdminSystemInfoSettingsView.get_update_data')
34 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
36 35
37 36
38 37 def route_path(name, params=None, **kwargs):
39 38 import urllib
40 39 from rhodecode.apps._base import ADMIN_PREFIX
41 40
42 41 base_url = {
43 42
44 43 'admin_settings':
45 44 ADMIN_PREFIX +'/settings',
46 45 'admin_settings_update':
47 46 ADMIN_PREFIX + '/settings/update',
48 47 'admin_settings_global':
49 48 ADMIN_PREFIX + '/settings/global',
50 49 'admin_settings_global_update':
51 50 ADMIN_PREFIX + '/settings/global/update',
52 51 'admin_settings_vcs':
53 52 ADMIN_PREFIX + '/settings/vcs',
54 53 'admin_settings_vcs_update':
55 54 ADMIN_PREFIX + '/settings/vcs/update',
56 55 'admin_settings_vcs_svn_pattern_delete':
57 56 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
58 57 'admin_settings_mapping':
59 58 ADMIN_PREFIX + '/settings/mapping',
60 59 'admin_settings_mapping_update':
61 60 ADMIN_PREFIX + '/settings/mapping/update',
62 61 'admin_settings_visual':
63 62 ADMIN_PREFIX + '/settings/visual',
64 63 'admin_settings_visual_update':
65 64 ADMIN_PREFIX + '/settings/visual/update',
66 65 'admin_settings_issuetracker':
67 66 ADMIN_PREFIX + '/settings/issue-tracker',
68 67 'admin_settings_issuetracker_update':
69 68 ADMIN_PREFIX + '/settings/issue-tracker/update',
70 69 'admin_settings_issuetracker_test':
71 70 ADMIN_PREFIX + '/settings/issue-tracker/test',
72 71 'admin_settings_issuetracker_delete':
73 72 ADMIN_PREFIX + '/settings/issue-tracker/delete',
74 73 'admin_settings_email':
75 74 ADMIN_PREFIX + '/settings/email',
76 75 'admin_settings_email_update':
77 76 ADMIN_PREFIX + '/settings/email/update',
78 77 'admin_settings_hooks':
79 78 ADMIN_PREFIX + '/settings/hooks',
80 79 'admin_settings_hooks_update':
81 80 ADMIN_PREFIX + '/settings/hooks/update',
82 81 'admin_settings_hooks_delete':
83 82 ADMIN_PREFIX + '/settings/hooks/delete',
84 83 'admin_settings_search':
85 84 ADMIN_PREFIX + '/settings/search',
86 85 'admin_settings_labs':
87 86 ADMIN_PREFIX + '/settings/labs',
88 87 'admin_settings_labs_update':
89 88 ADMIN_PREFIX + '/settings/labs/update',
90 89
91 90 'admin_settings_sessions':
92 91 ADMIN_PREFIX + '/settings/sessions',
93 92 'admin_settings_sessions_cleanup':
94 93 ADMIN_PREFIX + '/settings/sessions/cleanup',
95 94 'admin_settings_system':
96 95 ADMIN_PREFIX + '/settings/system',
97 96 'admin_settings_system_update':
98 97 ADMIN_PREFIX + '/settings/system/updates',
99 98 'admin_settings_open_source':
100 99 ADMIN_PREFIX + '/settings/open_source',
101 100
102 101
103 102 }[name].format(**kwargs)
104 103
105 104 if params:
106 105 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
107 106 return base_url
108 107
109 108
110 109 @pytest.mark.usefixtures('autologin_user', 'app')
111 110 class TestAdminSettingsController(object):
112 111
113 112 @pytest.mark.parametrize('urlname', [
114 113 'admin_settings_vcs',
115 114 'admin_settings_mapping',
116 115 'admin_settings_global',
117 116 'admin_settings_visual',
118 117 'admin_settings_email',
119 118 'admin_settings_hooks',
120 119 'admin_settings_search',
121 120 ])
122 121 def test_simple_get(self, urlname):
123 122 self.app.get(route_path(urlname))
124 123
125 124 def test_create_custom_hook(self, csrf_token):
126 125 response = self.app.post(
127 126 route_path('admin_settings_hooks_update'),
128 127 params={
129 128 'new_hook_ui_key': 'test_hooks_1',
130 129 'new_hook_ui_value': 'cd /tmp',
131 130 'csrf_token': csrf_token})
132 131
133 132 response = response.follow()
134 133 response.mustcontain('test_hooks_1')
135 134 response.mustcontain('cd /tmp')
136 135
137 136 def test_create_custom_hook_delete(self, csrf_token):
138 137 response = self.app.post(
139 138 route_path('admin_settings_hooks_update'),
140 139 params={
141 140 'new_hook_ui_key': 'test_hooks_2',
142 141 'new_hook_ui_value': 'cd /tmp2',
143 142 'csrf_token': csrf_token})
144 143
145 144 response = response.follow()
146 145 response.mustcontain('test_hooks_2')
147 146 response.mustcontain('cd /tmp2')
148 147
149 148 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
150 149
151 150 # delete
152 151 self.app.post(
153 152 route_path('admin_settings_hooks_delete'),
154 153 params={'hook_id': hook_id, 'csrf_token': csrf_token})
155 154 response = self.app.get(route_path('admin_settings_hooks'))
156 155 response.mustcontain(no=['test_hooks_2'])
157 156 response.mustcontain(no=['cd /tmp2'])
158 157
159 158
160 159 @pytest.mark.usefixtures('autologin_user', 'app')
161 160 class TestAdminSettingsGlobal(object):
162 161
163 162 def test_pre_post_code_code_active(self, csrf_token):
164 163 pre_code = 'rc-pre-code-187652122'
165 164 post_code = 'rc-postcode-98165231'
166 165
167 166 response = self.post_and_verify_settings({
168 167 'rhodecode_pre_code': pre_code,
169 168 'rhodecode_post_code': post_code,
170 169 'csrf_token': csrf_token,
171 170 })
172 171
173 172 response = response.follow()
174 173 response.mustcontain(pre_code, post_code)
175 174
176 175 def test_pre_post_code_code_inactive(self, csrf_token):
177 176 pre_code = 'rc-pre-code-187652122'
178 177 post_code = 'rc-postcode-98165231'
179 178 response = self.post_and_verify_settings({
180 179 'rhodecode_pre_code': '',
181 180 'rhodecode_post_code': '',
182 181 'csrf_token': csrf_token,
183 182 })
184 183
185 184 response = response.follow()
186 185 response.mustcontain(no=[pre_code, post_code])
187 186
188 187 def test_captcha_activate(self, csrf_token):
189 188 self.post_and_verify_settings({
190 189 'rhodecode_captcha_private_key': '1234567890',
191 190 'rhodecode_captcha_public_key': '1234567890',
192 191 'csrf_token': csrf_token,
193 192 })
194 193
195 194 response = self.app.get(ADMIN_PREFIX + '/register')
196 195 response.mustcontain('captcha')
197 196
198 197 def test_captcha_deactivate(self, csrf_token):
199 198 self.post_and_verify_settings({
200 199 'rhodecode_captcha_private_key': '',
201 200 'rhodecode_captcha_public_key': '1234567890',
202 201 'csrf_token': csrf_token,
203 202 })
204 203
205 204 response = self.app.get(ADMIN_PREFIX + '/register')
206 205 response.mustcontain(no=['captcha'])
207 206
208 207 def test_title_change(self, csrf_token):
209 208 old_title = 'RhodeCode'
210 209
211 210 for new_title in ['Changed', 'Żółwik', old_title]:
212 211 response = self.post_and_verify_settings({
213 212 'rhodecode_title': new_title,
214 213 'csrf_token': csrf_token,
215 214 })
216 215
217 216 response = response.follow()
218 217 response.mustcontain(
219 218 """<div class="branding">- %s</div>""" % new_title)
220 219
221 220 def post_and_verify_settings(self, settings):
222 221 old_title = 'RhodeCode'
223 222 old_realm = 'RhodeCode authentication'
224 223 params = {
225 224 'rhodecode_title': old_title,
226 225 'rhodecode_realm': old_realm,
227 226 'rhodecode_pre_code': '',
228 227 'rhodecode_post_code': '',
229 228 'rhodecode_captcha_private_key': '',
230 229 'rhodecode_captcha_public_key': '',
231 230 'rhodecode_create_personal_repo_group': False,
232 231 'rhodecode_personal_repo_group_pattern': '${username}',
233 232 }
234 233 params.update(settings)
235 234 response = self.app.post(
236 235 route_path('admin_settings_global_update'), params=params)
237 236
238 237 assert_session_flash(response, 'Updated application settings')
239 238 app_settings = SettingsModel().get_all_settings()
240 239 del settings['csrf_token']
241 240 for key, value in settings.iteritems():
242 241 assert app_settings[key] == value.decode('utf-8')
243 242
244 243 return response
245 244
246 245
247 246 @pytest.mark.usefixtures('autologin_user', 'app')
248 247 class TestAdminSettingsVcs(object):
249 248
250 249 def test_contains_svn_default_patterns(self):
251 250 response = self.app.get(route_path('admin_settings_vcs'))
252 251 expected_patterns = [
253 252 '/trunk',
254 253 '/branches/*',
255 254 '/tags/*',
256 255 ]
257 256 for pattern in expected_patterns:
258 257 response.mustcontain(pattern)
259 258
260 259 def test_add_new_svn_branch_and_tag_pattern(
261 260 self, backend_svn, form_defaults, disable_sql_cache,
262 261 csrf_token):
263 262 form_defaults.update({
264 263 'new_svn_branch': '/exp/branches/*',
265 264 'new_svn_tag': '/important_tags/*',
266 265 'csrf_token': csrf_token,
267 266 })
268 267
269 268 response = self.app.post(
270 269 route_path('admin_settings_vcs_update'),
271 270 params=form_defaults, status=302)
272 271 response = response.follow()
273 272
274 273 # Expect to find the new values on the page
275 274 response.mustcontain('/exp/branches/*')
276 275 response.mustcontain('/important_tags/*')
277 276
278 277 # Expect that those patterns are used to match branches and tags now
279 278 repo = backend_svn['svn-simple-layout'].scm_instance()
280 279 assert 'exp/branches/exp-sphinx-docs' in repo.branches
281 280 assert 'important_tags/v0.5' in repo.tags
282 281
283 282 def test_add_same_svn_value_twice_shows_an_error_message(
284 283 self, form_defaults, csrf_token, settings_util):
285 284 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
286 285 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
287 286
288 287 response = self.app.post(
289 288 route_path('admin_settings_vcs_update'),
290 289 params={
291 290 'paths_root_path': form_defaults['paths_root_path'],
292 291 'new_svn_branch': '/test',
293 292 'new_svn_tag': '/test',
294 293 'csrf_token': csrf_token,
295 294 },
296 295 status=200)
297 296
298 297 response.mustcontain("Pattern already exists")
299 298 response.mustcontain("Some form inputs contain invalid data.")
300 299
301 300 @pytest.mark.parametrize('section', [
302 301 'vcs_svn_branch',
303 302 'vcs_svn_tag',
304 303 ])
305 304 def test_delete_svn_patterns(
306 305 self, section, csrf_token, settings_util):
307 306 setting = settings_util.create_rhodecode_ui(
308 307 section, '/test_delete', cleanup=False)
309 308
310 309 self.app.post(
311 310 route_path('admin_settings_vcs_svn_pattern_delete'),
312 311 params={
313 312 'delete_svn_pattern': setting.ui_id,
314 313 'csrf_token': csrf_token},
315 314 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
316 315
317 316 @pytest.mark.parametrize('section', [
318 317 'vcs_svn_branch',
319 318 'vcs_svn_tag',
320 319 ])
321 320 def test_delete_svn_patterns_raises_404_when_no_xhr(
322 321 self, section, csrf_token, settings_util):
323 322 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
324 323
325 324 self.app.post(
326 325 route_path('admin_settings_vcs_svn_pattern_delete'),
327 326 params={
328 327 'delete_svn_pattern': setting.ui_id,
329 328 'csrf_token': csrf_token},
330 329 status=404)
331 330
332 331 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
333 332 form_defaults.update({
334 333 'csrf_token': csrf_token,
335 334 'extensions_hgsubversion': 'True',
336 335 })
337 336 response = self.app.post(
338 337 route_path('admin_settings_vcs_update'),
339 338 params=form_defaults,
340 339 status=302)
341 340
342 341 response = response.follow()
343 342 extensions_input = (
344 343 '<input id="extensions_hgsubversion" '
345 344 'name="extensions_hgsubversion" type="checkbox" '
346 345 'value="True" checked="checked" />')
347 346 response.mustcontain(extensions_input)
348 347
349 348 def test_extensions_hgevolve(self, form_defaults, csrf_token):
350 349 form_defaults.update({
351 350 'csrf_token': csrf_token,
352 351 'extensions_evolve': 'True',
353 352 })
354 353 response = self.app.post(
355 354 route_path('admin_settings_vcs_update'),
356 355 params=form_defaults,
357 356 status=302)
358 357
359 358 response = response.follow()
360 359 extensions_input = (
361 360 '<input id="extensions_evolve" '
362 361 'name="extensions_evolve" type="checkbox" '
363 362 'value="True" checked="checked" />')
364 363 response.mustcontain(extensions_input)
365 364
366 365 def test_has_a_section_for_pull_request_settings(self):
367 366 response = self.app.get(route_path('admin_settings_vcs'))
368 367 response.mustcontain('Pull Request Settings')
369 368
370 369 def test_has_an_input_for_invalidation_of_inline_comments(self):
371 370 response = self.app.get(route_path('admin_settings_vcs'))
372 371 assert_response = AssertResponse(response)
373 372 assert_response.one_element_exists(
374 373 '[name=rhodecode_use_outdated_comments]')
375 374
376 375 @pytest.mark.parametrize('new_value', [True, False])
377 376 def test_allows_to_change_invalidation_of_inline_comments(
378 377 self, form_defaults, csrf_token, new_value):
379 378 setting_key = 'use_outdated_comments'
380 379 setting = SettingsModel().create_or_update_setting(
381 380 setting_key, not new_value, 'bool')
382 381 Session().add(setting)
383 382 Session().commit()
384 383
385 384 form_defaults.update({
386 385 'csrf_token': csrf_token,
387 386 'rhodecode_use_outdated_comments': str(new_value),
388 387 })
389 388 response = self.app.post(
390 389 route_path('admin_settings_vcs_update'),
391 390 params=form_defaults,
392 391 status=302)
393 392 response = response.follow()
394 393 setting = SettingsModel().get_setting_by_name(setting_key)
395 394 assert setting.app_settings_value is new_value
396 395
397 396 @pytest.mark.parametrize('new_value', [True, False])
398 397 def test_allows_to_change_hg_rebase_merge_strategy(
399 398 self, form_defaults, csrf_token, new_value):
400 399 setting_key = 'hg_use_rebase_for_merging'
401 400
402 401 form_defaults.update({
403 402 'csrf_token': csrf_token,
404 403 'rhodecode_' + setting_key: str(new_value),
405 404 })
406 405
407 406 with mock.patch.dict(
408 407 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
409 408 self.app.post(
410 409 route_path('admin_settings_vcs_update'),
411 410 params=form_defaults,
412 411 status=302)
413 412
414 413 setting = SettingsModel().get_setting_by_name(setting_key)
415 414 assert setting.app_settings_value is new_value
416 415
417 416 @pytest.fixture
418 417 def disable_sql_cache(self, request):
419 418 patcher = mock.patch(
420 419 'rhodecode.lib.caching_query.FromCache.process_query')
421 420 request.addfinalizer(patcher.stop)
422 421 patcher.start()
423 422
424 423 @pytest.fixture
425 424 def form_defaults(self):
426 425 from rhodecode.apps.admin.views.settings import AdminSettingsView
427 426 return AdminSettingsView._form_defaults()
428 427
429 428 # TODO: johbo: What we really want is to checkpoint before a test run and
430 429 # reset the session afterwards.
431 430 @pytest.fixture(scope='class', autouse=True)
432 431 def cleanup_settings(self, request, baseapp):
433 432 ui_id = RhodeCodeUi.ui_id
434 433 original_ids = list(
435 434 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
436 435
437 436 @request.addfinalizer
438 437 def cleanup():
439 438 RhodeCodeUi.query().filter(
440 439 ui_id.notin_(original_ids)).delete(False)
441 440
442 441
443 442 @pytest.mark.usefixtures('autologin_user', 'app')
444 443 class TestLabsSettings(object):
445 444 def test_get_settings_page_disabled(self):
446 445 with mock.patch.dict(
447 446 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
448 447
449 448 response = self.app.get(
450 449 route_path('admin_settings_labs'), status=302)
451 450
452 451 assert response.location.endswith(route_path('admin_settings'))
453 452
454 453 def test_get_settings_page_enabled(self):
455 454 from rhodecode.apps.admin.views import settings
456 455 lab_settings = [
457 456 settings.LabSetting(
458 457 key='rhodecode_bool',
459 458 type='bool',
460 459 group='bool group',
461 460 label='bool label',
462 461 help='bool help'
463 462 ),
464 463 settings.LabSetting(
465 464 key='rhodecode_text',
466 465 type='unicode',
467 466 group='text group',
468 467 label='text label',
469 468 help='text help'
470 469 ),
471 470 ]
472 471 with mock.patch.dict(rhodecode.CONFIG,
473 472 {'labs_settings_active': 'true'}):
474 473 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
475 474 response = self.app.get(route_path('admin_settings_labs'))
476 475
477 476 assert '<label>bool group:</label>' in response
478 477 assert '<label for="rhodecode_bool">bool label</label>' in response
479 478 assert '<p class="help-block">bool help</p>' in response
480 479 assert 'name="rhodecode_bool" type="checkbox"' in response
481 480
482 481 assert '<label>text group:</label>' in response
483 482 assert '<label for="rhodecode_text">text label</label>' in response
484 483 assert '<p class="help-block">text help</p>' in response
485 484 assert 'name="rhodecode_text" size="60" type="text"' in response
486 485
487 486
488 487 @pytest.mark.usefixtures('app')
489 488 class TestOpenSourceLicenses(object):
490 489
491 490 def test_records_are_displayed(self, autologin_user):
492 491 sample_licenses = {
493 492 "python2.7-pytest-2.7.1": {
494 493 "UNKNOWN": None
495 494 },
496 495 "python2.7-Markdown-2.6.2": {
497 496 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
498 497 }
499 498 }
500 499 read_licenses_patch = mock.patch(
501 500 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
502 501 return_value=sample_licenses)
503 502 with read_licenses_patch:
504 503 response = self.app.get(
505 504 route_path('admin_settings_open_source'), status=200)
506 505
507 506 assert_response = AssertResponse(response)
508 507 assert_response.element_contains(
509 508 '.panel-heading', 'Licenses of Third Party Packages')
510 509 for name in sample_licenses:
511 510 response.mustcontain(name)
512 511 for license in sample_licenses[name]:
513 512 assert_response.element_contains('.panel-body', license)
514 513
515 514 def test_records_can_be_read(self, autologin_user):
516 515 response = self.app.get(
517 516 route_path('admin_settings_open_source'), status=200)
518 517 assert_response = AssertResponse(response)
519 518 assert_response.element_contains(
520 519 '.panel-heading', 'Licenses of Third Party Packages')
521 520
522 521 def test_forbidden_when_normal_user(self, autologin_regular_user):
523 522 self.app.get(
524 523 route_path('admin_settings_open_source'), status=404)
525 524
526 525
527 526 @pytest.mark.usefixtures('app')
528 527 class TestUserSessions(object):
529 528
530 529 def test_forbidden_when_normal_user(self, autologin_regular_user):
531 530 self.app.get(route_path('admin_settings_sessions'), status=404)
532 531
533 532 def test_show_sessions_page(self, autologin_user):
534 533 response = self.app.get(route_path('admin_settings_sessions'), status=200)
535 534 response.mustcontain('file')
536 535
537 536 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
538 537
539 538 post_data = {
540 539 'csrf_token': csrf_token,
541 540 'expire_days': '60'
542 541 }
543 542 response = self.app.post(
544 543 route_path('admin_settings_sessions_cleanup'), params=post_data,
545 544 status=302)
546 545 assert_session_flash(response, 'Cleaned up old sessions')
547 546
548 547
549 548 @pytest.mark.usefixtures('app')
550 549 class TestAdminSystemInfo(object):
551 550
552 551 def test_forbidden_when_normal_user(self, autologin_regular_user):
553 552 self.app.get(route_path('admin_settings_system'), status=404)
554 553
555 554 def test_system_info_page(self, autologin_user):
556 555 response = self.app.get(route_path('admin_settings_system'))
557 556 response.mustcontain('RhodeCode Community Edition, version {}'.format(
558 557 rhodecode.__version__))
559 558
560 559 def test_system_update_new_version(self, autologin_user):
561 560 update_data = {
562 561 'versions': [
563 562 {
564 563 'version': '100.3.1415926535',
565 564 'general': 'The latest version we are ever going to ship'
566 565 },
567 566 {
568 567 'version': '0.0.0',
569 568 'general': 'The first version we ever shipped'
570 569 }
571 570 ]
572 571 }
573 572 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
574 573 response = self.app.get(route_path('admin_settings_system_update'))
575 574 response.mustcontain('A <b>new version</b> is available')
576 575
577 576 def test_system_update_nothing_new(self, autologin_user):
578 577 update_data = {
579 578 'versions': [
580 579 {
581 580 'version': '0.0.0',
582 581 'general': 'The first version we ever shipped'
583 582 }
584 583 ]
585 584 }
586 585 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
587 586 response = self.app.get(route_path('admin_settings_system_update'))
588 587 response.mustcontain(
589 'You already have the <b>latest</b> stable version.')
588 'This instance is already running the <b>latest</b> stable version')
590 589
591 590 def test_system_update_bad_response(self, autologin_user):
592 591 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
593 592 response = self.app.get(route_path('admin_settings_system_update'))
594 593 response.mustcontain(
595 594 'Bad data sent from update server')
596 595
597 596
598 597 @pytest.mark.usefixtures("app")
599 598 class TestAdminSettingsIssueTracker(object):
600 599 RC_PREFIX = 'rhodecode_'
601 600 SHORT_PATTERN_KEY = 'issuetracker_pat_'
602 601 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
603 602
604 603 def test_issuetracker_index(self, autologin_user):
605 604 response = self.app.get(route_path('admin_settings_issuetracker'))
606 605 assert response.status_code == 200
607 606
608 607 def test_add_empty_issuetracker_pattern(
609 608 self, request, autologin_user, csrf_token):
610 609 post_url = route_path('admin_settings_issuetracker_update')
611 610 post_data = {
612 611 'csrf_token': csrf_token
613 612 }
614 613 self.app.post(post_url, post_data, status=302)
615 614
616 615 def test_add_issuetracker_pattern(
617 616 self, request, autologin_user, csrf_token):
618 617 pattern = 'issuetracker_pat'
619 618 another_pattern = pattern+'1'
620 619 post_url = route_path('admin_settings_issuetracker_update')
621 620 post_data = {
622 621 'new_pattern_pattern_0': pattern,
623 622 'new_pattern_url_0': 'http://url',
624 623 'new_pattern_prefix_0': 'prefix',
625 624 'new_pattern_description_0': 'description',
626 625 'new_pattern_pattern_1': another_pattern,
627 626 'new_pattern_url_1': 'https://url1',
628 627 'new_pattern_prefix_1': 'prefix1',
629 628 'new_pattern_description_1': 'description1',
630 629 'csrf_token': csrf_token
631 630 }
632 631 self.app.post(post_url, post_data, status=302)
633 632 settings = SettingsModel().get_all_settings()
634 633 self.uid = md5(pattern)
635 634 assert settings[self.PATTERN_KEY+self.uid] == pattern
636 635 self.another_uid = md5(another_pattern)
637 636 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
638 637
639 638 @request.addfinalizer
640 639 def cleanup():
641 640 defaults = SettingsModel().get_all_settings()
642 641
643 642 entries = [name for name in defaults if (
644 643 (self.uid in name) or (self.another_uid) in name)]
645 644 start = len(self.RC_PREFIX)
646 645 for del_key in entries:
647 646 # TODO: anderson: get_by_name needs name without prefix
648 647 entry = SettingsModel().get_setting_by_name(del_key[start:])
649 648 Session().delete(entry)
650 649
651 650 Session().commit()
652 651
653 652 def test_edit_issuetracker_pattern(
654 653 self, autologin_user, backend, csrf_token, request):
655 654 old_pattern = 'issuetracker_pat'
656 655 old_uid = md5(old_pattern)
657 656 pattern = 'issuetracker_pat_new'
658 657 self.new_uid = md5(pattern)
659 658
660 659 SettingsModel().create_or_update_setting(
661 660 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
662 661
663 662 post_url = route_path('admin_settings_issuetracker_update')
664 663 post_data = {
665 664 'new_pattern_pattern_0': pattern,
666 665 'new_pattern_url_0': 'https://url',
667 666 'new_pattern_prefix_0': 'prefix',
668 667 'new_pattern_description_0': 'description',
669 668 'uid': old_uid,
670 669 'csrf_token': csrf_token
671 670 }
672 671 self.app.post(post_url, post_data, status=302)
673 672 settings = SettingsModel().get_all_settings()
674 673 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
675 674 assert self.PATTERN_KEY+old_uid not in settings
676 675
677 676 @request.addfinalizer
678 677 def cleanup():
679 678 IssueTrackerSettingsModel().delete_entries(self.new_uid)
680 679
681 680 def test_replace_issuetracker_pattern_description(
682 681 self, autologin_user, csrf_token, request, settings_util):
683 682 prefix = 'issuetracker'
684 683 pattern = 'issuetracker_pat'
685 684 self.uid = md5(pattern)
686 685 pattern_key = '_'.join([prefix, 'pat', self.uid])
687 686 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
688 687 desc_key = '_'.join([prefix, 'desc', self.uid])
689 688 rc_desc_key = '_'.join(['rhodecode', desc_key])
690 689 new_description = 'new_description'
691 690
692 691 settings_util.create_rhodecode_setting(
693 692 pattern_key, pattern, 'unicode', cleanup=False)
694 693 settings_util.create_rhodecode_setting(
695 694 desc_key, 'old description', 'unicode', cleanup=False)
696 695
697 696 post_url = route_path('admin_settings_issuetracker_update')
698 697 post_data = {
699 698 'new_pattern_pattern_0': pattern,
700 699 'new_pattern_url_0': 'https://url',
701 700 'new_pattern_prefix_0': 'prefix',
702 701 'new_pattern_description_0': new_description,
703 702 'uid': self.uid,
704 703 'csrf_token': csrf_token
705 704 }
706 705 self.app.post(post_url, post_data, status=302)
707 706 settings = SettingsModel().get_all_settings()
708 707 assert settings[rc_pattern_key] == pattern
709 708 assert settings[rc_desc_key] == new_description
710 709
711 710 @request.addfinalizer
712 711 def cleanup():
713 712 IssueTrackerSettingsModel().delete_entries(self.uid)
714 713
715 714 def test_delete_issuetracker_pattern(
716 715 self, autologin_user, backend, csrf_token, settings_util):
717 716 pattern = 'issuetracker_pat'
718 717 uid = md5(pattern)
719 718 settings_util.create_rhodecode_setting(
720 719 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
721 720
722 721 post_url = route_path('admin_settings_issuetracker_delete')
723 722 post_data = {
724 723 '_method': 'delete',
725 724 'uid': uid,
726 725 'csrf_token': csrf_token
727 726 }
728 727 self.app.post(post_url, post_data, status=302)
729 728 settings = SettingsModel().get_all_settings()
730 729 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
@@ -1,208 +1,197 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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 urllib2
23 import packaging.version
24 23
25 24 from pyramid.view import view_config
26 25
27 26 import rhodecode
28 27 from rhodecode.apps._base import BaseAppView
29 28 from rhodecode.apps.admin.navigation import navigation_list
30 29 from rhodecode.lib import helpers as h
31 30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
32 31 from rhodecode.lib.utils2 import str2bool
33 32 from rhodecode.lib import system_info
34 from rhodecode.lib.ext_json import json
35 from rhodecode.model.settings import SettingsModel
33 from rhodecode.model.update import UpdateModel
36 34
37 35 log = logging.getLogger(__name__)
38 36
39 37
40 38 class AdminSystemInfoSettingsView(BaseAppView):
41 39 def load_default_context(self):
42 40 c = self._get_local_tmpl_context()
43
44 41 return c
45 42
46 @staticmethod
47 def get_update_data(update_url):
48 """Return the JSON update data."""
49 ver = rhodecode.__version__
50 log.debug('Checking for upgrade on `%s` server', update_url)
51 opener = urllib2.build_opener()
52 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
53 response = opener.open(update_url)
54 response_data = response.read()
55 data = json.loads(response_data)
56
57 return data
58
59 def get_update_url(self):
60 settings = SettingsModel().get_all_settings()
61 return settings.get('rhodecode_update_url')
62
63 43 @LoginRequired()
64 44 @HasPermissionAllDecorator('hg.admin')
65 45 @view_config(
66 46 route_name='admin_settings_system', request_method='GET',
67 47 renderer='rhodecode:templates/admin/settings/settings.mako')
68 48 def settings_system_info(self):
69 49 _ = self.request.translate
70 50 c = self.load_default_context()
71 51
72 52 c.active = 'system'
73 53 c.navlist = navigation_list(self.request)
74 54
75 55 # TODO(marcink), figure out how to allow only selected users to do this
76 56 c.allowed_to_snapshot = self._rhodecode_user.admin
77 57
78 58 snapshot = str2bool(self.request.params.get('snapshot'))
79 59
80 c.rhodecode_update_url = self.get_update_url()
60 c.rhodecode_update_url = UpdateModel().get_update_url()
81 61 server_info = system_info.get_system_info(self.request.environ)
82 62
83 63 for key, val in server_info.items():
84 64 setattr(c, key, val)
85 65
86 66 def val(name, subkey='human_value'):
87 67 return server_info[name][subkey]
88 68
89 69 def state(name):
90 70 return server_info[name]['state']
91 71
92 72 def val2(name):
93 73 val = server_info[name]['human_value']
94 74 state = server_info[name]['state']
95 75 return val, state
96 76
97 77 update_info_msg = _('Note: please make sure this server can '
98 78 'access `${url}` for the update link to work',
99 79 mapping=dict(url=c.rhodecode_update_url))
80 version = UpdateModel().get_stored_version()
81 is_outdated = UpdateModel().is_outdated(
82 rhodecode.__version__, version)
83 update_state = {
84 'type': 'warning',
85 'message': 'New version available: {}'.format(version)
86 } \
87 if is_outdated else {}
100 88 c.data_items = [
101 89 # update info
102 90 (_('Update info'), h.literal(
103 91 '<span class="link" id="check_for_update" >%s.</span>' % (
104 92 _('Check for updates')) +
105 93 '<br/> <span >%s.</span>' % (update_info_msg)
106 94 ), ''),
107 95
108 96 # RhodeCode specific
109 97 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
98 (_('Latest version'), version, update_state),
110 99 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
111 100 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
112 101 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
113 102 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
114 103 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
115 104 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
116 105 ('', '', ''), # spacer
117 106
118 107 # Database
119 108 (_('Database'), val('database')['url'], state('database')),
120 109 (_('Database version'), val('database')['version'], state('database')),
121 110 ('', '', ''), # spacer
122 111
123 112 # Platform/Python
124 113 (_('Platform'), val('platform')['name'], state('platform')),
125 114 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
126 115 (_('Python version'), val('python')['version'], state('python')),
127 116 (_('Python path'), val('python')['executable'], state('python')),
128 117 ('', '', ''), # spacer
129 118
130 119 # Systems stats
131 120 (_('CPU'), val('cpu')['text'], state('cpu')),
132 121 (_('Load'), val('load')['text'], state('load')),
133 122 (_('Memory'), val('memory')['text'], state('memory')),
134 123 (_('Uptime'), val('uptime')['text'], state('uptime')),
135 124 ('', '', ''), # spacer
136 125
137 126 # Repo storage
138 127 (_('Storage location'), val('storage')['path'], state('storage')),
139 128 (_('Storage info'), val('storage')['text'], state('storage')),
140 129 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
141 130
142 131 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
143 132 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
144 133
145 134 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
146 135 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
147 136
148 137 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
149 138 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
150 139
151 140 (_('Search info'), val('search')['text'], state('search')),
152 141 (_('Search location'), val('search')['location'], state('search')),
153 142 ('', '', ''), # spacer
154 143
155 144 # VCS specific
156 145 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
157 146 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
158 147 (_('GIT'), val('git'), state('git')),
159 148 (_('HG'), val('hg'), state('hg')),
160 149 (_('SVN'), val('svn'), state('svn')),
161 150
162 151 ]
163 152
164 153 if snapshot:
165 154 if c.allowed_to_snapshot:
166 155 c.data_items.pop(0) # remove server info
167 156 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
168 157 else:
169 158 h.flash('You are not allowed to do this', category='warning')
170 159 return self._get_template_context(c)
171 160
172 161 @LoginRequired()
173 162 @HasPermissionAllDecorator('hg.admin')
174 163 @view_config(
175 164 route_name='admin_settings_system_update', request_method='GET',
176 165 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
177 166 def settings_system_info_check_update(self):
178 167 _ = self.request.translate
179 168 c = self.load_default_context()
180 169
181 update_url = self.get_update_url()
170 update_url = UpdateModel().get_update_url()
182 171
183 172 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
184 173 try:
185 data = self.get_update_data(update_url)
174 data = UpdateModel().get_update_data(update_url)
186 175 except urllib2.URLError as e:
187 176 log.exception("Exception contacting upgrade server")
188 177 self.request.override_renderer = 'string'
189 178 return _err('Failed to contact upgrade server: %r' % e)
190 179 except ValueError as e:
191 180 log.exception("Bad data sent from update server")
192 181 self.request.override_renderer = 'string'
193 182 return _err('Bad data sent from update server')
194 183
195 184 latest = data['versions'][0]
196 185
197 186 c.update_url = update_url
198 187 c.latest_data = latest
199 188 c.latest_ver = latest['version']
200 189 c.cur_ver = rhodecode.__version__
201 190 c.should_upgrade = False
202 191
203 if (packaging.version.Version(c.latest_ver) >
204 packaging.version.Version(c.cur_ver)):
192 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
193 if is_oudated:
205 194 c.should_upgrade = True
206 195 c.important_notices = latest['general']
207
196 UpdateModel().store_version(latest['version'])
208 197 return self._get_template_context(c)
@@ -1,285 +1,299 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2017 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 import rhodecode
30 30 from rhodecode.lib import audit_logger
31 31 from rhodecode.lib.celerylib import get_logger, async_task, RequestContextTask
32 32 from rhodecode.lib.hooks_base import log_create_repository
33 33 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
34 34 from rhodecode.lib.utils2 import safe_int, str2bool
35 35 from rhodecode.model.db import Session, IntegrityError, Repository, User
36 36
37 37
38 38 @async_task(ignore_result=True, base=RequestContextTask)
39 39 def send_email(recipients, subject, body='', html_body='', email_config=None):
40 40 """
41 41 Sends an email with defined parameters from the .ini files.
42 42
43 43 :param recipients: list of recipients, it this is empty the defined email
44 44 address from field 'email_to' is used instead
45 45 :param subject: subject of the mail
46 46 :param body: body of the mail
47 47 :param html_body: html version of body
48 48 """
49 49 log = get_logger(send_email)
50 50
51 51 email_config = email_config or rhodecode.CONFIG
52 52 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
53 53 if not recipients:
54 54 # if recipients are not defined we send to email_config + all admins
55 55 admins = [
56 56 u.email for u in User.query().filter(User.admin == True).all()]
57 57 recipients = [email_config.get('email_to')] + admins
58 58
59 59 mail_server = email_config.get('smtp_server') or None
60 60 if mail_server is None:
61 61 log.error("SMTP server information missing. Sending email failed. "
62 62 "Make sure that `smtp_server` variable is configured "
63 63 "inside the .ini file")
64 64 return False
65 65
66 66 mail_from = email_config.get('app_email_from', 'RhodeCode')
67 67 user = email_config.get('smtp_username')
68 68 passwd = email_config.get('smtp_password')
69 69 mail_port = email_config.get('smtp_port')
70 70 tls = str2bool(email_config.get('smtp_use_tls'))
71 71 ssl = str2bool(email_config.get('smtp_use_ssl'))
72 72 debug = str2bool(email_config.get('debug'))
73 73 smtp_auth = email_config.get('smtp_auth')
74 74
75 75 try:
76 76 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
77 77 mail_port, ssl, tls, debug=debug)
78 78 m.send(recipients, subject, body, html_body)
79 79 except Exception:
80 80 log.exception('Mail sending failed')
81 81 return False
82 82 return True
83 83
84 84
85 85 @async_task(ignore_result=True, base=RequestContextTask)
86 86 def create_repo(form_data, cur_user):
87 87 from rhodecode.model.repo import RepoModel
88 88 from rhodecode.model.user import UserModel
89 89 from rhodecode.model.settings import SettingsModel
90 90
91 91 log = get_logger(create_repo)
92 92
93 93 cur_user = UserModel()._get_user(cur_user)
94 94 owner = cur_user
95 95
96 96 repo_name = form_data['repo_name']
97 97 repo_name_full = form_data['repo_name_full']
98 98 repo_type = form_data['repo_type']
99 99 description = form_data['repo_description']
100 100 private = form_data['repo_private']
101 101 clone_uri = form_data.get('clone_uri')
102 102 repo_group = safe_int(form_data['repo_group'])
103 103 landing_rev = form_data['repo_landing_rev']
104 104 copy_fork_permissions = form_data.get('copy_permissions')
105 105 copy_group_permissions = form_data.get('repo_copy_permissions')
106 106 fork_of = form_data.get('fork_parent_id')
107 107 state = form_data.get('repo_state', Repository.STATE_PENDING)
108 108
109 109 # repo creation defaults, private and repo_type are filled in form
110 110 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
111 111 enable_statistics = form_data.get(
112 112 'enable_statistics', defs.get('repo_enable_statistics'))
113 113 enable_locking = form_data.get(
114 114 'enable_locking', defs.get('repo_enable_locking'))
115 115 enable_downloads = form_data.get(
116 116 'enable_downloads', defs.get('repo_enable_downloads'))
117 117
118 118 try:
119 119 repo = RepoModel()._create_repo(
120 120 repo_name=repo_name_full,
121 121 repo_type=repo_type,
122 122 description=description,
123 123 owner=owner,
124 124 private=private,
125 125 clone_uri=clone_uri,
126 126 repo_group=repo_group,
127 127 landing_rev=landing_rev,
128 128 fork_of=fork_of,
129 129 copy_fork_permissions=copy_fork_permissions,
130 130 copy_group_permissions=copy_group_permissions,
131 131 enable_statistics=enable_statistics,
132 132 enable_locking=enable_locking,
133 133 enable_downloads=enable_downloads,
134 134 state=state
135 135 )
136 136 Session().commit()
137 137
138 138 # now create this repo on Filesystem
139 139 RepoModel()._create_filesystem_repo(
140 140 repo_name=repo_name,
141 141 repo_type=repo_type,
142 142 repo_group=RepoModel()._get_repo_group(repo_group),
143 143 clone_uri=clone_uri,
144 144 )
145 145 repo = Repository.get_by_repo_name(repo_name_full)
146 146 log_create_repository(created_by=owner.username, **repo.get_dict())
147 147
148 148 # update repo commit caches initially
149 149 repo.update_commit_cache()
150 150
151 151 # set new created state
152 152 repo.set_state(Repository.STATE_CREATED)
153 153 repo_id = repo.repo_id
154 154 repo_data = repo.get_api_data()
155 155
156 156 audit_logger.store(
157 157 'repo.create', action_data={'data': repo_data},
158 158 user=cur_user,
159 159 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
160 160
161 161 Session().commit()
162 162 except Exception as e:
163 163 log.warning('Exception occurred when creating repository, '
164 164 'doing cleanup...', exc_info=True)
165 165 if isinstance(e, IntegrityError):
166 166 Session().rollback()
167 167
168 168 # rollback things manually !
169 169 repo = Repository.get_by_repo_name(repo_name_full)
170 170 if repo:
171 171 Repository.delete(repo.repo_id)
172 172 Session().commit()
173 173 RepoModel()._delete_filesystem_repo(repo)
174 174 log.info('Cleanup of repo %s finished', repo_name_full)
175 175 raise
176 176
177 177 return True
178 178
179 179
180 180 @async_task(ignore_result=True, base=RequestContextTask)
181 181 def create_repo_fork(form_data, cur_user):
182 182 """
183 183 Creates a fork of repository using internal VCS methods
184 184 """
185 185 from rhodecode.model.repo import RepoModel
186 186 from rhodecode.model.user import UserModel
187 187
188 188 log = get_logger(create_repo_fork)
189 189
190 190 cur_user = UserModel()._get_user(cur_user)
191 191 owner = cur_user
192 192
193 193 repo_name = form_data['repo_name'] # fork in this case
194 194 repo_name_full = form_data['repo_name_full']
195 195 repo_type = form_data['repo_type']
196 196 description = form_data['description']
197 197 private = form_data['private']
198 198 clone_uri = form_data.get('clone_uri')
199 199 repo_group = safe_int(form_data['repo_group'])
200 200 landing_rev = form_data['landing_rev']
201 201 copy_fork_permissions = form_data.get('copy_permissions')
202 202 fork_id = safe_int(form_data.get('fork_parent_id'))
203 203
204 204 try:
205 205 fork_of = RepoModel()._get_repo(fork_id)
206 206 RepoModel()._create_repo(
207 207 repo_name=repo_name_full,
208 208 repo_type=repo_type,
209 209 description=description,
210 210 owner=owner,
211 211 private=private,
212 212 clone_uri=clone_uri,
213 213 repo_group=repo_group,
214 214 landing_rev=landing_rev,
215 215 fork_of=fork_of,
216 216 copy_fork_permissions=copy_fork_permissions
217 217 )
218 218
219 219 Session().commit()
220 220
221 221 base_path = Repository.base_path()
222 222 source_repo_path = os.path.join(base_path, fork_of.repo_name)
223 223
224 224 # now create this repo on Filesystem
225 225 RepoModel()._create_filesystem_repo(
226 226 repo_name=repo_name,
227 227 repo_type=repo_type,
228 228 repo_group=RepoModel()._get_repo_group(repo_group),
229 229 clone_uri=source_repo_path,
230 230 )
231 231 repo = Repository.get_by_repo_name(repo_name_full)
232 232 log_create_repository(created_by=owner.username, **repo.get_dict())
233 233
234 234 # update repo commit caches initially
235 235 config = repo._config
236 236 config.set('extensions', 'largefiles', '')
237 237 repo.update_commit_cache(config=config)
238 238
239 239 # set new created state
240 240 repo.set_state(Repository.STATE_CREATED)
241 241
242 242 repo_id = repo.repo_id
243 243 repo_data = repo.get_api_data()
244 244 audit_logger.store(
245 245 'repo.fork', action_data={'data': repo_data},
246 246 user=cur_user,
247 247 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
248 248
249 249 Session().commit()
250 250 except Exception as e:
251 251 log.warning('Exception %s occurred when forking repository, '
252 252 'doing cleanup...', exc_info=True)
253 253 if isinstance(e, IntegrityError):
254 254 Session().rollback()
255 255
256 256 # rollback things manually !
257 257 repo = Repository.get_by_repo_name(repo_name_full)
258 258 if repo:
259 259 Repository.delete(repo.repo_id)
260 260 Session().commit()
261 261 RepoModel()._delete_filesystem_repo(repo)
262 262 log.info('Cleanup of repo %s finished', repo_name_full)
263 263 raise
264 264
265 265 return True
266 266
267 267
268 268 @async_task(ignore_result=True)
269 269 def sync_repo(*args, **kwargs):
270 270 from rhodecode.model.scm import ScmModel
271 271 log = get_logger(sync_repo)
272 272 repo_name = kwargs['repo_name']
273 273 log.info('Pulling from %s', repo_name)
274 274 dbrepo = Repository.get_by_repo_name(repo_name)
275 275 if dbrepo and dbrepo.clone_uri:
276 276 ScmModel().pull_changes(kwargs['repo_name'], kwargs['username'])
277 277 else:
278 278 log.debug('Repo `%s` not found or without a clone_url', repo_name)
279 279
280 280
281 @async_task(ignore_result=True)
282 def check_for_update():
283 from rhodecode.model.update import UpdateModel
284 update_url = UpdateModel().get_update_url()
285 cur_ver = rhodecode.__version__
286
287 try:
288 data = UpdateModel().get_update_data(update_url)
289 latest = data['versions'][0]
290 UpdateModel().store_version(latest['version'])
291 except Exception:
292 pass
293
294
281 295 @async_task(ignore_result=False)
282 296 def beat_check(*args, **kwargs):
283 297 log = get_logger(beat_check)
284 298 log.info('Got args: %r and kwargs %r', args, kwargs)
285 299 return time.time()
@@ -1,27 +1,26 b''
1 1 ## -*- coding: utf-8 -*-
2 2 ## upgrade block rendered afte on-click check
3 3
4 4 <div class="alert ${'alert-warning' if c.should_upgrade else 'alert-success'}">
5 <p >
6
5 <p>
7 6 %if c.should_upgrade:
8 7 A <b>new version</b> is available:
9 8 %if c.latest_data.get('title'):
10 9 <b>${h.literal(c.latest_data['title'])}</b>
11 10 %else:
12 11 <b>${c.latest_ver}</b>
13 12 %endif
14 13 %else:
15 You already have the <b>latest</b> stable version.
14 This instance is already running the <b>latest</b> stable version ${c.latest_ver}.
16 15 %endif
17 16 </p>
18 17
19 18 % if c.should_upgrade and c.important_notices:
20 19 <div>Important notes for this release:</div>
21 20 <ul>
22 21 % for notice in c.important_notices:
23 22 <li>- ${notice}</li>
24 23 % endfor
25 24 </ul>
26 25 % endif
27 26 </div>
General Comments 0
You need to be logged in to leave comments. Login now