Merge lp:~twom/launchpad/git-active-review-on-more-git-repo into lp:launchpad

Proposed by Tom Wardill
Status: Merged
Merged at revision: 18763
Proposed branch: lp:~twom/launchpad/git-active-review-on-more-git-repo
Merge into: lp:launchpad
Diff against target: 301 lines (+165/-4)
8 files modified
lib/lp/_schema_circular_imports.py (+2/-0)
lib/lp/code/browser/configure.zcml (+10/-0)
lib/lp/code/browser/gitrepository.py (+29/-0)
lib/lp/code/browser/tests/test_gitrepository.py (+49/-2)
lib/lp/code/interfaces/gitrepository.py (+16/-0)
lib/lp/code/model/gitrepository.py (+17/-2)
lib/lp/code/templates/gitrepository-index.pt (+7/-0)
lib/lp/code/templates/gitrepository-pending-merges.pt (+35/-0)
To merge this branch: bzr merge lp:~twom/launchpad/git-active-review-on-more-git-repo
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+353884@code.launchpad.net

Commit message

Add available review targets and proposals to the git repository overview page

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Needs Fixing
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Colin Watson (cjwatson) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/_schema_circular_imports.py'
--- lib/lp/_schema_circular_imports.py 2018-05-01 16:08:03 +0000
+++ lib/lp/_schema_circular_imports.py 2018-08-29 17:12:28 +0000
@@ -521,6 +521,8 @@
521 IGitRepository, '_api_landing_candidates', IBranchMergeProposal)521 IGitRepository, '_api_landing_candidates', IBranchMergeProposal)
522patch_collection_property(522patch_collection_property(
523 IGitRepository, 'dependent_landings', IBranchMergeProposal)523 IGitRepository, 'dependent_landings', IBranchMergeProposal)
524patch_collection_return_type(
525 IGitRepository, 'getMergeProposals', IBranchMergeProposal)
524526
525# ILiveFSFile527# ILiveFSFile
526patch_reference_property(ILiveFSFile, 'livefsbuild', ILiveFSBuild)528patch_reference_property(ILiveFSFile, 'livefsbuild', ILiveFSBuild)
527529
=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml 2018-08-20 23:33:01 +0000
+++ lib/lp/code/browser/configure.zcml 2018-08-29 17:12:28 +0000
@@ -819,6 +819,9 @@
819 name="++repository-recipes"819 name="++repository-recipes"
820 template="../templates/gitrepository-recipes.pt"/>820 template="../templates/gitrepository-recipes.pt"/>
821 <browser:page821 <browser:page
822 name="++repository-pending-merges"
823 template="../templates/gitrepository-pending-merges.pt"/>
824 <browser:page
822 name="++repository-import-details"825 name="++repository-import-details"
823 template="../templates/import-details.pt"/>826 template="../templates/import-details.pt"/>
824 </browser:pages>827 </browser:pages>
@@ -902,6 +905,13 @@
902 permission="launchpad.AnyAllowedPerson"905 permission="launchpad.AnyAllowedPerson"
903 name="+try-again"906 name="+try-again"
904 template="../templates/inline-form-only-buttons.pt"/>907 template="../templates/inline-form-only-buttons.pt"/>
908 <browser:page
909 for="lp.code.interfaces.gitrepository.IGitRepository"
910 class="lp.code.browser.branchmergeproposallisting.BranchActiveReviewsView"
911 permission="zope.Public"
912 name="+activereviews"
913 template="../templates/active-reviews.pt"/>
914
905 <adapter915 <adapter
906 provides="lp.services.webapp.interfaces.IBreadcrumb"916 provides="lp.services.webapp.interfaces.IBreadcrumb"
907 for="lp.code.interfaces.gitrepository.IGitRepository"917 for="lp.code.interfaces.gitrepository.IGitRepository"
908918
=== modified file 'lib/lp/code/browser/gitrepository.py'
--- lib/lp/code/browser/gitrepository.py 2018-08-24 16:52:27 +0000
+++ lib/lp/code/browser/gitrepository.py 2018-08-29 17:12:28 +0000
@@ -57,6 +57,9 @@
57from lp.app.vocabularies import InformationTypeVocabulary57from lp.app.vocabularies import InformationTypeVocabulary
58from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription58from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
59from lp.code.browser.branch import CodeEditOwnerMixin59from lp.code.browser.branch import CodeEditOwnerMixin
60from lp.code.browser.branchmergeproposal import (
61 latest_proposals_for_each_branch,
62 )
60from lp.code.browser.codeimport import CodeImportTargetMixin63from lp.code.browser.codeimport import CodeImportTargetMixin
61from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin64from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
62from lp.code.browser.widgets.gitrepositorytarget import (65from lp.code.browser.widgets.gitrepositorytarget import (
@@ -316,6 +319,7 @@
316 return self.context.display_name319 return self.context.display_name
317320
318 label = page_title321 label = page_title
322 show_merge_links = True
319323
320 def initialize(self):324 def initialize(self):
321 super(GitRepositoryView, self).initialize()325 super(GitRepositoryView, self).initialize()
@@ -369,6 +373,31 @@
369 """Is this an imported repository?"""373 """Is this an imported repository?"""
370 return self.context.repository_type == GitRepositoryType.IMPORTED374 return self.context.repository_type == GitRepositoryType.IMPORTED
371375
376 @cachedproperty
377 def landing_candidates(self):
378 candidates = self.context.getPrecachedLandingCandidates(self.user)
379 return [proposal for proposal in candidates
380 if check_permission("launchpad.View", proposal)]
381
382 def _getBranchCountText(self, count):
383 """Help to show user friendly text."""
384 if count == 0:
385 return 'No branches'
386 elif count == 1:
387 return '1 branch'
388 else:
389 return '%s branches' % count
390
391 @cachedproperty
392 def landing_candidate_count_text(self):
393 return self._getBranchCountText(len(self.landing_candidates))
394
395 @cachedproperty
396 def landing_targets(self):
397 """Return a filtered list of landing targets."""
398 targets = self.context.getPrecachedLandingTargets(self.user)
399 return latest_proposals_for_each_branch(targets)
400
372401
373class GitRepositoryEditFormView(LaunchpadEditFormView):402class GitRepositoryEditFormView(LaunchpadEditFormView):
374 """Base class for forms that edit a Git repository."""403 """Base class for forms that edit a Git repository."""
375404
=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
--- lib/lp/code/browser/tests/test_gitrepository.py 2018-08-24 16:52:27 +0000
+++ lib/lp/code/browser/tests/test_gitrepository.py 2018-08-29 17:12:28 +0000
@@ -24,10 +24,16 @@
24from lp.app.enums import InformationType24from lp.app.enums import InformationType
25from lp.app.interfaces.launchpad import ILaunchpadCelebrities25from lp.app.interfaces.launchpad import ILaunchpadCelebrities
26from lp.app.interfaces.services import IService26from lp.app.interfaces.services import IService
27from lp.code.enums import GitRepositoryType27from lp.code.enums import (
28 BranchMergeProposalStatus,
29 GitRepositoryType,
30 )
28from lp.code.interfaces.revision import IRevisionSet31from lp.code.interfaces.revision import IRevisionSet
29from lp.code.tests.helpers import GitHostingFixture32from lp.code.tests.helpers import GitHostingFixture
30from lp.registry.enums import BranchSharingPolicy33from lp.registry.enums import (
34 BranchSharingPolicy,
35 VCSType,
36 )
31from lp.registry.interfaces.accesspolicy import IAccessPolicySource37from lp.registry.interfaces.accesspolicy import IAccessPolicySource
32from lp.registry.interfaces.person import PersonVisibility38from lp.registry.interfaces.person import PersonVisibility
33from lp.services.beautifulsoup import BeautifulSoup39from lp.services.beautifulsoup import BeautifulSoup
@@ -246,6 +252,47 @@
246 browser = self.getUserBrowser(url, user=user)252 browser = self.getUserBrowser(url, user=user)
247 self.assertIn(project_name, browser.contents)253 self.assertIn(project_name, browser.contents)
248254
255 def test_view_with_active_reviews(self):
256 repository = self.factory.makeGitRepository()
257 git_refs = self.factory.makeGitRefs(
258 repository,
259 paths=["refs/heads/master", "refs/heads/1.0", "refs/tags/1.1"])
260 self.factory.makeBranchMergeProposalForGit(
261 target_ref=git_refs[0],
262 set_state=BranchMergeProposalStatus.NEEDS_REVIEW)
263 with person_logged_in(repository.owner):
264 browser = self.getViewBrowser(repository)
265 self.assertIsNotNone(
266 find_tag_by_id(browser.contents, 'landing-candidates'))
267
268 def test_landing_candidate_count(self):
269 source_repository = self.factory.makeGitRepository()
270 view = create_initialized_view(source_repository, '+index')
271
272 self.assertEqual('No branches', view._getBranchCountText(0))
273 self.assertEqual('1 branch', view._getBranchCountText(1))
274 self.assertEqual('2 branches', view._getBranchCountText(2))
275
276 def test_view_with_landing_targets(self):
277 product = self.factory.makeProduct(name="foo", vcs=VCSType.GIT)
278 target_repository = self.factory.makeGitRepository(target=product)
279 source_repository = self.factory.makeGitRepository(target=product)
280 target_git_refs = self.factory.makeGitRefs(
281 target_repository,
282 paths=["refs/heads/master", "refs/heads/1.0", "refs/tags/1.1"])
283 source_git_refs = self.factory.makeGitRefs(
284 source_repository,
285 paths=["refs/heads/master"])
286 self.factory.makeBranchMergeProposalForGit(
287 target_ref=target_git_refs[0],
288 source_ref=source_git_refs[0],
289 set_state=BranchMergeProposalStatus.NEEDS_REVIEW)
290 with person_logged_in(target_repository.owner):
291 browser = self.getViewBrowser(
292 source_repository, user=source_repository.owner)
293 self.assertIsNotNone(
294 find_tag_by_id(browser.contents, 'landing-targets'))
295
249296
250class TestGitRepositoryViewPrivateArtifacts(BrowserTestCase):297class TestGitRepositoryViewPrivateArtifacts(BrowserTestCase):
251 """Tests that Git repositories with private team artifacts can be viewed.298 """Tests that Git repositories with private team artifacts can be viewed.
252299
=== modified file 'lib/lp/code/interfaces/gitrepository.py'
--- lib/lp/code/interfaces/gitrepository.py 2017-11-06 09:32:45 +0000
+++ lib/lp/code/interfaces/gitrepository.py 2018-08-29 17:12:28 +0000
@@ -58,6 +58,7 @@
58from lp.app.enums import InformationType58from lp.app.enums import InformationType
59from lp.app.validators import LaunchpadValidationError59from lp.app.validators import LaunchpadValidationError
60from lp.code.enums import (60from lp.code.enums import (
61 BranchMergeProposalStatus,
61 BranchSubscriptionDiffSize,62 BranchSubscriptionDiffSize,
62 BranchSubscriptionNotificationLevel,63 BranchSubscriptionNotificationLevel,
63 CodeReviewNotificationLevel,64 CodeReviewNotificationLevel,
@@ -531,6 +532,21 @@
531 Source and prerequisite repositories are preloaded.532 Source and prerequisite repositories are preloaded.
532 """533 """
533534
535 @operation_parameters(
536 status=List(
537 title=_("A list of merge proposal statuses to filter by."),
538 value_type=Choice(vocabulary=BranchMergeProposalStatus)),
539 merged_revision_ids=List(TextLine(
540 title=_('The target revision ID of the merge.'))))
541 @call_with(visible_by_user=REQUEST_USER)
542 # Really IBranchMergeProposal, patched in _schema_circular_imports.py.
543 @operation_returns_collection_of(Interface)
544 @export_read_operation()
545 @operation_for_version("devel")
546 def getMergeProposals(status=None, visible_by_user=None,
547 merged_revision_ids=None, eager_load=False):
548 """Return matching BranchMergeProposals."""
549
534 def getMergeProposalByID(id):550 def getMergeProposalByID(id):
535 """Return this repository's merge proposal with this id, or None."""551 """Return this repository's merge proposal with this id, or None."""
536552
537553
=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py 2018-07-24 11:57:33 +0000
+++ lib/lp/code/model/gitrepository.py 2018-08-29 17:12:28 +0000
@@ -888,7 +888,7 @@
888 BranchMergeProposal.source_git_repository == self)888 BranchMergeProposal.source_git_repository == self)
889889
890 def getPrecachedLandingTargets(self, user):890 def getPrecachedLandingTargets(self, user):
891 """See `IGitRef`."""891 """See `IGitRepository`."""
892 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)892 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
893 return DecoratedResultSet(self.landing_targets, pre_iter_hook=loader)893 return DecoratedResultSet(self.landing_targets, pre_iter_hook=loader)
894894
@@ -915,7 +915,7 @@
915 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))915 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
916916
917 def getPrecachedLandingCandidates(self, user):917 def getPrecachedLandingCandidates(self, user):
918 """See `IGitRef`."""918 """See `IGitRepository`."""
919 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)919 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
920 return DecoratedResultSet(920 return DecoratedResultSet(
921 self.landing_candidates, pre_iter_hook=loader)921 self.landing_candidates, pre_iter_hook=loader)
@@ -942,6 +942,21 @@
942 Not(BranchMergeProposal.queue_status.is_in(942 Not(BranchMergeProposal.queue_status.is_in(
943 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))943 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
944944
945 def getMergeProposals(self, status=None, visible_by_user=None,
946 merged_revision_ids=None, eager_load=False):
947 """See `IGitRepository`."""
948 if not status:
949 status = (
950 BranchMergeProposalStatus.CODE_APPROVED,
951 BranchMergeProposalStatus.NEEDS_REVIEW,
952 BranchMergeProposalStatus.WORK_IN_PROGRESS)
953
954 collection = getUtility(IAllGitRepositories).visibleByUser(
955 visible_by_user)
956 return collection.getMergeProposals(
957 status, target_repository=self,
958 merged_revision_ids=merged_revision_ids, eager_load=eager_load)
959
945 def getMergeProposalByID(self, id):960 def getMergeProposalByID(self, id):
946 """See `IGitRepository`."""961 """See `IGitRepository`."""
947 return self.landing_targets.find(BranchMergeProposal.id == id).one()962 return self.landing_targets.find(BranchMergeProposal.id == id).one()
948963
=== modified file 'lib/lp/code/templates/gitrepository-index.pt'
--- lib/lp/code/templates/gitrepository-index.pt 2018-01-26 12:01:02 +0000
+++ lib/lp/code/templates/gitrepository-index.pt 2018-08-29 17:12:28 +0000
@@ -44,6 +44,13 @@
44 </div>44 </div>
45 </div>45 </div>
4646
47 <div class="yui-g first">
48 <div id="repository-relations" class="portlet">
49 <tal:repository-pending-merges
50 replace="structure context/@@++repository-pending-merges" />
51 </div>
52 </div>
53
47 <div class="yui-g">54 <div class="yui-g">
48 <div id="repository-relations" class="portlet">55 <div id="repository-relations" class="portlet">
49 <tal:repository-recipes56 <tal:repository-recipes
5057
=== added file 'lib/lp/code/templates/gitrepository-pending-merges.pt'
--- lib/lp/code/templates/gitrepository-pending-merges.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/code/templates/gitrepository-pending-merges.pt 2018-08-29 17:12:28 +0000
@@ -0,0 +1,35 @@
1<div
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 tal:define="
6 context_menu view/context/menu:context;
7 features request/features"
8 tal:condition="view/show_merge_links">
9
10 <h3>Proposed merges</h3>
11 <div id="merge-links"
12 class="actions">
13 <div id="merge-summary">
14
15 <div id="landing-candidates"
16 tal:condition="view/landing_candidates">
17 <img src="/@@/merge-proposal-icon" />
18 <a href="+activereviews" tal:content="structure view/landing_candidate_count_text">
19 1 branch
20 </a>
21 proposed for merging into this repository.
22
23 </div>
24
25 <div id="landing-targets" tal:condition="view/landing_targets">
26 <tal:landing-candidates repeat="mergeproposal view/landing_targets">
27 <tal:merge-fragment
28 tal:replace="structure mergeproposal/@@+summary-fragment"/>
29 </tal:landing-candidates>
30 </div>
31
32 </div>
33 </div>
34
35</div>