Merge ~cjwatson/launchpad:diskpool-add-source-version into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: c16ac81d9570472366ba50b3c1d07147166cec81
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:diskpool-add-source-version
Merge into: launchpad:master
Prerequisite: ~cjwatson/launchpad:diskpool-add-archive
Diff against target: 888 lines (+191/-107)
12 files modified
lib/lp/archivepublisher/artifactory.py (+24/-17)
lib/lp/archivepublisher/deathrow.py (+10/-8)
lib/lp/archivepublisher/diskpool.py (+27/-22)
lib/lp/archivepublisher/model/ftparchive.py (+8/-1)
lib/lp/archivepublisher/publishing.py (+4/-3)
lib/lp/archivepublisher/tests/deathrow.txt (+1/-0)
lib/lp/archivepublisher/tests/test_artifactory.py (+63/-28)
lib/lp/archivepublisher/tests/test_deathrow.py (+1/-0)
lib/lp/archivepublisher/tests/test_ftparchive.py (+13/-12)
lib/lp/archivepublisher/tests/test_pool.py (+29/-13)
lib/lp/archivepublisher/tests/test_publisher.py (+6/-0)
lib/lp/soyuz/model/publishing.py (+5/-3)
Reviewer Review Type Date Requested Status
Jürgen Gmach Approve
Review via email: mp+423537@code.launchpad.net

Commit message

Tell DiskPool about source version when adding/removing files

Description of the change

