launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18350
[Merge] lp:~cjwatson/launchpad/git-mp-basic-model into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/git-mp-basic-model into lp:launchpad.
Commit message:
Model basic attributes of Git merge proposals, and add some corresponding helper properties to GitRef to make it look more like Branch.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1445017 in Launchpad itself: "Support for Launchpad Git merge proposals "
https://bugs.launchpad.net/launchpad/+bug/1445017
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-mp-basic-model/+merge/257122
Now that production and qastaging have the necessary DB patch and we've done some preliminary tidying, here's the start of the model code for Git merge proposals. There are no tests yet, but we'll be able to add those quite soon, once we're far enough up the stack to be able to actually create these objects.
The general plan here is to make it possible to treat IBranch and IGitRef similarly via duck-typing most of the time. (Ideally there'd be some common interface, but no great rush on that.) There are of course some cases where they are intrinsically different, such as different ways to identify revisions, and some where they should be similar but aren't yet (mostly due to the existence of BranchTarget).
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-mp-basic-model into lp:launchpad.
=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml 2015-04-19 12:56:32 +0000
+++ lib/lp/code/configure.zcml 2015-04-22 15:02:31 +0000
@@ -852,6 +852,11 @@
permission="launchpad.View"
interface="lp.code.interfaces.gitref.IGitRef" />
</class>
+ <class class="lp.code.model.gitref.GitRefFrozen">
+ <require
+ permission="launchpad.View"
+ interface="lp.code.interfaces.gitref.IGitRef" />
+ </class>
<!-- GitCollection -->
=== modified file 'lib/lp/code/interfaces/branchmergeproposal.py'
--- lib/lp/code/interfaces/branchmergeproposal.py 2015-04-21 16:53:45 +0000
+++ lib/lp/code/interfaces/branchmergeproposal.py 2015-04-22 15:02:31 +0000
@@ -72,6 +72,8 @@
)
from lp.code.interfaces.branch import IBranch
from lp.code.interfaces.diff import IPreviewDiff
+from lp.code.interfaces.gitref import IGitRef
+from lp.code.interfaces.gitrepository import IGitRepository
from lp.registry.interfaces.person import IPerson
from lp.services.database.constants import DEFAULT
from lp.services.fields import (
@@ -108,9 +110,14 @@
title=_('DB ID'), required=True, readonly=True,
description=_("The tracking number for this merge proposal."))
source_branchID = Int(
- title=_('Source branch ID'), required=True, readonly=True)
+ title=_('Source branch ID'), required=False, readonly=True)
+ source_git_repositoryID = Int(
+ title=_('Source Git repository ID'), required=False, readonly=True)
prerequisite_branchID = Int(
- title=_('Prerequisite branch ID'), required=True, readonly=True)
+ title=_('Prerequisite branch ID'), required=False, readonly=True)
+ prerequisite_git_repositoryID = Int(
+ title=_('Prerequisite Git repository ID'),
+ required=False, readonly=True)
# This is redefined from IPrivacy.private because the attribute is
# read-only. The value is determined by the involved branches.
@@ -124,15 +131,48 @@
source_branch = exported(
ReferenceChoice(
title=_('Source Branch'), schema=IBranch, vocabulary='Branch',
- required=True, readonly=True,
+ required=False, readonly=True,
description=_("The branch that has code to land.")))
+ source_git_repository = exported(
+ ReferenceChoice(
+ title=_('Source Git Repository'), schema=IGitRepository,
+ vocabulary='GitRepository', required=False, readonly=True,
+ description=_("The Git repository that has code to land.")))
+ source_git_path = exported(
+ TextLine(
+ title=_('Source Git branch path'), required=False, readonly=True,
+ description=_(
+ "The path of the Git branch that has code to land.")))
+ source_git_commit_sha1 = TextLine(
+ title=_('Source Git commit SHA-1'), required=False, readonly=True)
+ source_git_ref = Reference(
+ title=_('Source Git reference'),
+ schema=IGitRef, required=False, readonly=True)
target_branch = exported(
ReferenceChoice(
title=_('Target Branch'),
- schema=IBranch, vocabulary='Branch', required=True, readonly=True,
+ schema=IBranch, vocabulary='Branch', required=False, readonly=True,
description=_(
"The branch that the source branch will be merged into.")))
+ target_git_repository = exported(
+ ReferenceChoice(
+ title=_('Target Git Repository'), schema=IGitRepository,
+ vocabulary='GitRepository', required=False, readonly=True,
+ description=_(
+ "The Git repository that the source branch will be merged "
+ "into.")))
+ target_git_path = exported(
+ TextLine(
+ title=_('Target Git branch path'), required=False, readonly=True,
+ description=_(
+ "The path of the Git branch that the source branch will be "
+ "merged into.")))
+ target_git_commit_sha1 = TextLine(
+ title=_('Target Git commit SHA-1'), required=False, readonly=True)
+ target_git_ref = Reference(
+ title=_('Target Git reference'),
+ schema=IGitRef, required=False, readonly=True)
prerequisite_branch = exported(
ReferenceChoice(
@@ -142,6 +182,28 @@
"The branch that the source branch branched from. "
"If this branch is the same as the target branch, then "
"leave this field blank.")))
+ prerequisite_git_repository = exported(
+ ReferenceChoice(
+ title=_('Prerequisite Git Repository'), schema=IGitRepository,
+ vocabulary='GitRepository', required=False, readonly=True,
+ description=_(
+ "The Git repository containing the branch that the source "
+ "branch branched from. If this branch is the same as the "
+ "target branch, then leave this field blank.")))
+ prerequisite_git_path = exported(
+ TextLine(
+ title=_('Prerequisite Git branch path'),
+ required=False, readonly=True,
+ description=_(
+ "The path of the Git branch that the source branch branched "
+ "from. If this branch is the same as the target branch, then "
+ "leave this field blank.")))
+ prerequisite_git_commit_sha1 = TextLine(
+ title=_('Prerequisite Git commit SHA-1'),
+ required=False, readonly=True)
+ prerequisite_git_ref = Reference(
+ title=_('Prerequisite Git reference'),
+ schema=IGitRef, required=False, readonly=True)
merge_source = Attribute(
"The branch that has code to land (VCS-agnostic).")
@@ -221,8 +283,16 @@
Int(
title=_("Merged Revision Number"), required=False,
readonly=True,
- description=_("The revision number on the target branch which "
- "contains the merge from the source branch.")))
+ description=_(
+ "The revision number on the target branch which contains the "
+ "merge from the source branch (Bazaar only).")))
+
+ merged_revision_id = exported(
+ Text(
+ title=_("Merged Revision ID"), required=False, readonly=True,
+ description=_(
+ "The revision ID on the target branch which contains the "
+ "merge from the source branch (currently Git only).")))
date_merged = exported(
Datetime(
=== modified file 'lib/lp/code/interfaces/gitref.py'
--- lib/lp/code/interfaces/gitref.py 2015-04-21 10:48:06 +0000
+++ lib/lp/code/interfaces/gitref.py 2015-04-22 15:02:31 +0000
@@ -107,6 +107,41 @@
target = Attribute(
"The target of the repository containing this reference.")
+ namespace = Attribute(
+ "The namespace of the repository containing this reference, as an "
+ "`IGitNamespace`.")
+
+ def getCodebrowseUrl():
+ """Construct a browsing URL for this Git reference."""
+
+ information_type = Attribute(
+ "The type of information contained in the repository containing this "
+ "reference.")
+
+ def visibleByUser(user):
+ """Can the specified user see the repository containing this
+ reference?"""
+
+ reviewer = Attribute(
+ "The person or exclusive team that is responsible for reviewing "
+ "proposals and merging into this reference.")
+
+ code_reviewer = Attribute(
+ "The reviewer if set, otherwise the owner of the repository "
+ "containing this reference.")
+
+ def isPersonTrustedReviewer(reviewer):
+ """Return true if the `reviewer` is a trusted reviewer.
+
+ The reviewer is trusted if they either own the repository containing
+ this reference, or are in the team that owns the repository, or they
+ are in the review team for the repository.
+ """
+
+ subscriptions = Attribute(
+ "GitSubscriptions associated with the repository containing this "
+ "reference.")
+
subscribers = Attribute(
"Persons subscribed to the repository containing this reference.")
@@ -136,6 +171,18 @@
and their subscriptions.
"""
+ # XXX cjwatson 2015-04-16: These names are too awful to set in stone by
+ # exporting them on the webservice; find better names before exporting.
+ landing_targets = Attribute(
+ "A collection of the merge proposals where this reference is the "
+ "source.")
+ landing_candidates = Attribute(
+ "A collection of the merge proposals where this reference is the "
+ "target.")
+ dependent_landings = Attribute(
+ "A collection of the merge proposals that are dependent on this "
+ "reference.")
+
class IGitRefBatchNavigator(ITableBatchNavigator):
pass
=== modified file 'lib/lp/code/model/branchmergeproposal.py'
--- lib/lp/code/model/branchmergeproposal.py 2015-04-22 12:06:22 +0000
+++ lib/lp/code/model/branchmergeproposal.py 2015-04-22 15:02:31 +0000
@@ -23,12 +23,19 @@
Desc,
Join,
LeftJoin,
+ Not,
Or,
Select,
SQL,
)
-from storm.locals import Reference
-from storm.store import Store
+from storm.locals import (
+ Int,
+ Reference,
+ )
+from storm.store import (
+ EmptyResultSet,
+ Store,
+ )
from zope.component import getUtility
from zope.event import notify
from zope.interface import implements
@@ -66,6 +73,7 @@
from lp.code.interfaces.codereviewinlinecomment import (
ICodeReviewInlineCommentSet,
)
+from lp.code.interfaces.gitref import IGitRef
from lp.code.mail.branch import RecipientReason
from lp.code.model.branchrevision import BranchRevision
from lp.code.model.codereviewcomment import CodeReviewComment
@@ -98,7 +106,6 @@
from lp.services.database.sqlbase import (
quote,
SQLBase,
- sqlvalues,
)
from lp.services.job.interfaces.job import JobStatus
from lp.services.job.model.job import Job
@@ -175,25 +182,103 @@
storm_validator=validate_public_person, notNull=True)
source_branch = ForeignKey(
- dbName='source_branch', foreignKey='Branch', notNull=True)
+ dbName='source_branch', foreignKey='Branch', notNull=False)
+ source_git_repositoryID = Int(
+ name='source_git_repository', allow_none=True)
+ source_git_repository = Reference(
+ source_git_repositoryID, 'GitRepository.id')
+ source_git_path = StringCol(
+ dbName='source_git_path', default=None, notNull=False)
+ source_git_commit_sha1 = StringCol(
+ dbName='source_git_commit_sha1', default=None, notNull=False)
target_branch = ForeignKey(
- dbName='target_branch', foreignKey='Branch', notNull=True)
+ dbName='target_branch', foreignKey='Branch', notNull=False)
+ target_git_repositoryID = Int(
+ name='target_git_repository', allow_none=True)
+ target_git_repository = Reference(
+ target_git_repositoryID, 'GitRepository.id')
+ target_git_path = StringCol(
+ dbName='target_git_path', default=None, notNull=False)
+ target_git_commit_sha1 = StringCol(
+ dbName='target_git_commit_sha1', default=None, notNull=False)
prerequisite_branch = ForeignKey(
dbName='dependent_branch', foreignKey='Branch', notNull=False)
+ prerequisite_git_repositoryID = Int(
+ name='dependent_git_repository', allow_none=True)
+ prerequisite_git_repository = Reference(
+ prerequisite_git_repositoryID, 'GitRepository.id')
+ prerequisite_git_path = StringCol(
+ dbName='dependent_git_path', default=None, notNull=False)
+ prerequisite_git_commit_sha1 = StringCol(
+ dbName='dependent_git_commit_sha1', default=None, notNull=False)
+
+ @property
+ def source_git_ref(self):
+ from lp.code.model.gitref import GitRefFrozen
+ if self.source_git_repository is None:
+ return None
+ return GitRefFrozen(
+ self.source_git_repository, self.source_git_path,
+ self.source_git_commit_sha1)
+
+ @source_git_ref.setter
+ def source_git_ref(self, value):
+ self.source_git_repository = value.repository
+ self.source_git_path = value.path
+ self.source_git_commit_sha1 = value.commit_sha1
+
+ @property
+ def target_git_ref(self):
+ from lp.code.model.gitref import GitRefFrozen
+ if self.target_git_repository is None:
+ return None
+ return GitRefFrozen(
+ self.target_git_repository, self.target_git_path,
+ self.target_git_commit_sha1)
+
+ @target_git_ref.setter
+ def target_git_ref(self, value):
+ self.target_git_repository = value.repository
+ self.target_git_path = value.path
+ self.target_git_commit_sha1 = value.commit_sha1
+
+ @property
+ def prerequisite_git_ref(self):
+ from lp.code.model.gitref import GitRefFrozen
+ if self.prerequisite_git_repository is None:
+ return None
+ return GitRefFrozen(
+ self.prerequisite_git_repository, self.prerequisite_git_path,
+ self.prerequisite_git_commit_sha1)
+
+ @prerequisite_git_ref.setter
+ def prerequisite_git_ref(self, value):
+ self.prerequisite_git_repository = value.repository
+ self.prerequisite_git_path = value.path
+ self.prerequisite_git_commit_sha1 = value.commit_sha1
@property
def merge_source(self):
- return self.source_branch
+ if self.source_branch is not None:
+ return self.source_branch
+ else:
+ return self.source_git_ref
@property
def merge_target(self):
- return self.target_branch
+ if self.target_branch is not None:
+ return self.target_branch
+ else:
+ return self.target_git_ref
@property
def merge_prerequisite(self):
- return self.prerequisite_branch
+ if self.prerequisite_branch is not None:
+ return self.prerequisite_branch
+ else:
+ return self.prerequisite_git_ref
description = StringCol(default=None)
@@ -246,6 +331,7 @@
date_merged = UtcDateTimeCol(default=None)
merged_revno = IntCol(default=None)
+ merged_revision_id = StringCol(default=None)
merge_reporter = ForeignKey(
dbName='merge_reporter', foreignKey='Person',
@@ -257,6 +343,10 @@
Implies that these would be fixed, in the target, by the merge.
"""
+ if self.source_branch is None:
+ # XXX cjwatson 2015-04-16: Implement once Git refs have linked
+ # bug tasks.
+ return []
source_tasks = self.source_branch.getLinkedBugTasks(user)
target_tasks = self.target_branch.getLinkedBugTasks(user)
return [bugtask
@@ -497,13 +587,14 @@
reviewer, BranchMergeProposalStatus.REJECTED, revision_id,
_date_reviewed)
- def markAsMerged(self, merged_revno=None, date_merged=None,
- merge_reporter=None):
+ def markAsMerged(self, merged_revno=None, merged_revision_id=None,
+ date_merged=None, merge_reporter=None):
"""See `IBranchMergeProposal`."""
old_state = self.queue_status
self._transitionToState(
BranchMergeProposalStatus.MERGED, merge_reporter)
self.merged_revno = merged_revno
+ self.merged_revision_id = merged_revision_id
self.merge_reporter = merge_reporter
# The reviewer of a merged proposal is assumed to have approved, if
@@ -511,13 +602,14 @@
if old_state == BranchMergeProposalStatus.REJECTED:
self._mark_unreviewed()
- if merged_revno is not None:
+ if self.target_branch is not None and merged_revno is not None:
branch_revision = Store.of(self).find(
BranchRevision,
BranchRevision.branch == self.target_branch,
BranchRevision.sequence == merged_revno).one()
if branch_revision is not None:
date_merged = branch_revision.revision.revision_date
+ # XXX cjwatson 2015-04-12: Handle the Git case.
if date_merged is None:
date_merged = UTC_NOW
@@ -613,6 +705,9 @@
the reviewer if the branch is private and the reviewer is an open
team.
"""
+ if self.source_branch is None:
+ # This only applies to Bazaar, which has stacked branches.
+ return
source = self.source_branch
if (not source.visibleByUser(reviewer) and
self._acceptable_to_give_visibility(source, reviewer)):
@@ -670,6 +765,10 @@
def getUnlandedSourceBranchRevisions(self):
"""See `IBranchMergeProposal`."""
+ if self.source_branch is None:
+ # XXX cjwatson 2015-04-16: Implement for Git somehow, perhaps by
+ # calling turnip via memcached.
+ return []
store = Store.of(self)
source = SQL("""source AS (SELECT BranchRevision.branch,
BranchRevision.revision, Branchrevision.sequence FROM
@@ -867,6 +966,9 @@
def generateIncrementalDiff(self, old_revision, new_revision, diff=None):
"""See `IBranchMergeProposal`."""
+ if self.source_branch is None:
+ # XXX cjwatson 2015-04-16: Implement for Git.
+ return
if diff is None:
source_branch = self.source_branch.getBzrBranch()
ignore_branches = [self.target_branch.getBzrBranch()]
@@ -910,6 +1012,9 @@
return None
def _getNewerRevisions(self):
+ if self.source_branch is None:
+ # XXX cjwatson 2015-04-16: Implement for Git.
+ return EmptyResultSet()
start_date = self.date_review_requested
if start_date is None:
start_date = self.date_created
@@ -955,7 +1060,10 @@
person_ids = set()
for mp in branch_merge_proposals:
ids.add(mp.id)
- source_branch_ids.add(mp.source_branchID)
+ if mp.source_branchID is not None:
+ source_branch_ids.add(mp.source_branchID)
+ # XXX cjwatson 2015-04-22: Implement for Git once GitCollection
+ # supports MPs.
person_ids.add(mp.registrantID)
person_ids.add(mp.merge_reporterID)
@@ -1107,9 +1215,18 @@
return result
@staticmethod
- def activeProposalsForBranches(source_branch, target_branch):
- return BranchMergeProposal.select("""
- BranchMergeProposal.source_branch = %s AND
- BranchMergeProposal.target_branch = %s AND
- BranchMergeProposal.queue_status NOT IN %s
- """ % sqlvalues(source_branch, target_branch, FINAL_STATES))
+ def activeProposalsForBranches(source, target):
+ clauses = [Not(BranchMergeProposal.queue_status.is_in(FINAL_STATES))]
+ if IGitRef.providedBy(source):
+ clauses.extend([
+ BranchMergeProposal.source_git_repository == source.repository,
+ BranchMergeProposal.source_git_path == source.path,
+ BranchMergeProposal.target_git_repository == target.repository,
+ BranchMergeProposal.target_git_path == target.path,
+ ])
+ else:
+ clauses.extend([
+ BranchMergeProposal.source_branch == source,
+ BranchMergeProposal.target_branch == target,
+ ])
+ return IStore(BranchMergeProposal).find(BranchMergeProposal, *clauses)
=== modified file 'lib/lp/code/model/gitref.py'
--- lib/lp/code/model/gitref.py 2015-04-21 10:48:06 +0000
+++ lib/lp/code/model/gitref.py 2015-04-22 15:02:31 +0000
@@ -7,19 +7,28 @@
'GitRefFrozen',
]
+from urllib import quote_plus
+
import pytz
from storm.locals import (
DateTime,
Int,
+ Not,
Reference,
+ Store,
Unicode,
)
from zope.interface import implements
from lp.app.errors import NotFoundError
from lp.code.enums import GitObjectType
+from lp.code.interfaces.branchmergeproposal import (
+ BRANCH_MERGE_PROPOSAL_FINAL_STATES,
+ )
from lp.code.interfaces.gitref import IGitRef
+from lp.code.model.branchmergeproposal import BranchMergeProposal
from lp.services.database.enumcol import EnumCol
+from lp.services.database.interfaces import IStore
from lp.services.database.stormbase import StormBase
@@ -64,6 +73,50 @@
return self.repository.target
@property
+ def namespace(self):
+ """See `IGitRef`."""
+ return self.repository.namespace
+
+ def getCodebrowseUrl(self):
+ """See `IGitRef`."""
+ return "%s?h=%s" % (
+ self.repository.getCodebrowseUrl(), quote_plus(self.name))
+
+ @property
+ def information_type(self):
+ """See `IGitRef`."""
+ return self.repository.information_type
+
+ def visibleByUser(self, user):
+ """See `IGitRef`."""
+ return self.repository.visibleByUser(user)
+
+ @property
+ def reviewer(self):
+ """See `IGitRef`."""
+ # XXX cjwatson 2015-04-17: We should have ref-pattern-specific
+ # reviewers.
+ return self.repository.reviewer
+
+ @property
+ def code_reviewer(self):
+ """See `IGitRef`."""
+ # XXX cjwatson 2015-04-17: We should have ref-pattern-specific
+ # reviewers.
+ return self.repository.code_reviewer
+
+ def isPersonTrustedReviewer(self, reviewer):
+ """See `IGitRef`."""
+ # XXX cjwatson 2015-04-17: We should have ref-pattern-specific
+ # reviewers.
+ return self.repository.isPersonTrustedReviewer(reviewer)
+
+ @property
+ def subscriptions(self):
+ """See `IGitRef`."""
+ return self.repository.subscriptions
+
+ @property
def subscribers(self):
"""See `IGitRef`."""
return self.repository.subscribers
@@ -83,6 +136,34 @@
"""See `IGitRef`."""
return self.repository.getNotificationRecipients()
+ @property
+ def landing_targets(self):
+ """See `IGitRef`."""
+ return Store.of(self).find(
+ BranchMergeProposal,
+ BranchMergeProposal.source_git_repository == self.repository,
+ BranchMergeProposal.source_git_path == self.path)
+
+ @property
+ def landing_candidates(self):
+ """See `IGitRef`."""
+ return Store.of(self).find(
+ BranchMergeProposal,
+ BranchMergeProposal.target_git_repository == self.repository,
+ BranchMergeProposal.target_git_path == self.path,
+ Not(BranchMergeProposal.queue_status.is_in(
+ BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
+
+ @property
+ def dependent_landings(self):
+ """See `IGitRef`."""
+ return Store.of(self).find(
+ BranchMergeProposal,
+ BranchMergeProposal.prerequisite_git_repository == self.repository,
+ BranchMergeProposal.prerequisite_git_path == self.path,
+ Not(BranchMergeProposal.queue_status.is_in(
+ BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
+
class GitRef(StormBase, GitRefMixin):
"""See `IGitRef`."""
=== modified file 'lib/lp/code/stories/webservice/xx-branchmergeproposal.txt'
--- lib/lp/code/stories/webservice/xx-branchmergeproposal.txt 2015-04-19 12:56:32 +0000
+++ lib/lp/code/stories/webservice/xx-branchmergeproposal.txt 2015-04-22 15:02:31 +0000
@@ -54,8 +54,11 @@
date_reviewed: None
description: u'Merge\nit!'
merge_reporter_link: None
+ merged_revision_id: None
merged_revno: None
prerequisite_branch_link: u'http://api.launchpad.dev/devel/~...'
+ prerequisite_git_path: None
+ prerequisite_git_repository_link: None
preview_diff_link: None
preview_diffs_collection_link: u'http://.../preview_diffs'
private: False
@@ -67,9 +70,13 @@
reviewer_link: None
self_link: u'http://api.launchpad.dev/devel/~.../+merge/...'
source_branch_link: u'http://api.launchpad.dev/devel/~...'
+ source_git_path: None
+ source_git_repository_link: None
superseded_by_link: None
supersedes_link: None
target_branch_link: u'http://api.launchpad.dev/devel/~...'
+ target_git_path: None
+ target_git_repository_link: None
votes_collection_link:
u'http://api.launchpad.dev/devel/~.../+merge/.../votes'
web_link: u'http://code.../~.../+merge/...'
@@ -149,8 +156,11 @@
date_reviewed: None
description: None
merge_reporter_link: None
+ merged_revision_id: None
merged_revno: None
prerequisite_branch_link: None
+ prerequisite_git_path: None
+ prerequisite_git_repository_link: None
preview_diff_link: None
preview_diffs_collection_link: u'http://.../preview_diffs'
private: False
@@ -161,9 +171,13 @@
reviewer_link: None
self_link: u'http://.../~source/fooix/fix-it/+merge/...'
source_branch_link: u'http://.../~source/fooix/fix-it'
+ source_git_path: None
+ source_git_repository_link: None
superseded_by_link: None
supersedes_link: None
target_branch_link: u'http://.../~target/fooix/trunk'
+ target_git_path: None
+ target_git_repository_link: None
votes_collection_link: u'http://.../~source/fooix/fix-it/+merge/.../votes'
web_link: u'http://code.../~source/fooix/fix-it/+merge/...'
=== modified file 'lib/lp/security.py'
--- lib/lp/security.py 2015-04-21 16:53:45 +0000
+++ lib/lp/security.py 2015-04-22 15:02:31 +0000
@@ -2313,24 +2313,42 @@
required.append(self.obj.prerequisite_branch)
return required
+ @property
+ def git_repositories(self):
+ required = [
+ self.obj.source_git_repository, self.obj.target_git_repository]
+ if self.obj.prerequisite_git_repository:
+ required.append(self.obj.prerequisite_git_repository)
+ return required
+
def checkAuthenticated(self, user):
"""Is the user able to view the branch merge proposal?
The user can see a merge proposal if they can see the source, target
and prerequisite branches.
"""
- return all(map(
- lambda b: AccessBranch(b).checkAuthenticated(user),
- self.branches))
+ if self.obj.source_git_repository is not None:
+ return all(map(
+ lambda r: ViewGitRepository(r).checkAuthenticated(user),
+ self.git_repositories))
+ else:
+ return all(map(
+ lambda b: AccessBranch(b).checkAuthenticated(user),
+ self.branches))
def checkUnauthenticated(self):
"""Is anyone able to view the branch merge proposal?
Anyone can see a merge proposal between two public branches.
"""
- return all(map(
- lambda b: AccessBranch(b).checkUnauthenticated(),
- self.branches))
+ if self.obj.source_git_repository is not None:
+ return all(map(
+ lambda r: ViewGitRepository(r).checkUnauthenticated(),
+ self.git_repositories))
+ else:
+ return all(map(
+ lambda b: AccessBranch(b).checkUnauthenticated(),
+ self.branches))
class PreviewDiffView(DelegatedAuthorization):
Follow ups