Merge ~cjwatson/launchpad:fix-oci-show-default-git-repository into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 2c16aa5915b122c7c90e23ee434595fa072d0731
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:fix-oci-show-default-git-repository
Merge into: launchpad:master
Diff against target: 169 lines (+78/-2)
4 files modified
lib/lp/code/interfaces/gitnamespace.py (+8/-0)
lib/lp/code/model/gitnamespace.py (+25/-0)
lib/lp/code/xmlrpc/git.py (+4/-2)
lib/lp/code/xmlrpc/tests/test_git.py (+41/-0)
Reviewer Review Type Date Requested Status
Tom Wardill (community) Approve
Review via email: mp+401812@code.launchpad.net

Commit message

Fix creating /pillar/+oci/name via git push

Description of the change

This is a bit more involved than I'd initially anticipated, because OCI projects don't have owners so there isn't an obvious natural choice for the owner of the new repository. For the time being, use the OCI project admin in the case of distribution-based OCI projects, and otherwise the pillar's owner. I expect this may need to evolve further in future.

To post a comment you must log in.
Revision history for this message
Tom Wardill (twom) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/code/interfaces/gitnamespace.py b/lib/lp/code/interfaces/gitnamespace.py
index ed0a4d3..0e17f08 100644
--- a/lib/lp/code/interfaces/gitnamespace.py
+++ b/lib/lp/code/interfaces/gitnamespace.py
@@ -108,6 +108,14 @@ class IGitNamespacePolicy(Interface):
108 "True iff this namespace permits automatically setting a default "108 "True iff this namespace permits automatically setting a default "
109 "repository on push.")109 "repository on push.")
110110
111 # XXX cjwatson 2021-04-26: This is a slight hack for the benefit of the
112 # OCI project namespace. OCI projects don't have owners (recipes do),
113 # so the usual approach of using the target's owner doesn't work.
114 default_owner = Attribute(
115 "The default owner when automatically setting a default repository on "
116 "push for this namespace, or None if no usable default owner can be "
117 "determined.")
118
111 supports_repository_forking = Attribute(119 supports_repository_forking = Attribute(
112 "Does this namespace support repository forking at all?")120 "Does this namespace support repository forking at all?")
113121
diff --git a/lib/lp/code/model/gitnamespace.py b/lib/lp/code/model/gitnamespace.py
index 86169a2..3c2078f 100644
--- a/lib/lp/code/model/gitnamespace.py
+++ b/lib/lp/code/model/gitnamespace.py
@@ -63,6 +63,7 @@ from lp.code.model.branchnamespace import (
63 )63 )
64from lp.code.model.gitrepository import GitRepository64from lp.code.model.gitrepository import GitRepository
65from lp.registry.enums import PersonVisibility65from lp.registry.enums import PersonVisibility
66from lp.registry.interfaces.distribution import IDistribution
66from lp.registry.interfaces.distributionsourcepackage import (67from lp.registry.interfaces.distributionsourcepackage import (
67 IDistributionSourcePackage,68 IDistributionSourcePackage,
68 )69 )
@@ -285,6 +286,7 @@ class PersonalGitNamespace(_BaseGitNamespace):
285286
286 has_defaults = False287 has_defaults = False
287 allow_push_to_set_default = False288 allow_push_to_set_default = False
289 default_owner = None
288 supports_merge_proposals = True290 supports_merge_proposals = True
289 supports_code_imports = False291 supports_code_imports = False
290 allow_recipe_name_from_target = False292 allow_recipe_name_from_target = False
@@ -400,6 +402,11 @@ class ProjectGitNamespace(_BaseGitNamespace):
400 repository.sourcepackagename = None402 repository.sourcepackagename = None
401 repository.oci_project = None403 repository.oci_project = None
402404
405 @property
406 def default_owner(self):
407 """See `IGitNamespacePolicy`."""
408 return self.target.owner
409
403 def getAllowedInformationTypes(self, who=None):410 def getAllowedInformationTypes(self, who=None):
404 """See `IGitNamespace`."""411 """See `IGitNamespace`."""
405 # Some policies require that the repository owner or current user412 # Some policies require that the repository owner or current user
@@ -497,6 +504,11 @@ class PackageGitNamespace(_BaseGitNamespace):
497 repository.sourcepackagename = dsp.sourcepackagename504 repository.sourcepackagename = dsp.sourcepackagename
498 repository.oci_project = None505 repository.oci_project = None
499506
507 @property
508 def default_owner(self):
509 """See `IGitNamespacePolicy`."""
510 return self.target.owner
511
500 def getAllowedInformationTypes(self, who=None):512 def getAllowedInformationTypes(self, who=None):
501 """See `IGitNamespace`."""513 """See `IGitNamespace`."""
502 return PUBLIC_INFORMATION_TYPES514 return PUBLIC_INFORMATION_TYPES
@@ -586,6 +598,19 @@ class OCIProjectGitNamespace(_BaseGitNamespace):
586 repository.sourcepackagename = None598 repository.sourcepackagename = None
587 repository.oci_project = self.oci_project599 repository.oci_project = self.oci_project
588600
601 @property
602 def default_owner(self):
603 """See `IGitNamespacePolicy`."""
604 # XXX cjwatson 2021-04-26: This may be a bit too restrictive, since
605 # other users may be able to edit the OCI project. If this becomes
606 # a problem, it will probably be best to fix it by extending the set
607 # of people who have launchpad.Edit on repositories in OCI project
608 # namespaces.
609 if IDistribution.providedBy(self.target.pillar):
610 return self.target.pillar.oci_project_admin
611 else:
612 return self.target.pillar.owner
613
589 def getAllowedInformationTypes(self, who=None):614 def getAllowedInformationTypes(self, who=None):
590 """See `IGitNamespace`."""615 """See `IGitNamespace`."""
591 return PUBLIC_INFORMATION_TYPES616 return PUBLIC_INFORMATION_TYPES
diff --git a/lib/lp/code/xmlrpc/git.py b/lib/lp/code/xmlrpc/git.py
index 5931e94..21e728d 100644
--- a/lib/lp/code/xmlrpc/git.py
+++ b/lib/lp/code/xmlrpc/git.py
@@ -303,11 +303,13 @@ class GitAPI(LaunchpadXMLRPCView):
303 # split_git_unique_name should have left us without a repository name.303 # split_git_unique_name should have left us without a repository name.
304 assert repository is None304 assert repository is None
305 if owner is None:305 if owner is None:
306 if not get_git_namespace(target, None).allow_push_to_set_default:306 default_namespace = get_git_namespace(target, None)
307 if (not default_namespace.allow_push_to_set_default or
308 default_namespace.default_owner is None):
307 raise GitRepositoryCreationForbidden(309 raise GitRepositoryCreationForbidden(
308 "Cannot automatically set the default repository for this "310 "Cannot automatically set the default repository for this "
309 "target; push to a named repository instead.")311 "target; push to a named repository instead.")
310 repository_owner = target.owner312 repository_owner = default_namespace.default_owner
311 else:313 else:
312 repository_owner = owner314 repository_owner = owner
313 namespace = get_git_namespace(target, repository_owner)315 namespace = get_git_namespace(target, repository_owner)
diff --git a/lib/lp/code/xmlrpc/tests/test_git.py b/lib/lp/code/xmlrpc/tests/test_git.py
index b87b2a3..53a1e23 100644
--- a/lib/lp/code/xmlrpc/tests/test_git.py
+++ b/lib/lp/code/xmlrpc/tests/test_git.py
@@ -573,6 +573,26 @@ class TestGitAPIMixin:
573 self.assertEqual(573 self.assertEqual(
574 initial_count, getUtility(IAllGitRepositories).count())574 initial_count, getUtility(IAllGitRepositories).count())
575575
576 def test_translatePath_create_oci_project_not_owner(self):
577 # Somebody without edit permission on the OCI project cannot create
578 # a repository and immediately set it as the default for that
579 # project.
580 requester = self.factory.makePerson()
581 distribution = self.factory.makeDistribution(
582 oci_project_admin=self.factory.makeTeam())
583 oci_project = self.factory.makeOCIProject(pillar=distribution)
584 path = u"/%s/+oci/%s" % (oci_project.pillar.name, oci_project.name)
585 message = "%s is not a member of %s" % (
586 requester.displayname,
587 oci_project.pillar.oci_project_admin.displayname)
588 initial_count = getUtility(IAllGitRepositories).count()
589 self.assertPermissionDenied(
590 requester, path, message=message, permission="write")
591 # No repository was created.
592 login(ANONYMOUS)
593 self.assertEqual(
594 initial_count, getUtility(IAllGitRepositories).count())
595
576 def test_translatePath_grant_to_other(self):596 def test_translatePath_grant_to_other(self):
577 requester = self.factory.makePerson()597 requester = self.factory.makePerson()
578 other_person = self.factory.makePerson()598 other_person = self.factory.makePerson()
@@ -1496,13 +1516,34 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory):
1496 # A repository can be created and immediately set as the default for1516 # A repository can be created and immediately set as the default for
1497 # an OCI project.1517 # an OCI project.
1498 requester = self.factory.makePerson()1518 requester = self.factory.makePerson()
1519 oci_project_admin = self.factory.makeTeam(members=[requester])
1520 distribution = self.factory.makeDistribution(
1521 oci_project_admin=oci_project_admin)
1522 oci_project = self.factory.makeOCIProject(pillar=distribution)
1523 repository = self.assertCreates(
1524 requester,
1525 u"/%s/+oci/%s" % (oci_project.pillar.name, oci_project.name))
1526 self.assertTrue(repository.target_default)
1527 self.assertTrue(repository.owner_default)
1528 self.assertEqual(oci_project_admin, repository.owner)
1529
1530 def test_translatePath_create_oci_project_default_no_admin(self):
1531 # If the OCI project's distribution has no OCI project admin, then a
1532 # repository cannot (yet) be created and immediately set as the
1533 # default for that OCI project.
1534 requester = self.factory.makePerson()
1499 oci_project = self.factory.makeOCIProject()1535 oci_project = self.factory.makeOCIProject()
1500 path = u"/%s/+oci/%s" % (oci_project.pillar.name, oci_project.name)1536 path = u"/%s/+oci/%s" % (oci_project.pillar.name, oci_project.name)
1501 message = (1537 message = (
1502 "Cannot automatically set the default repository for this target; "1538 "Cannot automatically set the default repository for this target; "
1503 "push to a named repository instead.")1539 "push to a named repository instead.")
1540 initial_count = getUtility(IAllGitRepositories).count()
1504 self.assertPermissionDenied(1541 self.assertPermissionDenied(
1505 requester, path, message=message, permission="write")1542 requester, path, message=message, permission="write")
1543 # No repository was created.
1544 login(ANONYMOUS)
1545 self.assertEqual(
1546 initial_count, getUtility(IAllGitRepositories).count())
15061547
1507 def test_translatePath_create_project_owner_default(self):1548 def test_translatePath_create_project_owner_default(self):
1508 # A repository can be created and immediately set as its owner's1549 # A repository can be created and immediately set as its owner's

Subscribers

People subscribed via source and target branches

to status/vote changes: