# HG changeset patch # User Marcin Kuzminski # Date 2017-10-12 18:57:02 # Node ID 41032fb666fba8d84ea6bb1b44eb749520b61238 # Parent 23aaeb72bc9d63f3cc416e6a4aede87ae5ebbb22 pull-requests: trigger merge simulation during PR creation. Fixes #5396 diff --git a/rhodecode/api/views/pull_request_api.py b/rhodecode/api/views/pull_request_api.py --- a/rhodecode/api/views/pull_request_api.py +++ b/rhodecode/api/views/pull_request_api.py @@ -274,7 +274,8 @@ def merge_pull_request( pull_request = get_pull_request_or_error(pullrequestid) - check = MergeCheck.validate(pull_request, user=apiuser) + check = MergeCheck.validate( + pull_request, user=apiuser, translator=request.translate) merge_possible = not check.failed if not merge_possible: diff --git a/rhodecode/apps/repository/tests/test_repo_pullrequests.py b/rhodecode/apps/repository/tests/test_repo_pullrequests.py --- a/rhodecode/apps/repository/tests/test_repo_pullrequests.py +++ b/rhodecode/apps/repository/tests/test_repo_pullrequests.py @@ -232,8 +232,8 @@ class TestPullrequestsView(object): params={'update_commits': 'true', 'csrf_token': csrf_token}) - expected_msg = PullRequestModel.UPDATE_STATUS_MESSAGES[ - UpdateFailureReason.MISSING_SOURCE_REF] + expected_msg = str(PullRequestModel.UPDATE_STATUS_MESSAGES[ + UpdateFailureReason.MISSING_SOURCE_REF]) assert_session_flash(response, expected_msg, category='error') def test_missing_target_reference(self, pr_util, csrf_token): diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -362,12 +362,14 @@ class RepoPullRequestsView(RepoAppView, # check merge capabilities _merge_check = MergeCheck.validate( - pull_request_latest, user=self._rhodecode_user) + pull_request_latest, user=self._rhodecode_user, + translator=self.request.translate) c.pr_merge_errors = _merge_check.error_details c.pr_merge_possible = not _merge_check.failed c.pr_merge_message = _merge_check.merge_msg - c.pr_merge_info = MergeCheck.get_merge_conditions(pull_request_latest) + c.pr_merge_info = MergeCheck.get_merge_conditions( + pull_request_latest, translator=self.request.translate) c.pull_request_review_status = _merge_check.review_status if merge_checks: @@ -627,7 +629,7 @@ class RepoPullRequestsView(RepoAppView, try: source_repo_data = PullRequestModel().generate_repo_data( source_repo, commit_id=commit_id, - branch=branch_ref, bookmark=bookmark_ref) + branch=branch_ref, bookmark=bookmark_ref, translator=self.request.translate) except CommitDoesNotExistError as e: log.exception(e) h.flash(_('Commit does not exist'), 'error') @@ -643,7 +645,7 @@ class RepoPullRequestsView(RepoAppView, default_target_repo = source_repo.parent target_repo_data = PullRequestModel().generate_repo_data( - default_target_repo) + default_target_repo, translator=self.request.translate) selected_source_ref = source_repo_data['refs']['selected_ref'] @@ -676,7 +678,7 @@ class RepoPullRequestsView(RepoAppView, repo = Repository.get_by_repo_name(target_repo_name) if not repo: raise HTTPNotFound() - return PullRequestModel().generate_repo_data(repo) + return PullRequestModel().generate_repo_data(repo, translator=self.request.translate) @LoginRequired() @NotAnonymous() @@ -806,11 +808,12 @@ class RepoPullRequestsView(RepoAppView, try: pull_request = PullRequestModel().create( - self._rhodecode_user.user_id, source_repo, source_ref, target_repo, - target_ref, commit_ids, reviewers, pullrequest_title, - description, reviewer_rules + self._rhodecode_user.user_id, source_repo, source_ref, + target_repo, target_ref, commit_ids, reviewers, + pullrequest_title, description, reviewer_rules ) Session().commit() + h.flash(_('Successfully opened new pull request'), category='success') except Exception: @@ -938,7 +941,8 @@ class RepoPullRequestsView(RepoAppView, pull_request = PullRequest.get_or_404( self.request.matchdict['pull_request_id']) - check = MergeCheck.validate(pull_request, self._rhodecode_db_user) + check = MergeCheck.validate(pull_request, self._rhodecode_db_user, + translator=self.request.translate) merge_possible = not check.failed for err_type, error_msg in check.errors: diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py --- a/rhodecode/lib/base.py +++ b/rhodecode/lib/base.py @@ -618,6 +618,9 @@ def bootstrap_request(**kwargs): host = kwargs.pop('host', 'example.com:80') domain = kwargs.pop('domain', 'example.com') + def translate(self, msg): + return msg + class TestDummySession(pyramid.testing.DummySession): def save(*arg, **kw): pass @@ -625,6 +628,7 @@ def bootstrap_request(**kwargs): request = TestRequest(**kwargs) request.session = TestDummySession() + config = pyramid.testing.setUp(request=request) add_events_routes(config) return request diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -23,18 +23,17 @@ pull request model for RhodeCode """ -from collections import namedtuple + import json import logging import datetime import urllib +import collections -from pylons.i18n.translation import _ -from pylons.i18n.translation import lazy_ugettext from pyramid.threadlocal import get_current_request -from sqlalchemy import or_ from rhodecode import events +from rhodecode.translation import lazy_ugettext#, _ from rhodecode.lib import helpers as h, hooks_utils, diffs from rhodecode.lib import audit_logger from rhodecode.lib.compat import OrderedDict @@ -51,7 +50,7 @@ from rhodecode.model import BaseModel from rhodecode.model.changeset_status import ChangesetStatusModel from rhodecode.model.comment import CommentsModel from rhodecode.model.db import ( - PullRequest, PullRequestReviewers, ChangesetStatus, + or_, PullRequest, PullRequestReviewers, ChangesetStatus, PullRequestVersion, ChangesetComment, Repository) from rhodecode.model.meta import Session from rhodecode.model.notification import NotificationModel, \ @@ -65,7 +64,7 @@ log = logging.getLogger(__name__) # Data structure to hold the response data when updating commits during a pull # request update. -UpdateResponse = namedtuple('UpdateResponse', [ +UpdateResponse = collections.namedtuple('UpdateResponse', [ 'executed', 'reason', 'new', 'old', 'changes', 'source_changed', 'target_changed']) @@ -361,7 +360,7 @@ class PullRequestModel(BaseModel): reviewers_subquery = Session().query( PullRequestReviewers.pull_request_id).filter( PullRequestReviewers.user_id == user_id).subquery() - user_filter= or_( + user_filter = or_( PullRequest.user_id == user_id, PullRequest.pull_request_id.in_(reviewers_subquery) ) @@ -418,7 +417,8 @@ class PullRequestModel(BaseModel): def create(self, created_by, source_repo, source_ref, target_repo, target_ref, revisions, reviewers, title, description=None, - reviewer_data=None): + reviewer_data=None, translator=None): + translator = translator or get_current_request().translate created_by_user = self._get_user(created_by) source_repo = self._get_repo(source_repo) @@ -466,6 +466,9 @@ class PullRequestModel(BaseModel): pull_request=pull_request ) + MergeCheck.validate( + pull_request, user=created_by_user, translator=translator) + self.notify_reviewers(pull_request, reviewer_ids) self._trigger_pull_request_hook( pull_request, created_by_user, 'create') @@ -535,13 +538,13 @@ class PullRequestModel(BaseModel): log.warn("Merge failed, not updating the pull request.") return merge_state - def _merge_pull_request(self, pull_request, user, extras): + def _merge_pull_request(self, pull_request, user, extras, merge_msg=None): target_vcs = pull_request.target_repo.scm_instance() source_vcs = pull_request.source_repo.scm_instance() target_ref = self._refresh_reference( pull_request.target_ref_parts, target_vcs) - message = _( + message = merge_msg or ( 'Merge pull request #%(pr_id)s from ' '%(source_repo)s %(source_ref_name)s\n\n %(pr_title)s') % { 'pr_id': pull_request.pull_request_id, @@ -570,12 +573,13 @@ class PullRequestModel(BaseModel): close_branch=close_branch) return merge_state - def _comment_and_close_pr(self, pull_request, user, merge_state): + def _comment_and_close_pr(self, pull_request, user, merge_state, close_msg=None): pull_request.merge_rev = merge_state.merge_ref.commit_id pull_request.updated_on = datetime.datetime.now() + close_msg = close_msg or 'Pull request merged and closed' CommentsModel().create( - text=unicode(_('Pull request merged and closed')), + text=safe_unicode(close_msg), repo=pull_request.target_repo.repo_id, user=user.user_id, pull_request=pull_request.pull_request_id, @@ -1109,7 +1113,7 @@ class PullRequestModel(BaseModel): status_lbl = ChangesetStatus.get_status_lbl(status) default_message = ( - _('Closing with status change {transition_icon} {status}.') + 'Closing with status change {transition_icon} {status}.' ).format(transition_icon='>', status=status_lbl) text = message or default_message @@ -1151,13 +1155,16 @@ class PullRequestModel(BaseModel): return comment, status - def merge_status(self, pull_request): + def merge_status(self, pull_request, translator=None): + _ = translator or get_current_request().translate + if not self._is_merge_enabled(pull_request): return False, _('Server-side pull request merging is disabled.') if pull_request.is_closed(): return False, _('This pull request is closed.') merge_possible, msg = self._check_repo_requirements( - target=pull_request.target_repo, source=pull_request.source_repo) + target=pull_request.target_repo, source=pull_request.source_repo, + translator=_) if not merge_possible: return merge_possible, msg @@ -1171,12 +1178,13 @@ class PullRequestModel(BaseModel): return status - def _check_repo_requirements(self, target, source): + def _check_repo_requirements(self, target, source, translator): """ Check if `target` and `source` have compatible requirements. Currently this is just checking for largefiles. """ + _ = translator target_has_largefiles = self._has_largefiles(target) source_has_largefiles = self._has_largefiles(source) merge_possible = True @@ -1282,11 +1290,12 @@ class PullRequestModel(BaseModel): return self.MERGE_STATUS_MESSAGES[status_code] def generate_repo_data(self, repo, commit_id=None, branch=None, - bookmark=None): + bookmark=None, translator=None): + all_refs, selected_ref = \ self._get_repo_pullrequest_sources( repo.scm_instance(), commit_id=commit_id, - branch=branch, bookmark=bookmark) + branch=branch, bookmark=bookmark, translator=translator) refs_select2 = [] for element in all_refs: @@ -1327,7 +1336,8 @@ class PullRequestModel(BaseModel): pass def _get_repo_pullrequest_sources( - self, repo, commit_id=None, branch=None, bookmark=None): + self, repo, commit_id=None, branch=None, bookmark=None, + translator=None): """ Return a structure with repo's interesting commits, suitable for the selectors in pullrequest controller @@ -1338,6 +1348,7 @@ class PullRequestModel(BaseModel): by default - even if closed :param bookmark: a bookmark that must be in the list and selected """ + _ = translator or get_current_request().translate commit_id = safe_str(commit_id) if commit_id else None branch = safe_str(branch) if branch else None @@ -1505,10 +1516,8 @@ class MergeCheck(object): ) @classmethod - def validate(cls, pull_request, user, fail_early=False, translator=None): - # if migrated to pyramid... - # _ = lambda: translator or _ # use passed in translator if any - + def validate(cls, pull_request, user, translator, fail_early=False): + _ = translator merge_check = cls() # permissions to merge @@ -1557,7 +1566,8 @@ class MergeCheck(object): return merge_check # merge possible - merge_status, msg = PullRequestModel().merge_status(pull_request) + merge_status, msg = PullRequestModel().merge_status( + pull_request, translator=translator) merge_check.merge_possible = merge_status merge_check.merge_msg = msg if not merge_status: @@ -1572,7 +1582,8 @@ class MergeCheck(object): return merge_check @classmethod - def get_merge_conditions(cls, pull_request): + def get_merge_conditions(cls, pull_request, translator): + _ = translator merge_details = {} model = PullRequestModel() @@ -1604,8 +1615,8 @@ class MergeCheck(object): return merge_details -ChangeTuple = namedtuple('ChangeTuple', - ['added', 'common', 'removed', 'total']) +ChangeTuple = collections.namedtuple( + 'ChangeTuple', ['added', 'common', 'removed', 'total']) -FileChangeTuple = namedtuple('FileChangeTuple', - ['added', 'modified', 'removed']) +FileChangeTuple = collections.namedtuple( + 'FileChangeTuple', ['added', 'modified', 'removed']) diff --git a/rhodecode/tests/models/test_pullrequest.py b/rhodecode/tests/models/test_pullrequest.py --- a/rhodecode/tests/models/test_pullrequest.py +++ b/rhodecode/tests/models/test_pullrequest.py @@ -160,7 +160,7 @@ class TestPullRequestModel(object): status, msg = PullRequestModel().merge_status(pull_request) assert status is True assert msg.eval() == 'This pull request can be automatically merged.' - self.merge_mock.assert_called_once_with( + self.merge_mock.assert_called_with( pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, self.workspace_id, dry_run=True, @@ -189,7 +189,7 @@ class TestPullRequestModel(object): assert ( msg.eval() == 'This pull request cannot be merged because of merge conflicts.') - self.merge_mock.assert_called_once_with( + self.merge_mock.assert_called_with( pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, self.workspace_id, dry_run=True, @@ -221,7 +221,7 @@ class TestPullRequestModel(object): assert msg.eval() == ( 'This pull request cannot be merged because of an unhandled' ' exception.') - self.merge_mock.assert_called_once_with( + self.merge_mock.assert_called_with( pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, self.workspace_id, dry_run=True, @@ -294,7 +294,7 @@ class TestPullRequestModel(object): pr_title=safe_unicode(pull_request.title) ) ) - self.merge_mock.assert_called_once_with( + self.merge_mock.assert_called_with( pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, self.workspace_id, @@ -333,7 +333,7 @@ class TestPullRequestModel(object): pr_title=safe_unicode(pull_request.title) ) ) - self.merge_mock.assert_called_once_with( + self.merge_mock.assert_called_with( pull_request.target_ref_parts, pull_request.source_repo.scm_instance(), pull_request.source_ref_parts, self.workspace_id, diff --git a/rhodecode/translation.py b/rhodecode/translation.py --- a/rhodecode/translation.py +++ b/rhodecode/translation.py @@ -27,8 +27,20 @@ class LazyString(object): self.args = args self.kw = kw + def eval(self): + return _(*self.args, **self.kw) + + def __unicode__(self): + return unicode(self.eval()) + def __str__(self): - return _(*self.args, **self.kw) + return self.eval() + + def __mod__(self, other): + return self.eval() % other + + def format(self, *args): + return self.eval().format(*args) def lazy_ugettext(*args, **kw):