Some repository formats, such as Python indexes, organize files by version as well as by name. To accommodate this, tell the disk pool about the source package version as well as the source package name when adding or removing files. The Artifactory pool implementation also needs to store this in a property so that it can construct an entry for the file when updating its properties.

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

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 2873855..53b43a6 100644
3--- a/lib/lp/archivepublisher/artifactory.py
4+++ b/lib/lp/archivepublisher/artifactory.py
5@@ -44,9 +44,12 @@ from lp.soyuz.interfaces.publishing import (
6 class ArtifactoryPoolEntry:
7
8 def __init__(self, archive: IArchive, rootpath: ArtifactoryPath,
9- source: str, filename: str, logger: logging.Logger) -> None:
10+ source_name: str, source_version: str, filename: str,
11+ logger: logging.Logger) -> None:
12+ self.archive = archive
13 self.rootpath = rootpath
14- self.source = source
15+ self.source_name = source_name
16+ self.source_version = source_version
17 self.filename = filename
18 self.logger = logger
19
20@@ -60,7 +63,7 @@ class ArtifactoryPoolEntry:
21 # the pool structure, and doing so would introduce significant
22 # complications in terms of having to keep track of components just
23 # in order to update an artifact's properties.
24- return self.rootpath / poolify(self.source) / self.filename
25+ return self.rootpath / poolify(self.source_name) / self.filename
26
27 def makeReleaseID(self, pub_file: IPackageReleaseFile) -> str:
28 """
29@@ -129,7 +132,8 @@ class ArtifactoryPoolEntry:
30 """
31 properties = {}
32 properties["launchpad.release-id"] = [release_id]
33- properties["launchpad.source-name"] = [self.source]
34+ properties["launchpad.source-name"] = [self.source_name]
35+ properties["launchpad.source-version"] = [self.source_version]
36 if publications:
37 archives = {publication.archive for publication in publications}
38 if len(archives) > 1:
39@@ -259,12 +263,14 @@ class ArtifactoryPool:
40 session.auth = XJFrogArtApiAuth(write_creds.split(":", 1)[1])
41 return session
42
43- def _getEntry(self, sourcename, file) -> ArtifactoryPoolEntry:
44+ def _getEntry(self, source_name: str, source_version: str,
45+ file: str) -> ArtifactoryPoolEntry:
46 """See `DiskPool._getEntry`."""
47 return ArtifactoryPoolEntry(
48- self.archive, self.rootpath, sourcename, file, self.logger)
49+ self.archive, self.rootpath, source_name, source_version, file,
50+ self.logger)
51
52- def pathFor(self, comp: str, source: str,
53+ def pathFor(self, comp: str, source_name: str, source_version: str,
54 file: Optional[str] = None) -> Path:
55 """Return the path for the given pool folder or file.
56
57@@ -279,16 +285,17 @@ class ArtifactoryPool:
58 # the pool structure, and doing so would introduce significant
59 # complications in terms of having to keep track of components just
60 # in order to update an artifact's properties.
61- path = self.rootpath / poolify(source)
62+ path = self.rootpath / poolify(source_name)
63 if file:
64 path = path / file
65 return path
66
67- def addFile(self, component: str, sourcename: str, filename: str,
68- pub_file: IPackageReleaseFile):
69+ def addFile(self, component: str, source_name: str, source_version: str,
70+ filename: str, pub_file: IPackageReleaseFile):
71 """Add a file with the given contents to the pool.
72
73- `sourcename` and `filename` are used to calculate the location.
74+ `source_name`, `source_version`, and `filename` are used to
75+ calculate the location.
76
77 pub_file is an `IPackageReleaseFile` providing the file's contents
78 and SHA-1 hash. The SHA-1 hash is used to compare the given file
79@@ -308,10 +315,10 @@ class ArtifactoryPool:
80 This is similar to `DiskPool.addFile`, except that there is no
81 symlink handling and the component is ignored.
82 """
83- entry = self._getEntry(sourcename, filename)
84+ entry = self._getEntry(source_name, source_version, filename)
85 return entry.addFile(pub_file)
86
87- def removeFile(self, component: str, sourcename: str,
88+ def removeFile(self, component: str, source_name: str, source_version: str,
89 filename: str) -> int:
90 """Remove the specified file from the pool.
91
92@@ -324,13 +331,13 @@ class ArtifactoryPool:
93 This is similar to `DiskPool.removeFile`, except that there is no
94 symlink handling and the component is ignored.
95 """
96- entry = self._getEntry(sourcename, filename)
97+ entry = self._getEntry(source_name, source_version, filename)
98 return entry.removeFile()
99
100- def updateProperties(self, sourcename, filename, publications,
101- old_properties=None):
102+ def updateProperties(self, source_name: str, source_version: str,
103+ filename: str, publications, old_properties=None):
104 """Update a file's properties in Artifactory."""
105- entry = self._getEntry(sourcename, filename)
106+ entry = self._getEntry(source_name, source_version, filename)
107 entry.updateProperties(publications, old_properties=old_properties)
108
109 def getArtifactPatterns(self, repository_format):
110diff --git a/lib/lp/archivepublisher/deathrow.py b/lib/lp/archivepublisher/deathrow.py
111index 06e430a..9071a53 100644
112--- a/lib/lp/archivepublisher/deathrow.py
113+++ b/lib/lp/archivepublisher/deathrow.py
114@@ -87,10 +87,10 @@ class DeathRow:
115 removed."""
116 if dry_run:
117 # Don't actually remove the files if we are dry running
118- def _mockRemoveFile(cn, sn, fn):
119- self.logger.debug("(Not really!) removing %s %s/%s" %
120- (cn, sn, fn))
121- fullpath = self.diskpool.pathFor(cn, sn, fn)
122+ def _mockRemoveFile(cn, sn, sv, fn):
123+ self.logger.debug("(Not really!) removing %s %s/%s/%s" %
124+ (cn, sn, sv, fn))
125+ fullpath = self.diskpool.pathFor(cn, sn, sv, fn)
126 if not fullpath.exists():
127 raise NotInPool
128 return fullpath.lstat().st_size
129@@ -227,9 +227,10 @@ class DeathRow:
130
131 # Calculating the file path in pool.
132 pub_file_details = (
133- pub_file.libraryfile.filename,
134- pub_record.source_package_name,
135 pub_record.component_name,
136+ pub_record.source_package_name,
137+ pub_record.source_package_version,
138+ pub_file.libraryfile.filename,
139 )
140 file_path = str(self.diskpool.pathFor(*pub_file_details))
141
142@@ -264,10 +265,11 @@ class DeathRow:
143 "Removing %s files marked for reaping" % len(condemned_files))
144
145 for condemned_file in sorted(condemned_files, reverse=True):
146- file_name, source_name, component_name = details[condemned_file]
147+ component_name, source_name, source_version, file_name = (
148+ details[condemned_file])
149 try:
150 bytes += self._removeFile(
151- component_name, source_name, file_name)
152+ component_name, source_name, source_version, file_name)
153 except NotInPool as info:
154 # It's safe for us to let this slide because it means that
155 # the file is already gone.
156diff --git a/lib/lp/archivepublisher/diskpool.py b/lib/lp/archivepublisher/diskpool.py
157index 620c728..01ce763 100644
158--- a/lib/lp/archivepublisher/diskpool.py
159+++ b/lib/lp/archivepublisher/diskpool.py
160@@ -139,11 +139,13 @@ class DiskPoolEntry:
161 require manual removal after further investigation.
162 """
163 def __init__(self, archive: IArchive, rootpath: Path, temppath: Path,
164- source: str, filename: str, logger: logging.Logger) -> None:
165+ source_name: str, source_version: str, filename: str,
166+ logger: logging.Logger) -> None:
167 self.archive = archive
168 self.rootpath = rootpath
169 self.temppath = temppath
170- self.source = source
171+ self.source_name = source_name
172+ self.source_version = source_version
173 self.filename = filename
174 self.logger = logger
175
176@@ -165,7 +167,9 @@ class DiskPoolEntry:
177
178 def pathFor(self, component: str) -> Path:
179 """Return the path for this file in the given component."""
180- return self.rootpath / poolify(self.source, component) / self.filename
181+ return (
182+ self.rootpath / poolify(self.source_name, component) /
183+ self.filename)
184
185 def preferredComponent(self, add: Optional[str] = None,
186 remove: Optional[str] = None) -> Optional[str]:
187@@ -230,7 +234,7 @@ class DiskPoolEntry:
188 assert not targetpath.exists()
189
190 self.debug("Making new file in %s for %s/%s" %
191- (component, self.source, self.filename))
192+ (component, self.source_name, self.filename))
193
194 file_to_write = _diskpool_atomicfile(
195 targetpath, "wb", rootpath=self.temppath)
196@@ -253,13 +257,13 @@ class DiskPoolEntry:
197 if not self.file_component:
198 raise NotInPool(
199 "File for removing %s %s/%s is not in pool, skipping." %
200- (component, self.source, self.filename))
201+ (component, self.source_name, self.filename))
202
203 # Okay, it's there, if it's a symlink then we need to remove
204 # it simply.
205 if component in self.symlink_components:
206 self.debug("Removing %s %s/%s as it is a symlink"
207- % (component, self.source, self.filename))
208+ % (component, self.source_name, self.filename))
209 # ensure we are removing a symbolic link and
210 # it is published in one or more components
211 link_path = self.pathFor(component)
212@@ -269,13 +273,13 @@ class DiskPoolEntry:
213 if component != self.file_component:
214 raise MissingSymlinkInPool(
215 "Symlink for %s/%s in %s is missing, skipping." %
216- (self.source, self.filename, component))
217+ (self.source_name, self.filename, component))
218
219 # It's not a symlink, this means we need to check whether we
220 # have symlinks or not.
221 if len(self.symlink_components) == 0:
222 self.debug("Removing %s/%s from %s" %
223- (self.source, self.filename, component))
224+ (self.source_name, self.filename, component))
225 else:
226 # The target for removal is the real file, and there are symlinks
227 # pointing to it. In order to avoid breakage, we need to first
228@@ -398,33 +402,34 @@ class DiskPool:
229 self.entries = {}
230 self.logger = logger
231
232- def _getEntry(self, sourcename: str, file: str) -> DiskPoolEntry:
233- """Return a new DiskPoolEntry for the given sourcename and file."""
234+ def _getEntry(self, source_name: str, source_version: str,
235+ file: str) -> DiskPoolEntry:
236+ """Return a new DiskPoolEntry for the given source and file."""
237 return DiskPoolEntry(
238- self.archive, self.rootpath, self.temppath, sourcename,
239- file, self.logger)
240+ self.archive, self.rootpath, self.temppath, source_name,
241+ source_version, file, self.logger)
242
243- def pathFor(self, comp: str, source: str,
244+ def pathFor(self, comp: str, source_name: str, source_version: str,
245 file: Optional[str] = None) -> Path:
246 """Return the path for the given pool folder or file.
247
248 If file is none, the path to the folder containing all packages
249- for the given component and source package name will be returned.
250+ for the given component and source package will be returned.
251
252 If file is specified, the path to the specific package file will
253 be returned.
254 """
255- path = self.rootpath / poolify(source, comp)
256+ path = self.rootpath / poolify(source_name, comp)
257 if file:
258 path = path / file
259 return path
260
261- def addFile(self, component: str, sourcename: str, filename: str,
262- pub_file: IPackageReleaseFile):
263+ def addFile(self, component: str, source_name: str, source_version: str,
264+ filename: str, pub_file: IPackageReleaseFile):
265 """Add a file with the given contents to the pool.
266
267- Component, sourcename and filename are used to calculate the
268- on-disk location.
269+ `component`, `source_name`, `source_version`, and `filename` are
270+ used to calculate the on-disk location.
271
272 pub_file is an `IPackageReleaseFile` providing the file's contents
273 and SHA-1 hash. The SHA-1 hash is used to compare the given file
274@@ -450,10 +455,10 @@ class DiskPool:
275 either as a file or a symlink, and the hash check passes,
276 results.NONE will be returned and nothing will be done.
277 """
278- entry = self._getEntry(sourcename, filename)
279+ entry = self._getEntry(source_name, source_version, filename)
280 return entry.addFile(component, pub_file)
281
282- def removeFile(self, component: str, sourcename: str,
283+ def removeFile(self, component: str, source_name: str, source_version: str,
284 filename: str) -> int:
285 """Remove the specified file from the pool.
286
287@@ -469,5 +474,5 @@ class DiskPool:
288 will be deleted, and the file will be moved to replace it. The
289 size of the deleted symlink will be returned.
290 """
291- entry = self._getEntry(sourcename, filename)
292+ entry = self._getEntry(source_name, source_version, filename)
293 return entry.removeFile(component)
294diff --git a/lib/lp/archivepublisher/model/ftparchive.py b/lib/lp/archivepublisher/model/ftparchive.py
295index 469b1cb..7e94441 100644
296--- a/lib/lp/archivepublisher/model/ftparchive.py
297+++ b/lib/lp/archivepublisher/model/ftparchive.py
298@@ -693,8 +693,15 @@ class FTPArchiveHandler:
299
300 def updateFileList(sourcepackagename, filename, component,
301 architecturetag=None):
302+ # DiskPool.pathFor takes a source package version parameter. We
303+ # could fetch that in getSourceFiles/getBinaryFiles and pass it
304+ # down here. However, it adds another column to a query with an
305+ # already large number of rows, and it's only needed for
306+ # non-Debian-format archives, which by definition aren't
307+ # involved here; so we just pass None as the version.
308 ondiskname = str(
309- self._diskpool.pathFor(component, sourcepackagename, filename))
310+ self._diskpool.pathFor(
311+ component, sourcepackagename, None, filename))
312 if architecturetag is None:
313 architecturetag = "source"
314 filelist[component][architecturetag].append(ondiskname)
315diff --git a/lib/lp/archivepublisher/publishing.py b/lib/lp/archivepublisher/publishing.py
316index 69fb7e3..b0ddd32 100644
317--- a/lib/lp/archivepublisher/publishing.py
318+++ b/lib/lp/archivepublisher/publishing.py
319@@ -752,12 +752,13 @@ class Publisher:
320 for path, properties in sorted(artifacts.items()):
321 release_id = properties.get("launchpad.release-id")
322 source_name = properties.get("launchpad.source-name")
323- if not release_id or not source_name:
324+ source_version = properties.get("launchpad.source-version")
325+ if not release_id or not source_name or not source_version:
326 # Skip any files that Launchpad didn't put in Artifactory.
327 continue
328 self._diskpool.updateProperties(
329- source_name[0], path.name, pubs_by_id.get(release_id[0]),
330- old_properties=properties)
331+ source_name[0], source_version[0], path.name,
332+ pubs_by_id.get(release_id[0]), old_properties=properties)
333
334 def D_writeReleaseFiles(self, is_careful):
335 """Write out the Release files for the provided distribution.
336diff --git a/lib/lp/archivepublisher/tests/deathrow.txt b/lib/lp/archivepublisher/tests/deathrow.txt
337index 92b0752..5f8ac79 100644
338--- a/lib/lp/archivepublisher/tests/deathrow.txt
339+++ b/lib/lp/archivepublisher/tests/deathrow.txt
340@@ -208,6 +208,7 @@ Publish files on disk and build a list of all created file paths
341 ... file_path = quiet_disk_pool.pathFor(
342 ... pub.component.name,
343 ... pub.source_package_name,
344+ ... pub.source_package_version,
345 ... pub_file.libraryfile.filename
346 ... )
347 ... unique_file_paths.add(file_path)
348diff --git a/lib/lp/archivepublisher/tests/test_artifactory.py b/lib/lp/archivepublisher/tests/test_artifactory.py
349index 49f7ace..241f277 100644
350--- a/lib/lp/archivepublisher/tests/test_artifactory.py
351+++ b/lib/lp/archivepublisher/tests/test_artifactory.py
352@@ -62,7 +62,8 @@ class ArtifactoryPoolTestingFile(PoolTestingFile):
353 return super().checkIsFile(None)
354
355 def getProperties(self):
356- path = self.pool.pathFor(None, self.sourcename, self.filename)
357+ path = self.pool.pathFor(
358+ None, self.source_name, self.source_version, self.filename)
359 return path.properties
360
361
362@@ -81,8 +82,9 @@ class TestArtifactoryPool(TestCase):
363
364 def test_addFile(self):
365 foo = ArtifactoryPoolTestingFile(
366- self.pool, "foo", "foo-1.0.deb",
367- release_type=FakeReleaseType.BINARY, release_id=1)
368+ pool=self.pool, source_name="foo", source_version="1.0",
369+ filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
370+ release_id=1)
371 self.assertFalse(foo.checkIsFile())
372 result = foo.addToPool()
373 self.assertEqual(self.pool.results.FILE_ADDED, result)
374@@ -91,13 +93,15 @@ class TestArtifactoryPool(TestCase):
375 {
376 "launchpad.release-id": ["binary:1"],
377 "launchpad.source-name": ["foo"],
378+ "launchpad.source-version": ["1.0"],
379 },
380 foo.getProperties())
381
382 def test_addFile_exists_identical(self):
383 foo = ArtifactoryPoolTestingFile(
384- self.pool, "foo", "foo-1.0.deb",
385- release_type=FakeReleaseType.BINARY, release_id=1)
386+ pool=self.pool, source_name="foo", source_version="1.0",
387+ filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
388+ release_id=1)
389 foo.addToPool()
390 self.assertTrue(foo.checkIsFile())
391 result = foo.addToPool()
392@@ -106,15 +110,18 @@ class TestArtifactoryPool(TestCase):
393
394 def test_addFile_exists_overwrite(self):
395 foo = ArtifactoryPoolTestingFile(
396- self.pool, "foo", "foo-1.0.deb",
397- release_type=FakeReleaseType.BINARY, release_id=1)
398+ pool=self.pool, source_name="foo", source_version="1.0",
399+ filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
400+ release_id=1)
401 foo.addToPool()
402 self.assertTrue(foo.checkIsFile())
403 foo.contents = b"different"
404 self.assertRaises(PoolFileOverwriteError, foo.addToPool)
405
406 def test_removeFile(self):
407- foo = ArtifactoryPoolTestingFile(self.pool, "foo", "foo-1.0.deb")
408+ foo = ArtifactoryPoolTestingFile(
409+ pool=self.pool, source_name="foo", source_version="1.0",
410+ filename="foo-1.0.deb")
411 foo.addToPool()
412 self.assertTrue(foo.checkIsFile())
413 size = foo.removeFromPool()
414@@ -145,23 +152,28 @@ class TestArtifactoryPool(TestCase):
415 # with it. This test mainly ensures that we transform the response
416 # correctly.
417 ArtifactoryPoolTestingFile(
418- self.pool, "foo", "foo-1.0.deb",
419- release_type=FakeReleaseType.BINARY, release_id=1).addToPool()
420+ pool=self.pool, source_name="foo", source_version="1.0",
421+ filename="foo-1.0.deb", release_type=FakeReleaseType.BINARY,
422+ release_id=1).addToPool()
423 ArtifactoryPoolTestingFile(
424- self.pool, "foo", "foo-1.1.deb",
425- release_type=FakeReleaseType.BINARY, release_id=2).addToPool()
426+ pool=self.pool, source_name="foo", source_version="1.1",
427+ filename="foo-1.1.deb", release_type=FakeReleaseType.BINARY,
428+ release_id=2).addToPool()
429 ArtifactoryPoolTestingFile(
430- self.pool, "bar", "bar-1.0.whl",
431- release_type=FakeReleaseType.BINARY, release_id=3).addToPool()
432+ pool=self.pool, source_name="bar", source_version="1.0",
433+ filename="bar-1.0.whl", release_type=FakeReleaseType.BINARY,
434+ release_id=3).addToPool()
435 self.assertEqual(
436 {
437 PurePath("pool/f/foo/foo-1.0.deb"): {
438 "launchpad.release-id": ["binary:1"],
439 "launchpad.source-name": ["foo"],
440+ "launchpad.source-version": ["1.0"],
441 },
442 PurePath("pool/f/foo/foo-1.1.deb"): {
443 "launchpad.release-id": ["binary:2"],
444 "launchpad.source-name": ["foo"],
445+ "launchpad.source-version": ["1.1"],
446 },
447 },
448 self.pool.getAllArtifacts(
449@@ -171,6 +183,7 @@ class TestArtifactoryPool(TestCase):
450 PurePath("pool/b/bar/bar-1.0.whl"): {
451 "launchpad.release-id": ["binary:3"],
452 "launchpad.source-name": ["bar"],
453+ "launchpad.source-version": ["1.0"],
454 },
455 },
456 self.pool.getAllArtifacts(
457@@ -199,7 +212,7 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
458 spph = self.factory.makeSourcePackagePublishingHistory(
459 archive=self.archive, distroseries=dses[0],
460 pocket=PackagePublishingPocket.RELEASE, component="main",
461- sourcepackagename="foo")
462+ sourcepackagename="foo", version="1.0")
463 spr = spph.sourcepackagerelease
464 sprf = self.factory.makeSourcePackageReleaseFile(
465 sourcepackagerelease=spr,
466@@ -210,7 +223,8 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
467 spphs.append(spph.copyTo(
468 dses[1], PackagePublishingPocket.RELEASE, self.archive))
469 transaction.commit()
470- self.pool.addFile(None, spr.name, sprf.libraryfile.filename, sprf)
471+ self.pool.addFile(
472+ None, spr.name, spr.version, sprf.libraryfile.filename, sprf)
473 path = self.pool.rootpath / "f" / "foo" / "foo_1.0.dsc"
474 self.assertTrue(path.exists())
475 self.assertFalse(path.is_symlink())
476@@ -218,13 +232,16 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
477 {
478 "launchpad.release-id": ["source:%d" % spr.id],
479 "launchpad.source-name": ["foo"],
480+ "launchpad.source-version": ["1.0"],
481 },
482 path.properties)
483- self.pool.updateProperties(spr.name, sprf.libraryfile.filename, spphs)
484+ self.pool.updateProperties(
485+ spr.name, spr.version, sprf.libraryfile.filename, spphs)
486 self.assertEqual(
487 {
488 "launchpad.release-id": ["source:%d" % spr.id],
489 "launchpad.source-name": ["foo"],
490+ "launchpad.source-version": ["1.0"],
491 "deb.distribution": list(sorted(ds.name for ds in dses)),
492 "deb.component": ["main"],
493 },
494@@ -240,10 +257,12 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
495 self.factory.makeDistroArchSeries(
496 distroseries=ds, architecturetag=processor.name)
497 for ds in dses]
498+ spr = self.factory.makeSourcePackageRelease(
499+ archive=self.archive, sourcepackagename="foo", version="1.0")
500 bpph = self.factory.makeBinaryPackagePublishingHistory(
501 archive=self.archive, distroarchseries=dases[0],
502 pocket=PackagePublishingPocket.RELEASE, component="main",
503- sourcepackagename="foo", binarypackagename="foo",
504+ source_package_release=spr, binarypackagename="foo",
505 architecturespecific=True)
506 bpr = bpph.binarypackagerelease
507 bpf = self.factory.makeBinaryPackageFile(
508@@ -256,7 +275,8 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
509 dses[1], PackagePublishingPocket.RELEASE, self.archive)[0])
510 transaction.commit()
511 self.pool.addFile(
512- None, bpr.sourcepackagename, bpf.libraryfile.filename, bpf)
513+ None, bpr.sourcepackagename, bpr.sourcepackageversion,
514+ bpf.libraryfile.filename, bpf)
515 path = (
516 self.pool.rootpath / "f" / "foo" /
517 ("foo_1.0_%s.deb" % processor.name))
518@@ -266,14 +286,17 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
519 {
520 "launchpad.release-id": ["binary:%d" % bpr.id],
521 "launchpad.source-name": ["foo"],
522+ "launchpad.source-version": ["1.0"],
523 },
524 path.properties)
525 self.pool.updateProperties(
526- bpr.sourcepackagename, bpf.libraryfile.filename, bpphs)
527+ bpr.sourcepackagename, bpr.sourcepackageversion,
528+ bpf.libraryfile.filename, bpphs)
529 self.assertEqual(
530 {
531 "launchpad.release-id": ["binary:%d" % bpr.id],
532 "launchpad.source-name": ["foo"],
533+ "launchpad.source-version": ["1.0"],
534 "deb.distribution": list(sorted(ds.name for ds in dses)),
535 "deb.component": ["main"],
536 "deb.architecture": [processor.name],
537@@ -286,9 +309,11 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
538 dases = [
539 self.factory.makeDistroArchSeries(distroseries=ds)
540 for _ in range(2)]
541+ spr = self.factory.makeSourcePackageRelease(
542+ archive=self.archive, sourcepackagename="foo", version="1.0")
543 bpb = self.factory.makeBinaryPackageBuild(
544- archive=self.archive, distroarchseries=dases[0],
545- pocket=PackagePublishingPocket.RELEASE, sourcepackagename="foo")
546+ archive=self.archive, source_package_release=spr,
547+ distroarchseries=dases[0], pocket=PackagePublishingPocket.RELEASE)
548 bpr = self.factory.makeBinaryPackageRelease(
549 binarypackagename="foo", build=bpb, component="main",
550 architecturespecific=False)
551@@ -302,7 +327,8 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
552 {bpr: (bpr.component, bpr.section, bpr.priority, None)})
553 transaction.commit()
554 self.pool.addFile(
555- None, bpr.sourcepackagename, bpf.libraryfile.filename, bpf)
556+ None, bpr.sourcepackagename, bpr.sourcepackageversion,
557+ bpf.libraryfile.filename, bpf)
558 path = self.pool.rootpath / "f" / "foo" / "foo_1.0_all.deb"
559 self.assertTrue(path.exists())
560 self.assertFalse(path.is_symlink())
561@@ -310,14 +336,17 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
562 {
563 "launchpad.release-id": ["binary:%d" % bpr.id],
564 "launchpad.source-name": ["foo"],
565+ "launchpad.source-version": ["1.0"],
566 },
567 path.properties)
568 self.pool.updateProperties(
569- bpr.sourcepackagename, bpf.libraryfile.filename, bpphs)
570+ bpr.sourcepackagename, bpr.sourcepackageversion,
571+ bpf.libraryfile.filename, bpphs)
572 self.assertEqual(
573 {
574 "launchpad.release-id": ["binary:%d" % bpr.id],
575 "launchpad.source-name": ["foo"],
576+ "launchpad.source-version": ["1.0"],
577 "deb.distribution": [ds.name],
578 "deb.component": ["main"],
579 "deb.architecture": list(sorted(
580@@ -331,9 +360,11 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
581 ds = self.factory.makeDistroSeries(
582 distribution=self.archive.distribution)
583 das = self.factory.makeDistroArchSeries(distroseries=ds)
584+ spr = self.factory.makeSourcePackageRelease(
585+ archive=self.archive, sourcepackagename="foo", version="1.0")
586 bpb = self.factory.makeBinaryPackageBuild(
587- archive=self.archive, distroarchseries=das,
588- pocket=PackagePublishingPocket.RELEASE, sourcepackagename="foo")
589+ archive=self.archive, source_package_release=spr,
590+ distroarchseries=das, pocket=PackagePublishingPocket.RELEASE)
591 bpr = self.factory.makeBinaryPackageRelease(
592 binarypackagename="foo", build=bpb, component="main",
593 architecturespecific=False)
594@@ -347,22 +378,26 @@ class TestArtifactoryPoolFromLibrarian(TestCaseWithFactory):
595 {bpr: (bpr.component, bpr.section, bpr.priority, None)})
596 transaction.commit()
597 self.pool.addFile(
598- None, bpr.sourcepackagename, bpf.libraryfile.filename, bpf)
599+ None, bpr.sourcepackagename, bpr.sourcepackageversion,
600+ bpf.libraryfile.filename, bpf)
601 path = self.pool.rootpath / "f" / "foo" / "foo_1.0_all.deb"
602 path.set_properties({"deb.version": ["1.0"]}, recursive=False)
603 self.assertEqual(
604 {
605 "launchpad.release-id": ["binary:%d" % bpr.id],
606 "launchpad.source-name": ["foo"],
607+ "launchpad.source-version": ["1.0"],
608 "deb.version": ["1.0"],
609 },
610 path.properties)
611 self.pool.updateProperties(
612- bpr.sourcepackagename, bpf.libraryfile.filename, bpphs)
613+ bpr.sourcepackagename, bpr.sourcepackageversion,
614+ bpf.libraryfile.filename, bpphs)
615 self.assertEqual(
616 {
617 "launchpad.release-id": ["binary:%d" % bpr.id],
618 "launchpad.source-name": ["foo"],
619+ "launchpad.source-version": ["1.0"],
620 "deb.distribution": [ds.name],
621 "deb.component": ["main"],
622 "deb.architecture": [das.architecturetag],
623diff --git a/lib/lp/archivepublisher/tests/test_deathrow.py b/lib/lp/archivepublisher/tests/test_deathrow.py
624index 10efb6f..9fd62e5 100644
625--- a/lib/lp/archivepublisher/tests/test_deathrow.py
626+++ b/lib/lp/archivepublisher/tests/test_deathrow.py
627@@ -53,6 +53,7 @@ class TestDeathRow(TestCase):
628 return diskpool.pathFor(
629 pub.component.name,
630 pub.source_package_name,
631+ pub.source_package_version,
632 pub_file.libraryfile.filename)
633
634 def assertIsFile(self, path: Path) -> None:
635diff --git a/lib/lp/archivepublisher/tests/test_ftparchive.py b/lib/lp/archivepublisher/tests/test_ftparchive.py
636index 9ea5778..9b407a7 100755
637--- a/lib/lp/archivepublisher/tests/test_ftparchive.py
638+++ b/lib/lp/archivepublisher/tests/test_ftparchive.py
639@@ -147,10 +147,11 @@ class TestFTPArchive(TestCaseWithFactory):
640 with open_func(path) as result_file:
641 self.assertEqual(b"", result_file.read())
642
643- def _addRepositoryFile(self, component, sourcename, leafname,
644- samplename=None):
645+ def _addRepositoryFile(self, component, source_name, source_version,
646+ leafname, samplename=None):
647 """Create a repository file."""
648- fullpath = self._dp.pathFor(component, sourcename, leafname)
649+ fullpath = self._dp.pathFor(
650+ component, source_name, source_version, leafname)
651 fullpath.parent.mkdir(parents=True, exist_ok=True)
652 if samplename is None:
653 samplename = leafname
654@@ -515,9 +516,9 @@ class TestFTPArchive(TestCaseWithFactory):
655 self._publishDefaultFileLists(fa, 'main')
656
657 # Add mentioned files in the repository pool/.
658- self._addRepositoryFile('main', 'tiny', 'tiny_0.1.dsc')
659- self._addRepositoryFile('main', 'tiny', 'tiny_0.1.tar.gz')
660- self._addRepositoryFile('main', 'tiny', 'tiny_0.1_i386.deb')
661+ self._addRepositoryFile('main', 'tiny', '0.1', 'tiny_0.1.dsc')
662+ self._addRepositoryFile('main', 'tiny', '0.1', 'tiny_0.1.tar.gz')
663+ self._addRepositoryFile('main', 'tiny', '0.1', 'tiny_0.1_i386.deb')
664
665 # When include_long_descriptions is set, apt.conf has
666 # LongDescription "true" for that series.
667@@ -649,9 +650,9 @@ class TestFTPArchive(TestCaseWithFactory):
668 fa.createEmptyPocketRequests(fullpublish=True)
669 self._publishDefaultOverrides(fa, "main")
670 self._publishDefaultFileLists(fa, "main")
671- self._addRepositoryFile("main", "tiny", "tiny_0.1.dsc")
672- self._addRepositoryFile("main", "tiny", "tiny_0.1.tar.gz")
673- self._addRepositoryFile("main", "tiny", "tiny_0.1_i386.deb")
674+ self._addRepositoryFile("main", "tiny", "0.1", "tiny_0.1.dsc")
675+ self._addRepositoryFile("main", "tiny", "0.1", "tiny_0.1.tar.gz")
676+ self._addRepositoryFile("main", "tiny", "0.1", "tiny_0.1_i386.deb")
677 comp_dir = os.path.join(self._distsdir, "hoary-test", "main")
678 os.makedirs(os.path.join(comp_dir, "signed"))
679 with open(os.path.join(comp_dir, "signed", "stuff"), "w"):
680@@ -778,10 +779,10 @@ class TestFTPArchive(TestCaseWithFactory):
681 fa.publishFileLists(
682 self._distribution["hoary-test"], PackagePublishingPocket.RELEASE,
683 source_files, binary_files)
684- self._addRepositoryFile("main", "tiny", "tiny_0.1.dsc")
685+ self._addRepositoryFile("main", "tiny", "0.1", "tiny_0.1.dsc")
686 for i in range(50):
687 self._addRepositoryFile(
688- "main", "bin%d" % i, "bin%d_1_i386.deb" % i,
689+ "main", "bin%d" % i, "1", "bin%d_1_i386.deb" % i,
690 samplename="tiny_0.1_i386.deb")
691 apt_conf = fa.generateConfig(fullpublish=True)
692 fa.runApt(apt_conf)
693@@ -790,7 +791,7 @@ class TestFTPArchive(TestCaseWithFactory):
694 # something to do.
695 for i in range(49):
696 self._dp.pathFor(
697- "main", "bin%d" % i, "bin%d_1_i386.deb" % i).unlink()
698+ "main", "bin%d" % i, "1", "bin%d_1_i386.deb" % i).unlink()
699
700 cache_path = os.path.join(self._config.cacheroot, "packages-i386.db")
701 old_cache_size = os.stat(cache_path).st_size
702diff --git a/lib/lp/archivepublisher/tests/test_pool.py b/lib/lp/archivepublisher/tests/test_pool.py
703index 4beeea5..97669ff 100644
704--- a/lib/lp/archivepublisher/tests/test_pool.py
705+++ b/lib/lp/archivepublisher/tests/test_pool.py
706@@ -83,30 +83,34 @@ class FakePackageReleaseFile:
707
708 class PoolTestingFile:
709
710- def __init__(self, pool, sourcename, filename,
711+ def __init__(self, pool, source_name, source_version, filename,
712 release_type=FakeReleaseType.BINARY, release_id=1):
713 self.pool = pool
714- self.sourcename = sourcename
715+ self.source_name = source_name
716+ self.source_version = source_version
717 self.filename = filename
718- self.contents = sourcename.encode("UTF-8")
719+ self.contents = source_name.encode("UTF-8")
720 self.release_type = release_type
721 self.release_id = release_id
722
723 def addToPool(self, component: str):
724 return self.pool.addFile(
725- component, self.sourcename, self.filename,
726+ component, self.source_name, self.source_version, self.filename,
727 FakePackageReleaseFile(
728 self.contents, self.release_type, self.release_id))
729
730 def removeFromPool(self, component: str) -> int:
731- return self.pool.removeFile(component, self.sourcename, self.filename)
732+ return self.pool.removeFile(
733+ component, self.source_name, self.source_version, self.filename)
734
735 def checkExists(self, component: str) -> bool:
736- path = self.pool.pathFor(component, self.sourcename, self.filename)
737+ path = self.pool.pathFor(
738+ component, self.source_name, self.source_version, self.filename)
739 return path.exists()
740
741 def checkIsLink(self, component: str) -> bool:
742- path = self.pool.pathFor(component, self.sourcename, self.filename)
743+ path = self.pool.pathFor(
744+ component, self.source_name, self.source_version, self.filename)
745 return path.is_symlink()
746
747 def checkIsFile(self, component: str) -> bool:
748@@ -140,14 +144,18 @@ class TestPool(unittest.TestCase):
749
750 def testSimpleAdd(self):
751 """Adding a new file should work."""
752- foo = PoolTestingFile(self.pool, "foo", "foo-1.0.deb")
753+ foo = PoolTestingFile(
754+ pool=self.pool, source_name="foo", source_version="1.0",
755+ filename="foo-1.0.deb")
756 result = foo.addToPool("main")
757 self.assertEqual(self.pool.results.FILE_ADDED, result)
758 self.assertTrue(foo.checkIsFile("main"))
759
760 def testSimpleSymlink(self):
761 """Adding a file twice should result in a symlink."""
762- foo = PoolTestingFile(self.pool, "foo", "foo-1.0.deb")
763+ foo = PoolTestingFile(
764+ pool=self.pool, source_name="foo", source_version="1.0",
765+ filename="foo-1.0.deb")
766 foo.addToPool("main")
767 result = foo.addToPool("universe")
768 self.assertEqual(self.pool.results.SYMLINK_ADDED, result)
769@@ -156,7 +164,9 @@ class TestPool(unittest.TestCase):
770
771 def testSymlinkShuffleOnAdd(self):
772 """If the second add is a more preferred component, links shuffle."""
773- foo = PoolTestingFile(self.pool, "foo", "foo-1.0.deb")
774+ foo = PoolTestingFile(
775+ pool=self.pool, source_name="foo", source_version="1.0",
776+ filename="foo-1.0.deb")
777 foo.addToPool("universe")
778 result = foo.addToPool("main")
779 self.assertEqual(self.pool.results.SYMLINK_ADDED, result)
780@@ -165,7 +175,9 @@ class TestPool(unittest.TestCase):
781
782 def testRemoveSymlink(self):
783 """Remove file should just remove a symlink"""
784- foo = PoolTestingFile(self.pool, "foo", "foo-1.0.deb")
785+ foo = PoolTestingFile(
786+ pool=self.pool, source_name="foo", source_version="1.0",
787+ filename="foo-1.0.deb")
788 foo.addToPool("main")
789 foo.addToPool("universe")
790
791@@ -175,7 +187,9 @@ class TestPool(unittest.TestCase):
792
793 def testRemoveLoneFile(self):
794 """Removing a file with no symlinks removes it."""
795- foo = PoolTestingFile(self.pool, "foo", "foo-1.0.deb")
796+ foo = PoolTestingFile(
797+ pool=self.pool, source_name="foo", source_version="1.0",
798+ filename="foo-1.0.deb")
799 foo.addToPool("main")
800
801 size = foo.removeFromPool("main")
802@@ -184,7 +198,9 @@ class TestPool(unittest.TestCase):
803
804 def testSymlinkShuffleOnRemove(self):
805 """Removing a file with a symlink shuffles links."""
806- foo = PoolTestingFile(self.pool, "foo", "foo-1.0.deb")
807+ foo = PoolTestingFile(
808+ pool=self.pool, source_name="foo", source_version="1.0",
809+ filename="foo-1.0.deb")
810 foo.addToPool("universe")
811 foo.addToPool("main")
812
813diff --git a/lib/lp/archivepublisher/tests/test_publisher.py b/lib/lp/archivepublisher/tests/test_publisher.py
814index f247242..a2cbb54 100644
815--- a/lib/lp/archivepublisher/tests/test_publisher.py
816+++ b/lib/lp/archivepublisher/tests/test_publisher.py
817@@ -3607,6 +3607,7 @@ class TestArtifactoryPublishing(TestPublisherBase):
818 "launchpad.release-id":
819 ["source:%d" % source.sourcepackagereleaseID],
820 "launchpad.source-name": ["hello"],
821+ "launchpad.source-version": ["1.0"],
822 },
823 source_path.properties)
824 binary_path = (
825@@ -3620,6 +3621,7 @@ class TestArtifactoryPublishing(TestPublisherBase):
826 "launchpad.release-id":
827 ["binary:%d" % binary.binarypackagereleaseID],
828 "launchpad.source-name": ["hello"],
829+ "launchpad.source-version": ["1.0"],
830 },
831 binary_path.properties)
832
833@@ -3669,6 +3671,7 @@ class TestArtifactoryPublishing(TestPublisherBase):
834 "launchpad.release-id":
835 ["source:%d" % source.sourcepackagereleaseID],
836 "launchpad.source-name": ["hello"],
837+ "launchpad.source-version": ["1.0"],
838 },
839 source_path.properties)
840 self.assertEqual(
841@@ -3679,6 +3682,7 @@ class TestArtifactoryPublishing(TestPublisherBase):
842 "launchpad.release-id":
843 ["binary:%d" % binary.binarypackagereleaseID],
844 "launchpad.source-name": ["hello"],
845+ "launchpad.source-version": ["1.0"],
846 },
847 binary_path.properties)
848
849@@ -3722,6 +3726,7 @@ class TestArtifactoryPublishing(TestPublisherBase):
850 "launchpad.release-id":
851 ["source:%d" % source.sourcepackagereleaseID],
852 "launchpad.source-name": ["hello"],
853+ "launchpad.source-version": ["1.0"],
854 },
855 source_path.properties)
856 self.assertEqual(
857@@ -3729,6 +3734,7 @@ class TestArtifactoryPublishing(TestPublisherBase):
858 "launchpad.release-id":
859 ["binary:%d" % binary.binarypackagereleaseID],
860 "launchpad.source-name": ["hello"],
861+ "launchpad.source-version": ["1.0"],
862 },
863 binary_path.properties)
864
865diff --git a/lib/lp/soyuz/model/publishing.py b/lib/lp/soyuz/model/publishing.py
866index b229c47..b17194e 100644
867--- a/lib/lp/soyuz/model/publishing.py
868+++ b/lib/lp/soyuz/model/publishing.py
869@@ -178,14 +178,16 @@ class ArchivePublisherBase:
870 """See `IPublishing`"""
871 try:
872 for pub_file in self.files:
873- source = self.source_package_name
874+ source_name = self.source_package_name
875+ source_version = self.source_package_version
876 component = (
877 None if self.component is None else self.component.name)
878 filename = pub_file.libraryfile.filename
879- path = diskpool.pathFor(component, source, filename)
880+ path = diskpool.pathFor(
881+ component, source_name, source_version, filename)
882
883 action = diskpool.addFile(
884- component, source, filename, pub_file)
885+ component, source_name, source_version, filename, pub_file)
886 if action == diskpool.results.FILE_ADDED:
887 log.debug("Added %s from library" % path)
888 elif action == diskpool.results.SYMLINK_ADDED:

Subscribers

People subscribed via source and target branches

to status/vote changes: