##// END OF EJS Templates
pull-requests: trigger merge simulation during PR creation. Fixes #5396
marcink -
r2168:41032fb6 default
parent child Browse files
Show More
@@ -274,7 +274,8 b' def merge_pull_request('
274 274
275 275 pull_request = get_pull_request_or_error(pullrequestid)
276 276
277 check = MergeCheck.validate(pull_request, user=apiuser)
277 check = MergeCheck.validate(
278 pull_request, user=apiuser, translator=request.translate)
278 279 merge_possible = not check.failed
279 280
280 281 if not merge_possible:
@@ -232,8 +232,8 b' class TestPullrequestsView(object):'
232 232 params={'update_commits': 'true',
233 233 'csrf_token': csrf_token})
234 234
235 expected_msg = PullRequestModel.UPDATE_STATUS_MESSAGES[
236 UpdateFailureReason.MISSING_SOURCE_REF]
235 expected_msg = str(PullRequestModel.UPDATE_STATUS_MESSAGES[
236 UpdateFailureReason.MISSING_SOURCE_REF])
237 237 assert_session_flash(response, expected_msg, category='error')
238 238
239 239 def test_missing_target_reference(self, pr_util, csrf_token):
@@ -362,12 +362,14 b' class RepoPullRequestsView(RepoAppView, '
362 362
363 363 # check merge capabilities
364 364 _merge_check = MergeCheck.validate(
365 pull_request_latest, user=self._rhodecode_user)
365 pull_request_latest, user=self._rhodecode_user,
366 translator=self.request.translate)
366 367 c.pr_merge_errors = _merge_check.error_details
367 368 c.pr_merge_possible = not _merge_check.failed
368 369 c.pr_merge_message = _merge_check.merge_msg
369 370
370 c.pr_merge_info = MergeCheck.get_merge_conditions(pull_request_latest)
371 c.pr_merge_info = MergeCheck.get_merge_conditions(
372 pull_request_latest, translator=self.request.translate)
371 373
372 374 c.pull_request_review_status = _merge_check.review_status
373 375 if merge_checks:
@@ -627,7 +629,7 b' class RepoPullRequestsView(RepoAppView, '
627 629 try:
628 630 source_repo_data = PullRequestModel().generate_repo_data(
629 631 source_repo, commit_id=commit_id,
630 branch=branch_ref, bookmark=bookmark_ref)
632 branch=branch_ref, bookmark=bookmark_ref, translator=self.request.translate)
631 633 except CommitDoesNotExistError as e:
632 634 log.exception(e)
633 635 h.flash(_('Commit does not exist'), 'error')
@@ -643,7 +645,7 b' class RepoPullRequestsView(RepoAppView, '
643 645 default_target_repo = source_repo.parent
644 646
645 647 target_repo_data = PullRequestModel().generate_repo_data(
646 default_target_repo)
648 default_target_repo, translator=self.request.translate)
647 649
648 650 selected_source_ref = source_repo_data['refs']['selected_ref']
649 651
@@ -676,7 +678,7 b' class RepoPullRequestsView(RepoAppView, '
676 678 repo = Repository.get_by_repo_name(target_repo_name)
677 679 if not repo:
678 680 raise HTTPNotFound()
679 return PullRequestModel().generate_repo_data(repo)
681 return PullRequestModel().generate_repo_data(repo, translator=self.request.translate)
680 682
681 683 @LoginRequired()
682 684 @NotAnonymous()
@@ -806,11 +808,12 b' class RepoPullRequestsView(RepoAppView, '
806 808
807 809 try:
808 810 pull_request = PullRequestModel().create(
809 self._rhodecode_user.user_id, source_repo, source_ref, target_repo,
810 target_ref, commit_ids, reviewers, pullrequest_title,
811 description, reviewer_rules
811 self._rhodecode_user.user_id, source_repo, source_ref,
812 target_repo, target_ref, commit_ids, reviewers,
813 pullrequest_title, description, reviewer_rules
812 814 )
813 815 Session().commit()
816
814 817 h.flash(_('Successfully opened new pull request'),
815 818 category='success')
816 819 except Exception:
@@ -938,7 +941,8 b' class RepoPullRequestsView(RepoAppView, '
938 941 pull_request = PullRequest.get_or_404(
939 942 self.request.matchdict['pull_request_id'])
940 943
941 check = MergeCheck.validate(pull_request, self._rhodecode_db_user)
944 check = MergeCheck.validate(pull_request, self._rhodecode_db_user,
945 translator=self.request.translate)
942 946 merge_possible = not check.failed
943 947
944 948 for err_type, error_msg in check.errors:
@@ -618,6 +618,9 b' def bootstrap_request(**kwargs):'
618 618 host = kwargs.pop('host', 'example.com:80')
619 619 domain = kwargs.pop('domain', 'example.com')
620 620
621 def translate(self, msg):
622 return msg
623
621 624 class TestDummySession(pyramid.testing.DummySession):
622 625 def save(*arg, **kw):
623 626 pass
@@ -625,6 +628,7 b' def bootstrap_request(**kwargs):'
625 628 request = TestRequest(**kwargs)
626 629 request.session = TestDummySession()
627 630
631
628 632 config = pyramid.testing.setUp(request=request)
629 633 add_events_routes(config)
630 634 return request
@@ -23,18 +23,17 b''
23 23 pull request model for RhodeCode
24 24 """
25 25
26 from collections import namedtuple
26
27 27 import json
28 28 import logging
29 29 import datetime
30 30 import urllib
31 import collections
31 32
32 from pylons.i18n.translation import _
33 from pylons.i18n.translation import lazy_ugettext
34 33 from pyramid.threadlocal import get_current_request
35 from sqlalchemy import or_
36 34
37 35 from rhodecode import events
36 from rhodecode.translation import lazy_ugettext#, _
38 37 from rhodecode.lib import helpers as h, hooks_utils, diffs
39 38 from rhodecode.lib import audit_logger
40 39 from rhodecode.lib.compat import OrderedDict
@@ -51,7 +50,7 b' from rhodecode.model import BaseModel'
51 50 from rhodecode.model.changeset_status import ChangesetStatusModel
52 51 from rhodecode.model.comment import CommentsModel
53 52 from rhodecode.model.db import (
54 PullRequest, PullRequestReviewers, ChangesetStatus,
53 or_, PullRequest, PullRequestReviewers, ChangesetStatus,
55 54 PullRequestVersion, ChangesetComment, Repository)
56 55 from rhodecode.model.meta import Session
57 56 from rhodecode.model.notification import NotificationModel, \
@@ -65,7 +64,7 b' log = logging.getLogger(__name__)'
65 64
66 65 # Data structure to hold the response data when updating commits during a pull
67 66 # request update.
68 UpdateResponse = namedtuple('UpdateResponse', [
67 UpdateResponse = collections.namedtuple('UpdateResponse', [
69 68 'executed', 'reason', 'new', 'old', 'changes',
70 69 'source_changed', 'target_changed'])
71 70
@@ -418,7 +417,8 b' class PullRequestModel(BaseModel):'
418 417
419 418 def create(self, created_by, source_repo, source_ref, target_repo,
420 419 target_ref, revisions, reviewers, title, description=None,
421 reviewer_data=None):
420 reviewer_data=None, translator=None):
421 translator = translator or get_current_request().translate
422 422
423 423 created_by_user = self._get_user(created_by)
424 424 source_repo = self._get_repo(source_repo)
@@ -466,6 +466,9 b' class PullRequestModel(BaseModel):'
466 466 pull_request=pull_request
467 467 )
468 468
469 MergeCheck.validate(
470 pull_request, user=created_by_user, translator=translator)
471
469 472 self.notify_reviewers(pull_request, reviewer_ids)
470 473 self._trigger_pull_request_hook(
471 474 pull_request, created_by_user, 'create')
@@ -535,13 +538,13 b' class PullRequestModel(BaseModel):'
535 538 log.warn("Merge failed, not updating the pull request.")
536 539 return merge_state
537 540
538 def _merge_pull_request(self, pull_request, user, extras):
541 def _merge_pull_request(self, pull_request, user, extras, merge_msg=None):
539 542 target_vcs = pull_request.target_repo.scm_instance()
540 543 source_vcs = pull_request.source_repo.scm_instance()
541 544 target_ref = self._refresh_reference(
542 545 pull_request.target_ref_parts, target_vcs)
543 546
544 message = _(
547 message = merge_msg or (
545 548 'Merge pull request #%(pr_id)s from '
546 549 '%(source_repo)s %(source_ref_name)s\n\n %(pr_title)s') % {
547 550 'pr_id': pull_request.pull_request_id,
@@ -570,12 +573,13 b' class PullRequestModel(BaseModel):'
570 573 close_branch=close_branch)
571 574 return merge_state
572 575
573 def _comment_and_close_pr(self, pull_request, user, merge_state):
576 def _comment_and_close_pr(self, pull_request, user, merge_state, close_msg=None):
574 577 pull_request.merge_rev = merge_state.merge_ref.commit_id
575 578 pull_request.updated_on = datetime.datetime.now()
579 close_msg = close_msg or 'Pull request merged and closed'
576 580
577 581 CommentsModel().create(
578 text=unicode(_('Pull request merged and closed')),
582 text=safe_unicode(close_msg),
579 583 repo=pull_request.target_repo.repo_id,
580 584 user=user.user_id,
581 585 pull_request=pull_request.pull_request_id,
@@ -1109,7 +1113,7 b' class PullRequestModel(BaseModel):'
1109 1113 status_lbl = ChangesetStatus.get_status_lbl(status)
1110 1114
1111 1115 default_message = (
1112 _('Closing with status change {transition_icon} {status}.')
1116 'Closing with status change {transition_icon} {status}.'
1113 1117 ).format(transition_icon='>', status=status_lbl)
1114 1118 text = message or default_message
1115 1119
@@ -1151,13 +1155,16 b' class PullRequestModel(BaseModel):'
1151 1155
1152 1156 return comment, status
1153 1157
1154 def merge_status(self, pull_request):
1158 def merge_status(self, pull_request, translator=None):
1159 _ = translator or get_current_request().translate
1160
1155 1161 if not self._is_merge_enabled(pull_request):
1156 1162 return False, _('Server-side pull request merging is disabled.')
1157 1163 if pull_request.is_closed():
1158 1164 return False, _('This pull request is closed.')
1159 1165 merge_possible, msg = self._check_repo_requirements(
1160 target=pull_request.target_repo, source=pull_request.source_repo)
1166 target=pull_request.target_repo, source=pull_request.source_repo,
1167 translator=_)
1161 1168 if not merge_possible:
1162 1169 return merge_possible, msg
1163 1170
@@ -1171,12 +1178,13 b' class PullRequestModel(BaseModel):'
1171 1178
1172 1179 return status
1173 1180
1174 def _check_repo_requirements(self, target, source):
1181 def _check_repo_requirements(self, target, source, translator):
1175 1182 """
1176 1183 Check if `target` and `source` have compatible requirements.
1177 1184
1178 1185 Currently this is just checking for largefiles.
1179 1186 """
1187 _ = translator
1180 1188 target_has_largefiles = self._has_largefiles(target)
1181 1189 source_has_largefiles = self._has_largefiles(source)
1182 1190 merge_possible = True
@@ -1282,11 +1290,12 b' class PullRequestModel(BaseModel):'
1282 1290 return self.MERGE_STATUS_MESSAGES[status_code]
1283 1291
1284 1292 def generate_repo_data(self, repo, commit_id=None, branch=None,
1285 bookmark=None):
1293 bookmark=None, translator=None):
1294
1286 1295 all_refs, selected_ref = \
1287 1296 self._get_repo_pullrequest_sources(
1288 1297 repo.scm_instance(), commit_id=commit_id,
1289 branch=branch, bookmark=bookmark)
1298 branch=branch, bookmark=bookmark, translator=translator)
1290 1299
1291 1300 refs_select2 = []
1292 1301 for element in all_refs:
@@ -1327,7 +1336,8 b' class PullRequestModel(BaseModel):'
1327 1336 pass
1328 1337
1329 1338 def _get_repo_pullrequest_sources(
1330 self, repo, commit_id=None, branch=None, bookmark=None):
1339 self, repo, commit_id=None, branch=None, bookmark=None,
1340 translator=None):
1331 1341 """
1332 1342 Return a structure with repo's interesting commits, suitable for
1333 1343 the selectors in pullrequest controller
@@ -1338,6 +1348,7 b' class PullRequestModel(BaseModel):'
1338 1348 by default - even if closed
1339 1349 :param bookmark: a bookmark that must be in the list and selected
1340 1350 """
1351 _ = translator or get_current_request().translate
1341 1352
1342 1353 commit_id = safe_str(commit_id) if commit_id else None
1343 1354 branch = safe_str(branch) if branch else None
@@ -1505,10 +1516,8 b' class MergeCheck(object):'
1505 1516 )
1506 1517
1507 1518 @classmethod
1508 def validate(cls, pull_request, user, fail_early=False, translator=None):
1509 # if migrated to pyramid...
1510 # _ = lambda: translator or _ # use passed in translator if any
1511
1519 def validate(cls, pull_request, user, translator, fail_early=False):
1520 _ = translator
1512 1521 merge_check = cls()
1513 1522
1514 1523 # permissions to merge
@@ -1557,7 +1566,8 b' class MergeCheck(object):'
1557 1566 return merge_check
1558 1567
1559 1568 # merge possible
1560 merge_status, msg = PullRequestModel().merge_status(pull_request)
1569 merge_status, msg = PullRequestModel().merge_status(
1570 pull_request, translator=translator)
1561 1571 merge_check.merge_possible = merge_status
1562 1572 merge_check.merge_msg = msg
1563 1573 if not merge_status:
@@ -1572,7 +1582,8 b' class MergeCheck(object):'
1572 1582 return merge_check
1573 1583
1574 1584 @classmethod
1575 def get_merge_conditions(cls, pull_request):
1585 def get_merge_conditions(cls, pull_request, translator):
1586 _ = translator
1576 1587 merge_details = {}
1577 1588
1578 1589 model = PullRequestModel()
@@ -1604,8 +1615,8 b' class MergeCheck(object):'
1604 1615
1605 1616 return merge_details
1606 1617
1607 ChangeTuple = namedtuple('ChangeTuple',
1608 ['added', 'common', 'removed', 'total'])
1618 ChangeTuple = collections.namedtuple(
1619 'ChangeTuple', ['added', 'common', 'removed', 'total'])
1609 1620
1610 FileChangeTuple = namedtuple('FileChangeTuple',
1611 ['added', 'modified', 'removed'])
1621 FileChangeTuple = collections.namedtuple(
1622 'FileChangeTuple', ['added', 'modified', 'removed'])
@@ -160,7 +160,7 b' class TestPullRequestModel(object):'
160 160 status, msg = PullRequestModel().merge_status(pull_request)
161 161 assert status is True
162 162 assert msg.eval() == 'This pull request can be automatically merged.'
163 self.merge_mock.assert_called_once_with(
163 self.merge_mock.assert_called_with(
164 164 pull_request.target_ref_parts,
165 165 pull_request.source_repo.scm_instance(),
166 166 pull_request.source_ref_parts, self.workspace_id, dry_run=True,
@@ -189,7 +189,7 b' class TestPullRequestModel(object):'
189 189 assert (
190 190 msg.eval() ==
191 191 'This pull request cannot be merged because of merge conflicts.')
192 self.merge_mock.assert_called_once_with(
192 self.merge_mock.assert_called_with(
193 193 pull_request.target_ref_parts,
194 194 pull_request.source_repo.scm_instance(),
195 195 pull_request.source_ref_parts, self.workspace_id, dry_run=True,
@@ -221,7 +221,7 b' class TestPullRequestModel(object):'
221 221 assert msg.eval() == (
222 222 'This pull request cannot be merged because of an unhandled'
223 223 ' exception.')
224 self.merge_mock.assert_called_once_with(
224 self.merge_mock.assert_called_with(
225 225 pull_request.target_ref_parts,
226 226 pull_request.source_repo.scm_instance(),
227 227 pull_request.source_ref_parts, self.workspace_id, dry_run=True,
@@ -294,7 +294,7 b' class TestPullRequestModel(object):'
294 294 pr_title=safe_unicode(pull_request.title)
295 295 )
296 296 )
297 self.merge_mock.assert_called_once_with(
297 self.merge_mock.assert_called_with(
298 298 pull_request.target_ref_parts,
299 299 pull_request.source_repo.scm_instance(),
300 300 pull_request.source_ref_parts, self.workspace_id,
@@ -333,7 +333,7 b' class TestPullRequestModel(object):'
333 333 pr_title=safe_unicode(pull_request.title)
334 334 )
335 335 )
336 self.merge_mock.assert_called_once_with(
336 self.merge_mock.assert_called_with(
337 337 pull_request.target_ref_parts,
338 338 pull_request.source_repo.scm_instance(),
339 339 pull_request.source_ref_parts, self.workspace_id,
@@ -27,8 +27,20 b' class LazyString(object):'
27 27 self.args = args
28 28 self.kw = kw
29 29
30 def eval(self):
31 return _(*self.args, **self.kw)
32
33 def __unicode__(self):
34 return unicode(self.eval())
35
30 36 def __str__(self):
31 return _(*self.args, **self.kw)
37 return self.eval()
38
39 def __mod__(self, other):
40 return self.eval() % other
41
42 def format(self, *args):
43 return self.eval().format(*args)
32 44
33 45
34 46 def lazy_ugettext(*args, **kw):
General Comments 0
You need to be logged in to leave comments. Login now