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 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
1=== modified file 'lib/lp/_schema_circular_imports.py'
2--- lib/lp/_schema_circular_imports.py 2018-05-01 16:08:03 +0000
3+++ lib/lp/_schema_circular_imports.py 2018-08-29 17:12:28 +0000
4@@ -521,6 +521,8 @@
5 IGitRepository, '_api_landing_candidates', IBranchMergeProposal)
6 patch_collection_property(
7 IGitRepository, 'dependent_landings', IBranchMergeProposal)
8+patch_collection_return_type(
9+ IGitRepository, 'getMergeProposals', IBranchMergeProposal)
10
11 # ILiveFSFile
12 patch_reference_property(ILiveFSFile, 'livefsbuild', ILiveFSBuild)
13
14=== modified file 'lib/lp/code/browser/configure.zcml'
15--- lib/lp/code/browser/configure.zcml 2018-08-20 23:33:01 +0000
16+++ lib/lp/code/browser/configure.zcml 2018-08-29 17:12:28 +0000
17@@ -819,6 +819,9 @@
18 name="++repository-recipes"
19 template="../templates/gitrepository-recipes.pt"/>
20 <browser:page
21+ name="++repository-pending-merges"
22+ template="../templates/gitrepository-pending-merges.pt"/>
23+ <browser:page
24 name="++repository-import-details"
25 template="../templates/import-details.pt"/>
26 </browser:pages>
27@@ -902,6 +905,13 @@
28 permission="launchpad.AnyAllowedPerson"
29 name="+try-again"
30 template="../templates/inline-form-only-buttons.pt"/>
31+ <browser:page
32+ for="lp.code.interfaces.gitrepository.IGitRepository"
33+ class="lp.code.browser.branchmergeproposallisting.BranchActiveReviewsView"
34+ permission="zope.Public"
35+ name="+activereviews"
36+ template="../templates/active-reviews.pt"/>
37+
38 <adapter
39 provides="lp.services.webapp.interfaces.IBreadcrumb"
40 for="lp.code.interfaces.gitrepository.IGitRepository"
41
42=== modified file 'lib/lp/code/browser/gitrepository.py'
43--- lib/lp/code/browser/gitrepository.py 2018-08-24 16:52:27 +0000
44+++ lib/lp/code/browser/gitrepository.py 2018-08-29 17:12:28 +0000
45@@ -57,6 +57,9 @@
46 from lp.app.vocabularies import InformationTypeVocabulary
47 from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
48 from lp.code.browser.branch import CodeEditOwnerMixin
49+from lp.code.browser.branchmergeproposal import (
50+ latest_proposals_for_each_branch,
51+ )
52 from lp.code.browser.codeimport import CodeImportTargetMixin
53 from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
54 from lp.code.browser.widgets.gitrepositorytarget import (
55@@ -316,6 +319,7 @@
56 return self.context.display_name
57
58 label = page_title
59+ show_merge_links = True
60
61 def initialize(self):
62 super(GitRepositoryView, self).initialize()
63@@ -369,6 +373,31 @@
64 """Is this an imported repository?"""
65 return self.context.repository_type == GitRepositoryType.IMPORTED
66
67+ @cachedproperty
68+ def landing_candidates(self):
69+ candidates = self.context.getPrecachedLandingCandidates(self.user)
70+ return [proposal for proposal in candidates
71+ if check_permission("launchpad.View", proposal)]
72+
73+ def _getBranchCountText(self, count):
74+ """Help to show user friendly text."""
75+ if count == 0:
76+ return 'No branches'
77+ elif count == 1:
78+ return '1 branch'
79+ else:
80+ return '%s branches' % count
81+
82+ @cachedproperty
83+ def landing_candidate_count_text(self):
84+ return self._getBranchCountText(len(self.landing_candidates))
85+
86+ @cachedproperty
87+ def landing_targets(self):
88+ """Return a filtered list of landing targets."""
89+ targets = self.context.getPrecachedLandingTargets(self.user)
90+ return latest_proposals_for_each_branch(targets)
91+
92
93 class GitRepositoryEditFormView(LaunchpadEditFormView):
94 """Base class for forms that edit a Git repository."""
95
96=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
97--- lib/lp/code/browser/tests/test_gitrepository.py 2018-08-24 16:52:27 +0000
98+++ lib/lp/code/browser/tests/test_gitrepository.py 2018-08-29 17:12:28 +0000
99@@ -24,10 +24,16 @@
100 from lp.app.enums import InformationType
101 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
102 from lp.app.interfaces.services import IService
103-from lp.code.enums import GitRepositoryType
104+from lp.code.enums import (
105+ BranchMergeProposalStatus,
106+ GitRepositoryType,
107+ )
108 from lp.code.interfaces.revision import IRevisionSet
109 from lp.code.tests.helpers import GitHostingFixture
110-from lp.registry.enums import BranchSharingPolicy
111+from lp.registry.enums import (
112+ BranchSharingPolicy,
113+ VCSType,
114+ )
115 from lp.registry.interfaces.accesspolicy import IAccessPolicySource
116 from lp.registry.interfaces.person import PersonVisibility
117 from lp.services.beautifulsoup import BeautifulSoup
118@@ -246,6 +252,47 @@
119 browser = self.getUserBrowser(url, user=user)
120 self.assertIn(project_name, browser.contents)
121
122+ def test_view_with_active_reviews(self):
123+ repository = self.factory.makeGitRepository()
124+ git_refs = self.factory.makeGitRefs(
125+ repository,
126+ paths=["refs/heads/master", "refs/heads/1.0", "refs/tags/1.1"])
127+ self.factory.makeBranchMergeProposalForGit(
128+ target_ref=git_refs[0],
129+ set_state=BranchMergeProposalStatus.NEEDS_REVIEW)
130+ with person_logged_in(repository.owner):
131+ browser = self.getViewBrowser(repository)
132+ self.assertIsNotNone(
133+ find_tag_by_id(browser.contents, 'landing-candidates'))
134+
135+ def test_landing_candidate_count(self):
136+ source_repository = self.factory.makeGitRepository()
137+ view = create_initialized_view(source_repository, '+index')
138+
139+ self.assertEqual('No branches', view._getBranchCountText(0))
140+ self.assertEqual('1 branch', view._getBranchCountText(1))
141+ self.assertEqual('2 branches', view._getBranchCountText(2))
142+
143+ def test_view_with_landing_targets(self):
144+ product = self.factory.makeProduct(name="foo", vcs=VCSType.GIT)
145+ target_repository = self.factory.makeGitRepository(target=product)
146+ source_repository = self.factory.makeGitRepository(target=product)
147+ target_git_refs = self.factory.makeGitRefs(
148+ target_repository,
149+ paths=["refs/heads/master", "refs/heads/1.0", "refs/tags/1.1"])
150+ source_git_refs = self.factory.makeGitRefs(
151+ source_repository,
152+ paths=["refs/heads/master"])
153+ self.factory.makeBranchMergeProposalForGit(
154+ target_ref=target_git_refs[0],
155+ source_ref=source_git_refs[0],
156+ set_state=BranchMergeProposalStatus.NEEDS_REVIEW)
157+ with person_logged_in(target_repository.owner):
158+ browser = self.getViewBrowser(
159+ source_repository, user=source_repository.owner)
160+ self.assertIsNotNone(
161+ find_tag_by_id(browser.contents, 'landing-targets'))
162+
163
164 class TestGitRepositoryViewPrivateArtifacts(BrowserTestCase):
165 """Tests that Git repositories with private team artifacts can be viewed.
166
167=== modified file 'lib/lp/code/interfaces/gitrepository.py'
168--- lib/lp/code/interfaces/gitrepository.py 2017-11-06 09:32:45 +0000
169+++ lib/lp/code/interfaces/gitrepository.py 2018-08-29 17:12:28 +0000
170@@ -58,6 +58,7 @@
171 from lp.app.enums import InformationType
172 from lp.app.validators import LaunchpadValidationError
173 from lp.code.enums import (
174+ BranchMergeProposalStatus,
175 BranchSubscriptionDiffSize,
176 BranchSubscriptionNotificationLevel,
177 CodeReviewNotificationLevel,
178@@ -531,6 +532,21 @@
179 Source and prerequisite repositories are preloaded.
180 """
181
182+ @operation_parameters(
183+ status=List(
184+ title=_("A list of merge proposal statuses to filter by."),
185+ value_type=Choice(vocabulary=BranchMergeProposalStatus)),
186+ merged_revision_ids=List(TextLine(
187+ title=_('The target revision ID of the merge.'))))
188+ @call_with(visible_by_user=REQUEST_USER)
189+ # Really IBranchMergeProposal, patched in _schema_circular_imports.py.
190+ @operation_returns_collection_of(Interface)
191+ @export_read_operation()
192+ @operation_for_version("devel")
193+ def getMergeProposals(status=None, visible_by_user=None,
194+ merged_revision_ids=None, eager_load=False):
195+ """Return matching BranchMergeProposals."""
196+
197 def getMergeProposalByID(id):
198 """Return this repository's merge proposal with this id, or None."""
199
200
201=== modified file 'lib/lp/code/model/gitrepository.py'
202--- lib/lp/code/model/gitrepository.py 2018-07-24 11:57:33 +0000
203+++ lib/lp/code/model/gitrepository.py 2018-08-29 17:12:28 +0000
204@@ -888,7 +888,7 @@
205 BranchMergeProposal.source_git_repository == self)
206
207 def getPrecachedLandingTargets(self, user):
208- """See `IGitRef`."""
209+ """See `IGitRepository`."""
210 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
211 return DecoratedResultSet(self.landing_targets, pre_iter_hook=loader)
212
213@@ -915,7 +915,7 @@
214 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
215
216 def getPrecachedLandingCandidates(self, user):
217- """See `IGitRef`."""
218+ """See `IGitRepository`."""
219 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
220 return DecoratedResultSet(
221 self.landing_candidates, pre_iter_hook=loader)
222@@ -942,6 +942,21 @@
223 Not(BranchMergeProposal.queue_status.is_in(
224 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
225
226+ def getMergeProposals(self, status=None, visible_by_user=None,
227+ merged_revision_ids=None, eager_load=False):
228+ """See `IGitRepository`."""
229+ if not status:
230+ status = (
231+ BranchMergeProposalStatus.CODE_APPROVED,
232+ BranchMergeProposalStatus.NEEDS_REVIEW,
233+ BranchMergeProposalStatus.WORK_IN_PROGRESS)
234+
235+ collection = getUtility(IAllGitRepositories).visibleByUser(
236+ visible_by_user)
237+ return collection.getMergeProposals(
238+ status, target_repository=self,
239+ merged_revision_ids=merged_revision_ids, eager_load=eager_load)
240+
241 def getMergeProposalByID(self, id):
242 """See `IGitRepository`."""
243 return self.landing_targets.find(BranchMergeProposal.id == id).one()
244
245=== modified file 'lib/lp/code/templates/gitrepository-index.pt'
246--- lib/lp/code/templates/gitrepository-index.pt 2018-01-26 12:01:02 +0000
247+++ lib/lp/code/templates/gitrepository-index.pt 2018-08-29 17:12:28 +0000
248@@ -44,6 +44,13 @@
249 </div>
250 </div>
251
252+ <div class="yui-g first">
253+ <div id="repository-relations" class="portlet">
254+ <tal:repository-pending-merges
255+ replace="structure context/@@++repository-pending-merges" />
256+ </div>
257+ </div>
258+
259 <div class="yui-g">
260 <div id="repository-relations" class="portlet">
261 <tal:repository-recipes
262
263=== added file 'lib/lp/code/templates/gitrepository-pending-merges.pt'
264--- lib/lp/code/templates/gitrepository-pending-merges.pt 1970-01-01 00:00:00 +0000
265+++ lib/lp/code/templates/gitrepository-pending-merges.pt 2018-08-29 17:12:28 +0000
266@@ -0,0 +1,35 @@
267+<div
268+ xmlns:tal="http://xml.zope.org/namespaces/tal"
269+ xmlns:metal="http://xml.zope.org/namespaces/metal"
270+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
271+ tal:define="
272+ context_menu view/context/menu:context;
273+ features request/features"
274+ tal:condition="view/show_merge_links">
275+
276+ <h3>Proposed merges</h3>
277+ <div id="merge-links"
278+ class="actions">
279+ <div id="merge-summary">
280+
281+ <div id="landing-candidates"
282+ tal:condition="view/landing_candidates">
283+ <img src="/@@/merge-proposal-icon" />
284+ <a href="+activereviews" tal:content="structure view/landing_candidate_count_text">
285+ 1 branch
286+ </a>
287+ proposed for merging into this repository.
288+
289+ </div>
290+
291+ <div id="landing-targets" tal:condition="view/landing_targets">
292+ <tal:landing-candidates repeat="mergeproposal view/landing_targets">
293+ <tal:merge-fragment
294+ tal:replace="structure mergeproposal/@@+summary-fragment"/>
295+ </tal:landing-candidates>
296+ </div>
297+
298+ </div>
299+ </div>
300+
301+</div>