Merge lp:~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context-2 into lp:launchpad

Proposed by Michael Nelson
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 11041
Proposed branch: lp:~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context-2
Merge into: lp:launchpad
Prerequisite: lp:~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context
Diff against target: 372 lines (+120/-31)
11 files modified
lib/lp/buildmaster/interfaces/buildfarmjob.py (+28/-0)
lib/lp/buildmaster/model/builder.py (+1/-1)
lib/lp/buildmaster/model/buildfarmjob.py (+38/-2)
lib/lp/buildmaster/tests/test_buildfarmjob.py (+16/-1)
lib/lp/registry/model/distroseries.py (+2/-2)
lib/lp/soyuz/browser/archive.py (+4/-0)
lib/lp/soyuz/browser/build.py (+10/-3)
lib/lp/soyuz/configure.zcml (+2/-1)
lib/lp/soyuz/interfaces/binarypackagebuild.py (+0/-1)
lib/lp/soyuz/model/binarypackagebuild.py (+10/-7)
lib/lp/soyuz/tests/test_binarypackagebuild.py (+9/-13)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context-2
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+27594@code.launchpad.net

Commit message

List of builds presented in a PPA context includes all package builds rather than only binary package builds.

Description of the change

Please review my branch:

   bzr+ssh://bazaar.launchpad.net/~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context-2

revision 11014.

Demo URL:
https://launchpad.dev/~cprov/+archive/ppa/+builds?build_text=&build_state=all
(ie. it still works, with no visible changes).

Test command:
bin/test -vvm test_buildfarmjob -m test_binarypackagebuild

Pre-implementation call with: I chatted with Gary about the nested proxy
which is explained in getSpecificJob().

This branch updates the PPA builds view so that it operates on more general
IPackageBuilds rather than IBinaryPackageBuilds (so that later SPRecipe builds
will also be displayed here).

It does so by adding a getSpecificJob() method to IBuildFarmJob which returns
the related specific job (in this case, the BinaryPackageBuild). So the
browser view now finds a batch of IPackageBuild items, and then gets the
specific job for each item to display.

