Merge lp:~cjwatson/launchpad/archive-unambiguous-files-traversals into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18638
Proposed branch: lp:~cjwatson/launchpad/archive-unambiguous-files-traversals
Merge into: lp:launchpad
Diff against target: 695 lines (+262/-59)
18 files modified
lib/lp/services/librarian/browser.py (+9/-6)
lib/lp/soyuz/adapters/proxiedsourcefiles.py (+36/-0)
lib/lp/soyuz/browser/archive.py (+27/-1)
lib/lp/soyuz/browser/distributionsourcepackagerelease.py (+4/-4)
lib/lp/soyuz/browser/publishing.py (+7/-4)
lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt (+2/-2)
lib/lp/soyuz/browser/tests/publishing-views.txt (+3/-3)
lib/lp/soyuz/browser/tests/test_publishing_webservice.py (+4/-4)
lib/lp/soyuz/interfaces/archive.py (+16/-1)
lib/lp/soyuz/model/archive.py (+24/-1)
lib/lp/soyuz/model/publishing.py (+10/-3)
lib/lp/soyuz/stories/ppa/xx-ppa-files.txt (+21/-14)
lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt (+1/-1)
lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt (+1/-1)
lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt (+8/-8)
lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt (+4/-4)
lib/lp/soyuz/tests/test_archive.py (+83/-0)
lib/lp/soyuz/tests/test_publishing_models.py (+2/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/archive-unambiguous-files-traversals
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+345118@code.launchpad.net

Commit message

Disambiguate URLs to source package files in the face of filename clashes in imported archives.

Description of the change

This gives us a way to cope with fetching the source files for e.g. d3-format 1.0.2-1 vs. 1:1.0.2-1 in the imported Debian archive, which would be helpful to merge-o-matic.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

I guess we'll find all the clients that match on /+files/ paths.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/services/librarian/browser.py'
--- lib/lp/services/librarian/browser.py 2015-07-09 20:06:17 +0000
+++ lib/lp/services/librarian/browser.py 2018-05-08 18:13:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the1# Copyright 2010-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Browser file for LibraryFileAlias."""4"""Browser file for LibraryFileAlias."""
@@ -125,6 +125,13 @@
125 self.parent = parent125 self.parent = parent
126126
127 @property127 @property
128 def request(self):
129 request = get_current_browser_request()
130 if WebServiceLayer.providedBy(request):
131 request = IWebBrowserOriginatingRequest(request)
132 return request
133
134 @property
128 def http_url(self):135 def http_url(self):
129 """Return the webapp URL for the context `LibraryFileAlias`.136 """Return the webapp URL for the context `LibraryFileAlias`.
130137
@@ -137,11 +144,7 @@
137 if self.context.deleted:144 if self.context.deleted:
138 return None145 return None
139146
140 request = get_current_browser_request()147 parent_url = canonical_url(self.parent, request=self.request)
141 if WebServiceLayer.providedBy(request):
142 request = IWebBrowserOriginatingRequest(request)
143
144 parent_url = canonical_url(self.parent, request=request)
145 traversal_url = urlappend(parent_url, '+files')148 traversal_url = urlappend(parent_url, '+files')
146 url = urlappend(149 url = urlappend(
147 traversal_url,150 traversal_url,
148151
=== added file 'lib/lp/soyuz/adapters/proxiedsourcefiles.py'
--- lib/lp/soyuz/adapters/proxiedsourcefiles.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/adapters/proxiedsourcefiles.py 2018-05-08 18:13:25 +0000
@@ -0,0 +1,36 @@
1# Copyright 2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Proxied source files."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8__metaclass__ = type
9__all__ = [
10 'ProxiedSourceLibraryFileAlias',
11 ]
12
13from lp.services.librarian.browser import ProxiedLibraryFileAlias
14from lp.services.librarian.client import url_path_quote
15from lp.services.webapp.publisher import canonical_url
16from lp.services.webapp.url import urlappend
17
18
19class ProxiedSourceLibraryFileAlias(ProxiedLibraryFileAlias):
20 """A `ProxiedLibraryFileAlias` variant that traverses via +sourcefiles.
21
22 This can be used to construct unambiguous source file URLs even for
23 imports from upstream archives without robust historical filename
24 uniqueness checks.
25 """
26
27 @property
28 def http_url(self):
29 if self.context.deleted:
30 return None
31
32 url = canonical_url(self.parent.archive, request=self.request)
33 return urlappend(url, '/'.join([
34 '+sourcefiles', self.parent.source_package_name,
35 self.parent.source_package_version,
36 url_path_quote(self.context.filename.encode('utf-8'))]))
037
=== modified file 'lib/lp/soyuz/browser/archive.py'
--- lib/lp/soyuz/browser/archive.py 2017-04-22 13:13:22 +0000
+++ lib/lp/soyuz/browser/archive.py 2018-05-08 18:13:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Browser views for archive."""4"""Browser views for archive."""
@@ -455,6 +455,32 @@
455455
456 return self.context.getArchiveDependency(archive)456 return self.context.getArchiveDependency(archive)
457457
458 @stepthrough('+sourcefiles')
459 def traverse_sourcefiles(self, sourcepackagename):
460 """Traverse to a source file in the archive.
461
462 Normally, files in an archive are unique by filename, so the +files
463 traversal is sufficient. Unfortunately, a gina-imported archive may
464 contain the same filename with different contents due to a
465 combination of epochs and less stringent checks applied by the
466 upstream archive software. (In practice this only happens for
467 source packages because that's normally all we import using gina.)
468 This provides an unambiguous way to traverse to such files even with
469 this problem.
470
471 The path scheme is::
472
473 +sourcefiles/:sourcename/:sourceversion/:filename
474 """
475 if len(self.request.stepstogo) < 2:
476 return None
477
478 version = self.request.stepstogo.consume()
479 filename = self.request.stepstogo.consume()
480
481 return self.context.getSourceFileByName(
482 sourcepackagename, version, filename)
483
458484
459class ArchiveMenuMixin:485class ArchiveMenuMixin:
460486
461487
=== modified file 'lib/lp/soyuz/browser/distributionsourcepackagerelease.py'
--- lib/lp/soyuz/browser/distributionsourcepackagerelease.py 2014-12-18 13:05:10 +0000
+++ lib/lp/soyuz/browser/distributionsourcepackagerelease.py 2018-05-08 18:13:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -18,7 +18,6 @@
18from lp.registry.browser.distributionsourcepackage import (18from lp.registry.browser.distributionsourcepackage import (
19 PublishingHistoryViewMixin,19 PublishingHistoryViewMixin,
20 )20 )
21from lp.services.librarian.browser import ProxiedLibraryFileAlias
22from lp.services.propertycache import cachedproperty21from lp.services.propertycache import cachedproperty
23from lp.services.webapp import (22from lp.services.webapp import (
24 canonical_url,23 canonical_url,
@@ -27,6 +26,7 @@
27 stepthrough,26 stepthrough,
28 )27 )
29from lp.services.webapp.breadcrumb import Breadcrumb28from lp.services.webapp.breadcrumb import Breadcrumb
29from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
30from lp.soyuz.browser.build import get_build_by_id_str30from lp.soyuz.browser.build import get_build_by_id_str
31from lp.soyuz.enums import PackagePublishingStatus31from lp.soyuz.enums import PackagePublishingStatus
32from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet32from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
@@ -101,8 +101,8 @@
101 """The source package release files as `ProxiedLibraryFileAlias`."""101 """The source package release files as `ProxiedLibraryFileAlias`."""
102 last_publication = self._cached_publishing_history[0]102 last_publication = self._cached_publishing_history[0]
103 return [103 return [
104 ProxiedLibraryFileAlias(104 ProxiedSourceLibraryFileAlias(
105 source_file.libraryfile, last_publication.archive)105 source_file.libraryfile, last_publication)
106 for source_file in self.context.files]106 for source_file in self.context.files]
107107
108 @cachedproperty108 @cachedproperty
109109
=== modified file 'lib/lp/soyuz/browser/publishing.py'
--- lib/lp/soyuz/browser/publishing.py 2015-07-09 20:06:17 +0000
+++ lib/lp/soyuz/browser/publishing.py 2018-05-08 18:13:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Browser views for Soyuz publishing records."""4"""Browser views for Soyuz publishing records."""
@@ -30,6 +30,7 @@
30 canonical_url,30 canonical_url,
31 LaunchpadView,31 LaunchpadView,
32 )32 )
33from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
33from lp.soyuz.enums import PackagePublishingStatus34from lp.soyuz.enums import PackagePublishingStatus
34from lp.soyuz.interfaces.binarypackagebuild import BuildSetStatus35from lp.soyuz.interfaces.binarypackagebuild import BuildSetStatus
35from lp.soyuz.interfaces.packagediff import IPackageDiff36from lp.soyuz.interfaces.packagediff import IPackageDiff
@@ -270,8 +271,7 @@
270 def published_source_and_binary_files(self):271 def published_source_and_binary_files(self):
271 """Return list of dictionaries representing published files."""272 """Return list of dictionaries representing published files."""
272 files = sorted(273 files = sorted(
273 (ProxiedLibraryFileAlias(lfa, self.context.archive)274 self.context.getSourceAndBinaryLibraryFiles(),
274 for lfa in self.context.getSourceAndBinaryLibraryFiles()),
275 key=attrgetter('filename'))275 key=attrgetter('filename'))
276 result = []276 result = []
277 urls = set()277 urls = set()
@@ -285,14 +285,17 @@
285 urls.add(url)285 urls.add(url)
286286
287 custom_dict = {}287 custom_dict = {}
288 custom_dict["url"] = url
289 custom_dict["filename"] = library_file.filename288 custom_dict["filename"] = library_file.filename
290 custom_dict["filesize"] = library_file.content.filesize289 custom_dict["filesize"] = library_file.content.filesize
291 if (library_file.filename.endswith('.deb') or290 if (library_file.filename.endswith('.deb') or
292 library_file.filename.endswith('.udeb')):291 library_file.filename.endswith('.udeb')):
293 custom_dict['class'] = 'binary'292 custom_dict['class'] = 'binary'
293 custom_dict["url"] = ProxiedLibraryFileAlias(
294 library_file, self.context.archive).http_url
294 else:295 else:
295 custom_dict['class'] = 'source'296 custom_dict['class'] = 'source'
297 custom_dict["url"] = ProxiedSourceLibraryFileAlias(
298 library_file, self.context).http_url
296299
297 result.append(custom_dict)300 result.append(custom_dict)
298301
299302
=== modified file 'lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt'
--- lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt 2014-11-27 22:13:36 +0000
+++ lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt 2018-05-08 18:13:25 +0000
@@ -24,14 +24,14 @@
24 u'testing-dspr 1.0 source package in ubuntutest'24 u'testing-dspr 1.0 source package in ubuntutest'
2525
26The 'files' property returns a list of files included in the source26The 'files' property returns a list of files included in the source
27upload encapsulated as `ProxiedLibraryFileAlias` objects. Their27upload encapsulated as `ProxiedSourceLibraryFileAlias` objects. Their
28'http_url' points to the LP proxied url which normalizes the path28'http_url' points to the LP proxied url which normalizes the path
29tofiles allowing them to be downloaded using `dget`.29tofiles allowing them to be downloaded using `dget`.
3030
31 >>> for source_file in dspr_view.files:31 >>> for source_file in dspr_view.files:
32 ... print source_file.filename, source_file.http_url32 ... print source_file.filename, source_file.http_url
33 testing-dspr_1.0.dsc33 testing-dspr_1.0.dsc
34 http://.../ubuntutest/+archive/primary/+files/testing-dspr_1.0.dsc34 http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc
3535
36The 'sponsor' property indicates whether the upload was 'sponsored' or36The 'sponsor' property indicates whether the upload was 'sponsored' or
37not. When the upload was signed by someone else than the source37not. When the upload was signed by someone else than the source
3838
=== modified file 'lib/lp/soyuz/browser/tests/publishing-views.txt'
--- lib/lp/soyuz/browser/tests/publishing-views.txt 2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/browser/tests/publishing-views.txt 2018-05-08 18:13:25 +0000
@@ -63,7 +63,7 @@
63 >>> view = create_initialized_view(alsa_pub, "+listing-archive-detailed")63 >>> view = create_initialized_view(alsa_pub, "+listing-archive-detailed")
6464
65 >>> view.published_source_and_binary_files65 >>> view.published_source_and_binary_files
66 [{'url': u'http://launchpad.dev/ubuntutest/+archive/primary/+files/alsa-utils-test_666.dsc',66 [{'url': u'http://launchpad.dev/ubuntutest/+archive/primary/+sourcefiles/alsa-utils-test/666/alsa-utils-test_666.dsc',
67 'class': 'source',67 'class': 'source',
68 'filesize': 28,68 'filesize': 28,
69 'filename': u'alsa-utils-test_666.dsc'}]69 'filename': u'alsa-utils-test_666.dsc'}]
@@ -81,11 +81,11 @@
81 ... iceweasel_source_pub, "+listing-archive-detailed")81 ... iceweasel_source_pub, "+listing-archive-detailed")
8282
83 >>> ppa_source_view.published_source_and_binary_files83 >>> ppa_source_view.published_source_and_binary_files
84 [{'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+files/firefox_0.9.2.orig.tar.gz',84 [{'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/firefox_0.9.2.orig.tar.gz',
85 'class': 'source',85 'class': 'source',
86 'filesize': 9922560,86 'filesize': 9922560,
87 'filename': u'firefox_0.9.2.orig.tar.gz'},87 'filename': u'firefox_0.9.2.orig.tar.gz'},
88 {'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+files/iceweasel-1.0.dsc',88 {'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/iceweasel-1.0.dsc',
89 'class': 'source',89 'class': 'source',
90 'filesize': 123,90 'filesize': 123,
91 'filename': u'iceweasel-1.0.dsc'},91 'filename': u'iceweasel-1.0.dsc'},
9292
=== modified file 'lib/lp/soyuz/browser/tests/test_publishing_webservice.py'
--- lib/lp/soyuz/browser/tests/test_publishing_webservice.py 2018-02-01 18:44:21 +0000
+++ lib/lp/soyuz/browser/tests/test_publishing_webservice.py 2018-05-08 18:13:25 +0000
@@ -9,6 +9,7 @@
99
10from lp.services.librarian.browser import ProxiedLibraryFileAlias10from lp.services.librarian.browser import ProxiedLibraryFileAlias
11from lp.services.webapp.interfaces import OAuthPermission11from lp.services.webapp.interfaces import OAuthPermission
12from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
12from lp.testing import (13from lp.testing import (
13 api_url,14 api_url,
14 login_person,15 login_person,
@@ -47,8 +48,7 @@
47 with person_logged_in(person):48 with person_logged_in(person):
48 sprf = spph.sourcepackagerelease.files[0]49 sprf = spph.sourcepackagerelease.files[0]
49 expected_urls = [50 expected_urls = [
50 ProxiedLibraryFileAlias(51 ProxiedSourceLibraryFileAlias(sprf.libraryfile, spph).http_url]
51 sprf.libraryfile, spph.archive).http_url]
52 self.assertEqual(expected_urls, urls)52 self.assertEqual(expected_urls, urls)
5353
54 def test_sourceFileUrls_include_meta(self):54 def test_sourceFileUrls_include_meta(self):
@@ -75,8 +75,8 @@
75 info = response.jsonBody()75 info = response.jsonBody()
76 with person_logged_in(person):76 with person_logged_in(person):
77 expected_info = [{77 expected_info = [{
78 "url": ProxiedLibraryFileAlias(78 "url": ProxiedSourceLibraryFileAlias(
79 sprf.libraryfile, spph.archive).http_url,79 sprf.libraryfile, spph).http_url,
80 "size": sprf.libraryfile.content.filesize,80 "size": sprf.libraryfile.content.filesize,
81 "sha256": sprf.libraryfile.content.sha256,81 "sha256": sprf.libraryfile.content.sha256,
82 } for sprf in spph.sourcepackagerelease.files]82 } for sprf in spph.sourcepackagerelease.files]
8383
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2017-09-28 14:06:20 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2018-05-08 18:13:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Archive interfaces."""4"""Archive interfaces."""
@@ -936,6 +936,21 @@
936 :return the corresponding `ILibraryFileAlias` is the file was found.936 :return the corresponding `ILibraryFileAlias` is the file was found.
937 """937 """
938938
939 def getSourceFileByName(name, version, filename):
940 """Return the `ILibraryFileAlias` for a source name/version/filename.
941
942 This can be used to avoid ambiguities with `getFileByName` in
943 imported archives, where the upstream archive software may not
944 always have had robust historical filename uniqueness checks.
945
946 :param name: The name of the source package.
947 :param version: The version of the source package.
948 :param filename: The exact filename to look up.
949
950 :raises NotFoundError: if no matching file could be found.
951 :return: the corresponding `ILibraryFileAlias`.
952 """
953
939 def getBinaryPackageRelease(name, version, archtag):954 def getBinaryPackageRelease(name, version, archtag):
940 """Find the specified `IBinaryPackageRelease` in the archive.955 """Find the specified `IBinaryPackageRelease` in the archive.
941956
942957
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2018-01-30 16:19:15 +0000
+++ lib/lp/soyuz/model/archive.py 2018-05-08 18:13:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Database class for table Archive."""4"""Database class for table Archive."""
@@ -1688,6 +1688,29 @@
16881688
1689 return archive_file1689 return archive_file
16901690
1691 def getSourceFileByName(self, name, version, filename):
1692 """See `IArchive`."""
1693 result = IStore(LibraryFileAlias).find(
1694 LibraryFileAlias,
1695 SourcePackagePublishingHistory.archive == self,
1696 SourcePackagePublishingHistory.sourcepackagereleaseID ==
1697 SourcePackageRelease.id,
1698 SourcePackageRelease.sourcepackagename == SourcePackageName.id,
1699 SourcePackageName.name == name,
1700 SourcePackageRelease.version == version,
1701 SourcePackageRelease.id ==
1702 SourcePackageReleaseFile.sourcepackagereleaseID,
1703 SourcePackageReleaseFile.libraryfileID == LibraryFileAlias.id,
1704 LibraryFileAlias.filename == filename,
1705 LibraryFileAlias.content != None)
1706 result = result.config(distinct=True).order_by(LibraryFileAlias.id)
1707 # Unlike `getFileByName`, we are guaranteed at most one match even
1708 # for files in imported archives.
1709 archive_file = result.one()
1710 if archive_file is None:
1711 raise NotFoundError(filename)
1712 return archive_file
1713
1691 def getBinaryPackageRelease(self, name, version, archtag):1714 def getBinaryPackageRelease(self, name, version, archtag):
1692 """See `IArchive`."""1715 """See `IArchive`."""
1693 from lp.soyuz.model.distroarchseries import DistroArchSeries1716 from lp.soyuz.model.distroarchseries import DistroArchSeries
16941717
=== modified file 'lib/lp/soyuz/model/publishing.py'
--- lib/lp/soyuz/model/publishing.py 2017-06-02 21:46:50 +0000
+++ lib/lp/soyuz/model/publishing.py 2018-05-08 18:13:25 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -76,6 +76,7 @@
76 ScriptRequest,76 ScriptRequest,
77 )77 )
78from lp.services.worlddata.model.country import Country78from lp.services.worlddata.model.country import Country
79from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
79from lp.soyuz.enums import (80from lp.soyuz.enums import (
80 BinaryPackageFormat,81 BinaryPackageFormat,
81 PackagePublishingPriority,82 PackagePublishingPriority,
@@ -142,6 +143,12 @@
142 return [ProxiedLibraryFileAlias(file, parent).http_url for file in files]143 return [ProxiedLibraryFileAlias(file, parent).http_url for file in files]
143144
144145
146def proxied_source_urls(files, parent):
147 """Return the files passed through `ProxiedSourceLibraryFileAlias`."""
148 return [
149 ProxiedSourceLibraryFileAlias(file, parent).http_url for file in files]
150
151
145class ArchivePublisherBase:152class ArchivePublisherBase:
146 """Base class for `IArchivePublisher`."""153 """Base class for `IArchivePublisher`."""
147154
@@ -548,8 +555,8 @@
548 SourcePackageReleaseFile.sourcepackagerelease ==555 SourcePackageReleaseFile.sourcepackagerelease ==
549 SourcePackageRelease.id,556 SourcePackageRelease.id,
550 SourcePackageRelease.id == self.sourcepackagereleaseID)557 SourcePackageRelease.id == self.sourcepackagereleaseID)
551 source_urls = proxied_urls(558 source_urls = proxied_source_urls(
552 [source for source, _ in sources], self.archive)559 [source for source, _ in sources], self)
553 if include_meta:560 if include_meta:
554 meta = [561 meta = [
555 (content.filesize, content.sha256) for _, content in sources]562 (content.filesize, content.sha256) for _, content in sources]
556563
=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-files.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-files.txt 2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-files.txt 2018-05-08 18:13:25 +0000
@@ -90,18 +90,19 @@
9090
91 >>> ppa_links = [91 >>> ppa_links = [
92 ... ('(changes file)',92 ... ('(changes file)',
93 ... another_test_source.sourcepackagerelease.upload_changesfile),93 ... another_test_source.sourcepackagerelease.upload_changesfile,
94 ... None, None),
94 ... ]95 ... ]
9596
96 >>> ppa_1_0_links = [97 >>> ppa_1_0_links = [
97 ... ('test-pkg_1.0.dsc', dsc_file),98 ... ('test-pkg_1.0.dsc', dsc_file, 'test-pkg', '1.0'),
98 ... ('test-pkg_1.0.tar.gz', tar_gz),99 ... ('test-pkg_1.0.tar.gz', tar_gz, 'test-pkg', '1.0'),
99 ... ('test-bin_1.0_all.deb', deb_file),100 ... ('test-bin_1.0_all.deb', deb_file, None, None),
100 ... ]101 ... ]
101102
102 >>> ppa_1_1_links = [103 >>> ppa_1_1_links = [
103 ... ('test-pkg_1.1.dsc', another_dsc_file),104 ... ('test-pkg_1.1.dsc', another_dsc_file, 'test-pkg', '1.1'),
104 ... ('1.0 to 1.1', package_diff.diff_content),105 ... ('1.0 to 1.1', package_diff.diff_content, None, None),
105 ... ]106 ... ]
106107
107Links to files accessible via +files/ proxy in the Build page.108Links to files accessible via +files/ proxy in the Build page.
@@ -109,13 +110,14 @@
109 >>> build_id = build.id110 >>> build_id = build.id
110111
111 >>> builds_links = [112 >>> builds_links = [
112 ... ('see the log', build.log),113 ... ('see the log', build.log, None, None),
113 ... ]114 ... ]
114115
115 >>> build_links = [116 >>> build_links = [
116 ... ('test-bin_1.0_i386.changes', build.upload_changesfile),117 ... ('test-bin_1.0_i386.changes', build.upload_changesfile,
117 ... ('buildlog', build.log),118 ... None, None),
118 ... ('uploadlog', build.upload_log),119 ... ('buildlog', build.log, None, None),
120 ... ('uploadlog', build.upload_log, None, None),
119 ... ]121 ... ]
120122
121 >>> logout()123 >>> logout()
@@ -124,15 +126,20 @@
124126
125 >>> from mechanize import LinkNotFoundError127 >>> from mechanize import LinkNotFoundError
126 >>> def check_urls(browser, links, base_url):128 >>> def check_urls(browser, links, base_url):
127 ... for link, libraryfile in links:129 ... for link, libraryfile, source_name, source_version in links:
128 ... try:130 ... try:
129 ... found_url = browser.getLink(link).url131 ... found_url = browser.getLink(link).url
130 ... except LinkNotFoundError:132 ... except LinkNotFoundError:
131 ... print '%s: NOT FOUND' % libraryfile.filename133 ... print '%s: NOT FOUND' % libraryfile.filename
132 ... continue134 ... continue
133 ... found_url = found_url.replace('%7E', '~')135 ... found_url = found_url.replace('%7E', '~')
134 ... expected_url = '/'.join(136 ... if source_name is not None:
135 ... (base_url, '+files', libraryfile.filename))137 ... expected_url = '/'.join(
138 ... (base_url, '+sourcefiles', source_name,
139 ... source_version, libraryfile.filename))
140 ... else:
141 ... expected_url = '/'.join(
142 ... (base_url, '+files', libraryfile.filename))
136 ... if found_url == expected_url:143 ... if found_url == expected_url:
137 ... print '%s: OK' % libraryfile.filename144 ... print '%s: OK' % libraryfile.filename
138 ... else:145 ... else:
@@ -184,7 +191,7 @@
184 ... 'http://launchpad.dev/~no-priv/+archive/ubuntu/p3a/+packages')191 ... 'http://launchpad.dev/~no-priv/+archive/ubuntu/p3a/+packages')
185192
186Source and binary files, in the expandable-row area, are served via193Source and binary files, in the expandable-row area, are served via
187the PPA '+files' traversal.194the PPA '+sourcefiles' and '+files' traversals.
188195
189 >>> expander_id = find_tags_by_class(196 >>> expander_id = find_tags_by_class(
190 ... no_priv_browser.contents, 'expander')[1]['id']197 ... no_priv_browser.contents, 'expander')[1]['id']
191198
=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2015-04-09 05:16:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2018-05-08 18:13:25 +0000
@@ -383,7 +383,7 @@
383 >>> expander_url = foo_browser.getLink(id=expander_id).url383 >>> expander_url = foo_browser.getLink(id=expander_id).url
384 >>> anon_browser.open(expander_url)384 >>> anon_browser.open(expander_url)
385 >>> print anon_browser.getLink("orig").url385 >>> print anon_browser.getLink("orig").url
386 http://.../+files/foo.orig.tar.gz386 http://.../+sourcefiles/.../foo.orig.tar.gz
387387
388The uploader name is linkified to that user's home page:388The uploader name is linkified to that user's home page:
389389
390390
=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt 2016-03-30 10:01:57 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt 2018-05-08 18:13:25 +0000
@@ -201,7 +201,7 @@
201 View changes file201 View changes file
202202
203 >>> print anon_browser.getLink('testing-dspr_1.0.dsc').url203 >>> print anon_browser.getLink('testing-dspr_1.0.dsc').url
204 http://.../ubuntutest/+archive/primary/+files/testing-dspr_1.0.dsc204 http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc
205205
206The 'Downloads' section also lists and link to package diffs when they206The 'Downloads' section also lists and link to package diffs when they
207are available.207are available.
208208
=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2016-03-30 10:01:57 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2018-05-08 18:13:25 +0000
@@ -90,7 +90,7 @@
90 firefox_0.9.2.orig.tar.gz 9.5 MiB ...90 firefox_0.9.2.orig.tar.gz 9.5 MiB ...
9191
92 >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url92 >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url
93 http://launchpad.dev/ubuntu/+archive/primary/+files/firefox_0.9.2.orig.tar.gz93 http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz
9494
95This page also provides links to the binary packages generated by this95This page also provides links to the binary packages generated by this
96source in a specfic architecture:96source in a specfic architecture:
@@ -285,7 +285,7 @@
285 firefox_0.9.2.orig.tar.gz 9.5 MiB ...285 firefox_0.9.2.orig.tar.gz 9.5 MiB ...
286286
287 >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url287 >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url
288 http://launchpad.dev/ubuntu/+archive/primary/+files/firefox_0.9.2.orig.tar.gz288 http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz
289289
290If we go to the same page for alsa-utils, the changelog has text that is290If we go to the same page for alsa-utils, the changelog has text that is
291linkified.291linkified.
@@ -353,11 +353,11 @@
353 commercialpackage_1.0-1.dsc 567 bytes ...353 commercialpackage_1.0-1.dsc 567 bytes ...
354354
355 >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url355 >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url
356 http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0.orig.tar.gz356 http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz
357 >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url357 >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url
358 http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.diff.gz358 http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz
359 >>> print browser.getLink("commercialpackage_1.0-1.dsc").url359 >>> print browser.getLink("commercialpackage_1.0-1.dsc").url
360 http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.dsc360 http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc
361361
362This page also provides links to the binary packages generated by this362This page also provides links to the binary packages generated by this
363source in a specfic architecture:363source in a specfic architecture:
@@ -433,11 +433,11 @@
433 commercialpackage_1.0-1.dsc 567 bytes ...433 commercialpackage_1.0-1.dsc 567 bytes ...
434434
435 >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url435 >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url
436 http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0.orig.tar.gz436 http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz
437 >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url437 >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url
438 http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.diff.gz438 http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz
439 >>> print browser.getLink("commercialpackage_1.0-1.dsc").url439 >>> print browser.getLink("commercialpackage_1.0-1.dsc").url
440 http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.dsc440 http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc
441441
442442
443Tracing copied sources443Tracing copied sources
444444
=== modified file 'lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt'
--- lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt 2016-03-02 15:52:33 +0000
+++ lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt 2018-05-08 18:13:25 +0000
@@ -375,11 +375,11 @@
375 ... source_urls = webservice.named_get(375 ... source_urls = webservice.named_get(
376 ... pub_link, 'sourceFileUrls').jsonBody()376 ... pub_link, 'sourceFileUrls').jsonBody()
377 ... print source_urls377 ... print source_urls
378 [u'http://.../~cprov/+archive/ubuntu/ppa/+files/foobar-1.0.dsc']378 [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/cdrkit/1.0/foobar-1.0.dsc']
379 [u'http://.../~cprov/+archive/ubuntu/ppa/+files/firefox_0.9.2.orig.tar.gz',379 [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/firefox_0.9.2.orig.tar.gz',
380 u'http://.../~cprov/+archive/ubuntu/ppa/+files/iceweasel-1.0.dsc']380 u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/iceweasel-1.0.dsc']
381 []381 []
382 [u'http://.../~cprov/+archive/ubuntu/ppa/+files/testwebservice_666.dsc']382 [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/testwebservice/666/testwebservice_666.dsc']
383383
384binaryFileUrls() is similar:384binaryFileUrls() is similar:
385385
386386
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2018-02-14 11:13:47 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2018-05-08 18:13:25 +0000
@@ -2425,6 +2425,89 @@
2425 self.archive.getFileByName(pu.changesfile.filename))2425 self.archive.getFileByName(pu.changesfile.filename))
24262426
24272427
2428class TestGetSourceFileByName(TestCaseWithFactory):
2429 """Tests for Archive.getSourceFileByName."""
2430
2431 layer = LaunchpadZopelessLayer
2432
2433 def setUp(self):
2434 super(TestGetSourceFileByName, self).setUp()
2435 self.archive = self.factory.makeArchive()
2436
2437 def test_source_file_is_found(self):
2438 # A file from a published source package can be retrieved.
2439 pub = self.factory.makeSourcePackagePublishingHistory(
2440 archive=self.archive)
2441 dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
2442 self.assertRaises(
2443 NotFoundError, self.archive.getSourceFileByName,
2444 pub.source_package_name, pub.source_package_version, dsc.filename)
2445 pub.sourcepackagerelease.addFile(dsc)
2446 self.assertEqual(
2447 dsc, self.archive.getSourceFileByName(
2448 pub.source_package_name, pub.source_package_version,
2449 dsc.filename))
2450
2451 def test_nonexistent_source_file_is_not_found(self):
2452 # Something that looks like a source file but isn't is not
2453 # found.
2454 pub = self.factory.makeSourcePackagePublishingHistory(
2455 archive=self.archive)
2456 self.assertRaises(
2457 NotFoundError, self.archive.getSourceFileByName,
2458 pub.source_package_name, pub.source_package_version,
2459 'foo_1.0.dsc')
2460
2461 def test_nonexistent_source_package_version_is_not_found(self):
2462 # The source package version must match exactly.
2463 pub = self.factory.makeSourcePackagePublishingHistory(
2464 archive=self.archive)
2465 pub2 = self.factory.makeSourcePackagePublishingHistory(
2466 archive=self.archive, sourcepackagename=pub.source_package_name)
2467 dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
2468 pub2.sourcepackagerelease.addFile(dsc)
2469 self.assertRaises(
2470 NotFoundError, self.archive.getSourceFileByName,
2471 pub.source_package_name, pub.source_package_version,
2472 'foo_1.0.dsc')
2473
2474 def test_nonexistent_source_package_name_is_not_found(self):
2475 # The source package name must match exactly.
2476 pub = self.factory.makeSourcePackagePublishingHistory(
2477 archive=self.archive)
2478 pub2 = self.factory.makeSourcePackagePublishingHistory(
2479 archive=self.archive)
2480 dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
2481 pub2.sourcepackagerelease.addFile(dsc)
2482 self.assertRaises(
2483 NotFoundError, self.archive.getSourceFileByName,
2484 pub.source_package_name, pub.source_package_version,
2485 'foo_1.0.dsc')
2486
2487 def test_epoch_stripping_collision(self):
2488 # Even if the archive contains two source packages with identical
2489 # names and versions apart from epochs which have the same filenames
2490 # with different contents (the worst case), getSourceFileByName
2491 # returns the correct files.
2492 pub = self.factory.makeSourcePackagePublishingHistory(
2493 archive=self.archive, version='1.0-1')
2494 dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
2495 pub.sourcepackagerelease.addFile(dsc)
2496 pub2 = self.factory.makeSourcePackagePublishingHistory(
2497 archive=self.archive, sourcepackagename=pub.source_package_name,
2498 version='1:1.0-1')
2499 dsc2 = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
2500 pub2.sourcepackagerelease.addFile(dsc2)
2501 self.assertEqual(
2502 dsc, self.archive.getSourceFileByName(
2503 pub.source_package_name, pub.source_package_version,
2504 dsc.filename))
2505 self.assertEqual(
2506 dsc2, self.archive.getSourceFileByName(
2507 pub2.source_package_name, pub2.source_package_version,
2508 dsc2.filename))
2509
2510
2428class TestGetPublishedSources(TestCaseWithFactory):2511class TestGetPublishedSources(TestCaseWithFactory):
24292512
2430 layer = DatabaseFunctionalLayer2513 layer = DatabaseFunctionalLayer
24312514
=== modified file 'lib/lp/soyuz/tests/test_publishing_models.py'
--- lib/lp/soyuz/tests/test_publishing_models.py 2018-02-02 03:14:35 +0000
+++ lib/lp/soyuz/tests/test_publishing_models.py 2018-05-08 18:13:25 +0000
@@ -14,6 +14,7 @@
14from lp.services.database.constants import UTC_NOW14from lp.services.database.constants import UTC_NOW
15from lp.services.librarian.browser import ProxiedLibraryFileAlias15from lp.services.librarian.browser import ProxiedLibraryFileAlias
16from lp.services.webapp.publisher import canonical_url16from lp.services.webapp.publisher import canonical_url
17from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
17from lp.soyuz.enums import (18from lp.soyuz.enums import (
18 BinaryPackageFileType,19 BinaryPackageFileType,
19 BinaryPackageFormat,20 BinaryPackageFormat,
@@ -159,8 +160,7 @@
159160
160 def getURLsForSPPH(self, spph, include_meta=False):161 def getURLsForSPPH(self, spph, include_meta=False):
161 spr = spph.sourcepackagerelease162 spr = spph.sourcepackagerelease
162 archive = spph.archive163 urls = [ProxiedSourceLibraryFileAlias(f.libraryfile, spph).http_url
163 urls = [ProxiedLibraryFileAlias(f.libraryfile, archive).http_url
164 for f in spr.files]164 for f in spr.files]
165165
166 if include_meta:166 if include_meta: