Merge lp:~cjwatson/launchpad/archive-unambiguous-files-traversals into lp:launchpad
- archive-unambiguous-files-traversals
- Merge into devel
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 |
Related bugs: |
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.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/services/librarian/browser.py' |
2 | --- lib/lp/services/librarian/browser.py 2015-07-09 20:06:17 +0000 |
3 | +++ lib/lp/services/librarian/browser.py 2018-05-08 18:13:25 +0000 |
4 | @@ -1,4 +1,4 @@ |
5 | -# Copyright 2010 Canonical Ltd. This software is licensed under the |
6 | +# Copyright 2010-2018 Canonical Ltd. This software is licensed under the |
7 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | |
9 | """Browser file for LibraryFileAlias.""" |
10 | @@ -125,6 +125,13 @@ |
11 | self.parent = parent |
12 | |
13 | @property |
14 | + def request(self): |
15 | + request = get_current_browser_request() |
16 | + if WebServiceLayer.providedBy(request): |
17 | + request = IWebBrowserOriginatingRequest(request) |
18 | + return request |
19 | + |
20 | + @property |
21 | def http_url(self): |
22 | """Return the webapp URL for the context `LibraryFileAlias`. |
23 | |
24 | @@ -137,11 +144,7 @@ |
25 | if self.context.deleted: |
26 | return None |
27 | |
28 | - request = get_current_browser_request() |
29 | - if WebServiceLayer.providedBy(request): |
30 | - request = IWebBrowserOriginatingRequest(request) |
31 | - |
32 | - parent_url = canonical_url(self.parent, request=request) |
33 | + parent_url = canonical_url(self.parent, request=self.request) |
34 | traversal_url = urlappend(parent_url, '+files') |
35 | url = urlappend( |
36 | traversal_url, |
37 | |
38 | === added file 'lib/lp/soyuz/adapters/proxiedsourcefiles.py' |
39 | --- lib/lp/soyuz/adapters/proxiedsourcefiles.py 1970-01-01 00:00:00 +0000 |
40 | +++ lib/lp/soyuz/adapters/proxiedsourcefiles.py 2018-05-08 18:13:25 +0000 |
41 | @@ -0,0 +1,36 @@ |
42 | +# Copyright 2018 Canonical Ltd. This software is licensed under the |
43 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
44 | + |
45 | +"""Proxied source files.""" |
46 | + |
47 | +from __future__ import absolute_import, print_function, unicode_literals |
48 | + |
49 | +__metaclass__ = type |
50 | +__all__ = [ |
51 | + 'ProxiedSourceLibraryFileAlias', |
52 | + ] |
53 | + |
54 | +from lp.services.librarian.browser import ProxiedLibraryFileAlias |
55 | +from lp.services.librarian.client import url_path_quote |
56 | +from lp.services.webapp.publisher import canonical_url |
57 | +from lp.services.webapp.url import urlappend |
58 | + |
59 | + |
60 | +class ProxiedSourceLibraryFileAlias(ProxiedLibraryFileAlias): |
61 | + """A `ProxiedLibraryFileAlias` variant that traverses via +sourcefiles. |
62 | + |
63 | + This can be used to construct unambiguous source file URLs even for |
64 | + imports from upstream archives without robust historical filename |
65 | + uniqueness checks. |
66 | + """ |
67 | + |
68 | + @property |
69 | + def http_url(self): |
70 | + if self.context.deleted: |
71 | + return None |
72 | + |
73 | + url = canonical_url(self.parent.archive, request=self.request) |
74 | + return urlappend(url, '/'.join([ |
75 | + '+sourcefiles', self.parent.source_package_name, |
76 | + self.parent.source_package_version, |
77 | + url_path_quote(self.context.filename.encode('utf-8'))])) |
78 | |
79 | === modified file 'lib/lp/soyuz/browser/archive.py' |
80 | --- lib/lp/soyuz/browser/archive.py 2017-04-22 13:13:22 +0000 |
81 | +++ lib/lp/soyuz/browser/archive.py 2018-05-08 18:13:25 +0000 |
82 | @@ -1,4 +1,4 @@ |
83 | -# Copyright 2009-2017 Canonical Ltd. This software is licensed under the |
84 | +# Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
85 | # GNU Affero General Public License version 3 (see the file LICENSE). |
86 | |
87 | """Browser views for archive.""" |
88 | @@ -455,6 +455,32 @@ |
89 | |
90 | return self.context.getArchiveDependency(archive) |
91 | |
92 | + @stepthrough('+sourcefiles') |
93 | + def traverse_sourcefiles(self, sourcepackagename): |
94 | + """Traverse to a source file in the archive. |
95 | + |
96 | + Normally, files in an archive are unique by filename, so the +files |
97 | + traversal is sufficient. Unfortunately, a gina-imported archive may |
98 | + contain the same filename with different contents due to a |
99 | + combination of epochs and less stringent checks applied by the |
100 | + upstream archive software. (In practice this only happens for |
101 | + source packages because that's normally all we import using gina.) |
102 | + This provides an unambiguous way to traverse to such files even with |
103 | + this problem. |
104 | + |
105 | + The path scheme is:: |
106 | + |
107 | + +sourcefiles/:sourcename/:sourceversion/:filename |
108 | + """ |
109 | + if len(self.request.stepstogo) < 2: |
110 | + return None |
111 | + |
112 | + version = self.request.stepstogo.consume() |
113 | + filename = self.request.stepstogo.consume() |
114 | + |
115 | + return self.context.getSourceFileByName( |
116 | + sourcepackagename, version, filename) |
117 | + |
118 | |
119 | class ArchiveMenuMixin: |
120 | |
121 | |
122 | === modified file 'lib/lp/soyuz/browser/distributionsourcepackagerelease.py' |
123 | --- lib/lp/soyuz/browser/distributionsourcepackagerelease.py 2014-12-18 13:05:10 +0000 |
124 | +++ lib/lp/soyuz/browser/distributionsourcepackagerelease.py 2018-05-08 18:13:25 +0000 |
125 | @@ -1,4 +1,4 @@ |
126 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
127 | +# Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
128 | # GNU Affero General Public License version 3 (see the file LICENSE). |
129 | |
130 | __metaclass__ = type |
131 | @@ -18,7 +18,6 @@ |
132 | from lp.registry.browser.distributionsourcepackage import ( |
133 | PublishingHistoryViewMixin, |
134 | ) |
135 | -from lp.services.librarian.browser import ProxiedLibraryFileAlias |
136 | from lp.services.propertycache import cachedproperty |
137 | from lp.services.webapp import ( |
138 | canonical_url, |
139 | @@ -27,6 +26,7 @@ |
140 | stepthrough, |
141 | ) |
142 | from lp.services.webapp.breadcrumb import Breadcrumb |
143 | +from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias |
144 | from lp.soyuz.browser.build import get_build_by_id_str |
145 | from lp.soyuz.enums import PackagePublishingStatus |
146 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet |
147 | @@ -101,8 +101,8 @@ |
148 | """The source package release files as `ProxiedLibraryFileAlias`.""" |
149 | last_publication = self._cached_publishing_history[0] |
150 | return [ |
151 | - ProxiedLibraryFileAlias( |
152 | - source_file.libraryfile, last_publication.archive) |
153 | + ProxiedSourceLibraryFileAlias( |
154 | + source_file.libraryfile, last_publication) |
155 | for source_file in self.context.files] |
156 | |
157 | @cachedproperty |
158 | |
159 | === modified file 'lib/lp/soyuz/browser/publishing.py' |
160 | --- lib/lp/soyuz/browser/publishing.py 2015-07-09 20:06:17 +0000 |
161 | +++ lib/lp/soyuz/browser/publishing.py 2018-05-08 18:13:25 +0000 |
162 | @@ -1,4 +1,4 @@ |
163 | -# Copyright 2009-2013 Canonical Ltd. This software is licensed under the |
164 | +# Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
165 | # GNU Affero General Public License version 3 (see the file LICENSE). |
166 | |
167 | """Browser views for Soyuz publishing records.""" |
168 | @@ -30,6 +30,7 @@ |
169 | canonical_url, |
170 | LaunchpadView, |
171 | ) |
172 | +from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias |
173 | from lp.soyuz.enums import PackagePublishingStatus |
174 | from lp.soyuz.interfaces.binarypackagebuild import BuildSetStatus |
175 | from lp.soyuz.interfaces.packagediff import IPackageDiff |
176 | @@ -270,8 +271,7 @@ |
177 | def published_source_and_binary_files(self): |
178 | """Return list of dictionaries representing published files.""" |
179 | files = sorted( |
180 | - (ProxiedLibraryFileAlias(lfa, self.context.archive) |
181 | - for lfa in self.context.getSourceAndBinaryLibraryFiles()), |
182 | + self.context.getSourceAndBinaryLibraryFiles(), |
183 | key=attrgetter('filename')) |
184 | result = [] |
185 | urls = set() |
186 | @@ -285,14 +285,17 @@ |
187 | urls.add(url) |
188 | |
189 | custom_dict = {} |
190 | - custom_dict["url"] = url |
191 | custom_dict["filename"] = library_file.filename |
192 | custom_dict["filesize"] = library_file.content.filesize |
193 | if (library_file.filename.endswith('.deb') or |
194 | library_file.filename.endswith('.udeb')): |
195 | custom_dict['class'] = 'binary' |
196 | + custom_dict["url"] = ProxiedLibraryFileAlias( |
197 | + library_file, self.context.archive).http_url |
198 | else: |
199 | custom_dict['class'] = 'source' |
200 | + custom_dict["url"] = ProxiedSourceLibraryFileAlias( |
201 | + library_file, self.context).http_url |
202 | |
203 | result.append(custom_dict) |
204 | |
205 | |
206 | === modified file 'lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt' |
207 | --- lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt 2014-11-27 22:13:36 +0000 |
208 | +++ lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt 2018-05-08 18:13:25 +0000 |
209 | @@ -24,14 +24,14 @@ |
210 | u'testing-dspr 1.0 source package in ubuntutest' |
211 | |
212 | The 'files' property returns a list of files included in the source |
213 | -upload encapsulated as `ProxiedLibraryFileAlias` objects. Their |
214 | +upload encapsulated as `ProxiedSourceLibraryFileAlias` objects. Their |
215 | 'http_url' points to the LP proxied url which normalizes the path |
216 | tofiles allowing them to be downloaded using `dget`. |
217 | |
218 | >>> for source_file in dspr_view.files: |
219 | ... print source_file.filename, source_file.http_url |
220 | testing-dspr_1.0.dsc |
221 | - http://.../ubuntutest/+archive/primary/+files/testing-dspr_1.0.dsc |
222 | + http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc |
223 | |
224 | The 'sponsor' property indicates whether the upload was 'sponsored' or |
225 | not. When the upload was signed by someone else than the source |
226 | |
227 | === modified file 'lib/lp/soyuz/browser/tests/publishing-views.txt' |
228 | --- lib/lp/soyuz/browser/tests/publishing-views.txt 2014-07-24 09:37:03 +0000 |
229 | +++ lib/lp/soyuz/browser/tests/publishing-views.txt 2018-05-08 18:13:25 +0000 |
230 | @@ -63,7 +63,7 @@ |
231 | >>> view = create_initialized_view(alsa_pub, "+listing-archive-detailed") |
232 | |
233 | >>> view.published_source_and_binary_files |
234 | - [{'url': u'http://launchpad.dev/ubuntutest/+archive/primary/+files/alsa-utils-test_666.dsc', |
235 | + [{'url': u'http://launchpad.dev/ubuntutest/+archive/primary/+sourcefiles/alsa-utils-test/666/alsa-utils-test_666.dsc', |
236 | 'class': 'source', |
237 | 'filesize': 28, |
238 | 'filename': u'alsa-utils-test_666.dsc'}] |
239 | @@ -81,11 +81,11 @@ |
240 | ... iceweasel_source_pub, "+listing-archive-detailed") |
241 | |
242 | >>> ppa_source_view.published_source_and_binary_files |
243 | - [{'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+files/firefox_0.9.2.orig.tar.gz', |
244 | + [{'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/firefox_0.9.2.orig.tar.gz', |
245 | 'class': 'source', |
246 | 'filesize': 9922560, |
247 | 'filename': u'firefox_0.9.2.orig.tar.gz'}, |
248 | - {'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+files/iceweasel-1.0.dsc', |
249 | + {'url': u'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/iceweasel-1.0.dsc', |
250 | 'class': 'source', |
251 | 'filesize': 123, |
252 | 'filename': u'iceweasel-1.0.dsc'}, |
253 | |
254 | === modified file 'lib/lp/soyuz/browser/tests/test_publishing_webservice.py' |
255 | --- lib/lp/soyuz/browser/tests/test_publishing_webservice.py 2018-02-01 18:44:21 +0000 |
256 | +++ lib/lp/soyuz/browser/tests/test_publishing_webservice.py 2018-05-08 18:13:25 +0000 |
257 | @@ -9,6 +9,7 @@ |
258 | |
259 | from lp.services.librarian.browser import ProxiedLibraryFileAlias |
260 | from lp.services.webapp.interfaces import OAuthPermission |
261 | +from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias |
262 | from lp.testing import ( |
263 | api_url, |
264 | login_person, |
265 | @@ -47,8 +48,7 @@ |
266 | with person_logged_in(person): |
267 | sprf = spph.sourcepackagerelease.files[0] |
268 | expected_urls = [ |
269 | - ProxiedLibraryFileAlias( |
270 | - sprf.libraryfile, spph.archive).http_url] |
271 | + ProxiedSourceLibraryFileAlias(sprf.libraryfile, spph).http_url] |
272 | self.assertEqual(expected_urls, urls) |
273 | |
274 | def test_sourceFileUrls_include_meta(self): |
275 | @@ -75,8 +75,8 @@ |
276 | info = response.jsonBody() |
277 | with person_logged_in(person): |
278 | expected_info = [{ |
279 | - "url": ProxiedLibraryFileAlias( |
280 | - sprf.libraryfile, spph.archive).http_url, |
281 | + "url": ProxiedSourceLibraryFileAlias( |
282 | + sprf.libraryfile, spph).http_url, |
283 | "size": sprf.libraryfile.content.filesize, |
284 | "sha256": sprf.libraryfile.content.sha256, |
285 | } for sprf in spph.sourcepackagerelease.files] |
286 | |
287 | === modified file 'lib/lp/soyuz/interfaces/archive.py' |
288 | --- lib/lp/soyuz/interfaces/archive.py 2017-09-28 14:06:20 +0000 |
289 | +++ lib/lp/soyuz/interfaces/archive.py 2018-05-08 18:13:25 +0000 |
290 | @@ -1,4 +1,4 @@ |
291 | -# Copyright 2009-2017 Canonical Ltd. This software is licensed under the |
292 | +# Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
293 | # GNU Affero General Public License version 3 (see the file LICENSE). |
294 | |
295 | """Archive interfaces.""" |
296 | @@ -936,6 +936,21 @@ |
297 | :return the corresponding `ILibraryFileAlias` is the file was found. |
298 | """ |
299 | |
300 | + def getSourceFileByName(name, version, filename): |
301 | + """Return the `ILibraryFileAlias` for a source name/version/filename. |
302 | + |
303 | + This can be used to avoid ambiguities with `getFileByName` in |
304 | + imported archives, where the upstream archive software may not |
305 | + always have had robust historical filename uniqueness checks. |
306 | + |
307 | + :param name: The name of the source package. |
308 | + :param version: The version of the source package. |
309 | + :param filename: The exact filename to look up. |
310 | + |
311 | + :raises NotFoundError: if no matching file could be found. |
312 | + :return: the corresponding `ILibraryFileAlias`. |
313 | + """ |
314 | + |
315 | def getBinaryPackageRelease(name, version, archtag): |
316 | """Find the specified `IBinaryPackageRelease` in the archive. |
317 | |
318 | |
319 | === modified file 'lib/lp/soyuz/model/archive.py' |
320 | --- lib/lp/soyuz/model/archive.py 2018-01-30 16:19:15 +0000 |
321 | +++ lib/lp/soyuz/model/archive.py 2018-05-08 18:13:25 +0000 |
322 | @@ -1,4 +1,4 @@ |
323 | -# Copyright 2009-2017 Canonical Ltd. This software is licensed under the |
324 | +# Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
325 | # GNU Affero General Public License version 3 (see the file LICENSE). |
326 | |
327 | """Database class for table Archive.""" |
328 | @@ -1688,6 +1688,29 @@ |
329 | |
330 | return archive_file |
331 | |
332 | + def getSourceFileByName(self, name, version, filename): |
333 | + """See `IArchive`.""" |
334 | + result = IStore(LibraryFileAlias).find( |
335 | + LibraryFileAlias, |
336 | + SourcePackagePublishingHistory.archive == self, |
337 | + SourcePackagePublishingHistory.sourcepackagereleaseID == |
338 | + SourcePackageRelease.id, |
339 | + SourcePackageRelease.sourcepackagename == SourcePackageName.id, |
340 | + SourcePackageName.name == name, |
341 | + SourcePackageRelease.version == version, |
342 | + SourcePackageRelease.id == |
343 | + SourcePackageReleaseFile.sourcepackagereleaseID, |
344 | + SourcePackageReleaseFile.libraryfileID == LibraryFileAlias.id, |
345 | + LibraryFileAlias.filename == filename, |
346 | + LibraryFileAlias.content != None) |
347 | + result = result.config(distinct=True).order_by(LibraryFileAlias.id) |
348 | + # Unlike `getFileByName`, we are guaranteed at most one match even |
349 | + # for files in imported archives. |
350 | + archive_file = result.one() |
351 | + if archive_file is None: |
352 | + raise NotFoundError(filename) |
353 | + return archive_file |
354 | + |
355 | def getBinaryPackageRelease(self, name, version, archtag): |
356 | """See `IArchive`.""" |
357 | from lp.soyuz.model.distroarchseries import DistroArchSeries |
358 | |
359 | === modified file 'lib/lp/soyuz/model/publishing.py' |
360 | --- lib/lp/soyuz/model/publishing.py 2017-06-02 21:46:50 +0000 |
361 | +++ lib/lp/soyuz/model/publishing.py 2018-05-08 18:13:25 +0000 |
362 | @@ -1,4 +1,4 @@ |
363 | -# Copyright 2009-2017 Canonical Ltd. This software is licensed under the |
364 | +# Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
365 | # GNU Affero General Public License version 3 (see the file LICENSE). |
366 | |
367 | __metaclass__ = type |
368 | @@ -76,6 +76,7 @@ |
369 | ScriptRequest, |
370 | ) |
371 | from lp.services.worlddata.model.country import Country |
372 | +from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias |
373 | from lp.soyuz.enums import ( |
374 | BinaryPackageFormat, |
375 | PackagePublishingPriority, |
376 | @@ -142,6 +143,12 @@ |
377 | return [ProxiedLibraryFileAlias(file, parent).http_url for file in files] |
378 | |
379 | |
380 | +def proxied_source_urls(files, parent): |
381 | + """Return the files passed through `ProxiedSourceLibraryFileAlias`.""" |
382 | + return [ |
383 | + ProxiedSourceLibraryFileAlias(file, parent).http_url for file in files] |
384 | + |
385 | + |
386 | class ArchivePublisherBase: |
387 | """Base class for `IArchivePublisher`.""" |
388 | |
389 | @@ -548,8 +555,8 @@ |
390 | SourcePackageReleaseFile.sourcepackagerelease == |
391 | SourcePackageRelease.id, |
392 | SourcePackageRelease.id == self.sourcepackagereleaseID) |
393 | - source_urls = proxied_urls( |
394 | - [source for source, _ in sources], self.archive) |
395 | + source_urls = proxied_source_urls( |
396 | + [source for source, _ in sources], self) |
397 | if include_meta: |
398 | meta = [ |
399 | (content.filesize, content.sha256) for _, content in sources] |
400 | |
401 | === modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-files.txt' |
402 | --- lib/lp/soyuz/stories/ppa/xx-ppa-files.txt 2016-01-26 15:47:37 +0000 |
403 | +++ lib/lp/soyuz/stories/ppa/xx-ppa-files.txt 2018-05-08 18:13:25 +0000 |
404 | @@ -90,18 +90,19 @@ |
405 | |
406 | >>> ppa_links = [ |
407 | ... ('(changes file)', |
408 | - ... another_test_source.sourcepackagerelease.upload_changesfile), |
409 | + ... another_test_source.sourcepackagerelease.upload_changesfile, |
410 | + ... None, None), |
411 | ... ] |
412 | |
413 | >>> ppa_1_0_links = [ |
414 | - ... ('test-pkg_1.0.dsc', dsc_file), |
415 | - ... ('test-pkg_1.0.tar.gz', tar_gz), |
416 | - ... ('test-bin_1.0_all.deb', deb_file), |
417 | + ... ('test-pkg_1.0.dsc', dsc_file, 'test-pkg', '1.0'), |
418 | + ... ('test-pkg_1.0.tar.gz', tar_gz, 'test-pkg', '1.0'), |
419 | + ... ('test-bin_1.0_all.deb', deb_file, None, None), |
420 | ... ] |
421 | |
422 | >>> ppa_1_1_links = [ |
423 | - ... ('test-pkg_1.1.dsc', another_dsc_file), |
424 | - ... ('1.0 to 1.1', package_diff.diff_content), |
425 | + ... ('test-pkg_1.1.dsc', another_dsc_file, 'test-pkg', '1.1'), |
426 | + ... ('1.0 to 1.1', package_diff.diff_content, None, None), |
427 | ... ] |
428 | |
429 | Links to files accessible via +files/ proxy in the Build page. |
430 | @@ -109,13 +110,14 @@ |
431 | >>> build_id = build.id |
432 | |
433 | >>> builds_links = [ |
434 | - ... ('see the log', build.log), |
435 | + ... ('see the log', build.log, None, None), |
436 | ... ] |
437 | |
438 | >>> build_links = [ |
439 | - ... ('test-bin_1.0_i386.changes', build.upload_changesfile), |
440 | - ... ('buildlog', build.log), |
441 | - ... ('uploadlog', build.upload_log), |
442 | + ... ('test-bin_1.0_i386.changes', build.upload_changesfile, |
443 | + ... None, None), |
444 | + ... ('buildlog', build.log, None, None), |
445 | + ... ('uploadlog', build.upload_log, None, None), |
446 | ... ] |
447 | |
448 | >>> logout() |
449 | @@ -124,15 +126,20 @@ |
450 | |
451 | >>> from mechanize import LinkNotFoundError |
452 | >>> def check_urls(browser, links, base_url): |
453 | - ... for link, libraryfile in links: |
454 | + ... for link, libraryfile, source_name, source_version in links: |
455 | ... try: |
456 | ... found_url = browser.getLink(link).url |
457 | ... except LinkNotFoundError: |
458 | ... print '%s: NOT FOUND' % libraryfile.filename |
459 | ... continue |
460 | ... found_url = found_url.replace('%7E', '~') |
461 | - ... expected_url = '/'.join( |
462 | - ... (base_url, '+files', libraryfile.filename)) |
463 | + ... if source_name is not None: |
464 | + ... expected_url = '/'.join( |
465 | + ... (base_url, '+sourcefiles', source_name, |
466 | + ... source_version, libraryfile.filename)) |
467 | + ... else: |
468 | + ... expected_url = '/'.join( |
469 | + ... (base_url, '+files', libraryfile.filename)) |
470 | ... if found_url == expected_url: |
471 | ... print '%s: OK' % libraryfile.filename |
472 | ... else: |
473 | @@ -184,7 +191,7 @@ |
474 | ... 'http://launchpad.dev/~no-priv/+archive/ubuntu/p3a/+packages') |
475 | |
476 | Source and binary files, in the expandable-row area, are served via |
477 | -the PPA '+files' traversal. |
478 | +the PPA '+sourcefiles' and '+files' traversals. |
479 | |
480 | >>> expander_id = find_tags_by_class( |
481 | ... no_priv_browser.contents, 'expander')[1]['id'] |
482 | |
483 | === modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt' |
484 | --- lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2015-04-09 05:16:37 +0000 |
485 | +++ lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2018-05-08 18:13:25 +0000 |
486 | @@ -383,7 +383,7 @@ |
487 | >>> expander_url = foo_browser.getLink(id=expander_id).url |
488 | >>> anon_browser.open(expander_url) |
489 | >>> print anon_browser.getLink("orig").url |
490 | - http://.../+files/foo.orig.tar.gz |
491 | + http://.../+sourcefiles/.../foo.orig.tar.gz |
492 | |
493 | The uploader name is linkified to that user's home page: |
494 | |
495 | |
496 | === modified file 'lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt' |
497 | --- lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt 2016-03-30 10:01:57 +0000 |
498 | +++ lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt 2018-05-08 18:13:25 +0000 |
499 | @@ -201,7 +201,7 @@ |
500 | View changes file |
501 | |
502 | >>> print anon_browser.getLink('testing-dspr_1.0.dsc').url |
503 | - http://.../ubuntutest/+archive/primary/+files/testing-dspr_1.0.dsc |
504 | + http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc |
505 | |
506 | The 'Downloads' section also lists and link to package diffs when they |
507 | are available. |
508 | |
509 | === modified file 'lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt' |
510 | --- lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2016-03-30 10:01:57 +0000 |
511 | +++ lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2018-05-08 18:13:25 +0000 |
512 | @@ -90,7 +90,7 @@ |
513 | firefox_0.9.2.orig.tar.gz 9.5 MiB ... |
514 | |
515 | >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url |
516 | - http://launchpad.dev/ubuntu/+archive/primary/+files/firefox_0.9.2.orig.tar.gz |
517 | + http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz |
518 | |
519 | This page also provides links to the binary packages generated by this |
520 | source in a specfic architecture: |
521 | @@ -285,7 +285,7 @@ |
522 | firefox_0.9.2.orig.tar.gz 9.5 MiB ... |
523 | |
524 | >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url |
525 | - http://launchpad.dev/ubuntu/+archive/primary/+files/firefox_0.9.2.orig.tar.gz |
526 | + http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz |
527 | |
528 | If we go to the same page for alsa-utils, the changelog has text that is |
529 | linkified. |
530 | @@ -353,11 +353,11 @@ |
531 | commercialpackage_1.0-1.dsc 567 bytes ... |
532 | |
533 | >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url |
534 | - http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0.orig.tar.gz |
535 | + http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz |
536 | >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url |
537 | - http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.diff.gz |
538 | + http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz |
539 | >>> print browser.getLink("commercialpackage_1.0-1.dsc").url |
540 | - http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.dsc |
541 | + http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc |
542 | |
543 | This page also provides links to the binary packages generated by this |
544 | source in a specfic architecture: |
545 | @@ -433,11 +433,11 @@ |
546 | commercialpackage_1.0-1.dsc 567 bytes ... |
547 | |
548 | >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url |
549 | - http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0.orig.tar.gz |
550 | + http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz |
551 | >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url |
552 | - http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.diff.gz |
553 | + http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz |
554 | >>> print browser.getLink("commercialpackage_1.0-1.dsc").url |
555 | - http://launchpad.dev/ubuntu/+archive/partner/+files/commercialpackage_1.0-1.dsc |
556 | + http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc |
557 | |
558 | |
559 | Tracing copied sources |
560 | |
561 | === modified file 'lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt' |
562 | --- lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt 2016-03-02 15:52:33 +0000 |
563 | +++ lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt 2018-05-08 18:13:25 +0000 |
564 | @@ -375,11 +375,11 @@ |
565 | ... source_urls = webservice.named_get( |
566 | ... pub_link, 'sourceFileUrls').jsonBody() |
567 | ... print source_urls |
568 | - [u'http://.../~cprov/+archive/ubuntu/ppa/+files/foobar-1.0.dsc'] |
569 | - [u'http://.../~cprov/+archive/ubuntu/ppa/+files/firefox_0.9.2.orig.tar.gz', |
570 | - u'http://.../~cprov/+archive/ubuntu/ppa/+files/iceweasel-1.0.dsc'] |
571 | + [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/cdrkit/1.0/foobar-1.0.dsc'] |
572 | + [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/firefox_0.9.2.orig.tar.gz', |
573 | + u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/iceweasel-1.0.dsc'] |
574 | [] |
575 | - [u'http://.../~cprov/+archive/ubuntu/ppa/+files/testwebservice_666.dsc'] |
576 | + [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/testwebservice/666/testwebservice_666.dsc'] |
577 | |
578 | binaryFileUrls() is similar: |
579 | |
580 | |
581 | === modified file 'lib/lp/soyuz/tests/test_archive.py' |
582 | --- lib/lp/soyuz/tests/test_archive.py 2018-02-14 11:13:47 +0000 |
583 | +++ lib/lp/soyuz/tests/test_archive.py 2018-05-08 18:13:25 +0000 |
584 | @@ -2425,6 +2425,89 @@ |
585 | self.archive.getFileByName(pu.changesfile.filename)) |
586 | |
587 | |
588 | +class TestGetSourceFileByName(TestCaseWithFactory): |
589 | + """Tests for Archive.getSourceFileByName.""" |
590 | + |
591 | + layer = LaunchpadZopelessLayer |
592 | + |
593 | + def setUp(self): |
594 | + super(TestGetSourceFileByName, self).setUp() |
595 | + self.archive = self.factory.makeArchive() |
596 | + |
597 | + def test_source_file_is_found(self): |
598 | + # A file from a published source package can be retrieved. |
599 | + pub = self.factory.makeSourcePackagePublishingHistory( |
600 | + archive=self.archive) |
601 | + dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc') |
602 | + self.assertRaises( |
603 | + NotFoundError, self.archive.getSourceFileByName, |
604 | + pub.source_package_name, pub.source_package_version, dsc.filename) |
605 | + pub.sourcepackagerelease.addFile(dsc) |
606 | + self.assertEqual( |
607 | + dsc, self.archive.getSourceFileByName( |
608 | + pub.source_package_name, pub.source_package_version, |
609 | + dsc.filename)) |
610 | + |
611 | + def test_nonexistent_source_file_is_not_found(self): |
612 | + # Something that looks like a source file but isn't is not |
613 | + # found. |
614 | + pub = self.factory.makeSourcePackagePublishingHistory( |
615 | + archive=self.archive) |
616 | + self.assertRaises( |
617 | + NotFoundError, self.archive.getSourceFileByName, |
618 | + pub.source_package_name, pub.source_package_version, |
619 | + 'foo_1.0.dsc') |
620 | + |
621 | + def test_nonexistent_source_package_version_is_not_found(self): |
622 | + # The source package version must match exactly. |
623 | + pub = self.factory.makeSourcePackagePublishingHistory( |
624 | + archive=self.archive) |
625 | + pub2 = self.factory.makeSourcePackagePublishingHistory( |
626 | + archive=self.archive, sourcepackagename=pub.source_package_name) |
627 | + dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc') |
628 | + pub2.sourcepackagerelease.addFile(dsc) |
629 | + self.assertRaises( |
630 | + NotFoundError, self.archive.getSourceFileByName, |
631 | + pub.source_package_name, pub.source_package_version, |
632 | + 'foo_1.0.dsc') |
633 | + |
634 | + def test_nonexistent_source_package_name_is_not_found(self): |
635 | + # The source package name must match exactly. |
636 | + pub = self.factory.makeSourcePackagePublishingHistory( |
637 | + archive=self.archive) |
638 | + pub2 = self.factory.makeSourcePackagePublishingHistory( |
639 | + archive=self.archive) |
640 | + dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc') |
641 | + pub2.sourcepackagerelease.addFile(dsc) |
642 | + self.assertRaises( |
643 | + NotFoundError, self.archive.getSourceFileByName, |
644 | + pub.source_package_name, pub.source_package_version, |
645 | + 'foo_1.0.dsc') |
646 | + |
647 | + def test_epoch_stripping_collision(self): |
648 | + # Even if the archive contains two source packages with identical |
649 | + # names and versions apart from epochs which have the same filenames |
650 | + # with different contents (the worst case), getSourceFileByName |
651 | + # returns the correct files. |
652 | + pub = self.factory.makeSourcePackagePublishingHistory( |
653 | + archive=self.archive, version='1.0-1') |
654 | + dsc = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc') |
655 | + pub.sourcepackagerelease.addFile(dsc) |
656 | + pub2 = self.factory.makeSourcePackagePublishingHistory( |
657 | + archive=self.archive, sourcepackagename=pub.source_package_name, |
658 | + version='1:1.0-1') |
659 | + dsc2 = self.factory.makeLibraryFileAlias(filename='foo_1.0.dsc') |
660 | + pub2.sourcepackagerelease.addFile(dsc2) |
661 | + self.assertEqual( |
662 | + dsc, self.archive.getSourceFileByName( |
663 | + pub.source_package_name, pub.source_package_version, |
664 | + dsc.filename)) |
665 | + self.assertEqual( |
666 | + dsc2, self.archive.getSourceFileByName( |
667 | + pub2.source_package_name, pub2.source_package_version, |
668 | + dsc2.filename)) |
669 | + |
670 | + |
671 | class TestGetPublishedSources(TestCaseWithFactory): |
672 | |
673 | layer = DatabaseFunctionalLayer |
674 | |
675 | === modified file 'lib/lp/soyuz/tests/test_publishing_models.py' |
676 | --- lib/lp/soyuz/tests/test_publishing_models.py 2018-02-02 03:14:35 +0000 |
677 | +++ lib/lp/soyuz/tests/test_publishing_models.py 2018-05-08 18:13:25 +0000 |
678 | @@ -14,6 +14,7 @@ |
679 | from lp.services.database.constants import UTC_NOW |
680 | from lp.services.librarian.browser import ProxiedLibraryFileAlias |
681 | from lp.services.webapp.publisher import canonical_url |
682 | +from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias |
683 | from lp.soyuz.enums import ( |
684 | BinaryPackageFileType, |
685 | BinaryPackageFormat, |
686 | @@ -159,8 +160,7 @@ |
687 | |
688 | def getURLsForSPPH(self, spph, include_meta=False): |
689 | spr = spph.sourcepackagerelease |
690 | - archive = spph.archive |
691 | - urls = [ProxiedLibraryFileAlias(f.libraryfile, archive).http_url |
692 | + urls = [ProxiedSourceLibraryFileAlias(f.libraryfile, spph).http_url |
693 | for f in spr.files] |
694 | |
695 | if include_meta: |
I guess we'll find all the clients that match on /+files/ paths.