was_built from IBinaryPackageBuild to IBuildFarmJob (so it is available in the
template regardless of the build farm job type).

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
2--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-05-12 08:53:07 +0000
3+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-06-16 14:07:27 +0000
4@@ -11,6 +11,8 @@
5 'IBuildFarmJob',
6 'IBuildFarmJobOld',
7 'IBuildFarmJobSource',
8+ 'InconsistentBuildFarmJobError',
9+ 'ISpecificBuildFarmJob',
10 'BuildFarmJobType',
11 ]
12
13@@ -27,6 +29,15 @@
14 from lp.soyuz.interfaces.processor import IProcessor
15
16
17+class InconsistentBuildFarmJobError(Exception):
18+ """Raised when a BuildFarmJob is in an inconsistent state.
19+
20+ For example, if a BuildFarmJob has a job type for which no adapter
21+ is yet implemented. Or when adapting the BuildFarmJob to a specific
22+ type of build job (such as a BinaryPackageBuild) fails.
23+ """
24+
25+
26 class BuildFarmJobType(DBEnumeratedType):
27 """Soyuz build farm job type.
28
29@@ -246,8 +257,25 @@
30 vocabulary=BuildFarmJobType,
31 description=_("The specific type of job."))
32
33+ def getSpecificJob():
34+ """Return the specific build job associated with this record.
35+
36+ :raises InconsistentBuildFarmJobError: if a specific job could not be
37+ returned.
38+ """
39+
40 title = exported(TextLine(title=_("Title"), required=False))
41
42+ was_built = Attribute("Whether or not modified by the builddfarm.")
43+
44+
45+class ISpecificBuildFarmJob(IBuildFarmJob):
46+ """A marker interface with which to define adapters for IBuildFarmJob.
47+
48+ This enables the registered adapters for ISpecificBuildFarmJob to be
49+ iterated when calculating IBuildFarmJob.specific_job.
50+ """
51+
52
53 class IBuildFarmJobSource(Interface):
54 """A utility of BuildFarmJob used to create _things_."""
55
56=== modified file 'lib/lp/buildmaster/model/builder.py'
57--- lib/lp/buildmaster/model/builder.py 2010-06-04 17:01:21 +0000
58+++ lib/lp/buildmaster/model/builder.py 2010-06-16 14:07:27 +0000
59@@ -429,7 +429,7 @@
60 # of the options.
61
62 def getBuildRecords(self, build_state=None, name=None, arch_tag=None,
63- user=None):
64+ user=None, binary_only=True):
65 """See IHasBuildRecords."""
66 return getUtility(IBinaryPackageBuildSet).getBuildsForBuilder(
67 self.id, build_state, name, arch_tag, user)
68
69=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
70--- lib/lp/buildmaster/model/buildfarmjob.py 2010-05-20 13:12:04 +0000
71+++ lib/lp/buildmaster/model/buildfarmjob.py 2010-06-16 14:07:27 +0000
72@@ -19,8 +19,9 @@
73 from storm.locals import Bool, DateTime, Int, Reference, Storm
74 from storm.store import Store
75
76-from zope.component import getUtility
77+from zope.component import ComponentLookupError, getAdapter, getUtility
78 from zope.interface import classProvides, implements
79+from zope.proxy import isProxy
80 from zope.security.proxy import removeSecurityProxy
81
82 from canonical.database.constants import UTC_NOW
83@@ -32,7 +33,7 @@
84 from lp.buildmaster.interfaces.buildbase import BuildStatus
85 from lp.buildmaster.interfaces.buildfarmjob import (
86 BuildFarmJobType, IBuildFarmJob, IBuildFarmJobOld,
87- IBuildFarmJobSource)
88+ IBuildFarmJobSource, InconsistentBuildFarmJobError, ISpecificBuildFarmJob)
89 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
90
91
92@@ -302,6 +303,41 @@
93 """
94 pass
95
96+ @property
97+ def was_built(self):
98+ """See `IBuild`"""
99+ return self.status not in [BuildStatus.NEEDSBUILD,
100+ BuildStatus.BUILDING,
101+ BuildStatus.SUPERSEDED]
102+
103+ def getSpecificJob(self):
104+ """See `IBuild`"""
105+ # Adapt ourselves based on our job type.
106+ try:
107+ build = getAdapter(
108+ self, ISpecificBuildFarmJob, self.job_type.name)
109+ except ComponentLookupError:
110+ raise InconsistentBuildFarmJobError(
111+ "No adapter was found for the build farm job type %s." % (
112+ self.job_type.name))
113+
114+ # Since the adapters of to ISpecificBuildFarmJob proxy their
115+ # results manually, we don't want the second proxy added by
116+ # getAdapter above.
117+ build_without_outer_proxy = removeSecurityProxy(build)
118+
119+ if build_without_outer_proxy is None:
120+ raise InconsistentBuildFarmJobError(
121+ "There is no related specific job for the build farm "
122+ "job with id %d." % self.id)
123+
124+ # Just to be on the safe side, make sure the build is still
125+ # proxied before returning it.
126+ assert isProxy(build_without_outer_proxy), (
127+ "Unproxied result returned from ISpecificBuildFarmJob adapter.")
128+
129+ return build_without_outer_proxy
130+
131
132 class BuildFarmJobDerived:
133 implements(IBuildFarmJob)
134
135=== modified file 'lib/lp/buildmaster/tests/test_buildfarmjob.py'
136--- lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-05-13 15:44:40 +0000
137+++ lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-06-16 14:07:27 +0000
138@@ -19,7 +19,8 @@
139
140 from lp.buildmaster.interfaces.buildbase import BuildStatus
141 from lp.buildmaster.interfaces.buildfarmjob import (
142- BuildFarmJobType, IBuildFarmJob, IBuildFarmJobSource)
143+ BuildFarmJobType, IBuildFarmJob, IBuildFarmJobSource,
144+ InconsistentBuildFarmJobError)
145 from lp.buildmaster.model.buildfarmjob import BuildFarmJob
146 from lp.testing import login, TestCaseWithFactory
147
148@@ -150,6 +151,20 @@
149 date_created=ten_years_ago)
150 self.failUnlessEqual(ten_years_ago, build_farm_job.date_created)
151
152+ def test_getSpecificJob_none(self):
153+ # An exception is raised if there is no related specific job.
154+ self.assertRaises(
155+ InconsistentBuildFarmJobError, self.build_farm_job.getSpecificJob)
156+
157+ def test_getSpecificJob_unimplemented_type(self):
158+ # An `IBuildFarmJob` with an unimplemented type results in an
159+ # exception.
160+ removeSecurityProxy(self.build_farm_job).job_type = (
161+ BuildFarmJobType.RECIPEBRANCHBUILD)
162+
163+ self.assertRaises(
164+ InconsistentBuildFarmJobError, self.build_farm_job.getSpecificJob)
165+
166
167 class TestBuildFarmJobSecurity(TestBuildFarmJobBase):
168
169
170=== modified file 'lib/lp/registry/model/distroseries.py'
171--- lib/lp/registry/model/distroseries.py 2010-06-09 15:32:32 +0000
172+++ lib/lp/registry/model/distroseries.py 2010-06-16 14:07:27 +0000
173@@ -1111,11 +1111,11 @@
174 for pubrecord in result]
175
176 def getBuildRecords(self, build_state=None, name=None, pocket=None,
177- arch_tag=None, user=None):
178+ arch_tag=None, user=None, binary_only=True):
179 """See IHasBuildRecords"""
180 # Ignore "user", since it would not make any difference to the
181 # records returned here (private builds are only in PPA right
182- # now).
183+ # now). We also ignore binary_only and always return binaries.
184
185 # Find out the distroarchseries in question.
186 arch_ids = DistroArchSeriesSet().getIdsForArchitectures(
187
188=== modified file 'lib/lp/soyuz/browser/archive.py'
189--- lib/lp/soyuz/browser/archive.py 2010-05-27 11:06:25 +0000
190+++ lib/lp/soyuz/browser/archive.py 2010-06-16 14:07:27 +0000
191@@ -1807,6 +1807,10 @@
192
193 __used_for__ = IHasBuildRecords
194
195+ # The archive builds view presents all package builds (binary
196+ # or source package recipe builds).
197+ binary_only = False
198+
199 @property
200 def default_build_state(self):
201 """See `IBuildRecordsView`.
202
203=== modified file 'lib/lp/soyuz/browser/build.py'
204--- lib/lp/soyuz/browser/build.py 2010-05-17 09:57:44 +0000
205+++ lib/lp/soyuz/browser/build.py 2010-06-16 14:07:27 +0000
206@@ -316,12 +316,15 @@
207 Return a list of built CompleteBuild instances, or empty
208 list if no builds were contained in the received batch.
209 """
210- builds = list(batch)
211+ builds = [build.getSpecificJob() for build in batch]
212 if not builds:
213 return []
214
215+ # This pre-population of queue entries is only implemented for
216+ # IBinaryPackageBuilds.
217 prefetched_data = dict()
218- build_ids = [build.id for build in builds]
219+ build_ids = [
220+ build.id for build in builds if IBinaryPackageBuild.providedBy(build)]
221 results = getUtility(IBinaryPackageBuildSet).getQueueEntriesForBuildIDs(
222 build_ids)
223 for (buildqueue, _builder, build_job) in results:
224@@ -349,6 +352,10 @@
225
226 page_title = 'Builds'
227
228+ # Currenly most build records views are interested in binaries
229+ # only, but subclasses can set this if desired.
230+ binary_only = True
231+
232 @property
233 def label(self):
234 return 'Builds for %s' % self.context.displayname
235@@ -372,7 +379,7 @@
236 # request context build records according the selected state
237 builds = self.context.getBuildRecords(
238 build_state=self.state, name=self.text, arch_tag=self.arch_tag,
239- user=self.user)
240+ user=self.user, binary_only=self.binary_only)
241 self.batchnav = BatchNavigator(builds, self.request)
242 # We perform this extra step because we don't what to issue one
243 # extra query to retrieve the BuildQueue for each Build (batch item)
244
245=== modified file 'lib/lp/soyuz/configure.zcml'
246--- lib/lp/soyuz/configure.zcml 2010-06-16 14:07:27 +0000
247+++ lib/lp/soyuz/configure.zcml 2010-06-16 14:07:27 +0000
248@@ -516,9 +516,10 @@
249 factory="lp.soyuz.browser.build.BuildBreadcrumb"
250 permission="zope.Public"/>
251 <adapter
252- provides="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild"
253+ provides="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJob"
254 for="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob"
255 factory="lp.soyuz.model.binarypackagebuild.get_binary_build_for_build_farm_job"
256+ name="PACKAGEBUILD"
257 permission="zope.Public"/>
258
259 <!-- BinaryPackageBuildSet -->
260
261=== modified file 'lib/lp/soyuz/interfaces/binarypackagebuild.py'
262--- lib/lp/soyuz/interfaces/binarypackagebuild.py 2010-05-04 15:24:56 +0000
263+++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2010-06-16 14:07:27 +0000
264@@ -75,7 +75,6 @@
265 description=_("The current source publication for this build.")))
266
267 distro_series = Attribute("Direct parent needed by CanonicalURL")
268- was_built = Attribute("Whether or not modified by the builddfarm.")
269 arch_tag = exported(
270 Text(title=_("Architecture tag"), required=False))
271 distributionsourcepackagerelease = Attribute("The page showing the "
272
273=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
274--- lib/lp/soyuz/model/binarypackagebuild.py 2010-06-16 14:07:27 +0000
275+++ lib/lp/soyuz/model/binarypackagebuild.py 2010-06-16 14:07:27 +0000
276@@ -15,7 +15,7 @@
277
278 from zope.interface import implements
279 from zope.component import getUtility
280-from zope.security.proxy import removeSecurityProxy
281+from zope.security.proxy import ProxyFactory, removeSecurityProxy
282 from storm.expr import (
283 Desc, In, Join, LeftJoin)
284 from storm.store import Store
285@@ -67,11 +67,6 @@
286
287 def get_binary_build_for_build_farm_job(build_farm_job):
288 """Factory method to returning a binary for a build farm job."""
289- # No need to query the db if the build_farm_job doesn't have
290- # the correct job type.
291- if build_farm_job.job_type != BuildFarmJobType.PACKAGEBUILD:
292- return None
293-
294 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
295 find_spec = (BinaryPackageBuild, PackageBuild, BuildFarmJob)
296 resulting_tuple = store.find(
297@@ -83,7 +78,9 @@
298 if resulting_tuple is None:
299 return None
300
301- return resulting_tuple[0]
302+ # We specifically return a proxied BinaryPackageBuild so that we can
303+ # be sure it has the correct proxy.
304+ return ProxyFactory(resulting_tuple[0])
305
306
307 class BinaryPackageBuild(PackageBuildDerived, SQLBase):
308@@ -716,6 +713,12 @@
309
310 raise NotFoundError(filename)
311
312+ def getSpecificJob(self):
313+ """See `IBuildFarmJob`."""
314+ # If we are asked to adapt an object that is already a binary
315+ # package build, then don't hit the db.
316+ return self
317+
318
319 class BinaryPackageBuildSet:
320 implements(IBinaryPackageBuildSet)
321
322=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
323--- lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-16 14:07:27 +0000
324+++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-16 14:07:27 +0000
325@@ -15,7 +15,6 @@
326 from canonical.testing import LaunchpadZopelessLayer
327 from lp.services.job.model.job import Job
328 from lp.buildmaster.interfaces.buildbase import BuildStatus
329-from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
330 from lp.buildmaster.interfaces.builder import IBuilderSet
331 from lp.buildmaster.interfaces.buildqueue import IBuildQueue
332 from lp.buildmaster.interfaces.packagebuild import IPackageBuild
333@@ -128,17 +127,7 @@
334 store = Store.of(build_farm_job)
335 store.flush()
336
337- binary_package_build = IBinaryPackageBuild(build_farm_job)
338- self.failUnlessEqual(self.build, binary_package_build)
339-
340- def test_adapt_from_build_farm_job_wrong_type(self):
341- # An `IBuildFarmJob` of the wrong type results is None.
342- build_farm_job = self.build.build_farm_job
343- removeSecurityProxy(build_farm_job).job_type = (
344- BuildFarmJobType.RECIPEBRANCHBUILD)
345-
346- binary_package_build = IBinaryPackageBuild(build_farm_job)
347- self.failUnlessEqual(None, binary_package_build)
348+ self.failUnlessEqual(self.build, build_farm_job.getSpecificJob())
349
350 def test_adapt_from_build_farm_job_prefetching(self):
351 # The package_build is prefetched for efficiency.
352@@ -150,13 +139,20 @@
353 store.flush()
354 store.reset()
355
356- binary_package_build = IBinaryPackageBuild(build_farm_job)
357+ binary_package_build = build_farm_job.getSpecificJob()
358
359 self.assertStatementCount(
360 0, getattr, binary_package_build, "package_build")
361 self.assertStatementCount(
362 0, getattr, binary_package_build, "build_farm_job")
363
364+ def test_getSpecificJob_noop(self):
365+ # If getSpecificJob is called on the binary build it is a noop.
366+ store = Store.of(self.build)
367+ store.flush()
368+ build, statements = record_statements(self.build.getSpecificJob)
369+ self.assertEqual(0, len(statements))
370+
371
372 class TestBuildUpdateDependencies(TestCaseWithFactory):
373