Merge ~cjwatson/launchpad:artifactory-ppa-privatize into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 47cbbc99b131f8174130e1a8de943def357ef412
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:artifactory-ppa-privatize
Merge into: launchpad:master
Diff against target: 206 lines (+116/-17)
4 files modified
lib/lp/soyuz/browser/archive.py (+32/-5)
lib/lp/soyuz/browser/tests/test_archive_admin_view.py (+28/-5)
lib/lp/soyuz/model/archive.py (+26/-6)
lib/lp/soyuz/tests/test_archive_privacy.py (+30/-1)
Reviewer Review Type Date Requested Status
Jürgen Gmach Approve
Review via email: mp+437678@code.launchpad.net

Commit message

Allow making non-empty Artifactory PPAs private

Description of the change

The reasons why we can't change the privacy of non-empty locally-published PPAs don't quite apply in the Artifactory case. There are still some reasons to be careful, but we can at least give ourselves the ability to fix the failure mode where we've forgotten to make an archive private.

To post a comment you must log in.
Revision history for this message
Jürgen Gmach (jugmac00) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/soyuz/browser/archive.py b/lib/lp/soyuz/browser/archive.py
2index 6bbca05..c47b057 100644
3--- a/lib/lp/soyuz/browser/archive.py
4+++ b/lib/lp/soyuz/browser/archive.py
5@@ -114,6 +114,7 @@ from lp.soyuz.browser.sourceslist import SourcesListEntriesWidget
6 from lp.soyuz.browser.widgets.archive import PPANameWidget
7 from lp.soyuz.enums import (
8 ArchivePermissionType,
9+ ArchivePublishingMethod,
10 ArchiveStatus,
11 PackageCopyPolicy,
12 PackagePublishingStatus,
13@@ -2394,14 +2395,40 @@ class ArchiveAdminView(BaseArchiveEditView, EnableProcessorsMixin):
14 """Validate the save action on ArchiveAdminView."""
15 super().validate_save(action, data)
16
17+ # XXX cjwatson 2023-02-22: This check duplicates
18+ # Archive._validate_archive_privacy. Can we avoid this and just
19+ # catch exceptions somewhere?
20 if data.get("private") != self.context.private:
21 # The privacy is being switched.
22 if not self.context.getPublishedSources().is_empty():
23- self.setFieldError(
24- "private",
25- "This archive already has published sources. It is "
26- "not possible to switch the privacy.",
27- )
28+ if (
29+ # For local publishing, we can't switch privacy after
30+ # anything has been published to it, because the publisher
31+ # uses different paths on disk for public and private
32+ # archives.
33+ self.context.publishing_method
34+ == ArchivePublishingMethod.LOCAL
35+ # Refuse to switch from private to public even for
36+ # non-local publishing, partly as a safety measure and
37+ # partly because the files in the archive would have to be
38+ # unrestricted and we don't have code to do that yet.
39+ #
40+ # Switching an archive from public to private should
41+ # ideally also restrict any files published only in that
42+ # archive. However, that's quite complex because we'd
43+ # have to check whether the files are published anywhere
44+ # else, and nothing breaks if we leave the files alone.
45+ # It's not ideal that we might have files reachable from
46+ # the public librarian that are now only published in a
47+ # private archive, but since Launchpad won't give out any
48+ # links to those, it could be worse.
49+ or self.context.private
50+ ):
51+ self.setFieldError(
52+ "private",
53+ "This archive already has published sources. It is "
54+ "not possible to switch the privacy.",
55+ )
56
57 if self.owner_is_private_team and not data["private"]:
58 self.setFieldError(
59diff --git a/lib/lp/soyuz/browser/tests/test_archive_admin_view.py b/lib/lp/soyuz/browser/tests/test_archive_admin_view.py
60index d31966c..fe9cd31 100644
61--- a/lib/lp/soyuz/browser/tests/test_archive_admin_view.py
62+++ b/lib/lp/soyuz/browser/tests/test_archive_admin_view.py
63@@ -81,8 +81,8 @@ class TestArchiveAdminView(TestCaseWithFactory):
64 self.assertEqual(0, len(view.errors))
65 self.assertFalse(view.context.private)
66
67- def test_set_private_with_packages(self):
68- # A PPA that does have packages cannot be privatised.
69+ def test_set_private_with_packages_local(self):
70+ # A local PPA that does have packages cannot be made private.
71 self.publish_to_ppa(self.ppa)
72 view = self.initialize_admin_view(self.ppa, {"field.private": "on"})
73 self.assertEqual(1, len(view.errors))
74@@ -92,9 +92,32 @@ class TestArchiveAdminView(TestCaseWithFactory):
75 view.errors[0],
76 )
77
78- def test_set_public_with_packages(self):
79- # A PPA that does have (or had) packages published is presented
80- # with a disabled 'private' field.
81+ def test_set_public_with_packages_local(self):
82+ # A local PPA that does have (or had) packages published cannot be
83+ # made public.
84+ self.ppa.private = True
85+ self.publish_to_ppa(self.ppa)
86+
87+ view = self.initialize_admin_view(self.ppa, {"field.private": "off"})
88+ self.assertEqual(1, len(view.errors))
89+ self.assertEqual(
90+ "This archive already has published sources. "
91+ "It is not possible to switch the privacy.",
92+ view.errors[0],
93+ )
94+
95+ def test_set_private_with_packages_artifactory(self):
96+ # An Artifactory PPA that does have packages can be made private.
97+ self.ppa.publishing_method = ArchivePublishingMethod.ARTIFACTORY
98+ self.publish_to_ppa(self.ppa)
99+ view = self.initialize_admin_view(self.ppa, {"field.private": "on"})
100+ self.assertEqual(0, len(view.errors))
101+ self.assertTrue(view.context.private)
102+
103+ def test_set_public_with_packages_artifactory(self):
104+ # An Artifactory PPA that does have (or had) packages published
105+ # cannot be made public.
106+ self.ppa.publishing_method = ArchivePublishingMethod.ARTIFACTORY
107 self.ppa.private = True
108 self.publish_to_ppa(self.ppa)
109
110diff --git a/lib/lp/soyuz/model/archive.py b/lib/lp/soyuz/model/archive.py
111index 32b23dd..e5ed691 100644
112--- a/lib/lp/soyuz/model/archive.py
113+++ b/lib/lp/soyuz/model/archive.py
114@@ -243,13 +243,33 @@ class Archive(SQLBase):
115 self.owner.visibility != PersonVisibility.PRIVATE
116 ), "Private teams may not have public PPAs."
117
118- # If the privacy is being changed ensure there are no sources
119- # published.
120 if not self.getPublishedSources().is_empty():
121- raise CannotSwitchPrivacy(
122- "This archive has had sources published and therefore "
123- "cannot have its privacy switched."
124- )
125+ if (
126+ # For local publishing, we can't switch privacy after
127+ # anything has been published to it, because the publisher
128+ # uses different paths on disk for public and private
129+ # archives.
130+ self.publishing_method == ArchivePublishingMethod.LOCAL
131+ # Refuse to switch from private to public even for non-local
132+ # publishing, partly as a safety measure and partly because
133+ # the files in the archive would have to be unrestricted and
134+ # we don't have code to do that yet.
135+ #
136+ # Switching an archive from public to private should ideally
137+ # also restrict any files published only in that archive.
138+ # However, that's quite complex because we'd have to check
139+ # whether the files are published anywhere else, and nothing
140+ # breaks if we leave the files alone. It's not ideal that
141+ # we might have files reachable from the public librarian
142+ # that are now only published in a private archive, but
143+ # since Launchpad won't give out any links to those, it
144+ # could be worse.
145+ or not value
146+ ):
147+ raise CannotSwitchPrivacy(
148+ "This archive has had sources published and therefore "
149+ "cannot have its privacy switched."
150+ )
151
152 return value
153
154diff --git a/lib/lp/soyuz/tests/test_archive_privacy.py b/lib/lp/soyuz/tests/test_archive_privacy.py
155index 6e06174..581bf64 100644
156--- a/lib/lp/soyuz/tests/test_archive_privacy.py
157+++ b/lib/lp/soyuz/tests/test_archive_privacy.py
158@@ -5,6 +5,7 @@
159
160 from zope.security.interfaces import Unauthorized
161
162+from lp.soyuz.enums import ArchivePublishingMethod
163 from lp.soyuz.interfaces.archive import CannotSwitchPrivacy
164 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
165 from lp.testing import (
166@@ -77,7 +78,7 @@ class TestPrivacySwitching(TestCaseWithFactory):
167 private_ppa.private = False
168 self.assertFalse(private_ppa.private)
169
170- def test_switch_privacy_with_pubs_fails(self):
171+ def test_switch_privacy_with_pubs_fails_local(self):
172 # Changing the privacy is not possible when the archive already
173 # has published sources.
174 public_ppa = self.factory.makeArchive(private=False)
175@@ -95,3 +96,31 @@ class TestPrivacySwitching(TestCaseWithFactory):
176 self.assertRaises(
177 CannotSwitchPrivacy, setattr, private_ppa, "private", False
178 )
179+
180+ def test_make_private_with_pubs_succeeds_artifactory(self):
181+ # Making a public Artifactory archive private is fine even if the
182+ # archive already has published sources.
183+ public_ppa = self.factory.makeArchive(
184+ private=False,
185+ publishing_method=ArchivePublishingMethod.ARTIFACTORY,
186+ )
187+ publisher = SoyuzTestPublisher()
188+ publisher.prepareBreezyAutotest()
189+ publisher.getPubSource(archive=public_ppa)
190+
191+ public_ppa.private = True
192+ self.assertTrue(public_ppa.private)
193+
194+ def test_make_public_with_pubs_fails_artifactory(self):
195+ # Making a public Artifactory archive private fails if the archive
196+ # already has published sources.
197+ private_ppa = self.factory.makeArchive(
198+ private=True, publishing_method=ArchivePublishingMethod.ARTIFACTORY
199+ )
200+ publisher = SoyuzTestPublisher()
201+ publisher.prepareBreezyAutotest()
202+ publisher.getPubSource(archive=private_ppa)
203+
204+ self.assertRaises(
205+ CannotSwitchPrivacy, setattr, private_ppa, "private", False
206+ )

Subscribers

People subscribed via source and target branches

to status/vote changes: