Merge lp:~wgrant/launchpad/abpb into lp:launchpad

Proposed by William Grant on 2014-05-06
Status: Work in progress
Proposed branch: lp:~wgrant/launchpad/abpb
Merge into: lp:launchpad
Prerequisite: lp:~wgrant/launchpad/archive-build-navigation
Diff against target: 781 lines (+249/-42)
25 files modified
lib/lp/buildmaster/browser/builder.py (+6/-1)
lib/lp/buildmaster/interfaces/buildfarmjob.py (+1/-0)
lib/lp/buildmaster/model/builder.py (+8/-2)
lib/lp/registry/model/distribution.py (+3/-1)
lib/lp/registry/model/distroseries.py (+3/-1)
lib/lp/registry/model/sourcepackage.py (+4/-1)
lib/lp/security.py (+2/-1)
lib/lp/soyuz/browser/archive.py (+8/-2)
lib/lp/soyuz/browser/build.py (+36/-0)
lib/lp/soyuz/browser/configure.zcml (+3/-0)
lib/lp/soyuz/browser/distributionsourcepackagerelease.py (+9/-3)
lib/lp/soyuz/configure.zcml (+13/-0)
lib/lp/soyuz/interfaces/binarypackagebuild.py (+20/-0)
lib/lp/soyuz/model/archive.py (+6/-2)
lib/lp/soyuz/model/binarypackagebuild.py (+63/-0)
lib/lp/soyuz/model/distributionsourcepackagerelease.py (+7/-2)
lib/lp/soyuz/model/distroarchseries.py (+5/-1)
lib/lp/soyuz/model/distroarchseriesbinarypackagerelease.py (+4/-1)
lib/lp/soyuz/model/distroseriessourcepackagerelease.py (+7/-3)
lib/lp/soyuz/model/publishing.py (+11/-8)
lib/lp/soyuz/model/queue.py (+8/-3)
lib/lp/soyuz/scripts/tests/test_copypackage.py (+6/-2)
lib/lp/soyuz/stories/soyuz/xx-private-builds.txt (+5/-3)
lib/lp/soyuz/tests/test_hasbuildrecords.py (+6/-4)
lib/lp/soyuz/tests/test_packageupload.py (+5/-1)
To merge this branch: bzr merge lp:~wgrant/launchpad/abpb
Reviewer Review Type Date Requested Status
Launchpad code reviewers 2014-05-06 Pending
Review via email: mp+218379@code.launchpad.net
To post a comment you must log in.
lp:~wgrant/launchpad/abpb updated on 2014-05-12
17005. By William Grant on 2014-05-06

Adjust the three +build traversals to return ArchiveBinaryPackageBuilds.

17006. By William Grant on 2014-05-07

Stop decorating BPBs into ABPBs manually. Use decorate_binarypackagebuilds instead.

17007. By William Grant on 2014-05-07

Decorate them in Builder.getBuildRecords too.

17008. By William Grant on 2014-05-07

Fix test_hasbuildrecords to unwrap.

17009. By William Grant on 2014-05-07

Fix test_copy_source_and_binaries_from_ppa_does_not_create_builds to compare the BPBs, not the ABPBs. The ABPBs from different archives clearly must differ.

17010. By William Grant on 2014-05-07

Fix lint.

17011. By William Grant on 2014-05-09

Fix ArchiveBuildUrl to return the DSPR in the ABPB's distro, not the BPB's original distro.

Unmerged revisions

17011. By William Grant on 2014-05-09

Fix ArchiveBuildUrl to return the DSPR in the ABPB's distro, not the BPB's original distro.

17010. By William Grant on 2014-05-07

Fix lint.

17009. By William Grant on 2014-05-07

Fix test_copy_source_and_binaries_from_ppa_does_not_create_builds to compare the BPBs, not the ABPBs. The ABPBs from different archives clearly must differ.

17008. By William Grant on 2014-05-07

Fix test_hasbuildrecords to unwrap.

17007. By William Grant on 2014-05-07

Decorate them in Builder.getBuildRecords too.

17006. By William Grant on 2014-05-07

Stop decorating BPBs into ABPBs manually. Use decorate_binarypackagebuilds instead.

17005. By William Grant on 2014-05-06

Adjust the three +build traversals to return ArchiveBinaryPackageBuilds.

17004. By William Grant on 2014-05-06

Merge archive-build-navigation.

17003. By William Grant on 2014-05-06

Fix xx-private-builds.txt to cope with the fact that builds copied into the primary archive now have URLs under the primary archive.

17002. By William Grant on 2014-05-06

Add build_farm_job_id to IBinaryPackageBuild so it gets delegated through ABPB.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/buildmaster/browser/builder.py'
2--- lib/lp/buildmaster/browser/builder.py 2014-05-06 09:11:14 +0000
3+++ lib/lp/buildmaster/browser/builder.py 2014-05-12 07:22:06 +0000
4@@ -64,6 +64,10 @@
5 get_build_by_id_str,
6 )
7 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
8+from lp.soyuz.model.binarypackagebuild import (
9+ BUILD_ARCHIVE,
10+ decorate_binarypackagebuilds,
11+ )
12
13
14 class BuilderSetNavigation(GetitemNavigation):
15@@ -75,7 +79,8 @@
16 build = get_build_by_id_str(IBinaryPackageBuildSet, name)
17 if build is None:
18 return None
19- return self.redirectSubTree(canonical_url(build))
20+ return self.redirectSubTree(canonical_url(
21+ decorate_binarypackagebuilds([build], archive=BUILD_ARCHIVE)[0]))
22
23 @stepthrough('+recipebuild')
24 def traverse_recipebuild(self, name):
25
26=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
27--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2013-11-21 04:13:12 +0000
28+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2014-05-12 07:22:06 +0000
29@@ -65,6 +65,7 @@
30 id = Attribute('The build farm job ID.')
31
32 build_farm_job = Attribute('Generic build farm job record')
33+ build_farm_job_id = Attribute('The BuildFarmJobDB ID.')
34
35 processor = Reference(
36 IProcessor, title=_("Processor"), required=False, readonly=True,
37
38=== modified file 'lib/lp/buildmaster/model/builder.py'
39--- lib/lp/buildmaster/model/builder.py 2013-11-28 09:12:45 +0000
40+++ lib/lp/buildmaster/model/builder.py 2014-05-12 07:22:06 +0000
41@@ -186,9 +186,15 @@
42 def getBuildRecords(self, build_state=None, name=None, pocket=None,
43 arch_tag=None, user=None, binary_only=True):
44 """See IHasBuildRecords."""
45+ from lp.soyuz.model.binarypackagebuild import (
46+ BUILD_ARCHIVE,
47+ decorate_binarypackagebuilds,
48+ )
49 if binary_only:
50- return getUtility(IBinaryPackageBuildSet).getBuildsForBuilder(
51- self.id, build_state, name, pocket, arch_tag, user)
52+ return decorate_binarypackagebuilds(
53+ getUtility(IBinaryPackageBuildSet).getBuildsForBuilder(
54+ self.id, build_state, name, pocket, arch_tag, user),
55+ archive=BUILD_ARCHIVE)
56 else:
57 if arch_tag is not None or name is not None or pocket is not None:
58 raise IncompatibleArguments(
59
60=== modified file 'lib/lp/registry/model/distribution.py'
61--- lib/lp/registry/model/distribution.py 2014-01-07 01:45:40 +0000
62+++ lib/lp/registry/model/distribution.py 2014-05-12 07:22:06 +0000
63@@ -170,6 +170,7 @@
64 from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
65 from lp.soyuz.interfaces.publishing import active_publishing_status
66 from lp.soyuz.model.archive import Archive
67+from lp.soyuz.model.binarypackagebuild import decorate_binarypackagebuilds
68 from lp.soyuz.model.binarypackagename import BinaryPackageName
69 from lp.soyuz.model.distributionsourcepackagerelease import (
70 DistributionSourcePackageRelease,
71@@ -951,8 +952,9 @@
72 # now).
73 # The "binary_only" option is not yet supported for
74 # IDistribution.
75- return getUtility(IBinaryPackageBuildSet).getBuildsForDistro(
76+ bpbs = getUtility(IBinaryPackageBuildSet).getBuildsForDistro(
77 self, build_state, name, pocket, arch_tag)
78+ return decorate_binarypackagebuilds(bpbs, archive=self.main_archive)
79
80 def searchSourcePackageCaches(
81 self, text, has_packaging=None, publishing_distroseries=None):
82
83=== modified file 'lib/lp/registry/model/distroseries.py'
84--- lib/lp/registry/model/distroseries.py 2014-01-06 14:24:05 +0000
85+++ lib/lp/registry/model/distroseries.py 2014-05-12 07:22:06 +0000
86@@ -135,6 +135,7 @@
87 from lp.soyuz.interfaces.sourcepackageformat import (
88 ISourcePackageFormatSelectionSet,
89 )
90+from lp.soyuz.model.binarypackagebuild import decorate_binarypackagebuilds
91 from lp.soyuz.model.binarypackagename import BinaryPackageName
92 from lp.soyuz.model.component import Component
93 from lp.soyuz.model.distroarchseries import (
94@@ -1014,8 +1015,9 @@
95 # Ignore "user", since it would not make any difference to the
96 # records returned here (private builds are only in PPA right
97 # now). We also ignore binary_only and always return binaries.
98- return getUtility(IBinaryPackageBuildSet).getBuildsForDistro(
99+ bpbs = getUtility(IBinaryPackageBuildSet).getBuildsForDistro(
100 self, build_state, name, pocket, arch_tag)
101+ return decorate_binarypackagebuilds(bpbs, archive=self.main_archive)
102
103 def createUploadedSourcePackageRelease(
104 self, sourcepackagename, version, maintainer, builddepends,
105
106=== modified file 'lib/lp/registry/model/sourcepackage.py'
107--- lib/lp/registry/model/sourcepackage.py 2013-11-19 02:51:24 +0000
108+++ lib/lp/registry/model/sourcepackage.py 2014-05-12 07:22:06 +0000
109@@ -75,6 +75,7 @@
110 from lp.soyuz.model.binarypackagebuild import (
111 BinaryPackageBuild,
112 BinaryPackageBuildSet,
113+ decorate_binarypackagebuilds,
114 )
115 from lp.soyuz.model.distributionsourcepackagerelease import (
116 DistributionSourcePackageRelease,
117@@ -656,8 +657,10 @@
118
119 # End of duplication (see XXX cprov 2006-09-25 above).
120
121- return IStore(BinaryPackageBuild).using(clauseTables).find(
122+ bpbs = IStore(BinaryPackageBuild).using(clauseTables).find(
123 BinaryPackageBuild, *condition_clauses).order_by(*orderBy)
124+ return decorate_binarypackagebuilds(
125+ bpbs, archive=self.distribution.main_archive)
126
127 @property
128 def latest_published_component(self):
129
130=== modified file 'lib/lp/security.py'
131--- lib/lp/security.py 2014-03-17 21:50:33 +0000
132+++ lib/lp/security.py 2014-05-12 07:22:06 +0000
133@@ -21,6 +21,7 @@
134 queryAdapter,
135 )
136 from zope.interface import Interface
137+from zope.security.proxy import removeSecurityProxy
138
139 from lp.answers.interfaces.faq import IFAQ
140 from lp.answers.interfaces.faqtarget import IFAQTarget
141@@ -1874,7 +1875,7 @@
142 # user to retry build if so.
143 # strict_component is True because the source package already exists,
144 # otherwise, how can they give it back?
145- check_perms = self.obj.archive.checkUpload(
146+ check_perms = removeSecurityProxy(self.obj.archive).checkUpload(
147 user.person, self.obj.distro_series,
148 self.obj.source_package_release.sourcepackagename,
149 self.obj.current_component, self.obj.pocket,
150
151=== modified file 'lib/lp/soyuz/browser/archive.py'
152--- lib/lp/soyuz/browser/archive.py 2014-05-06 09:11:14 +0000
153+++ lib/lp/soyuz/browser/archive.py 2014-05-12 07:22:06 +0000
154@@ -164,6 +164,7 @@
155 Archive,
156 validate_ppa,
157 )
158+from lp.soyuz.model.binarypackagebuild import decorate_binarypackagebuilds
159 from lp.soyuz.model.publishing import SourcePackagePublishingHistory
160 from lp.soyuz.scripts.packagecopier import check_copy_permissions
161
162@@ -238,9 +239,14 @@
163 @stepthrough('+build')
164 def traverse_build(self, name):
165 build = get_build_by_id_str(IBinaryPackageBuildSet, name)
166- if build is None or build.archive != self.context:
167+ # XXX: This should check whether self.context is build.archive
168+ # or has some of the build's binaries published in it.
169+ if (build is None
170+ or (self.context != build.archive
171+ and self.context not in
172+ build.sourcepackagerelease.published_archives)):
173 return None
174- return build
175+ return decorate_binarypackagebuilds([build], self.context)[0]
176
177 @stepthrough('+recipebuild')
178 def traverse_recipebuild(self, name):
179
180=== modified file 'lib/lp/soyuz/browser/build.py'
181--- lib/lp/soyuz/browser/build.py 2014-05-06 09:11:14 +0000
182+++ lib/lp/soyuz/browser/build.py 2014-05-12 07:22:06 +0000
183@@ -131,6 +131,42 @@
184 return u"+build/%d" % self.context.id
185
186
187+class ArchiveBuildUrl:
188+ """Dynamic URL declaration for IArchiveBinaryPackageBuild.
189+
190+ When dealing with primary and partner builds we want to present them
191+ under their archive's distribution's IDistributionSourcePackageRelease:
192+
193+ /ubuntu/+source/foo/1.0/+build/1234
194+
195+ On the other hand, PPA builds will be presented under the PPA:
196+
197+ /~cprov/+archive/+build/1235
198+
199+ And copy archives will be presented under the archive:
200+
201+ /ubuntu/+archive/my-special-archive/+build/1234
202+ """
203+ implements(ICanonicalUrlData)
204+ rootsite = None
205+
206+ def __init__(self, context):
207+ self.context = context
208+
209+ @property
210+ def inside(self):
211+ if self.context.parent_archive.is_main:
212+ distro = self.context.parent_archive.distribution
213+ return distro.getSourcePackageRelease(
214+ self.context.source_package_release)
215+ else:
216+ return self.context.parent_archive
217+
218+ @property
219+ def path(self):
220+ return u"+build/%d" % self.context.id
221+
222+
223 class BuildNavigation(GetitemNavigation, FileNavigationMixin):
224 usedfor = IBinaryPackageBuild
225
226
227=== modified file 'lib/lp/soyuz/browser/configure.zcml'
228--- lib/lp/soyuz/browser/configure.zcml 2014-04-24 07:30:36 +0000
229+++ lib/lp/soyuz/browser/configure.zcml 2014-05-12 07:22:06 +0000
230@@ -338,6 +338,9 @@
231 <browser:url
232 for="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild"
233 urldata="lp.soyuz.browser.build.BuildUrl"/>
234+ <browser:url
235+ for="lp.soyuz.interfaces.binarypackagebuild.IArchiveBinaryPackageBuild"
236+ urldata="lp.soyuz.browser.build.ArchiveBuildUrl"/>
237 <browser:navigation
238 module="lp.soyuz.browser.build"
239 classes="
240
241=== modified file 'lib/lp/soyuz/browser/distributionsourcepackagerelease.py'
242--- lib/lp/soyuz/browser/distributionsourcepackagerelease.py 2014-05-06 09:11:14 +0000
243+++ lib/lp/soyuz/browser/distributionsourcepackagerelease.py 2014-05-12 07:22:06 +0000
244@@ -32,6 +32,7 @@
245 from lp.soyuz.interfaces.distributionsourcepackagerelease import (
246 IDistributionSourcePackageRelease,
247 )
248+from lp.soyuz.model.binarypackagebuild import decorate_binarypackagebuilds
249
250
251 class DistributionSourcePackageReleaseBreadcrumb(Breadcrumb):
252@@ -48,13 +49,18 @@
253 @stepthrough('+build')
254 def traverse_build(self, name):
255 build = get_build_by_id_str(IBinaryPackageBuildSet, name)
256+ # XXX: This should check whether any of all_distro_archives is
257+ # build.archive, or any of them contain the build's binaries.
258 if (build is None
259- or build.archive not in
260- self.context.distribution.all_distro_archives
261+ or (build.archive not in
262+ self.context.distribution.all_distro_archives
263+ and self.context.distribution.main_archive not in
264+ build.sourcepackagerelease.published_archives)
265 or build.source_package_release !=
266 self.context.sourcepackagerelease):
267 return None
268- return build
269+ return decorate_binarypackagebuilds(
270+ [build], archive=self.context.distribution.main_archive)[0]
271
272
273 class DistributionSourcePackageReleaseView(LaunchpadView):
274
275=== modified file 'lib/lp/soyuz/configure.zcml'
276--- lib/lp/soyuz/configure.zcml 2014-02-18 11:40:52 +0000
277+++ lib/lp/soyuz/configure.zcml 2014-05-12 07:22:06 +0000
278@@ -491,6 +491,19 @@
279 permission="launchpad.Admin"
280 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildAdmin"/>
281 </class>
282+ <class
283+ class="lp.soyuz.model.binarypackagebuild.ArchiveBinaryPackageBuild">
284+ <require
285+ permission="launchpad.View"
286+ interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildView"
287+ attributes="parent_archive build" />
288+ <require
289+ permission="launchpad.Edit"
290+ interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildEdit"/>
291+ <require
292+ permission="launchpad.Admin"
293+ interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildAdmin"/>
294+ </class>
295 <adapter
296 provides="lp.services.webapp.interfaces.IBreadcrumb"
297 for="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild"
298
299=== modified file 'lib/lp/soyuz/interfaces/binarypackagebuild.py'
300--- lib/lp/soyuz/interfaces/binarypackagebuild.py 2013-11-26 01:36:10 +0000
301+++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2014-05-12 07:22:06 +0000
302@@ -8,6 +8,7 @@
303 __all__ = [
304 'BuildSetStatus',
305 'CannotBeRescored',
306+ 'IArchiveBinaryPackageBuild',
307 'IBinaryPackageBuild',
308 'IBuildRescoreForm',
309 'IBinaryPackageBuildSet',
310@@ -261,6 +262,25 @@
311 export_as_webservice_entry(singular_name='build', plural_name='builds')
312
313
314+class IArchiveBinaryPackageBuild(IBinaryPackageBuild):
315+ """A build in an archive.
316+
317+ `Archive`s need to be self-contained, for reasons of access control,
318+ federation, deletion, and more. Since `BinaryPackageBuild`s produce
319+ `BinaryPackageRelease`s that can be copied between archives, a
320+ `BinaryPackageBuild` has no single archive parent -- they, like
321+ `BinaryPackageRelease`s and `SourcePackageRelease`s, are
322+ contextless.
323+
324+ `ArchiveBinaryPackageBuild` is a virtual class combining a
325+ `BinaryPackageBuild` with an `Archive` as its parent, giving it a
326+ URL. An `ArchiveBinaryPackageBuild` should be returned whenever
327+ there is an obvious `Archive` context.
328+ """
329+ parent_archive = Attribute("Parent archive")
330+ build = Attribute("BinaryPackageBuild")
331+
332+
333 class BuildSetStatus(EnumeratedType):
334 """`IBuildSet` status type
335
336
337=== modified file 'lib/lp/soyuz/model/archive.py'
338--- lib/lp/soyuz/model/archive.py 2014-04-23 14:24:15 +0000
339+++ lib/lp/soyuz/model/archive.py 2014-05-12 07:22:06 +0000
340@@ -180,7 +180,10 @@
341 from lp.soyuz.model.archiveauthtoken import ArchiveAuthToken
342 from lp.soyuz.model.archivedependency import ArchiveDependency
343 from lp.soyuz.model.archivepermission import ArchivePermission
344-from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
345+from lp.soyuz.model.binarypackagebuild import (
346+ BinaryPackageBuild,
347+ decorate_binarypackagebuilds,
348+ )
349 from lp.soyuz.model.binarypackagename import BinaryPackageName
350 from lp.soyuz.model.binarypackagerelease import (
351 BinaryPackageRelease,
352@@ -483,8 +486,9 @@
353 # will implicitly have permission to see it.
354
355 if binary_only:
356- return getUtility(IBinaryPackageBuildSet).getBuildsForArchive(
357+ bpbs = getUtility(IBinaryPackageBuildSet).getBuildsForArchive(
358 self, build_state, name, pocket, arch_tag)
359+ return decorate_binarypackagebuilds(bpbs, archive=self)
360 else:
361 if arch_tag is not None or name is not None or pocket is not None:
362 raise IncompatibleArguments(
363
364=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
365--- lib/lp/soyuz/model/binarypackagebuild.py 2013-12-04 07:07:04 +0000
366+++ lib/lp/soyuz/model/binarypackagebuild.py 2014-05-12 07:22:06 +0000
367@@ -5,7 +5,9 @@
368 __all__ = [
369 'BinaryPackageBuild',
370 'BinaryPackageBuildSet',
371+ 'BUILD_ARCHIVE',
372 'COPY_ARCHIVE_SCORE_PENALTY',
373+ 'decorate_binarypackagebuilds',
374 'PRIVATE_ARCHIVE_SCORE_BONUS',
375 'SCORE_BY_COMPONENT',
376 'SCORE_BY_POCKET',
377@@ -16,6 +18,7 @@
378 from operator import itemgetter
379
380 import apt_pkg
381+from lazr.delegates import delegates
382 import pytz
383 from sqlobject import SQLObjectNotFound
384 from storm.expr import (
385@@ -92,6 +95,7 @@
386 from lp.soyuz.interfaces.binarypackagebuild import (
387 BuildSetStatus,
388 CannotBeRescored,
389+ IArchiveBinaryPackageBuild,
390 IBinaryPackageBuild,
391 IBinaryPackageBuildSet,
392 UnparsableDependencies,
393@@ -144,6 +148,46 @@
394 COPY_ARCHIVE_SCORE_PENALTY = 2600
395
396
397+BUILD_ARCHIVE = object()
398+
399+
400+def decorate_binarypackagebuilds(bpbs, archive=None):
401+ """Decorate `BinaryPackageBuild`s into `ArchiveBinaryPackageBuild`s.
402+
403+ Three types of input are supported, depending on the value of archive:
404+
405+ - archive=None: bpbs is a sequence of (build, archive) pairs
406+
407+ - archive=BUILD_ARCHIVE: bpbs is a sequence of builds, each of
408+ which will be decorated with the archive in which it was built.
409+
410+ - archive=<some archive>: bpbs is a sequence of builds, each of
411+ which will be decoreated with the given archive.
412+
413+ If bpbs is an IResultSet, the return value will be a DecoratedResultSet.
414+ If it's a list or tuple, the return value will be a list.
415+ Otherwise, the return value will be a generator.
416+ """
417+ if archive is None:
418+ splitter = lambda build: (build[1], build[0])
419+ elif archive is BUILD_ARCHIVE:
420+ splitter = lambda build: (build.archive, build)
421+ else:
422+ splitter = lambda build: (archive, build)
423+
424+ if IResultSet.providedBy(bpbs):
425+ return DecoratedResultSet(
426+ bpbs,
427+ lambda build: ArchiveBinaryPackageBuild(*splitter(build)))
428+ else:
429+ seq = (
430+ ArchiveBinaryPackageBuild(*splitter(build))
431+ for build in bpbs)
432+ if isinstance(bpbs, (list, tuple)):
433+ seq = list(seq)
434+ return seq
435+
436+
437 class BinaryPackageBuild(PackageBuildMixin, SQLBase):
438 implements(IBinaryPackageBuild)
439 _table = 'BinaryPackageBuild'
440@@ -887,6 +931,25 @@
441 return changes.signer
442
443
444+class ArchiveBinaryPackageBuild:
445+
446+ implements(IArchiveBinaryPackageBuild)
447+ delegates(IBinaryPackageBuild, 'build')
448+
449+ def __init__(self, parent_archive, build):
450+ self.parent_archive = parent_archive
451+ self.build = build
452+
453+ def __eq__(self, other):
454+ return (
455+ isinstance(other, self.__class__)
456+ and self.parent_archive == other.parent_archive
457+ and self.build == other.build)
458+
459+ def __ne__(self, other):
460+ return not self.__eq__(other)
461+
462+
463 class BinaryPackageBuildSet(SpecificBuildFarmJobSourceMixin):
464 implements(IBinaryPackageBuildSet)
465
466
467=== modified file 'lib/lp/soyuz/model/distributionsourcepackagerelease.py'
468--- lib/lp/soyuz/model/distributionsourcepackagerelease.py 2014-01-13 07:09:40 +0000
469+++ lib/lp/soyuz/model/distributionsourcepackagerelease.py 2014-05-12 07:22:06 +0000
470@@ -28,7 +28,10 @@
471 )
472 from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
473 from lp.soyuz.model.archive import Archive
474-from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
475+from lp.soyuz.model.binarypackagebuild import (
476+ BinaryPackageBuild,
477+ decorate_binarypackagebuilds,
478+ )
479 from lp.soyuz.model.binarypackagename import BinaryPackageName
480 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
481 from lp.soyuz.model.distroseriesbinarypackage import DistroSeriesBinaryPackage
482@@ -125,9 +128,11 @@
483 Archive.purpose.is_in(MAIN_ARCHIVE_PURPOSES)).config(
484 distinct=True)
485
486- return builds_built_in_main_archives.union(
487+ result = builds_built_in_main_archives.union(
488 builds_published_in_main_archives).order_by(
489 Desc(BinaryPackageBuild.id))
490+ return decorate_binarypackagebuilds(
491+ result, archive=self.distribution.main_archive)
492
493 @property
494 def binary_package_names(self):
495
496=== modified file 'lib/lp/soyuz/model/distroarchseries.py'
497--- lib/lp/soyuz/model/distroarchseries.py 2014-02-28 11:12:31 +0000
498+++ lib/lp/soyuz/model/distroarchseries.py 2014-05-12 07:22:06 +0000
499@@ -266,6 +266,9 @@
500 # now).
501 # Ignore "binary_only" as for a distro arch series it is only
502 # the binaries that are relevant.
503+ from lp.soyuz.model.binarypackagebuild import (
504+ decorate_binarypackagebuilds,
505+ )
506
507 # For consistency we return an empty resultset if arch_tag
508 # is provided but doesn't match our architecture.
509@@ -274,8 +277,9 @@
510
511 # Use the facility provided by IBinaryPackageBuildSet to
512 # retrieve the records.
513- return getUtility(IBinaryPackageBuildSet).getBuildsForDistro(
514+ bpbs = getUtility(IBinaryPackageBuildSet).getBuildsForDistro(
515 self, build_state, name, pocket)
516+ return decorate_binarypackagebuilds(bpbs, archive=self.main_archive)
517
518 def getReleasedPackages(self, binary_name, pocket=None,
519 include_pending=False, archive=None):
520
521=== modified file 'lib/lp/soyuz/model/distroarchseriesbinarypackagerelease.py'
522--- lib/lp/soyuz/model/distroarchseriesbinarypackagerelease.py 2013-01-21 16:27:29 +0000
523+++ lib/lp/soyuz/model/distroarchseriesbinarypackagerelease.py 2014-05-12 07:22:06 +0000
524@@ -17,6 +17,7 @@
525 IDistroArchSeriesBinaryPackageRelease,
526 )
527 from lp.soyuz.interfaces.publishing import active_publishing_status
528+from lp.soyuz.model.binarypackagebuild import decorate_binarypackagebuilds
529 from lp.soyuz.model.distributionsourcepackagerelease import (
530 DistributionSourcePackageRelease,
531 )
532@@ -185,7 +186,9 @@
533 @property
534 def build(self):
535 """See `IBinaryPackageRelease`."""
536- return self.binarypackagerelease.build
537+ return decorate_binarypackagebuilds(
538+ [self.binarypackagerelease.build],
539+ archive=self.distribution.main_archive)[0]
540
541 @property
542 def binpackageformat(self):
543
544=== modified file 'lib/lp/soyuz/model/distroseriessourcepackagerelease.py'
545--- lib/lp/soyuz/model/distroseriessourcepackagerelease.py 2013-01-07 02:40:55 +0000
546+++ lib/lp/soyuz/model/distroseriessourcepackagerelease.py 2014-05-12 07:22:06 +0000
547@@ -26,7 +26,10 @@
548 IDistroSeriesSourcePackageRelease,
549 )
550 from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
551-from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
552+from lp.soyuz.model.binarypackagebuild import (
553+ BinaryPackageBuild,
554+ decorate_binarypackagebuilds,
555+ )
556 from lp.soyuz.model.binarypackagename import BinaryPackageName
557 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
558 from lp.soyuz.model.publishing import (
559@@ -119,9 +122,10 @@
560 self.distroseries.distribution,
561 self.sourcepackagerelease).builds
562
563- return (
564+ return decorate_binarypackagebuilds(
565 [build for build in distro_builds
566- if build.distro_arch_series.distroseries == self.distroseries])
567+ if build.distro_arch_series.distroseries == self.distroseries],
568+ archive=self.distribution.main_archive)
569
570 @property
571 def files(self):
572
573=== modified file 'lib/lp/soyuz/model/publishing.py'
574--- lib/lp/soyuz/model/publishing.py 2014-05-06 06:32:46 +0000
575+++ lib/lp/soyuz/model/publishing.py 2014-05-12 07:22:06 +0000
576@@ -17,10 +17,7 @@
577
578 from collections import defaultdict
579 from datetime import datetime
580-from operator import (
581- attrgetter,
582- itemgetter,
583- )
584+from operator import attrgetter
585 import os
586 import re
587 import sys
588@@ -58,7 +55,6 @@
589 from lp.services.database import bulk
590 from lp.services.database.constants import UTC_NOW
591 from lp.services.database.datetimecol import UtcDateTimeCol
592-from lp.services.database.decoratedresultset import DecoratedResultSet
593 from lp.services.database.enumcol import EnumCol
594 from lp.services.database.interfaces import (
595 IMasterStore,
596@@ -110,7 +106,10 @@
597 )
598 from lp.soyuz.interfaces.queue import QueueInconsistentStateError
599 from lp.soyuz.interfaces.section import ISectionSet
600-from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
601+from lp.soyuz.model.binarypackagebuild import (
602+ BinaryPackageBuild,
603+ decorate_binarypackagebuilds,
604+ )
605 from lp.soyuz.model.binarypackagename import BinaryPackageName
606 from lp.soyuz.model.binarypackagerelease import (
607 BinaryPackageRelease,
608@@ -540,7 +539,8 @@
609 @staticmethod
610 def _convertBuilds(builds_for_sources):
611 """Convert from IPublishingSet getBuilds to SPPH getBuilds."""
612- return [build[1] for build in builds_for_sources]
613+ return decorate_binarypackagebuilds(
614+ [(build[1], build[0].archive) for build in builds_for_sources])
615
616 def getBuilds(self):
617 """See `ISourcePackagePublishingHistory`."""
618@@ -1892,6 +1892,8 @@
619 # Thank you, Zope, for security wrapping an abstract data
620 # structure.
621 summary = removeSecurityProxy(summary)
622+ summary['builds'] = decorate_binarypackagebuilds(
623+ summary['builds'], archive=archive)
624 summary['date_published'] = source_pub.datepublished
625 summary['source_package_name'] = source_pub.source_package_name
626 source_build_statuses[source_pub.id] = summary
627@@ -1918,7 +1920,8 @@
628 for source_pub, builds in unpublished_per_source.items():
629 summary = {
630 'status': BuildSetStatus.FULLYBUILT_PENDING,
631- 'builds': builds,
632+ 'builds': decorate_binarypackagebuilds(
633+ builds, archive=archive),
634 'date_published': source_pub.datepublished,
635 'source_package_name': source_pub.source_package_name,
636 }
637
638=== modified file 'lib/lp/soyuz/model/queue.py'
639--- lib/lp/soyuz/model/queue.py 2014-04-09 12:32:50 +0000
640+++ lib/lp/soyuz/model/queue.py 2014-05-12 07:22:06 +0000
641@@ -234,10 +234,15 @@
642
643 def binaryFileUrls(self):
644 """See `IPackageUpload`."""
645+ from lp.soyuz.model.binarypackagebuild import (
646+ decorate_binarypackagebuilds,
647+ )
648+ bpbs = [build.build for build in self.builds]
649 return [
650- ProxiedLibraryFileAlias(file.libraryfile, build.build).http_url
651- for build in self.builds
652- for bpr in build.build.binarypackages
653+ ProxiedLibraryFileAlias(file.libraryfile, abpb).http_url
654+ for abpb in decorate_binarypackagebuilds(
655+ bpbs, archive=self.archive)
656+ for bpr in abpb.binarypackages
657 for file in bpr.files]
658
659 @property
660
661=== modified file 'lib/lp/soyuz/scripts/tests/test_copypackage.py'
662--- lib/lp/soyuz/scripts/tests/test_copypackage.py 2013-09-11 08:17:34 +0000
663+++ lib/lp/soyuz/scripts/tests/test_copypackage.py 2014-05-12 07:22:06 +0000
664@@ -1710,7 +1710,9 @@
665 def test_copy_source_and_binaries_from_ppa_does_not_create_builds(self):
666 # Copying a source package and its binaries from a PPA to the
667 # primary archive copies the binaries and does not create any new
668- # build records.
669+ # build records. The ArchiveBinaryPackageBuilds returned by
670+ # getBuilds() are distinct, but they wrap the same
671+ # BinaryPackageBuilds.
672 source = self.test_publisher.getPubSource(archive=self.ppa)
673 self.test_publisher.getPubBinaries(pub_source=source)
674 self.layer.txn.commit()
675@@ -1726,7 +1728,9 @@
676 "%s %s in %s" % (spr.name, spr.version, self.series.name),
677 copied[0].displayname)
678 self.assertEqual(2, len(copied[0].getPublishedBinaries()))
679- self.assertEqual(initial_builds, copied[0].getBuilds())
680+ self.assertEqual(
681+ [abpb.build for abpb in initial_builds],
682+ [abpb.build for abpb in copied[0].getBuilds()])
683
684 def makeSeriesWithExtraArchitecture(self):
685 """Make a new distroseries with an additional architecture."""
686
687=== modified file 'lib/lp/soyuz/stories/soyuz/xx-private-builds.txt'
688--- lib/lp/soyuz/stories/soyuz/xx-private-builds.txt 2013-12-03 05:18:41 +0000
689+++ lib/lp/soyuz/stories/soyuz/xx-private-builds.txt 2014-05-12 07:22:06 +0000
690@@ -329,7 +329,9 @@
691 i386 build of privacy-test 666 : PPA named p3a for Celso Providelo : Celso Providelo
692
693 When accessing the distroseries source package release page, the builds
694-portlet will display a link to the newly unembargoed build:
695+portlet will display a link to the newly unembargoed build. It's the
696+same build as the PPA, with the same ID, but structurally under the
697+primary archive:
698
699 >>> browser.open(
700 ... "http://launchpad.dev/ubuntutest/breezy-autotest/"
701@@ -340,7 +342,7 @@
702 breezy-autotest i386 Successfully built
703
704 >>> print browser.getLink('i386').url
705- http://launchpad.dev/~cprov/+archive/p3a/+build/...
706+ http://launchpad.dev/ubuntutest/+source/privacy-test/666/+build/...
707
708 Similarly, when accessing the distribution source package release page,
709 the main content will display a link to the newly unembargoed build:
710@@ -353,4 +355,4 @@
711 Breezy Badger Autotest: i386
712
713 >>> print browser.getLink('i386').url
714- http://launchpad.dev/~cprov/+archive/p3a/+build/...
715+ http://launchpad.dev/ubuntutest/+source/privacy-test/666/+build/...
716
717=== modified file 'lib/lp/soyuz/tests/test_hasbuildrecords.py'
718--- lib/lp/soyuz/tests/test_hasbuildrecords.py 2013-11-28 08:51:32 +0000
719+++ lib/lp/soyuz/tests/test_hasbuildrecords.py 2014-05-12 07:22:06 +0000
720@@ -56,7 +56,7 @@
721
722 def test_getBuildRecords_no_archs(self):
723 # getBuildRecords() called without any arguments returns all builds.
724- builds = self.context.getBuildRecords()
725+ builds = [abpb.build for abpb in self.context.getBuildRecords()]
726 self.assertContentEqual(builds, self.builds)
727
728 def test_getBuildRecords_by_arch_tag(self):
729@@ -65,7 +65,9 @@
730 # Target one of the builds to hppa so that we have three builds
731 # in total, two of which are i386 and one hppa.
732 i386_builds = self.builds[:2]
733- builds = self.context.getBuildRecords(arch_tag="i386")
734+ builds = [
735+ abpb.build
736+ for abpb in self.context.getBuildRecords(arch_tag="i386")]
737 self.assertContentEqual(i386_builds, builds)
738
739
740@@ -295,7 +297,7 @@
741 build2.queueBuild()
742 build1.buildqueue_record.lastscore = 10
743 build2.buildqueue_record.lastscore = 1000
744- builds = list(source_package.getBuildRecords())
745+ builds = [abpb.build for abpb in source_package.getBuildRecords()]
746 self.assertEquals([build2, build1], builds)
747
748 def test_copy_archive_without_leak(self):
749@@ -328,7 +330,7 @@
750 distroseries, PackagePublishingPocket.RELEASE, copy)
751 [copy_build] = copy_spph.createMissingBuilds()
752 builds = copy.getBuildRecords()
753- self.assertEquals([copy_build], list(builds))
754+ self.assertEquals([copy_build], [abpb.build for abpb in builds])
755 source = SourcePackage(spn, spph.distroseries)
756 # SourcePackage.getBuildRecords() doesn't have two build records.
757 builds = source.getBuildRecords().count()
758
759=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
760--- lib/lp/soyuz/tests/test_packageupload.py 2014-04-09 12:32:50 +0000
761+++ lib/lp/soyuz/tests/test_packageupload.py 2014-05-12 07:22:06 +0000
762@@ -47,6 +47,7 @@
763 QueueInconsistentStateError,
764 )
765 from lp.soyuz.interfaces.section import ISectionSet
766+from lp.soyuz.model.binarypackagebuild import ArchiveBinaryPackageBuild
767 from lp.soyuz.model.packagecopyjob import IPackageCopyJobSource
768 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
769 from lp.testing import (
770@@ -1125,7 +1126,10 @@
771 self.assertNotEqual(0, len(ws_binary_file_urls))
772 with person_logged_in(person):
773 binary_file_urls = [
774- ProxiedLibraryFileAlias(file.libraryfile, build.build).http_url
775+ ProxiedLibraryFileAlias(
776+ file.libraryfile,
777+ ArchiveBinaryPackageBuild(
778+ self.distroseries.main_archive, build.build)).http_url
779 for build in upload.builds
780 for bpr in build.build.binarypackages
781 for file in bpr.files]