Merge ~cjwatson/launchpad:artifactory-pypi into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: de9a1370945f3348ad8762721a898aa1f925d76a
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:artifactory-pypi
Merge into: launchpad:master
Prerequisite: ~cjwatson/launchpad:diskpool-add-source-version
Diff against target: 659 lines (+284/-57)
5 files modified
lib/lp/archivepublisher/artifactory.py (+21/-7)
lib/lp/archivepublisher/config.py (+7/-2)
lib/lp/archivepublisher/tests/test_artifactory.py (+207/-47)
lib/lp/archivepublisher/tests/test_config.py (+37/-1)
lib/lp/registry/interfaces/sourcepackage.py (+12/-0)
Reviewer Review Type Date Requested Status
Jürgen Gmach Approve
Review via email: mp+423563@code.launchpad.net

Commit message

Handle path changes to publish Python packages via Artifactory

Description of the change

Unlike .debs, Python packages (sdists and wheels) are published in <name>/<version>/ subdirectories.

To post a comment you must log in.
Revision history for this message
Jürgen Gmach (jugmac00) :
review: Approve
Revision history for this message
Otto Co-Pilot (otto-copilot) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/archivepublisher/artifactory.py b/lib/lp/archivepublisher/artifactory.py
2index 53b43a6..5ce6460 100644
3--- a/lib/lp/archivepublisher/artifactory.py
4+++ b/lib/lp/archivepublisher/artifactory.py
5@@ -41,6 +41,21 @@ from lp.soyuz.interfaces.publishing import (
6 )
7
8
9+def _path_for(archive: IArchive, rootpath: ArtifactoryPath, source_name: str,
10+ source_version: str, filename: Optional[str] = None) -> Path:
11+ repository_format = archive.repository_format
12+ if repository_format == ArchiveRepositoryFormat.DEBIAN:
13+ path = rootpath / poolify(source_name)
14+ elif repository_format == ArchiveRepositoryFormat.PYTHON:
15+ path = rootpath / source_name / source_version
16+ else:
17+ raise AssertionError(
18+ "Unsupported repository format: %r" % repository_format)
19+ if filename:
20+ path = path / filename
21+ return path
22+
23+
24 class ArtifactoryPoolEntry:
25
26 def __init__(self, archive: IArchive, rootpath: ArtifactoryPath,
27@@ -63,7 +78,9 @@ class ArtifactoryPoolEntry:
28 # the pool structure, and doing so would introduce significant
29 # complications in terms of having to keep track of components just
30 # in order to update an artifact's properties.
31- return self.rootpath / poolify(self.source_name) / self.filename
32+ return _path_for(
33+ self.archive, self.rootpath, self.source_name, self.source_version,
34+ self.filename)
35
36 def makeReleaseID(self, pub_file: IPackageReleaseFile) -> str:
37 """
38@@ -156,8 +173,7 @@ class ArtifactoryPoolEntry:
39 else:
40 properties["launchpad.channel"] = sorted({
41 "%s:%s" % (
42- pub.distroseries.getSuite(pub.pocket),
43- pub.channel_string)
44+ pub.distroseries.getSuite(pub.pocket), pub.channel)
45 for pub in publications})
46 return properties
47
48@@ -285,10 +301,8 @@ class ArtifactoryPool:
49 # the pool structure, and doing so would introduce significant
50 # complications in terms of having to keep track of components just
51 # in order to update an artifact's properties.
52- path = self.rootpath / poolify(source_name)
53- if file:
54- path = path / file
55- return path
56+ return _path_for(
57+ self.archive, self.rootpath, source_name, source_version, file)
58
59 def addFile(self, component: str, source_name: str, source_version: str,
60 filename: str, pub_file: IPackageReleaseFile):
61diff --git a/lib/lp/archivepublisher/config.py b/lib/lp/archivepublisher/config.py
62index 55603dc..06ae2a7 100644
63--- a/lib/lp/archivepublisher/config.py
64+++ b/lib/lp/archivepublisher/config.py
65@@ -20,6 +20,7 @@ from lp.soyuz.enums import (
66 archive_suffixes,
67 ArchivePublishingMethod,
68 ArchivePurpose,
69+ ArchiveRepositoryFormat,
70 )
71
72
73@@ -111,8 +112,12 @@ def getPubConfig(archive):
74 pubconf.signingroot = None
75 pubconf.signingautokey = False
76
77- pubconf.poolroot = os.path.join(pubconf.archiveroot, 'pool')
78- pubconf.distsroot = os.path.join(pubconf.archiveroot, 'dists')
79+ if archive.repository_format == ArchiveRepositoryFormat.DEBIAN:
80+ pubconf.poolroot = os.path.join(pubconf.archiveroot, 'pool')
81+ pubconf.distsroot = os.path.join(pubconf.archiveroot, 'dists')
82+ else:
83+ pubconf.poolroot = pubconf.archiveroot
84+ pubconf.distsroot = None
85
86 # META_DATA custom uploads are stored in a separate directory
87 # outside the archive root so Ubuntu Software Center can get some
88diff --git a/lib/lp/archivepublisher/tests/test_artifactory.py b/lib/lp/archivepublisher/tests/test_artifactory.py
89index 241f277..8f3e7b1 100644
90--- a/lib/lp/archivepublisher/tests/test_artifactory.py
91+++ b/lib/lp/archivepublisher/tests/test_artifactory.py
92@@ -5,6 +5,7 @@
93
94 from pathlib import PurePath
95
96+from artifactory import ArtifactoryPath
97 import transaction
98 from zope.component import getUtility
99
100@@ -18,12 +19,16 @@ from lp.archivepublisher.tests.test_pool import (
101 PoolTestingFile,
102 )
103 from lp.registry.interfaces.pocket import PackagePublishingPocket
104-from lp.registry.interfaces.sourcepackage import SourcePackageFileType
105+from lp.registry.interfaces.sourcepackage import (
106+ SourcePackageFileType,
107+ SourcePackageType,
108+ )
109 from lp.services.log.logger import BufferLogger
110 from lp.soyuz.enums import (
111 ArchivePurpose,
112 ArchiveRepositoryFormat,
113 BinaryPackageFileType,
114+ BinaryPackageFormat,
115 )
116 from lp.soyuz.interfaces.publishing import (
117 IPublishingSet,
118@@ -77,17 +82,54 @@ class TestArtifactoryPool(TestCase):
119 self.repository_name = "repository"
120 self.artifactory = self.useFixture(
121 FakeArtifactoryFixture(self.base_url, self.repository_name))
122- root_url = "%s/%s/pool" % (self.base_url, self.repository_name)
123- self.pool = ArtifactoryPool(FakeArchive(), root_url, BufferLogger())
124+
125+ def makePool(self, repository_format=ArchiveRepositoryFormat.DEBIAN):
126+ # Matches behaviour of lp.archivepublisher.config.getPubConfig.
127+ root_url = "%s/%s" % (self.base_url, self.repository_name)
128+ if repository_format == ArchiveRepositoryFormat.DEBIAN:
129+ root_url += "/pool"
130+ return ArtifactoryPool(
131+ FakeArchive(repository_format), root_url, BufferLogger())
132+
133+ def test_pathFor_debian_without_file(self):
134+ pool = self.makePool()
135+ self.assertEqual(
136+ ArtifactoryPath(
137+ "https://foo.example.com/artifactory/repository/pool/f/foo"),
138+ pool.pathFor(None, "foo", "1.0"))
139+
140+ def test_pathFor_debian_with_file(self):
141+ pool = self.makePool()
142+ self.assertEqual(
143+ ArtifactoryPath(
144+ "https://foo.example.com/artifactory/repository/pool/f/foo/"
145+ "foo-1.0.deb"),
146+ pool.pathFor(None, "foo", "1.0", "foo-1.0.deb"))
147+
148+ def test_pathFor_python_without_file(self):
149+ pool = self.makePool(ArchiveRepositoryFormat.PYTHON)
150+ self.assertEqual(
151+ ArtifactoryPath(
152+ "https://foo.example.com/artifactory/repository/foo/1.0"),
153+ pool.pathFor(None, "foo", "1.0"))
154+
155+ def test_pathFor_python_with_file(self):
156+ pool = self.makePool(ArchiveRepositoryFormat.PYTHON)
157+ self.assertEqual(
158+ ArtifactoryPath(
159+ "https://foo.example.com/artifactory/repository/foo/1.0/"
160+ "foo-1.0.whl"),
161+ pool.pathFor(None, "foo", "1.0", "foo-1.0.whl"))
162
163 def test_addFile(self):
164+ pool = self.makePool()
165 foo = ArtifactoryPoolTestingFile(
166- pool=self.pool, source_name="foo", source_version="1.0",
167+ pool=pool, source_name="foo", source_version="1.0",
168 filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
169 release_id=1)
170 self.assertFalse(foo.checkIsFile())
171 result = foo.addToPool()
172- self.assertEqual(self.pool.results.FILE_ADDED, result)
173+ self.assertEqual(pool.results.FILE_ADDED, result)
174 self.assertTrue(foo.checkIsFile())
175 self.assertEqual(
176 {
177@@ -98,19 +140,21 @@ class TestArtifactoryPool(TestCase):
178 foo.getProperties())
179
180 def test_addFile_exists_identical(self):
181+ pool = self.makePool()
182 foo = ArtifactoryPoolTestingFile(
183- pool=self.pool, source_name="foo", source_version="1.0",
184+ pool=pool, source_name="foo", source_version="1.0",
185 filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
186 release_id=1)
187 foo.addToPool()
188 self.assertTrue(foo.checkIsFile())
189 result = foo.addToPool()
190- self.assertEqual(self.pool.results.NONE, result)
191+ self.assertEqual(pool.results.NONE, result)
192 self.assertTrue(foo.checkIsFile())
193
194 def test_addFile_exists_overwrite(self):
195+ pool = self.makePool()
196 foo = ArtifactoryPoolTestingFile(
197- pool=self.pool, source_name="foo", source_version="1.0",
198+ pool=pool, source_name="foo", source_version="1.0",
199 filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
200 release_id=1)
201 foo.addToPool()
202@@ -119,8 +163,9 @@ class TestArtifactoryPool(TestCase):
203 self.assertRaises(PoolFileOverwriteError, foo.addToPool)
204
205 def test_removeFile(self):
206+ pool = self.makePool()
207 foo = ArtifactoryPoolTestingFile(
208- pool=self.pool, source_name="foo", source_version="1.0",
209+ pool=pool, source_name="foo", source_version="1.0",
210 filename="foo-1.0.deb")
211 foo.addToPool()
212 self.assertTrue(foo.checkIsFile())
213@@ -129,6 +174,7 @@ class TestArtifactoryPool(TestCase):
214 self.assertEqual(3, size)
215
216 def test_getArtifactPatterns_debian(self):
217+ pool = self.makePool()
218 self.assertEqual(
219 [
220 "*.ddeb",
221@@ -138,12 +184,13 @@ class TestArtifactoryPool(TestCase):
222 "*.tar.*",
223 "*.udeb",
224 ],
225- self.pool.getArtifactPatterns(ArchiveRepositoryFormat.DEBIAN))
226+ pool.getArtifactPatterns(ArchiveRepositoryFormat.DEBIAN))
227
228 def test_getArtifactPatterns_python(self):
229+ pool = self.makePool()
230 self.assertEqual(
231 ["*.whl"],
232- self.pool.getArtifactPatterns(ArchiveRepositoryFormat.PYTHON))
233+ pool.getArtifactPatterns(ArchiveRepositoryFormat.PYTHON))
234
235 def test_getAllArtifacts(self):
236 # getAllArtifacts mostly relies on constructing a correct AQL query,
237@@ -151,16 +198,17 @@ class TestArtifactoryPool(TestCase):
238 # instance, although `FakeArtifactoryFixture` tries to do something
239 # with it. This test mainly ensures that we transform the response
240 # correctly.
241+ pool = self.makePool()
242 ArtifactoryPoolTestingFile(
243- pool=self.pool, source_name="foo", source_version="1.0",
244+ pool=pool, source_name="foo", source_version="1.0",
245 filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
246 release_id=1).addToPool()
247 ArtifactoryPoolTestingFile(
248- pool=self.pool, source_name="foo", source_version="1.1",
249+ pool=pool, source_name="foo", source_version="1.1",
250 filename="foo-1.1.deb", release_type=FakeReleaseType.BINARY,
251 release_id=2).addToPool()
252 ArtifactoryPoolTestingFile(
253- pool=self.pool, source_name="bar", source_version="1.0",
254+ pool=pool, source_name="bar", source_version="1.0",
255 filename="bar-1.0.whl", release_type=FakeReleaseType.BINARY,
256 release_id=3).addToPool()
257 self.assertEqual(
258@@ -176,7 +224,7 @@ class TestArtifactoryPool(TestCase):
259 "launchpad.source-version": ["1.1"],
260 },
261 },
262- self.pool.getAllArtifacts(
263+ pool.getAllArtifacts(
264 self.repository_name, ArchiveRepositoryFormat.DEBIAN))
265 self.assertEqual(
266 {
267@@ -186,7 +234,7 @@ class TestArtifactoryPool(TestCase):
268 "launchpad.source-version": ["1.0"],
269 },
270 },
271- self.pool.getAllArtifacts(
272+ pool.getAllArtifacts(
273 self.repository_name, ArchiveRepositoryFormat.PYTHON))
274
275
276@@ -200,17 +248,24 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
277 self.repository_name = "repository"
278 self.artifactory = self.useFixture(
279 FakeArtifactoryFixture(self.base_url, self.repository_name))
280- root_url = "%s/%s/pool" % (self.base_url, self.repository_name)
281- self.archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
282- self.pool = ArtifactoryPool(self.archive, root_url, BufferLogger())
283+
284+ def makePool(self, repository_format=ArchiveRepositoryFormat.DEBIAN):
285+ # Matches behaviour of lp.archivepublisher.config.getPubConfig.
286+ root_url = "%s/%s" % (self.base_url, self.repository_name)
287+ if repository_format == ArchiveRepositoryFormat.DEBIAN:
288+ root_url += "/pool"
289+ archive = self.factory.makeArchive(
290+ purpose=ArchivePurpose.PPA, repository_format=repository_format)
291+ return ArtifactoryPool(archive, root_url, BufferLogger())
292
293 def test_updateProperties_debian_source(self):
294+ pool = self.makePool()
295 dses = [
296 self.factory.makeDistroSeries(
297- distribution=self.archive.distribution)
298+ distribution=pool.archive.distribution)
299 for _ in range(2)]
300 spph = self.factory.makeSourcePackagePublishingHistory(
301- archive=self.archive, distroseries=dses[0],
302+ archive=pool.archive, distroseries=dses[0],
303 pocket=PackagePublishingPocket.RELEASE, component="main",
304 sourcepackagename="foo", version="1.0")
305 spr = spph.sourcepackagerelease
306@@ -221,11 +276,11 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
307 filetype=SourcePackageFileType.DSC)
308 spphs = [spph]
309 spphs.append(spph.copyTo(
310- dses[1], PackagePublishingPocket.RELEASE, self.archive))
311+ dses[1], PackagePublishingPocket.RELEASE, pool.archive))
312 transaction.commit()
313- self.pool.addFile(
314+ pool.addFile(
315 None, spr.name, spr.version, sprf.libraryfile.filename, sprf)
316- path = self.pool.rootpath / "f" / "foo" / "foo_1.0.dsc"
317+ path = pool.rootpath / "f" / "foo" / "foo_1.0.dsc"
318 self.assertTrue(path.exists())
319 self.assertFalse(path.is_symlink())
320 self.assertEqual(
321@@ -235,7 +290,7 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
322 "launchpad.source-version": ["1.0"],
323 },
324 path.properties)
325- self.pool.updateProperties(
326+ pool.updateProperties(
327 spr.name, spr.version, sprf.libraryfile.filename, spphs)
328 self.assertEqual(
329 {
330@@ -248,9 +303,10 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
331 path.properties)
332
333 def test_updateProperties_debian_binary_multiple_series(self):
334+ pool = self.makePool()
335 dses = [
336 self.factory.makeDistroSeries(
337- distribution=self.archive.distribution)
338+ distribution=pool.archive.distribution)
339 for _ in range(2)]
340 processor = self.factory.makeProcessor()
341 dases = [
342@@ -258,9 +314,9 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
343 distroseries=ds, architecturetag=processor.name)
344 for ds in dses]
345 spr = self.factory.makeSourcePackageRelease(
346- archive=self.archive, sourcepackagename="foo", version="1.0")
347+ archive=pool.archive, sourcepackagename="foo", version="1.0")
348 bpph = self.factory.makeBinaryPackagePublishingHistory(
349- archive=self.archive, distroarchseries=dases[0],
350+ archive=pool.archive, distroarchseries=dases[0],
351 pocket=PackagePublishingPocket.RELEASE, component="main",
352 source_package_release=spr, binarypackagename="foo",
353 architecturespecific=True)
354@@ -272,14 +328,13 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
355 filetype=BinaryPackageFileType.DEB)
356 bpphs = [bpph]
357 bpphs.append(bpph.copyTo(
358- dses[1], PackagePublishingPocket.RELEASE, self.archive)[0])
359+ dses[1], PackagePublishingPocket.RELEASE, pool.archive)[0])
360 transaction.commit()
361- self.pool.addFile(
362+ pool.addFile(
363 None, bpr.sourcepackagename, bpr.sourcepackageversion,
364 bpf.libraryfile.filename, bpf)
365 path = (
366- self.pool.rootpath / "f" / "foo" /
367- ("foo_1.0_%s.deb" % processor.name))
368+ pool.rootpath / "f" / "foo" / ("foo_1.0_%s.deb" % processor.name))
369 self.assertTrue(path.exists())
370 self.assertFalse(path.is_symlink())
371 self.assertEqual(
372@@ -289,7 +344,7 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
373 "launchpad.source-version": ["1.0"],
374 },
375 path.properties)
376- self.pool.updateProperties(
377+ pool.updateProperties(
378 bpr.sourcepackagename, bpr.sourcepackageversion,
379 bpf.libraryfile.filename, bpphs)
380 self.assertEqual(
381@@ -304,15 +359,16 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
382 path.properties)
383
384 def test_updateProperties_debian_binary_multiple_architectures(self):
385+ pool = self.makePool()
386 ds = self.factory.makeDistroSeries(
387- distribution=self.archive.distribution)
388+ distribution=pool.archive.distribution)
389 dases = [
390 self.factory.makeDistroArchSeries(distroseries=ds)
391 for _ in range(2)]
392 spr = self.factory.makeSourcePackageRelease(
393- archive=self.archive, sourcepackagename="foo", version="1.0")
394+ archive=pool.archive, sourcepackagename="foo", version="1.0")
395 bpb = self.factory.makeBinaryPackageBuild(
396- archive=self.archive, source_package_release=spr,
397+ archive=pool.archive, source_package_release=spr,
398 distroarchseries=dases[0], pocket=PackagePublishingPocket.RELEASE)
399 bpr = self.factory.makeBinaryPackageRelease(
400 binarypackagename="foo", build=bpb, component="main",
401@@ -323,13 +379,13 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
402 filename="foo_1.0_all.deb"),
403 filetype=BinaryPackageFileType.DEB)
404 bpphs = getUtility(IPublishingSet).publishBinaries(
405- self.archive, ds, PackagePublishingPocket.RELEASE,
406+ pool.archive, ds, PackagePublishingPocket.RELEASE,
407 {bpr: (bpr.component, bpr.section, bpr.priority, None)})
408 transaction.commit()
409- self.pool.addFile(
410+ pool.addFile(
411 None, bpr.sourcepackagename, bpr.sourcepackageversion,
412 bpf.libraryfile.filename, bpf)
413- path = self.pool.rootpath / "f" / "foo" / "foo_1.0_all.deb"
414+ path = pool.rootpath / "f" / "foo" / "foo_1.0_all.deb"
415 self.assertTrue(path.exists())
416 self.assertFalse(path.is_symlink())
417 self.assertEqual(
418@@ -339,7 +395,7 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
419 "launchpad.source-version": ["1.0"],
420 },
421 path.properties)
422- self.pool.updateProperties(
423+ pool.updateProperties(
424 bpr.sourcepackagename, bpr.sourcepackageversion,
425 bpf.libraryfile.filename, bpphs)
426 self.assertEqual(
427@@ -354,16 +410,120 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
428 },
429 path.properties)
430
431+ def test_updateProperties_python_sdist(self):
432+ pool = self.makePool(ArchiveRepositoryFormat.PYTHON)
433+ dses = [
434+ self.factory.makeDistroSeries(
435+ distribution=pool.archive.distribution)
436+ for _ in range(2)]
437+ spph = self.factory.makeSourcePackagePublishingHistory(
438+ archive=pool.archive, distroseries=dses[0],
439+ pocket=PackagePublishingPocket.RELEASE, component="main",
440+ sourcepackagename="foo", version="1.0", channel="edge",
441+ format=SourcePackageType.SDIST)
442+ spr = spph.sourcepackagerelease
443+ sprf = self.factory.makeSourcePackageReleaseFile(
444+ sourcepackagerelease=spr,
445+ library_file=self.factory.makeLibraryFileAlias(
446+ filename="foo-1.0.tar.gz"),
447+ filetype=SourcePackageFileType.SDIST)
448+ spphs = [spph]
449+ spphs.append(spph.copyTo(
450+ dses[1], PackagePublishingPocket.RELEASE, pool.archive))
451+ transaction.commit()
452+ pool.addFile(
453+ None, spr.name, spr.version, sprf.libraryfile.filename, sprf)
454+ path = pool.rootpath / "foo" / "1.0" / "foo-1.0.tar.gz"
455+ self.assertTrue(path.exists())
456+ self.assertFalse(path.is_symlink())
457+ self.assertEqual(
458+ {
459+ "launchpad.release-id": ["source:%d" % spr.id],
460+ "launchpad.source-name": ["foo"],
461+ "launchpad.source-version": ["1.0"],
462+ },
463+ path.properties)
464+ pool.updateProperties(
465+ spr.name, spr.version, sprf.libraryfile.filename, spphs)
466+ self.assertEqual(
467+ {
468+ "launchpad.release-id": ["source:%d" % spr.id],
469+ "launchpad.source-name": ["foo"],
470+ "launchpad.source-version": ["1.0"],
471+ "launchpad.channel": list(
472+ sorted("%s:edge" % ds.name for ds in dses)),
473+ },
474+ path.properties)
475+
476+ def test_updateProperties_python_wheel(self):
477+ pool = self.makePool(ArchiveRepositoryFormat.PYTHON)
478+ dses = [
479+ self.factory.makeDistroSeries(
480+ distribution=pool.archive.distribution)
481+ for _ in range(2)]
482+ processor = self.factory.makeProcessor()
483+ dases = [
484+ self.factory.makeDistroArchSeries(
485+ distroseries=ds, architecturetag=processor.name)
486+ for ds in dses]
487+ spr = self.factory.makeSourcePackageRelease(
488+ archive=pool.archive, sourcepackagename="foo", version="1.0",
489+ format=SourcePackageType.SDIST)
490+ bpph = self.factory.makeBinaryPackagePublishingHistory(
491+ archive=pool.archive, distroarchseries=dases[0],
492+ pocket=PackagePublishingPocket.RELEASE, component="main",
493+ source_package_release=spr, binarypackagename="foo",
494+ binpackageformat=BinaryPackageFormat.WHL,
495+ architecturespecific=False, channel="edge")
496+ bpr = bpph.binarypackagerelease
497+ bpf = self.factory.makeBinaryPackageFile(
498+ binarypackagerelease=bpr,
499+ library_file=self.factory.makeLibraryFileAlias(
500+ filename="foo-1.0-py3-none-any.whl"),
501+ filetype=BinaryPackageFileType.WHL)
502+ bpphs = [bpph]
503+ bpphs.append(
504+ getUtility(IPublishingSet).copyBinaries(
505+ pool.archive, dses[1], PackagePublishingPocket.RELEASE, [bpph],
506+ channel="edge")[0])
507+ transaction.commit()
508+ pool.addFile(
509+ None, bpr.sourcepackagename, bpr.sourcepackageversion,
510+ bpf.libraryfile.filename, bpf)
511+ path = pool.rootpath / "foo" / "1.0" / "foo-1.0-py3-none-any.whl"
512+ self.assertTrue(path.exists())
513+ self.assertFalse(path.is_symlink())
514+ self.assertEqual(
515+ {
516+ "launchpad.release-id": ["binary:%d" % bpr.id],
517+ "launchpad.source-name": ["foo"],
518+ "launchpad.source-version": ["1.0"],
519+ },
520+ path.properties)
521+ pool.updateProperties(
522+ bpr.sourcepackagename, bpr.sourcepackageversion,
523+ bpf.libraryfile.filename, bpphs)
524+ self.assertEqual(
525+ {
526+ "launchpad.release-id": ["binary:%d" % bpr.id],
527+ "launchpad.source-name": ["foo"],
528+ "launchpad.source-version": ["1.0"],
529+ "launchpad.channel": list(
530+ sorted("%s:edge" % ds.name for ds in dses)),
531+ },
532+ path.properties)
533+
534 def test_updateProperties_preserves_externally_set_properties(self):
535 # Artifactory sets some properties by itself as part of scanning
536 # packages. We leave those untouched.
537+ pool = self.makePool()
538 ds = self.factory.makeDistroSeries(
539- distribution=self.archive.distribution)
540+ distribution=pool.archive.distribution)
541 das = self.factory.makeDistroArchSeries(distroseries=ds)
542 spr = self.factory.makeSourcePackageRelease(
543- archive=self.archive, sourcepackagename="foo", version="1.0")
544+ archive=pool.archive, sourcepackagename="foo", version="1.0")
545 bpb = self.factory.makeBinaryPackageBuild(
546- archive=self.archive, source_package_release=spr,
547+ archive=pool.archive, source_package_release=spr,
548 distroarchseries=das, pocket=PackagePublishingPocket.RELEASE)
549 bpr = self.factory.makeBinaryPackageRelease(
550 binarypackagename="foo", build=bpb, component="main",
551@@ -374,13 +534,13 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
552 filename="foo_1.0_all.deb"),
553 filetype=BinaryPackageFileType.DEB)
554 bpphs = getUtility(IPublishingSet).publishBinaries(
555- self.archive, ds, PackagePublishingPocket.RELEASE,
556+ pool.archive, ds, PackagePublishingPocket.RELEASE,
557 {bpr: (bpr.component, bpr.section, bpr.priority, None)})
558 transaction.commit()
559- self.pool.addFile(
560+ pool.addFile(
561 None, bpr.sourcepackagename, bpr.sourcepackageversion,
562 bpf.libraryfile.filename, bpf)
563- path = self.pool.rootpath / "f" / "foo" / "foo_1.0_all.deb"
564+ path = pool.rootpath / "f" / "foo" / "foo_1.0_all.deb"
565 path.set_properties({"deb.version": ["1.0"]}, recursive=False)
566 self.assertEqual(
567 {
568@@ -390,7 +550,7 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
569 "deb.version": ["1.0"],
570 },
571 path.properties)
572- self.pool.updateProperties(
573+ pool.updateProperties(
574 bpr.sourcepackagename, bpr.sourcepackageversion,
575 bpf.libraryfile.filename, bpphs)
576 self.assertEqual(
577diff --git a/lib/lp/archivepublisher/tests/test_config.py b/lib/lp/archivepublisher/tests/test_config.py
578index 915b4e7..75b1431 100644
579--- a/lib/lp/archivepublisher/tests/test_config.py
580+++ b/lib/lp/archivepublisher/tests/test_config.py
581@@ -16,7 +16,11 @@ from lp.archivepublisher.config import getPubConfig
582 from lp.registry.interfaces.distribution import IDistributionSet
583 from lp.services.config import config
584 from lp.services.log.logger import BufferLogger
585-from lp.soyuz.enums import ArchivePurpose
586+from lp.soyuz.enums import (
587+ ArchivePublishingMethod,
588+ ArchivePurpose,
589+ ArchiveRepositoryFormat,
590+ )
591 from lp.soyuz.interfaces.archive import IArchiveSet
592 from lp.testing import TestCaseWithFactory
593 from lp.testing.layers import ZopelessDatabaseLayer
594@@ -235,3 +239,35 @@ class TestGetPubConfigPPACompatUefi(TestCaseWithFactory):
595 signingroot = "/var/tmp/ppa-signing-keys.test/uefi/%s/%s" % (
596 self.ppa.owner.name, self.ppa.name)
597 self.assertEqual(signingroot, self.ppa_config.signingroot)
598+
599+
600+class TestGetPubConfigPPARepositoryFormatPython(TestCaseWithFactory):
601+
602+ layer = ZopelessDatabaseLayer
603+
604+ def setUp(self):
605+ super().setUp()
606+ self.base_url = "https://foo.example.com/artifactory"
607+ self.pushConfig("artifactory", base_url=self.base_url)
608+ self.ppa = self.factory.makeArchive(
609+ purpose=ArchivePurpose.PPA,
610+ publishing_method=ArchivePublishingMethod.ARTIFACTORY,
611+ repository_format=ArchiveRepositoryFormat.PYTHON)
612+ self.ppa_config = getPubConfig(self.ppa)
613+
614+ def test_config(self):
615+ # Python-format archives published via Artifactory use paths under
616+ # the Artifactory base URL, and have various features disabled that
617+ # only make sense for locally-published Debian-format archives.
618+ self.assertIsNone(self.ppa_config.distroroot)
619+ archiveroot = "%s/%s" % (self.base_url, self.ppa.name)
620+ self.assertEqual(archiveroot, self.ppa_config.archiveroot)
621+ self.assertEqual(archiveroot, self.ppa_config.poolroot)
622+ self.assertIsNone(self.ppa_config.distsroot)
623+ self.assertIsNone(self.ppa_config.overrideroot)
624+ self.assertIsNone(self.ppa_config.cacheroot)
625+ self.assertIsNone(self.ppa_config.miscroot)
626+ self.assertEqual(
627+ "/var/tmp/archive/%s-temp" % self.ppa.distribution.name,
628+ self.ppa_config.temproot)
629+ self.assertIsNone(self.ppa_config.metaroot)
630diff --git a/lib/lp/registry/interfaces/sourcepackage.py b/lib/lp/registry/interfaces/sourcepackage.py
631index eddd622..21d02ca 100644
632--- a/lib/lp/registry/interfaces/sourcepackage.py
633+++ b/lib/lp/registry/interfaces/sourcepackage.py
634@@ -418,6 +418,12 @@ class SourcePackageFileType(DBEnumeratedType):
635 This file is a detached signature for an Ubuntu component "orig"
636 file.""")
637
638+ SDIST = DBItem(11, """
639+ Python Source Distribution
640+
641+ This file is a Python source distribution ("sdist").
642+ """)
643+
644
645 class SourcePackageType(DBEnumeratedType):
646 """Source Package Format
647@@ -447,6 +453,12 @@ class SourcePackageType(DBEnumeratedType):
648 This is the source package format used by Gentoo.
649 """)
650
651+ SDIST = DBItem(4, """
652+ The Python Format
653+
654+ This is the source package format used by Python packages.
655+ """)
656+
657
658 class SourcePackageUrgency(DBEnumeratedType):
659 """Source Package Urgency

Subscribers

People subscribed via source and target branches

to status/vote changes: