launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24099
[Merge] ~twom/launchpad:oci-gitrepository into launchpad:master
Tom Wardill has proposed merging ~twom/launchpad:oci-gitrepository into launchpad:master.
Commit message:
Add GitNamespace for OCIProjects
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~twom/launchpad/+git/launchpad/+merge/375021
The addition of the ociprojectname column to GitRepository requires a working Namespace implementation.
Add the model, following the pattern from DistributionSourcePackage.
Add tests for the model
Add required helper methods to IDistribution
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~twom/launchpad:oci-gitrepository into launchpad:master.
diff --git a/lib/lp/code/configure.zcml b/lib/lp/code/configure.zcml
index 0ae4a3c..4cda403 100644
--- a/lib/lp/code/configure.zcml
+++ b/lib/lp/code/configure.zcml
@@ -894,6 +894,10 @@
<allow interface="lp.code.interfaces.gitnamespace.IGitNamespace" />
<allow interface="lp.code.interfaces.gitnamespace.IGitNamespacePolicy" />
</class>
+ <class class="lp.code.model.gitnamespace.OCIProjectGitNamespace">
+ <allow interface="lp.code.interfaces.gitnamespace.IGitNamespace" />
+ <allow interface="lp.code.interfaces.gitnamespace.IGitNamespacePolicy" />
+ </class>
<securedutility
class="lp.code.model.gitnamespace.GitNamespaceSet"
provides="lp.code.interfaces.gitnamespace.IGitNamespaceSet">
diff --git a/lib/lp/code/interfaces/gitcollection.py b/lib/lp/code/interfaces/gitcollection.py
index 4ab976b..9b79940 100644
--- a/lib/lp/code/interfaces/gitcollection.py
+++ b/lib/lp/code/interfaces/gitcollection.py
@@ -138,6 +138,9 @@ class IGitCollection(Interface):
def inDistributionSourcePackage(distro_source_package):
"""Restrict to repositories in a package for a distribution."""
+ def inOCIProject(oci_project):
+ """Restrict to repositories in a OCI Project for a distribution."""
+
def isPersonal():
"""Restrict the collection to personal repositories."""
diff --git a/lib/lp/code/interfaces/gitnamespace.py b/lib/lp/code/interfaces/gitnamespace.py
index 99841d7..ce61dde 100644
--- a/lib/lp/code/interfaces/gitnamespace.py
+++ b/lib/lp/code/interfaces/gitnamespace.py
@@ -22,6 +22,7 @@ from lp.code.errors import InvalidNamespace
from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
+from lp.registry.interfaces.ociproject import IOCIProject
from lp.registry.interfaces.person import IPerson
from lp.registry.interfaces.product import IProduct
@@ -198,7 +199,8 @@ class IGitNamespacePolicy(Interface):
class IGitNamespaceSet(Interface):
"""Interface for getting Git repository namespaces."""
- def get(person, project=None, distribution=None, sourcepackagename=None):
+ def get(person, project=None, distribution=None, sourcepackagename=None,
+ ociprojectname=None):
"""Return the appropriate `IGitNamespace` for the given objects."""
@@ -209,6 +211,10 @@ def get_git_namespace(target, owner):
return getUtility(IGitNamespaceSet).get(
owner, distribution=target.distribution,
sourcepackagename=target.sourcepackagename)
+ elif IOCIProject.providedBy(target):
+ return getUtility(IGitNamespaceSet).get(
+ owner, distribution=target.distribution,
+ ociprojectname=target.ociprojectname)
elif target is None or IPerson.providedBy(target):
return getUtility(IGitNamespaceSet).get(owner)
else:
diff --git a/lib/lp/code/interfaces/gitrepository.py b/lib/lp/code/interfaces/gitrepository.py
index 2fca251..ed3fefc 100644
--- a/lib/lp/code/interfaces/gitrepository.py
+++ b/lib/lp/code/interfaces/gitrepository.py
@@ -73,6 +73,7 @@ from lp.code.interfaces.hasrecipes import IHasRecipes
from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
+from lp.registry.interfaces.ociprojectname import IOCIProjectName
from lp.registry.interfaces.person import IPerson
from lp.registry.interfaces.persondistributionsourcepackage import (
IPersonDistributionSourcePackageFactory,
@@ -204,6 +205,12 @@ class IGitRepositoryView(IHasRecipes):
shortened_path = Attribute(
"The shortest reasonable version of the path to this repository.")
+ ociprojectname = exported(
+ Reference(
+ title=_("OCI Project Name"), required=False, readonly=False,
+ schema=IOCIProjectName,
+ description=_("The OCI project that this repository belongs to.")))
+
@operation_parameters(
reviewer=Reference(
title=_("A person for which the reviewer status is in question."),
diff --git a/lib/lp/code/model/gitcollection.py b/lib/lp/code/model/gitcollection.py
index 70f6d01..e5ec794 100644
--- a/lib/lp/code/model/gitcollection.py
+++ b/lib/lp/code/model/gitcollection.py
@@ -458,6 +458,14 @@ class GenericGitCollection:
[GitRepository.distribution == distribution,
GitRepository.sourcepackagename == sourcepackagename])
+ def inOCIProject(self, oci_project):
+ """See `IGitcollection`."""
+ distribution = oci_project.pillar
+ ociprojectname = oci_project.ociprojectname
+ return self._filterBy(
+ [GitRepository.distribution == distribution,
+ GitRepository.ociprojectname == ociprojectname])
+
def isPersonal(self):
"""See `IGitCollection`."""
return self._filterBy(
diff --git a/lib/lp/code/model/gitnamespace.py b/lib/lp/code/model/gitnamespace.py
index 75ea427..ca35444 100644
--- a/lib/lp/code/model/gitnamespace.py
+++ b/lib/lp/code/model/gitnamespace.py
@@ -6,6 +6,7 @@
__metaclass__ = type
__all__ = [
'GitNamespaceSet',
+ 'OCIProjectGitNamespace',
'PackageGitNamespace',
'PersonalGitNamespace',
'ProjectGitNamespace',
@@ -541,12 +542,85 @@ class PackageGitNamespace(_BaseGitNamespace):
self_dsp.sourcepackagename == other_dsp.sourcepackagename)
+@implementer(IGitNamespace, IGitNamespacePolicy)
+class OCIProjectGitNamespace(_BaseGitNamespace):
+ """A namespace for OCI Project repositories.
+
+ This namesace is for all the repositories owned by a particular person
+ in a particular OCI Project in a particular distribution.
+ """
+
+ has_defaults = True
+ allow_push_to_set_default = False
+ supports_merge_proposals = True
+ supports_code_imports = True
+ allow_recipe_name_from_target = True
+
+ def __init__(self, person, oci_project):
+ self.owner = person
+ self.oci_project = oci_project
+
+ def _getRepositoriesClause(self):
+ return And(
+ GitRepository.owner == self.owner,
+ GitRepository.ociprojectname == self.oci_project.ociprojectname,
+ GitRepository.distribution == self.oci_project.distribution)
+
+ # Marker for references to Git URL layouts: ##GITNAMESPACE##
+ @property
+ def name(self):
+ """See `IGitNamespace`."""
+ ocip = self.oci_project
+ return '~%s/%s/+oci/%s' % (
+ self.owner.name, ocip.pillar.name, ocip.ociprojectname.name)
+
+ @property
+ def target(self):
+ """See `IGitNamespace`."""
+ return IHasGitRepositories(self.oci_project)
+
+ def _retargetRepository(self, repository):
+ ocip = self.oci_project
+ repository.project = None
+ repository.distribution = ocip.distribution
+ repository.ociprojectname = ocip.ociprojectname
+
+ def getAllowedInformationTypes(self, who=None):
+ """See `IGitNamespace`."""
+ return PUBLIC_INFORMATION_TYPES
+
+ def getDefaultInformationType(self, who=None):
+ """See `IGitNamespace`."""
+ return InformationType.PUBLIC
+
+ def areRepositoriesMergeable(self, this, other):
+ """See `IGitNamespacePolicy`."""
+ # Repositories are mergeable into a oci project repository if the
+ # package is the same.
+ # XXX cjwatson 2015-04-18: Allow merging from a project repository
+ # if any (active?) series links this package to that project.
+ if this.namespace != self:
+ raise AssertionError(
+ "Namespace of %s is not %s." % (this.unique_name, self.name))
+ other_namespace = other.namespace
+ if zope_isinstance(other_namespace, OCIProjectGitNamespace):
+ return self.target == other_namespace.target
+ else:
+ return False
+
+ @property
+ def collection(self):
+ """See `IGitNamespacePolicy`."""
+ return getUtility(IAllGitRepositories).inOCIProject(
+ self.oci_project)
+
+
@implementer(IGitNamespaceSet)
class GitNamespaceSet:
"""Only implementation of `IGitNamespaceSet`."""
def get(self, person, project=None, distribution=None,
- sourcepackagename=None):
+ sourcepackagename=None, ociprojectname=None):
"""See `IGitNamespaceSet`."""
if project is not None:
assert distribution is None and sourcepackagename is None, (
@@ -555,10 +629,15 @@ class GitNamespaceSet:
% (project, distribution, sourcepackagename))
return ProjectGitNamespace(person, project)
elif distribution is not None:
+ if sourcepackagename is not None:
+ return PackageGitNamespace(
+ person, distribution.getSourcePackage(sourcepackagename))
+ elif ociprojectname is not None:
+ return OCIProjectGitNamespace(
+ person, distribution.getOCIProject(ociprojectname.name))
assert sourcepackagename is not None, (
- "distribution implies sourcepackagename. Got %r, %r"
- % (distribution, sourcepackagename))
- return PackageGitNamespace(
- person, distribution.getSourcePackage(sourcepackagename))
+ "distribution implies sourcepackagename or ociprojectname. "
+ "Got %r, %r, %r"
+ % (distribution, sourcepackagename, ociprojectname))
else:
return PersonalGitNamespace(person)
diff --git a/lib/lp/code/model/gitrepository.py b/lib/lp/code/model/gitrepository.py
index 93a2d16..f431a44 100644
--- a/lib/lp/code/model/gitrepository.py
+++ b/lib/lp/code/model/gitrepository.py
@@ -153,6 +153,7 @@ from lp.registry.interfaces.accesspolicy import (
from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
+from lp.registry.interfaces.ociproject import IOCIProject
from lp.registry.interfaces.person import (
IPerson,
IPersonSet,
@@ -318,6 +319,9 @@ class GitRepository(StormBase, WebhookTargetMixin, GitIdentityMixin):
_default_branch = Unicode(name='default_branch', allow_none=True)
+ ociprojectname_id = Int(name='ociprojectname', allow_none=True)
+ ociprojectname = Reference(ociprojectname_id, 'OCIProjectName.id')
+
def __init__(self, repository_type, registrant, owner, target, name,
information_type, date_created, reviewer=None,
description=None):
@@ -339,6 +343,10 @@ class GitRepository(StormBase, WebhookTargetMixin, GitIdentityMixin):
elif IDistributionSourcePackage.providedBy(target):
self.distribution = target.distribution
self.sourcepackagename = target.sourcepackagename
+ elif IOCIProject.providedBy(target):
+ # XXX twom 2019-10-28 This should have support for product
+ self.ociprojectname = target.ociprojectname
+ self.distribution = target.pillar
self.owner_default = False
self.target_default = False
@@ -367,10 +375,16 @@ class GitRepository(StormBase, WebhookTargetMixin, GitIdentityMixin):
if self.project is not None:
fmt = "~%(owner)s/%(project)s"
names["project"] = self.project.name
- elif self.distribution is not None:
+ elif (self.distribution is not None
+ and self.sourcepackagename is not None):
fmt = "~%(owner)s/%(distribution)s/+source/%(source)s"
names["distribution"] = self.distribution.name
names["source"] = self.sourcepackagename.name
+ elif (self.distribution is not None
+ and self.ociprojectname is not None):
+ fmt = "~%(owner)s/%(distribution)s/+oci/%(ociproject)s"
+ names["distribution"] = self.distribution.name
+ names["ociproject"] = self.ociprojectname.name
else:
fmt = "~%(owner)s"
fmt += "/+git/%(repository)s"
@@ -384,8 +398,12 @@ class GitRepository(StormBase, WebhookTargetMixin, GitIdentityMixin):
"""See `IGitRepository`."""
if self.project is not None:
return self.project
- elif self.distribution is not None:
+ elif (self.distribution is not None
+ and self.sourcepackagename is not None):
return self.distribution.getSourcePackage(self.sourcepackagename)
+ elif (self.distribution is not None
+ and self.ociprojectname is not None):
+ return self.distribution.getOCIProject(self.ociprojectname.name)
else:
return self.owner
diff --git a/lib/lp/code/model/tests/test_gitcollection.py b/lib/lp/code/model/tests/test_gitcollection.py
index bd80685..91f6e4b 100644
--- a/lib/lp/code/model/tests/test_gitcollection.py
+++ b/lib/lp/code/model/tests/test_gitcollection.py
@@ -381,6 +381,20 @@ class TestGitCollectionFilters(TestCaseWithFactory):
sorted([repository, repository2]),
sorted(collection.getRepositories()))
+ def test_in_oci_project(self):
+ # 'inOCIProject' returns a new collection that only
+ # has repositories for the oci project in the distribution.
+ ocip = self.factory.makeOCIProject()
+ ocip_other_distro = self.factory.makeOCIProject()
+ repository = self.factory.makeGitRepository(target=ocip)
+ repository2 = self.factory.makeGitRepository(target=ocip)
+ self.factory.makeGitRepository(target=ocip_other_distro)
+ self.factory.makeGitRepository()
+ collection = self.all_repositories.inOCIProject(ocip)
+ self.assertEqual(
+ sorted([repository, repository2]),
+ sorted(collection.getRepositories()))
+
def test_withIds(self):
# 'withIds' returns a new collection that only has repositories with
# the given ids.
diff --git a/lib/lp/code/model/tests/test_gitnamespace.py b/lib/lp/code/model/tests/test_gitnamespace.py
index 8e8696f..4ca44cb 100644
--- a/lib/lp/code/model/tests/test_gitnamespace.py
+++ b/lib/lp/code/model/tests/test_gitnamespace.py
@@ -31,6 +31,7 @@ from lp.code.interfaces.gitnamespace import (
)
from lp.code.interfaces.gitrepository import IGitRepositorySet
from lp.code.model.gitnamespace import (
+ OCIProjectGitNamespace,
PackageGitNamespace,
PersonalGitNamespace,
ProjectGitNamespace,
@@ -436,6 +437,102 @@ class TestProjectGitNamespace(TestCaseWithFactory, NamespaceMixin):
repositories[0].namespace.collection.getRepositories())
+class TestOCIProjectGitNamespace(TestCaseWithFactory, NamespaceMixin):
+ """Tests for `OCIProjectGitNamespace`."""
+
+ layer = DatabaseFunctionalLayer
+
+ def getNamespace(self, person=None):
+ if person is None:
+ person = self.factory.makePerson()
+ return get_git_namespace(self.factory.makeOCIProject(), person)
+
+ def test_name(self):
+ person = self.factory.makePerson()
+ oci_project = self.factory.makeOCIProject()
+ namespace = OCIProjectGitNamespace(person, oci_project)
+ self.assertEqual(
+ "~%s/%s/+oci/%s" % (
+ person.name, oci_project.distribution.name,
+ oci_project.ociprojectname.name),
+ namespace.name)
+
+ def test_owner(self):
+ # The person passed to an oci project namespace is the owner.
+ person = self.factory.makePerson()
+ oci_project = self.factory.makeOCIProject()
+ namespace = PackageGitNamespace(person, oci_project)
+ self.assertEqual(person, removeSecurityProxy(namespace).owner)
+
+ def test_target(self):
+ # The target for an oci project namespace is the oci project.
+ person = self.factory.makePerson()
+ oci_project = self.factory.makeOCIProject()
+ namespace = PackageGitNamespace(person, oci_project)
+ self.assertEqual(oci_project, namespace.target)
+
+ def test_supports_merge_proposals(self):
+ # OCI Project namespaces support merge proposals.
+ self.assertTrue(self.getNamespace().supports_merge_proposals)
+
+ def test_areRepositoriesMergeable_same_repository(self):
+ # A package repository is mergeable into itself.
+ oci_project = self.factory.makeOCIProject()
+ repository = self.factory.makeGitRepository(target=oci_project)
+ self.assertTrue(
+ repository.namespace.areRepositoriesMergeable(
+ repository, repository))
+
+ def test_areRepositoriesMergeable_same_namespace(self):
+ # Repositories of the same package are mergeable.
+ oci_project = self.factory.makeOCIProject()
+ this = self.factory.makeGitRepository(target=oci_project)
+ other = self.factory.makeGitRepository(target=oci_project)
+ self.assertTrue(this.namespace.areRepositoriesMergeable(this, other))
+
+ def test_areRepositoriesMergeable_different_namespace(self):
+ # Repositories of a different package are not mergeable.
+ this_oci_project = self.factory.makeOCIProject()
+ this = self.factory.makeGitRepository(target=this_oci_project)
+ other_oci_project = self.factory.makeOCIProject()
+ other = self.factory.makeGitRepository(target=other_oci_project)
+ self.assertFalse(this.namespace.areRepositoriesMergeable(this, other))
+
+ def test_areRepositoriesMergeable_personal(self):
+ # Personal repositories are not mergeable into oci project
+ # repositories.
+ owner = self.factory.makePerson()
+ oci_project = self.factory.makeOCIProject()
+ this = self.factory.makeGitRepository(owner=owner, target=oci_project)
+ other = self.factory.makeGitRepository(owner=owner, target=owner)
+ self.assertFalse(this.namespace.areRepositoriesMergeable(this, other))
+
+ def test_areRepositoriesMergeable_project(self):
+ # Project repositories are not mergeable into oci project repositories.
+ owner = self.factory.makePerson()
+ oci_project = self.factory.makeOCIProject()
+ this = self.factory.makeGitRepository(owner=owner, target=oci_project)
+ project = self.factory.makeProduct()
+ other = self.factory.makeGitRepository(owner=owner, target=project)
+ self.assertFalse(this.namespace.areRepositoriesMergeable(this, other))
+
+ def test_collection(self):
+ # An oci projects namespace's collection is of
+ # repositories for the same oci project.
+ oci_project = self.factory.makeOCIProject()
+ repositories = [
+ self.factory.makeGitRepository(target=oci_project)
+ for _ in range(3)]
+ self.factory.makeGitRepository(
+ target=self.factory.makeOCIProject())
+ self.factory.makeGitRepository(target=self.factory.makeProduct())
+ self.factory.makeGitRepository(
+ owner=repositories[0].owner, target=repositories[0].owner)
+ self.assertContentEqual(
+ repositories,
+ repositories[0].namespace.collection.getRepositories())
+
+
class TestProjectGitNamespacePrivacyWithInformationType(TestCaseWithFactory):
"""Tests for the privacy aspects of `ProjectGitNamespace`.
@@ -688,6 +785,15 @@ class TestPackageGitNamespace(TestCaseWithFactory, NamespaceMixin):
other = self.factory.makeGitRepository(owner=owner, target=project)
self.assertFalse(this.namespace.areRepositoriesMergeable(this, other))
+ def test_areRepositoriesMergeable_oci_project(self):
+ # OCI Project repositories are not mergeable into package repositories.
+ owner = self.factory.makePerson()
+ dsp = self.factory.makeDistributionSourcePackage()
+ this = self.factory.makeGitRepository(owner=owner, target=dsp)
+ oci_project = self.factory.makeOCIProject()
+ other = self.factory.makeGitRepository(owner=owner, target=oci_project)
+ self.assertFalse(this.namespace.areRepositoriesMergeable(this, other))
+
def test_collection(self):
# A package namespace's collection is of repositories for the same
# package.
diff --git a/lib/lp/code/model/tests/test_gitrepository.py b/lib/lp/code/model/tests/test_gitrepository.py
index 145bff4..299a088 100644
--- a/lib/lp/code/model/tests/test_gitrepository.py
+++ b/lib/lp/code/model/tests/test_gitrepository.py
@@ -218,6 +218,13 @@ class TestGitRepository(TestCaseWithFactory):
dsp.sourcepackagename.name, repository.name),
repository.unique_name)
+ def test_unique_name_oci_project(self):
+ ociprojectname = self.factory.makeOCIProjectName()
+ oci_project = self.factory.makeOCIProject(
+ ociprojectname=ociprojectname)
+ repository = self.factory.makeGitRepository(target=oci_project)
+ self.assertEqual(ociprojectname, repository.ociprojectname)
+
def test_unique_name_personal(self):
owner = self.factory.makePerson()
repository = self.factory.makeGitRepository(owner=owner, target=owner)
@@ -235,6 +242,11 @@ class TestGitRepository(TestCaseWithFactory):
repository = self.factory.makeGitRepository(target=dsp)
self.assertEqual(dsp, repository.target)
+ def test_target_ociproject(self):
+ oci_project = self.factory.makeOCIProject()
+ repository = self.factory.makeGitRepository(target=oci_project)
+ self.assertEqual(oci_project, repository.target)
+
def test_target_personal(self):
owner = self.factory.makePerson()
repository = self.factory.makeGitRepository(owner=owner, target=owner)
diff --git a/lib/lp/registry/interfaces/distribution.py b/lib/lp/registry/interfaces/distribution.py
index 88a0545..c7ddb95 100644
--- a/lib/lp/registry/interfaces/distribution.py
+++ b/lib/lp/registry/interfaces/distribution.py
@@ -500,6 +500,11 @@ class IDistributionPublic(
order to create a mirror.
"""
+ def getOCIProject(name):
+ """Return a `OCIProject` with the given name for this
+ distribution, or None.
+ """
+
@operation_parameters(
name=TextLine(title=_("Package name"), required=True))
# Really returns IDistributionSourcePackage, see
diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py
index 100dccd..b48cdbb 100644
--- a/lib/lp/registry/interfaces/ociproject.py
+++ b/lib/lp/registry/interfaces/ociproject.py
@@ -24,6 +24,7 @@ from zope.schema import (
from lp import _
from lp.bugs.interfaces.bugtarget import IBugTarget
+from lp.code.interfaces.hasgitrepositories import IHasGitRepositories
from lp.registry.interfaces.distribution import IDistribution
from lp.registry.interfaces.ociprojectname import IOCIProjectName
from lp.registry.interfaces.series import SeriesStatus
@@ -79,7 +80,7 @@ class IOCIProjectEdit(Interface):
"""Creates a new `IOCIProjectSeries`."""
-class IOCIProject(IOCIProjectView, IOCIProjectEdit,
+class IOCIProject(IHasGitRepositories, IOCIProjectView, IOCIProjectEdit,
IOCIProjectEditableAttributes):
"""A project containing Open Container Initiative recipes."""
diff --git a/lib/lp/registry/model/distribution.py b/lib/lp/registry/model/distribution.py
index 3bdedbe..2288d0e 100644
--- a/lib/lp/registry/model/distribution.py
+++ b/lib/lp/registry/model/distribution.py
@@ -101,6 +101,7 @@ from lp.registry.interfaces.distributionmirror import (
MirrorFreshness,
MirrorStatus,
)
+from lp.registry.interfaces.ociproject import IOCIProjectSet
from lp.registry.interfaces.oopsreferences import IHasOOPSReferences
from lp.registry.interfaces.person import (
validate_person,
@@ -826,6 +827,11 @@ class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
name = %s
""" % sqlvalues(self.id, name))
+ def getOCIProject(self, name):
+ oci_project = getUtility(IOCIProjectSet).getByDistributionAndName(
+ self, name)
+ return oci_project
+
def getSourcePackage(self, name):
"""See `IDistribution`."""
if ISourcePackageName.providedBy(name):
diff --git a/lib/lp/registry/tests/test_distribution.py b/lib/lp/registry/tests/test_distribution.py
index 8e0b5ba..b9f49e6 100644
--- a/lib/lp/registry/tests/test_distribution.py
+++ b/lib/lp/registry/tests/test_distribution.py
@@ -304,6 +304,17 @@ class TestDistribution(TestCaseWithFactory):
InformationType.PUBLIC,
distro.getDefaultSpecificationInformationType())
+ def test_getOCIProject(self):
+ distro = self.factory.makeDistribution()
+ ociprojectname = self.factory.makeOCIProjectName(name=u'first-project')
+ first_project = self.factory.makeOCIProject(
+ ociprojectname=ociprojectname,
+ pillar=distro)
+ # make another project to ensure we don't default
+ self.factory.makeOCIProject(pillar=distro)
+ result = distro.getOCIProject(u'first-project')
+ self.assertEqual(first_project, result)
+
class TestDistributionCurrentSourceReleases(
CurrentSourceReleasesMixin, TestCase):
diff --git a/lib/lp/security.py b/lib/lp/security.py
index 21caa71..66c7f73 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -3434,6 +3434,11 @@ class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):
usedfor = ISnapBaseSet
+class ViewOCIProject(AnonymousAuthorization):
+ """Anyone can view an `IOCIProject`."""
+ usedfor = IOCIProject
+
+
class EditOCIProject(AuthorizationBase):
permission = 'launchpad.Edit'
usedfor = IOCIProject