launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24148
[Merge] ~cjwatson/launchpad:oci-git-listing into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:oci-git-listing into launchpad:master with ~cjwatson/launchpad:oci-git-owner-default as a prerequisite.
Commit message:
Add git listing views for OCI projects
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1847444 in Launchpad itself: "Support OCI image building"
https://bugs.launchpad.net/launchpad/+bug/1847444
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/376146
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:oci-git-listing into launchpad:master.
diff --git a/lib/lp/code/browser/configure.zcml b/lib/lp/code/browser/configure.zcml
index cbcd9ea..499a86a 100644
--- a/lib/lp/code/browser/configure.zcml
+++ b/lib/lp/code/browser/configure.zcml
@@ -1047,6 +1047,12 @@
name="+git"
template="../templates/gitlisting.pt"/>
<browser:page
+ for="lp.registry.interfaces.ociproject.IOCIProject"
+ class="lp.code.browser.gitlisting.OCIProjectGitListingView"
+ permission="zope.Public"
+ name="+git"
+ template="../templates/gitlisting.pt"/>
+ <browser:page
for="lp.registry.interfaces.personproduct.IPersonProduct"
class="lp.code.browser.gitlisting.PersonTargetGitListingView"
permission="zope.Public"
@@ -1059,6 +1065,12 @@
name="+git"
template="../templates/gitlisting.pt"/>
<browser:page
+ for="lp.registry.interfaces.personociproject.IPersonOCIProject"
+ class="lp.code.browser.gitlisting.PersonOCIProjectGitListingView"
+ permission="zope.Public"
+ name="+git"
+ template="../templates/gitlisting.pt"/>
+ <browser:page
for="lp.registry.interfaces.person.IPerson"
class="lp.code.browser.gitlisting.PlainGitListingView"
permission="zope.Public"
@@ -1096,6 +1108,16 @@
name="+code"/>
<browser:defaultView
+ for="lp.registry.interfaces.ociproject.IOCIProject"
+ layer="lp.code.publisher.CodeLayer"
+ name="+code"/>
+
+ <browser:defaultView
+ for="lp.registry.interfaces.personociproject.IPersonOCIProject"
+ layer="lp.code.publisher.CodeLayer"
+ name="+code"/>
+
+ <browser:defaultView
for="lp.registry.interfaces.distribution.IDistribution"
layer="lp.code.publisher.CodeLayer"
name="+code"/>
diff --git a/lib/lp/code/browser/gitlisting.py b/lib/lp/code/browser/gitlisting.py
index 8e51d71..3827f73 100644
--- a/lib/lp/code/browser/gitlisting.py
+++ b/lib/lp/code/browser/gitlisting.py
@@ -28,6 +28,7 @@ from lp.code.interfaces.gitrepository import IGitRepositorySet
from lp.registry.interfaces.persondistributionsourcepackage import (
IPersonDistributionSourcePackage,
)
+from lp.registry.interfaces.personociproject import IPersonOCIProject
from lp.registry.interfaces.personproduct import IPersonProduct
from lp.services.config import config
from lp.services.propertycache import cachedproperty
@@ -143,6 +144,8 @@ class PersonTargetGitListingView(BaseGitListingView):
return self.context.product
elif IPersonDistributionSourcePackage.providedBy(self.context):
return self.context.distro_source_package
+ elif IPersonOCIProject.providedBy(self.context):
+ return self.context.oci_project
else:
raise Exception("Unknown context: %r" % self.context)
@@ -158,6 +161,12 @@ class PersonTargetGitListingView(BaseGitListingView):
return None
+class OCIProjectGitListingView(TargetGitListingView):
+
+ # OCIProject:+branches doesn't exist.
+ show_bzr_link = False
+
+
class PersonDistributionSourcePackageGitListingView(
PersonTargetGitListingView):
@@ -165,6 +174,12 @@ class PersonDistributionSourcePackageGitListingView(
show_bzr_link = False
+class PersonOCIProjectGitListingView(PersonTargetGitListingView):
+
+ # PersonOCIProject:+branches doesn't exist.
+ show_bzr_link = False
+
+
class PlainGitListingView(BaseGitListingView):
page_title = 'Git'
diff --git a/lib/lp/code/browser/tests/test_gitlisting.py b/lib/lp/code/browser/tests/test_gitlisting.py
index 2552f73..72954ad 100644
--- a/lib/lp/code/browser/tests/test_gitlisting.py
+++ b/lib/lp/code/browser/tests/test_gitlisting.py
@@ -13,6 +13,7 @@ from lp.app.enums import InformationType
from lp.code.enums import BranchMergeProposalStatus
from lp.code.interfaces.gitrepository import IGitRepositorySet
from lp.registry.enums import VCSType
+from lp.registry.interfaces.personociproject import IPersonOCIProjectFactory
from lp.registry.model.persondistributionsourcepackage import (
PersonDistributionSourcePackage,
)
@@ -34,6 +35,10 @@ class TestTargetGitListingView:
layer = DatabaseFunctionalLayer
+ def setDefaultRepository(self, target, repository):
+ getUtility(IGitRepositorySet).setDefaultRepository(
+ target=target, repository=repository)
+
def test_rendering(self):
main_repo = self.factory.makeGitRepository(
owner=self.owner, target=self.target, name="foo")
@@ -53,8 +58,7 @@ class TestTargetGitListingView:
target=self.target, name="bar")
with admin_logged_in():
- getUtility(IGitRepositorySet).setDefaultRepository(
- target=self.target, repository=main_repo)
+ self.setDefaultRepository(target=self.target, repository=main_repo)
getUtility(IGitRepositorySet).setDefaultRepositoryForOwner(
owner=other_repo.owner, target=self.target,
repository=other_repo, user=other_repo.owner)
@@ -115,8 +119,7 @@ class TestTargetGitListingView:
self.factory.makeGitRefs(other_repo)
with admin_logged_in():
- getUtility(IGitRepositorySet).setDefaultRepository(
- target=self.target, repository=main_repo)
+ self.setDefaultRepository(target=self.target, repository=main_repo)
getUtility(IGitRepositorySet).setDefaultRepositoryForOwner(
owner=other_repo.owner, target=self.target,
repository=other_repo, user=other_repo.owner)
@@ -155,7 +158,7 @@ class TestTargetGitListingView:
other_repo = self.factory.makeGitRepository(
target=self.target, information_type=InformationType.PUBLIC)
with admin_logged_in():
- getUtility(IGitRepositorySet).setDefaultRepository(
+ self.setDefaultRepository(
target=self.target, repository=invisible_repo)
# An anonymous user can't see the default.
@@ -339,8 +342,7 @@ class TestProductGitListingView(TestTargetGitListingView,
paths=["refs/heads/master", "refs/heads/1.0", "refs/tags/1.1"])
with admin_logged_in():
- getUtility(IGitRepositorySet).setDefaultRepository(
- target=self.target, repository=main_repo)
+ self.setDefaultRepository(target=self.target, repository=main_repo)
self.factory.makeBranchMergeProposalForGit(
target_ref=git_refs[0],
@@ -415,6 +417,48 @@ class TestPersonDistributionSourcePackageGitListingView(
self.assertNotIn('View Bazaar branches', view())
+class TestOCIProjectGitListingView(
+ TestTargetGitListingView, TestCaseWithFactory):
+
+ def setUp(self):
+ super(TestOCIProjectGitListingView, self).setUp()
+ self.owner = self.factory.makePerson(name="foowner")
+ distro = self.factory.makeDistribution(name="foo", owner=self.owner)
+ self.target = self.factory.makeOCIProject(
+ pillar=distro, ociprojectname="bar")
+ self.target_path = "foo/+oci/bar"
+
+ def setDefaultRepository(self, target, repository):
+ getUtility(IGitRepositorySet).setDefaultRepository(
+ target=target, repository=repository, force_oci=True)
+
+ def test_bzr_link(self):
+ # There's no OCIProject:+branches, nor any ability to create Bazaar
+ # branches for OCI projects.
+ view = create_initialized_view(self.target, '+git')
+ self.assertNotIn('View Bazaar branches', view())
+
+
+class TestPersonOCIProjectGitListingView(
+ TestPersonTargetGitListingView, TestCaseWithFactory):
+
+ def setUp(self):
+ super(TestPersonOCIProjectGitListingView, self).setUp()
+ self.owner = self.factory.makePerson(name="dev")
+ distro = self.factory.makeDistribution(name="foo", owner=self.owner)
+ self.target = self.factory.makeOCIProject(
+ pillar=distro, ociprojectname="bar")
+ self.target_path = "foo/+oci/bar"
+ self.owner_target = getUtility(IPersonOCIProjectFactory).create(
+ self.owner, self.target)
+
+ def test_bzr_link(self):
+ # There's no PersonOCIProject:+branches, nor any ability to create
+ # Bazaar branches for OCI projects.
+ view = create_initialized_view(self.owner_target, '+git')
+ self.assertNotIn('View Bazaar branches', view())
+
+
class TestPlainGitListingView:
layer = DatabaseFunctionalLayer
diff --git a/lib/lp/code/browser/tests/test_vcslisting.py b/lib/lp/code/browser/tests/test_vcslisting.py
index 2d7741d..127d9d3 100644
--- a/lib/lp/code/browser/tests/test_vcslisting.py
+++ b/lib/lp/code/browser/tests/test_vcslisting.py
@@ -126,6 +126,62 @@ class TestPersonDistributionSourcePackageDefaultVCSView(TestCaseWithFactory):
self.assertCodeViewClass(VCSType.GIT, PersonTargetGitListingView)
+class TestOCIProjectDefaultVCSView(TestCaseWithFactory):
+ """Tests that OCIProject:+code delegates to +git.
+
+ This is regardless of the distribution's preferred VCS. It can't delegate
+ to +branches, as OCIProject:+branches doesn't exist.
+ """
+
+ layer = DatabaseFunctionalLayer
+
+ def assertCodeViewClass(self, vcs, cls):
+ distro = self.factory.makeDistribution(vcs=vcs)
+ oci_project = self.factory.makeOCIProject(pillar=distro)
+ self.assertEqual(vcs, distro.vcs)
+ view = test_traverse(
+ '/%s/+oci/%s/+code' % (distro.name, oci_project.name))[1]
+ self.assertIsInstance(view, cls)
+
+ def test_default_unset(self):
+ self.assertCodeViewClass(None, TargetGitListingView)
+
+ def test_default_bzr(self):
+ self.assertCodeViewClass(VCSType.BZR, TargetGitListingView)
+
+ def test_git(self):
+ self.assertCodeViewClass(VCSType.GIT, TargetGitListingView)
+
+
+class TestPersonOCIProjectDefaultVCSView(TestCaseWithFactory):
+ """Tests that OCIProject:+code delegates to +git.
+
+ This is regardless of the distribution's preferred VCS. It can't
+ delegate to +branches, as PersonOCIProject:+branches doesn't exist.
+ """
+
+ layer = DatabaseFunctionalLayer
+
+ def assertCodeViewClass(self, vcs, cls):
+ person = self.factory.makePerson()
+ distro = self.factory.makeDistribution(vcs=vcs)
+ oci_project = self.factory.makeOCIProject(pillar=distro)
+ self.assertEqual(vcs, distro.vcs)
+ view = test_traverse(
+ '~%s/%s/+oci/%s/+code'
+ % (person.name, distro.name, oci_project.name))[1]
+ self.assertIsInstance(view, cls)
+
+ def test_default_unset(self):
+ self.assertCodeViewClass(None, PersonTargetGitListingView)
+
+ def test_default_bzr(self):
+ self.assertCodeViewClass(VCSType.BZR, PersonTargetGitListingView)
+
+ def test_git(self):
+ self.assertCodeViewClass(VCSType.GIT, PersonTargetGitListingView)
+
+
class TestDistributionDefaultVCSView(TestCaseWithFactory):
"""Tests that Distribution:+code delegates to +git or +branches."""
diff --git a/lib/lp/code/browser/vcslisting.py b/lib/lp/code/browser/vcslisting.py
index bc76a38..5291234 100644
--- a/lib/lp/code/browser/vcslisting.py
+++ b/lib/lp/code/browser/vcslisting.py
@@ -8,9 +8,11 @@ __metaclass__ = type
from zope.component import queryMultiAdapter
from lp.registry.enums import VCSType
+from lp.registry.interfaces.ociproject import IOCIProject
from lp.registry.interfaces.persondistributionsourcepackage import (
IPersonDistributionSourcePackage,
)
+from lp.registry.interfaces.personociproject import IPersonOCIProject
from lp.registry.interfaces.personproduct import IPersonProduct
from lp.services.webapp import stepto
@@ -19,9 +21,14 @@ class TargetDefaultVCSNavigationMixin:
@stepto("+code")
def traverse_code_view(self):
- if self.context.pillar.vcs in (VCSType.BZR, None):
+ if IOCIProject.providedBy(self.context):
+ # OCI projects only support Git.
+ vcs = VCSType.GIT
+ else:
+ vcs = self.context.pillar.vcs
+ if vcs in (VCSType.BZR, None):
view_name = '+branches'
- elif self.context.pillar.vcs == VCSType.GIT:
+ elif vcs == VCSType.GIT:
view_name = '+git'
else:
raise AssertionError("Unknown VCS")
@@ -37,11 +44,18 @@ class PersonTargetDefaultVCSNavigationMixin:
target = self.context.product
elif IPersonDistributionSourcePackage.providedBy(self.context):
target = self.context.distro_source_package
+ elif IPersonOCIProject.providedBy(self.context):
+ target = self.context.oci_project
else:
raise AssertionError("Unknown target: %r" % self.context)
- if target.pillar.vcs in (VCSType.BZR, None):
+ if IOCIProject.providedBy(target):
+ # OCI projects only support Git.
+ vcs = VCSType.GIT
+ else:
+ vcs = target.pillar.vcs
+ if vcs in (VCSType.BZR, None):
view_name = '+branches'
- elif target.pillar.vcs == VCSType.GIT:
+ elif vcs == VCSType.GIT:
view_name = '+git'
else:
raise AssertionError("Unknown VCS")
diff --git a/lib/lp/code/templates/gitlisting.pt b/lib/lp/code/templates/gitlisting.pt
index 8afb6e1..583deef 100644
--- a/lib/lp/code/templates/gitlisting.pt
+++ b/lib/lp/code/templates/gitlisting.pt
@@ -26,7 +26,7 @@
<span tal:condition="not: view/default_information_type"
id="privacy-text">
You can't create new repositories for
- <tal:name replace="context/displayname"/>.
+ <tal:name replace="context/display_name"/>.
<tal:sharing-link condition="context/required:launchpad.Edit">
<br/>This can be fixed by changing the branch sharing policy on the
<a tal:attributes="href string:${view/target/fmt:url:mainsite}/+sharing">sharing page</a>.
@@ -36,7 +36,7 @@
<span tal:condition="view/default_information_type"
tal:attributes="class string:sprite ${private_class}"
id="privacy-text">
- New repositories for <tal:name replace="view/target/displayname"/> are
+ New repositories for <tal:name replace="view/target/display_name"/> are
<strong tal:content="view/default_information_type_title" />.
</span>
</div>
@@ -78,7 +78,7 @@ git push --set-upstream origin master
tal:define="count context/menu:branches/active_review_count|nothing;
link context/menu:branches/active_reviews|nothing"
tal:condition="python: count > 0">
- <tal:project replace="context/displayname"/> has
+ <tal:project replace="context/display_name"/> has
<tal:active-count replace="count"/>
<tal:link replace="structure python: link.render().lower()"/>.
</p>
diff --git a/lib/lp/registry/browser/configure.zcml b/lib/lp/registry/browser/configure.zcml
index e277e94..2c12cc0 100644
--- a/lib/lp/registry/browser/configure.zcml
+++ b/lib/lp/registry/browser/configure.zcml
@@ -2562,6 +2562,10 @@
path_expression="string:${oci_project/pillar/name}/+oci/${oci_project/name}"
attribute_to_parent="person"
/>
+ <browser:navigation
+ module="lp.registry.browser.personociproject"
+ classes="PersonOCIProjectNavigation"
+ />
<browser:url
for="lp.registry.interfaces.personproduct.IPersonProduct"
path_expression="product/name"
diff --git a/lib/lp/registry/browser/personociproject.py b/lib/lp/registry/browser/personociproject.py
new file mode 100644
index 0000000..aa991eb
--- /dev/null
+++ b/lib/lp/registry/browser/personociproject.py
@@ -0,0 +1,63 @@
+# Copyright 2019 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Views, menus, and traversal related to `PersonOCIProject`s."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'PersonOCIProjectNavigation',
+ ]
+
+from zope.component import queryAdapter
+from zope.interface import implementer
+from zope.traversing.interfaces import IPathAdapter
+
+from lp.code.browser.vcslisting import PersonTargetDefaultVCSNavigationMixin
+from lp.registry.interfaces.personociproject import IPersonOCIProject
+from lp.services.webapp import (
+ canonical_url,
+ Navigation,
+ StandardLaunchpadFacets,
+ )
+from lp.services.webapp.breadcrumb import Breadcrumb
+from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
+
+
+class PersonOCIProjectNavigation(
+ PersonTargetDefaultVCSNavigationMixin, Navigation):
+
+ usedfor = IPersonOCIProject
+
+
+# XXX cjwatson 2019-11-26: Do we need two breadcrumbs, one for the
+# distribution and one for the OCI project?
+@implementer(IMultiFacetedBreadcrumb)
+class PersonOCIProjectBreadcrumb(Breadcrumb):
+ """Breadcrumb for an `IPersonOCIProject`."""
+
+ @property
+ def text(self):
+ return self.context.oci_project.display_name
+
+ @property
+ def url(self):
+ if self._url is None:
+ return canonical_url(
+ self.context.oci_project, rootsite=self.rootsite)
+ else:
+ return self._url
+
+ @property
+ def icon(self):
+ return queryAdapter(
+ self.context.oci_project, IPathAdapter, name='image').icon()
+
+
+class PersonOCIProjectFacets(StandardLaunchpadFacets):
+ """The links that will appear in the facet menu for an `IPersonOCIProject`.
+ """
+
+ usedfor = IPersonOCIProject
+ enable_only = ['branches']