launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #22453
[Merge] lp:~cjwatson/launchpad/archive-unambiguous-files-traversals into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/archive-unambiguous-files-traversals into lp:launchpad.
Commit message:
Disambiguate URLs to source package files in the face of filename clashes in imported archives.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/archive-unambiguous-files-traversals/+merge/345118
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.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/archive-unambiguous-files-traversals into lp:launchpad.
=== 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-04 22:20:39 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Browser file for LibraryFileAlias."""
@@ -125,6 +125,13 @@
self.parent = parent
@property
+ def request(self):
+ request = get_current_browser_request()
+ if WebServiceLayer.providedBy(request):
+ request = IWebBrowserOriginatingRequest(request)
+ return request
+
+ @property
def http_url(self):
"""Return the webapp URL for the context `LibraryFileAlias`.
@@ -137,11 +144,7 @@
if self.context.deleted:
return None
- request = get_current_browser_request()
- if WebServiceLayer.providedBy(request):
- request = IWebBrowserOriginatingRequest(request)
-
- parent_url = canonical_url(self.parent, request=request)
+ parent_url = canonical_url(self.parent, request=self.request)
traversal_url = urlappend(parent_url, '+files')
url = urlappend(
traversal_url,
=== 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-04 22:20:39 +0000
@@ -0,0 +1,37 @@
+# Copyright 2018 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Proxied source files."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'ProxiedSourceLibraryFileAlias',
+ ]
+
+from lp.services.librarian.browser import ProxiedLibraryFileAlias
+from lp.services.librarian.client import url_path_quote
+from lp.services.webapp.publisher import canonical_url
+from lp.services.webapp.url import urlappend
+
+
+class ProxiedSourceLibraryFileAlias(ProxiedLibraryFileAlias):
+ """A `ProxiedLibraryFileAlias` variant that traverses via +sourcefiles.
+
+ This can be used to construct unambiguous source file URLs even for
+ imports from upstream archives without robust historical filename
+ uniqueness checks.
+ """
+
+ @property
+ def http_url(self):
+ if self.context.deleted:
+ return None
+
+ url = canonical_url(self.parent.archive, request=self.request)
+ url = urlappend(url, '+sourcefiles')
+ url = urlappend(url, self.parent.source_package_name)
+ url = urlappend(url, self.parent.source_package_version)
+ return urlappend(
+ url, url_path_quote(self.context.filename.encode('utf-8')))
=== 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-04 22:20:39 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Browser views for archive."""
@@ -455,6 +455,32 @@
return self.context.getArchiveDependency(archive)
+ @stepthrough('+sourcefiles')
+ def traverse_sourcefiles(self, sourcepackagename):
+ """Traverse to a source file in the archive.
+
+ Normally, files in an archive are unique by filename, so the +files
+ traversal is sufficient. Unfortunately, a gina-imported archive may
+ contain the same filename with different contents due to a
+ combination of epochs and less stringent checks applied by the
+ upstream archive software. (In practice this only happens for
+ source packages because that's normally all we import using gina.)
+ This provides an unambiguous way to traverse to such files even with
+ this problem.
+
+ The path scheme is::
+
+ +sourcefiles/:sourcename/:sourceversion/:filename
+ """
+ if len(self.request.stepstogo) < 2:
+ return None
+
+ version = self.request.stepstogo.consume()
+ filename = self.request.stepstogo.consume()
+
+ return self.context.getSourceFileByName(
+ sourcepackagename, version, filename)
+
class ArchiveMenuMixin:
=== 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-04 22:20:39 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -18,7 +18,6 @@
from lp.registry.browser.distributionsourcepackage import (
PublishingHistoryViewMixin,
)
-from lp.services.librarian.browser import ProxiedLibraryFileAlias
from lp.services.propertycache import cachedproperty
from lp.services.webapp import (
canonical_url,
@@ -27,6 +26,7 @@
stepthrough,
)
from lp.services.webapp.breadcrumb import Breadcrumb
+from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
from lp.soyuz.browser.build import get_build_by_id_str
from lp.soyuz.enums import PackagePublishingStatus
from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
@@ -101,8 +101,8 @@
"""The source package release files as `ProxiedLibraryFileAlias`."""
last_publication = self._cached_publishing_history[0]
return [
- ProxiedLibraryFileAlias(
- source_file.libraryfile, last_publication.archive)
+ ProxiedSourceLibraryFileAlias(
+ source_file.libraryfile, last_publication)
for source_file in self.context.files]
@cachedproperty
=== 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-04 22:20:39 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Browser views for Soyuz publishing records."""
@@ -30,6 +30,7 @@
canonical_url,
LaunchpadView,
)
+from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
from lp.soyuz.enums import PackagePublishingStatus
from lp.soyuz.interfaces.binarypackagebuild import BuildSetStatus
from lp.soyuz.interfaces.packagediff import IPackageDiff
@@ -270,8 +271,7 @@
def published_source_and_binary_files(self):
"""Return list of dictionaries representing published files."""
files = sorted(
- (ProxiedLibraryFileAlias(lfa, self.context.archive)
- for lfa in self.context.getSourceAndBinaryLibraryFiles()),
+ self.context.getSourceAndBinaryLibraryFiles(),
key=attrgetter('filename'))
result = []
urls = set()
@@ -285,14 +285,17 @@
urls.add(url)
custom_dict = {}
- custom_dict["url"] = url
custom_dict["filename"] = library_file.filename
custom_dict["filesize"] = library_file.content.filesize
if (library_file.filename.endswith('.deb') or
library_file.filename.endswith('.udeb')):
custom_dict['class'] = 'binary'
+ custom_dict["url"] = ProxiedLibraryFileAlias(
+ library_file, self.context.archive).http_url
else:
custom_dict['class'] = 'source'
+ custom_dict["url"] = ProxiedSourceLibraryFileAlias(
+ library_file, self.context).http_url
result.append(custom_dict)
=== 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-04 22:20:39 +0000
@@ -24,14 +24,14 @@
u'testing-dspr 1.0 source package in ubuntutest'
The 'files' property returns a list of files included in the source
-upload encapsulated as `ProxiedLibraryFileAlias` objects. Their
+upload encapsulated as `ProxiedSourceLibraryFileAlias` objects. Their
'http_url' points to the LP proxied url which normalizes the path
tofiles allowing them to be downloaded using `dget`.
>>> for source_file in dspr_view.files:
... print source_file.filename, source_file.http_url
testing-dspr_1.0.dsc
- http://.../ubuntutest/+archive/primary/+files/testing-dspr_1.0.dsc
+ http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc
The 'sponsor' property indicates whether the upload was 'sponsored' or
not. When the upload was signed by someone else than the source
=== 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-04 22:20:39 +0000
@@ -63,7 +63,7 @@
>>> view = create_initialized_view(alsa_pub, "+listing-archive-detailed")
>>> view.published_source_and_binary_files
- [{'url': u'http://launchpad.dev/ubuntutest/+archive/primary/+files/alsa-utils-test_666.dsc',
+ [{'url': u'http://launchpad.dev/ubuntutest/+archive/primary/+sourcefiles/alsa-utils-test/666/alsa-utils-test_666.dsc',
'class': 'source',
'filesize': 28,
'filename': u'alsa-utils-test_666.dsc'}]
@@ -81,11 +81,11 @@
... iceweasel_source_pub, "+listing-archive-detailed")
>>> ppa_source_view.published_source_and_binary_files
- [{'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+files/firefox_0.9.2.orig.tar.gz',
+ [{'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/firefox_0.9.2.orig.tar.gz',
'class': 'source',
'filesize': 9922560,
'filename': u'firefox_0.9.2.orig.tar.gz'},
- {'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+files/iceweasel-1.0.dsc',
+ {'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/iceweasel-1.0.dsc',
'class': 'source',
'filesize': 123,
'filename': u'iceweasel-1.0.dsc'},
=== 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-04 22:20:39 +0000
@@ -9,6 +9,7 @@
from lp.services.librarian.browser import ProxiedLibraryFileAlias
from lp.services.webapp.interfaces import OAuthPermission
+from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
from lp.testing import (
api_url,
login_person,
@@ -47,8 +48,7 @@
with person_logged_in(person):
sprf = spph.sourcepackagerelease.files[0]
expected_urls = [
- ProxiedLibraryFileAlias(
- sprf.libraryfile, spph.archive).http_url]
+ ProxiedSourceLibraryFileAlias(sprf.libraryfile, spph).http_url]
self.assertEqual(expected_urls, urls)
def test_sourceFileUrls_include_meta(self):
@@ -75,8 +75,8 @@
info = response.jsonBody()
with person_logged_in(person):
expected_info = [{
- "url": ProxiedLibraryFileAlias(
- sprf.libraryfile, spph.archive).http_url,
+ "url": ProxiedSourceLibraryFileAlias(
+ sprf.libraryfile, spph).http_url,
"size": sprf.libraryfile.content.filesize,
"sha256": sprf.libraryfile.content.sha256,
} for sprf in spph.sourcepackagerelease.files]
=== 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-04 22:20:39 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Archive interfaces."""
@@ -936,6 +936,21 @@
:return the corresponding `ILibraryFileAlias` is the file was found.
"""
+ def getSourceFileByName(name, version, filename):
+ """Return the `ILibraryFileAlias` for a source name/version/filename.
+
+ This can be used to avoid ambiguities with `getFileByName` in
+ imported archives, where the upstream archive software may not
+ always have had robust historical filename uniqueness checks.
+
+ :param name: The name of the source package.
+ :param version: The version of the source package.
+ :param filename: The exact filename to look up.
+
+ :raises NotFoundError: if no matching file could be found.
+ :return: the corresponding `ILibraryFileAlias`.
+ """
+
def getBinaryPackageRelease(name, version, archtag):
"""Find the specified `IBinaryPackageRelease` in the archive.
=== 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-04 22:20:39 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Database class for table Archive."""
@@ -1688,6 +1688,29 @@
return archive_file
+ def getSourceFileByName(self, name, version, filename):
+ """See `IArchive`."""
+ result = IStore(LibraryFileAlias).find(
+ LibraryFileAlias,
+ SourcePackagePublishingHistory.archive == self,
+ SourcePackagePublishingHistory.sourcepackagereleaseID ==
+ SourcePackageRelease.id,
+ SourcePackageRelease.sourcepackagename == SourcePackageName.id,
+ SourcePackageName.name == name,
+ SourcePackageRelease.version == version,
+ SourcePackageRelease.id ==
+ SourcePackageReleaseFile.sourcepackagereleaseID,
+ SourcePackageReleaseFile.libraryfileID == LibraryFileAlias.id,
+ LibraryFileAlias.filename == filename,
+ LibraryFileAlias.content != None)
+ result = result.config(distinct=True).order_by(LibraryFileAlias.id)
+ # Unlike `getFileByName`, we are guaranteed at most one match even
+ # for files in imported archives.
+ archive_file = result.one()
+ if archive_file is None:
+ raise NotFoundError(filename)
+ return archive_file
+
def getBinaryPackageRelease(self, name, version, archtag):
"""See `IArchive`."""
from lp.soyuz.model.distroarchseries import DistroArchSeries
=== 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-04 22:20:39 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -76,6 +76,7 @@
ScriptRequest,
)
from lp.services.worlddata.model.country import Country
+from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
from lp.soyuz.enums import (
BinaryPackageFormat,
PackagePublishingPriority,
@@ -142,6 +143,12 @@
return [ProxiedLibraryFileAlias(file, parent).http_url for file in files]
+def proxied_source_urls(files, parent):
+ """Return the files passed through `ProxiedSourceLibraryFileAlias`."""
+ return [
+ ProxiedSourceLibraryFileAlias(file, parent).http_url for file in files]
+
+
class ArchivePublisherBase:
"""Base class for `IArchivePublisher`."""
@@ -548,8 +555,8 @@
SourcePackageReleaseFile.sourcepackagerelease ==
SourcePackageRelease.id,
SourcePackageRelease.id == self.sourcepackagereleaseID)
- source_urls = proxied_urls(
- [source for source, _ in sources], self.archive)
+ source_urls = proxied_source_urls(
+ [source for source, _ in sources], self)
if include_meta:
meta = [
(content.filesize, content.sha256) for _, content in sources]
=== 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-04 22:20:39 +0000
@@ -90,18 +90,19 @@
>>> ppa_links = [
... ('(changes file)',
- ... another_test_source.sourcepackagerelease.upload_changesfile),
+ ... another_test_source.sourcepackagerelease.upload_changesfile,
+ ... None, None),
... ]
>>> ppa_1_0_links = [
- ... ('test-pkg_1.0.dsc', dsc_file),
- ... ('test-pkg_1.0.tar.gz', tar_gz),
- ... ('test-bin_1.0_all.deb', deb_file),
+ ... ('test-pkg_1.0.dsc', dsc_file, 'test-pkg', '1.0'),
+ ... ('test-pkg_1.0.tar.gz', tar_gz, 'test-pkg', '1.0'),
+ ... ('test-bin_1.0_all.deb', deb_file, None, None),
... ]
>>> ppa_1_1_links = [
- ... ('test-pkg_1.1.dsc', another_dsc_file),
- ... ('1.0 to 1.1', package_diff.diff_content),
+ ... ('test-pkg_1.1.dsc', another_dsc_file, 'test-pkg', '1.1'),
+ ... ('1.0 to 1.1', package_diff.diff_content, None, None),
... ]
Links to files accessible via +files/ proxy in the Build page.
@@ -109,13 +110,14 @@
>>> build_id = build.id
>>> builds_links = [
- ... ('see the log', build.log),
+ ... ('see the log', build.log, None, None),
... ]
>>> build_links = [
- ... ('test-bin_1.0_i386.changes', build.upload_changesfile),
- ... ('buildlog', build.log),
- ... ('uploadlog', build.upload_log),
+ ... ('test-bin_1.0_i386.changes', build.upload_changesfile,
+ ... None, None),
+ ... ('buildlog', build.log, None, None),
+ ... ('uploadlog', build.upload_log, None, None),
... ]
>>> logout()
@@ -124,15 +126,20 @@
>>> from mechanize import LinkNotFoundError
>>> def check_urls(browser, links, base_url):
- ... for link, libraryfile in links:
+ ... for link, libraryfile, source_name, source_version in links:
... try:
... found_url = browser.getLink(link).url
... except LinkNotFoundError:
... print '%s: NOT FOUND' % libraryfile.filename
... continue
... found_url = found_url.replace('%7E', '~')
- ... expected_url = '/'.join(
- ... (base_url, '+files', libraryfile.filename))
+ ... if source_name is not None:
+ ... expected_url = '/'.join(
+ ... (base_url, '+sourcefiles', source_name,
+ ... source_version, libraryfile.filename))
+ ... else:
+ ... expected_url = '/'.join(
+ ... (base_url, '+files', libraryfile.filename))
... if found_url == expected_url:
... print '%s: OK' % libraryfile.filename
... else:
@@ -184,7 +191,7 @@
... 'http://launchpad.dev/~no-priv/+archive/ubuntu/p3a/+packages')
Source and binary files, in the expandable-row area, are served via
-the PPA '+files' traversal.
+the PPA '+sourcefiles' and '+files' traversals.
>>> expander_id = find_tags_by_class(
... no_priv_browser.contents, 'expander')[1]['id']
=== 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-04 22:20:39 +0000
@@ -383,7 +383,7 @@
>>> expander_url = foo_browser.getLink(id=expander_id).url
>>> anon_browser.open(expander_url)
>>> print anon_browser.getLink("orig").url
- http://.../+files/foo.orig.tar.gz
+ http://.../+sourcefiles/.../foo.orig.tar.gz
The uploader name is linkified to that user's home page:
=== 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-04 22:20:39 +0000
@@ -201,7 +201,7 @@
View changes file
>>> print anon_browser.getLink('testing-dspr_1.0.dsc').url
- http://.../ubuntutest/+archive/primary/+files/testing-dspr_1.0.dsc
+ http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc
The 'Downloads' section also lists and link to package diffs when they
are available.
=== 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-04 22:20:39 +0000
@@ -90,7 +90,7 @@
firefox_0.9.2.orig.tar.gz 9.5 MiB ...
>>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url
- http://launchpad.dev/ubuntu/+archive/primary/+files/firefox_0.9.2.orig.tar.gz
+ http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz
This page also provides links to the binary packages generated by this
source in a specfic architecture:
@@ -285,7 +285,7 @@
firefox_0.9.2.orig.tar.gz 9.5 MiB ...
>>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url
- http://launchpad.dev/ubuntu/+archive/primary/+files/firefox_0.9.2.orig.tar.gz
+ http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz
If we go to the same page for alsa-utils, the changelog has text that is
linkified.
@@ -353,11 +353,11 @@
commercialpackage_1.0-1.dsc 567 bytes ...
>>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url
- http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0.orig.tar.gz
+ http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz
>>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url
- http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.diff.gz
+ http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz
>>> print browser.getLink("commercialpackage_1.0-1.dsc").url
- http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.dsc
+ http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc
This page also provides links to the binary packages generated by this
source in a specfic architecture:
@@ -433,11 +433,11 @@
commercialpackage_1.0-1.dsc 567 bytes ...
>>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url
- http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0.orig.tar.gz
+ http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz
>>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url
- http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.diff.gz
+ http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz
>>> print browser.getLink("commercialpackage_1.0-1.dsc").url
- http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.dsc
+ http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc
Tracing copied sources
=== 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-04 22:20:39 +0000
@@ -375,11 +375,11 @@
... source_urls = webservice.named_get(
... pub_link, 'sourceFileUrls').jsonBody()
... print source_urls
- [u'http://.../~cprov/+archive/ubuntu/ppa/+files/foobar-1.0.dsc']
- [u'http://.../~cprov/+archive/ubuntu/ppa/+files/firefox_0.9.2.orig.tar.gz',
- u'http://.../~cprov/+archive/ubuntu/ppa/+files/iceweasel-1.0.dsc']
+ [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/cdrkit/1.0/foobar-1.0.dsc']
+ [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/firefox_0.9.2.orig.tar.gz',
+ u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/iceweasel-1.0.dsc']
[]
- [u'http://.../~cprov/+archive/ubuntu/ppa/+files/testwebservice_666.dsc']
+ [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/testwebservice/666/testwebservice_666.dsc']
binaryFileUrls() is similar:
=== 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-04 22:20:39 +0000
@@ -2425,6 +2425,89 @@
self.archive.getFileByName(pu.changesfile.filename))
+class TestGetSourceFileByName(TestCaseWithFactory):
+ """Tests for Archive.getSourceFileByName."""
+
+ layer = LaunchpadZopelessLayer
+
+ def setUp(self):
+ super(TestGetSourceFileByName, self).setUp()
+ self.archive = self.factory.makeArchive()
+
+ def test_source_file_is_found(self):
+ # A file from a published source package can be retrieved.
+ pub = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive)
+ dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
+ self.assertRaises(
+ NotFoundError, self.archive.getSourceFileByName,
+ pub.source_package_name, pub.source_package_version, dsc.filename)
+ pub.sourcepackagerelease.addFile(dsc)
+ self.assertEqual(
+ dsc, self.archive.getSourceFileByName(
+ pub.source_package_name, pub.source_package_version,
+ dsc.filename))
+
+ def test_nonexistent_source_file_is_not_found(self):
+ # Something that looks like a source file but isn't is not
+ # found.
+ pub = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive)
+ self.assertRaises(
+ NotFoundError, self.archive.getSourceFileByName,
+ pub.source_package_name, pub.source_package_version,
+ 'foo_1.0.dsc')
+
+ def test_nonexistent_source_package_version_is_not_found(self):
+ # The source package version must match exactly.
+ pub = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive)
+ pub2 = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive, sourcepackagename=pub.source_package_name)
+ dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
+ pub2.sourcepackagerelease.addFile(dsc)
+ self.assertRaises(
+ NotFoundError, self.archive.getSourceFileByName,
+ pub.source_package_name, pub.source_package_version,
+ 'foo_1.0.dsc')
+
+ def test_nonexistent_source_package_name_is_not_found(self):
+ # The source package name must match exactly.
+ pub = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive)
+ pub2 = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive)
+ dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
+ pub2.sourcepackagerelease.addFile(dsc)
+ self.assertRaises(
+ NotFoundError, self.archive.getSourceFileByName,
+ pub.source_package_name, pub.source_package_version,
+ 'foo_1.0.dsc')
+
+ def test_epoch_stripping_collision(self):
+ # Even if the archive contains two source packages with identical
+ # names and versions apart from epochs which have the same filenames
+ # with different contents (the worst case), getSourceFileByName
+ # returns the correct files.
+ pub = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive, version='1.0-1')
+ dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
+ pub.sourcepackagerelease.addFile(dsc)
+ pub2 = self.factory.makeSourcePackagePublishingHistory(
+ archive=self.archive, sourcepackagename=pub.source_package_name,
+ version='1:1.0-1')
+ dsc2 = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc')
+ pub2.sourcepackagerelease.addFile(dsc2)
+ self.assertEqual(
+ dsc, self.archive.getSourceFileByName(
+ pub.source_package_name, pub.source_package_version,
+ dsc.filename))
+ self.assertEqual(
+ dsc2, self.archive.getSourceFileByName(
+ pub2.source_package_name, pub2.source_package_version,
+ dsc2.filename))
+
+
class TestGetPublishedSources(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
=== 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-04 22:20:39 +0000
@@ -14,6 +14,7 @@
from lp.services.database.constants import UTC_NOW
from lp.services.librarian.browser import ProxiedLibraryFileAlias
from lp.services.webapp.publisher import canonical_url
+from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
from lp.soyuz.enums import (
BinaryPackageFileType,
BinaryPackageFormat,
@@ -159,8 +160,7 @@
def getURLsForSPPH(self, spph, include_meta=False):
spr = spph.sourcepackagerelease
- archive = spph.archive
- urls = [ProxiedLibraryFileAlias(f.libraryfile, archive).http_url
+ urls = [ProxiedSourceLibraryFileAlias(f.libraryfile, spph).http_url
for f in spr.files]
if include_meta:
Follow ups