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
=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-05-12 08:53:07 +0000
+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-06-16 14:07:27 +0000
@@ -11,6 +11,8 @@
11 'IBuildFarmJob',11 'IBuildFarmJob',
12 'IBuildFarmJobOld',12 'IBuildFarmJobOld',
13 'IBuildFarmJobSource',13 'IBuildFarmJobSource',
14 'InconsistentBuildFarmJobError',
15 'ISpecificBuildFarmJob',
14 'BuildFarmJobType',16 'BuildFarmJobType',
15 ]17 ]
1618
@@ -27,6 +29,15 @@
27from lp.soyuz.interfaces.processor import IProcessor29from lp.soyuz.interfaces.processor import IProcessor
2830
2931
32class InconsistentBuildFarmJobError(Exception):
33 """Raised when a BuildFarmJob is in an inconsistent state.
34
35 For example, if a BuildFarmJob has a job type for which no adapter
36 is yet implemented. Or when adapting the BuildFarmJob to a specific
37 type of build job (such as a BinaryPackageBuild) fails.
38 """
39
40
30class BuildFarmJobType(DBEnumeratedType):41class BuildFarmJobType(DBEnumeratedType):
31 """Soyuz build farm job type.42 """Soyuz build farm job type.
3243
@@ -246,8 +257,25 @@
246 vocabulary=BuildFarmJobType,257 vocabulary=BuildFarmJobType,
247 description=_("The specific type of job."))258 description=_("The specific type of job."))
248259
260 def getSpecificJob():
261 """Return the specific build job associated with this record.
262
263 :raises InconsistentBuildFarmJobError: if a specific job could not be
264 returned.
265 """
266
249 title = exported(TextLine(title=_("Title"), required=False))267 title = exported(TextLine(title=_("Title"), required=False))
250268
269 was_built = Attribute("Whether or not modified by the builddfarm.")
270
271
272class ISpecificBuildFarmJob(IBuildFarmJob):
273 """A marker interface with which to define adapters for IBuildFarmJob.
274
275 This enables the registered adapters for ISpecificBuildFarmJob to be
276 iterated when calculating IBuildFarmJob.specific_job.
277 """
278
251279
252class IBuildFarmJobSource(Interface):280class IBuildFarmJobSource(Interface):
253 """A utility of BuildFarmJob used to create _things_."""281 """A utility of BuildFarmJob used to create _things_."""
254282
=== modified file 'lib/lp/buildmaster/model/builder.py'
--- lib/lp/buildmaster/model/builder.py 2010-06-04 17:01:21 +0000
+++ lib/lp/buildmaster/model/builder.py 2010-06-16 14:07:27 +0000
@@ -429,7 +429,7 @@
429 # of the options.429 # of the options.
430430
431 def getBuildRecords(self, build_state=None, name=None, arch_tag=None,431 def getBuildRecords(self, build_state=None, name=None, arch_tag=None,
432 user=None):432 user=None, binary_only=True):
433 """See IHasBuildRecords."""433 """See IHasBuildRecords."""
434 return getUtility(IBinaryPackageBuildSet).getBuildsForBuilder(434 return getUtility(IBinaryPackageBuildSet).getBuildsForBuilder(
435 self.id, build_state, name, arch_tag, user)435 self.id, build_state, name, arch_tag, user)
436436
=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
--- lib/lp/buildmaster/model/buildfarmjob.py 2010-05-20 13:12:04 +0000
+++ lib/lp/buildmaster/model/buildfarmjob.py 2010-06-16 14:07:27 +0000
@@ -19,8 +19,9 @@
19from storm.locals import Bool, DateTime, Int, Reference, Storm19from storm.locals import Bool, DateTime, Int, Reference, Storm
20from storm.store import Store20from storm.store import Store
2121
22from zope.component import getUtility22from zope.component import ComponentLookupError, getAdapter, getUtility
23from zope.interface import classProvides, implements23from zope.interface import classProvides, implements
24from zope.proxy import isProxy
24from zope.security.proxy import removeSecurityProxy25from zope.security.proxy import removeSecurityProxy
2526
26from canonical.database.constants import UTC_NOW27from canonical.database.constants import UTC_NOW
@@ -32,7 +33,7 @@
32from lp.buildmaster.interfaces.buildbase import BuildStatus33from lp.buildmaster.interfaces.buildbase import BuildStatus
33from lp.buildmaster.interfaces.buildfarmjob import (34from lp.buildmaster.interfaces.buildfarmjob import (
34 BuildFarmJobType, IBuildFarmJob, IBuildFarmJobOld,35 BuildFarmJobType, IBuildFarmJob, IBuildFarmJobOld,
35 IBuildFarmJobSource)36 IBuildFarmJobSource, InconsistentBuildFarmJobError, ISpecificBuildFarmJob)
36from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet37from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
3738
3839
@@ -302,6 +303,41 @@
302 """303 """
303 pass304 pass
304305
306 @property
307 def was_built(self):
308 """See `IBuild`"""
309 return self.status not in [BuildStatus.NEEDSBUILD,
310 BuildStatus.BUILDING,
311 BuildStatus.SUPERSEDED]
312
313 def getSpecificJob(self):
314 """See `IBuild`"""
315 # Adapt ourselves based on our job type.
316 try:
317 build = getAdapter(
318 self, ISpecificBuildFarmJob, self.job_type.name)
319 except ComponentLookupError:
320 raise InconsistentBuildFarmJobError(
321 "No adapter was found for the build farm job type %s." % (
322 self.job_type.name))
323
324 # Since the adapters of to ISpecificBuildFarmJob proxy their
325 # results manually, we don't want the second proxy added by
326 # getAdapter above.
327 build_without_outer_proxy = removeSecurityProxy(build)
328
329 if build_without_outer_proxy is None:
330 raise InconsistentBuildFarmJobError(
331 "There is no related specific job for the build farm "
332 "job with id %d." % self.id)
333
334 # Just to be on the safe side, make sure the build is still
335 # proxied before returning it.
336 assert isProxy(build_without_outer_proxy), (
337 "Unproxied result returned from ISpecificBuildFarmJob adapter.")
338
339 return build_without_outer_proxy
340
305341
306class BuildFarmJobDerived:342class BuildFarmJobDerived:
307 implements(IBuildFarmJob)343 implements(IBuildFarmJob)
308344
=== modified file 'lib/lp/buildmaster/tests/test_buildfarmjob.py'
--- lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-05-13 15:44:40 +0000
+++ lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-06-16 14:07:27 +0000
@@ -19,7 +19,8 @@
1919
20from lp.buildmaster.interfaces.buildbase import BuildStatus20from lp.buildmaster.interfaces.buildbase import BuildStatus
21from lp.buildmaster.interfaces.buildfarmjob import (21from lp.buildmaster.interfaces.buildfarmjob import (
22 BuildFarmJobType, IBuildFarmJob, IBuildFarmJobSource)22 BuildFarmJobType, IBuildFarmJob, IBuildFarmJobSource,
23 InconsistentBuildFarmJobError)
23from lp.buildmaster.model.buildfarmjob import BuildFarmJob24from lp.buildmaster.model.buildfarmjob import BuildFarmJob
24from lp.testing import login, TestCaseWithFactory25from lp.testing import login, TestCaseWithFactory
2526
@@ -150,6 +151,20 @@
150 date_created=ten_years_ago)151 date_created=ten_years_ago)
151 self.failUnlessEqual(ten_years_ago, build_farm_job.date_created)152 self.failUnlessEqual(ten_years_ago, build_farm_job.date_created)
152153
154 def test_getSpecificJob_none(self):
155 # An exception is raised if there is no related specific job.
156 self.assertRaises(
157 InconsistentBuildFarmJobError, self.build_farm_job.getSpecificJob)
158
159 def test_getSpecificJob_unimplemented_type(self):
160 # An `IBuildFarmJob` with an unimplemented type results in an
161 # exception.
162 removeSecurityProxy(self.build_farm_job).job_type = (
163 BuildFarmJobType.RECIPEBRANCHBUILD)
164
165 self.assertRaises(
166 InconsistentBuildFarmJobError, self.build_farm_job.getSpecificJob)
167
153168
154class TestBuildFarmJobSecurity(TestBuildFarmJobBase):169class TestBuildFarmJobSecurity(TestBuildFarmJobBase):
155170
156171
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2010-06-09 15:32:32 +0000
+++ lib/lp/registry/model/distroseries.py 2010-06-16 14:07:27 +0000
@@ -1111,11 +1111,11 @@
1111 for pubrecord in result]1111 for pubrecord in result]
11121112
1113 def getBuildRecords(self, build_state=None, name=None, pocket=None,1113 def getBuildRecords(self, build_state=None, name=None, pocket=None,
1114 arch_tag=None, user=None):1114 arch_tag=None, user=None, binary_only=True):
1115 """See IHasBuildRecords"""1115 """See IHasBuildRecords"""
1116 # Ignore "user", since it would not make any difference to the1116 # Ignore "user", since it would not make any difference to the
1117 # records returned here (private builds are only in PPA right1117 # records returned here (private builds are only in PPA right
1118 # now).1118 # now). We also ignore binary_only and always return binaries.
11191119
1120 # Find out the distroarchseries in question.1120 # Find out the distroarchseries in question.
1121 arch_ids = DistroArchSeriesSet().getIdsForArchitectures(1121 arch_ids = DistroArchSeriesSet().getIdsForArchitectures(
11221122
=== modified file 'lib/lp/soyuz/browser/archive.py'
--- lib/lp/soyuz/browser/archive.py 2010-05-27 11:06:25 +0000
+++ lib/lp/soyuz/browser/archive.py 2010-06-16 14:07:27 +0000
@@ -1807,6 +1807,10 @@
18071807
1808 __used_for__ = IHasBuildRecords1808 __used_for__ = IHasBuildRecords
18091809
1810 # The archive builds view presents all package builds (binary
1811 # or source package recipe builds).
1812 binary_only = False
1813
1810 @property1814 @property
1811 def default_build_state(self):1815 def default_build_state(self):
1812 """See `IBuildRecordsView`.1816 """See `IBuildRecordsView`.
18131817
=== modified file 'lib/lp/soyuz/browser/build.py'
--- lib/lp/soyuz/browser/build.py 2010-05-17 09:57:44 +0000
+++ lib/lp/soyuz/browser/build.py 2010-06-16 14:07:27 +0000
@@ -316,12 +316,15 @@
316 Return a list of built CompleteBuild instances, or empty316 Return a list of built CompleteBuild instances, or empty
317 list if no builds were contained in the received batch.317 list if no builds were contained in the received batch.
318 """318 """
319 builds = list(batch)319 builds = [build.getSpecificJob() for build in batch]
320 if not builds:320 if not builds:
321 return []321 return []
322322
323 # This pre-population of queue entries is only implemented for
324 # IBinaryPackageBuilds.
323 prefetched_data = dict()325 prefetched_data = dict()
324 build_ids = [build.id for build in builds]326 build_ids = [
327 build.id for build in builds if IBinaryPackageBuild.providedBy(build)]
325 results = getUtility(IBinaryPackageBuildSet).getQueueEntriesForBuildIDs(328 results = getUtility(IBinaryPackageBuildSet).getQueueEntriesForBuildIDs(
326 build_ids)329 build_ids)
327 for (buildqueue, _builder, build_job) in results:330 for (buildqueue, _builder, build_job) in results:
@@ -349,6 +352,10 @@
349352
350 page_title = 'Builds'353 page_title = 'Builds'
351354
355 # Currenly most build records views are interested in binaries
356 # only, but subclasses can set this if desired.
357 binary_only = True
358
352 @property359 @property
353 def label(self):360 def label(self):
354 return 'Builds for %s' % self.context.displayname361 return 'Builds for %s' % self.context.displayname
@@ -372,7 +379,7 @@
372 # request context build records according the selected state379 # request context build records according the selected state
373 builds = self.context.getBuildRecords(380 builds = self.context.getBuildRecords(
374 build_state=self.state, name=self.text, arch_tag=self.arch_tag,381 build_state=self.state, name=self.text, arch_tag=self.arch_tag,
375 user=self.user)382 user=self.user, binary_only=self.binary_only)
376 self.batchnav = BatchNavigator(builds, self.request)383 self.batchnav = BatchNavigator(builds, self.request)
377 # We perform this extra step because we don't what to issue one384 # We perform this extra step because we don't what to issue one
378 # extra query to retrieve the BuildQueue for each Build (batch item)385 # extra query to retrieve the BuildQueue for each Build (batch item)
379386
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2010-06-16 14:07:27 +0000
+++ lib/lp/soyuz/configure.zcml 2010-06-16 14:07:27 +0000
@@ -516,9 +516,10 @@
516 factory="lp.soyuz.browser.build.BuildBreadcrumb"516 factory="lp.soyuz.browser.build.BuildBreadcrumb"
517 permission="zope.Public"/>517 permission="zope.Public"/>
518 <adapter518 <adapter
519 provides="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild"519 provides="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJob"
520 for="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob"520 for="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob"
521 factory="lp.soyuz.model.binarypackagebuild.get_binary_build_for_build_farm_job"521 factory="lp.soyuz.model.binarypackagebuild.get_binary_build_for_build_farm_job"
522 name="PACKAGEBUILD"
522 permission="zope.Public"/>523 permission="zope.Public"/>
523524
524 <!-- BinaryPackageBuildSet -->525 <!-- BinaryPackageBuildSet -->
525526
=== modified file 'lib/lp/soyuz/interfaces/binarypackagebuild.py'
--- lib/lp/soyuz/interfaces/binarypackagebuild.py 2010-05-04 15:24:56 +0000
+++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2010-06-16 14:07:27 +0000
@@ -75,7 +75,6 @@
75 description=_("The current source publication for this build.")))75 description=_("The current source publication for this build.")))
7676
77 distro_series = Attribute("Direct parent needed by CanonicalURL")77 distro_series = Attribute("Direct parent needed by CanonicalURL")
78 was_built = Attribute("Whether or not modified by the builddfarm.")
79 arch_tag = exported(78 arch_tag = exported(
80 Text(title=_("Architecture tag"), required=False))79 Text(title=_("Architecture tag"), required=False))
81 distributionsourcepackagerelease = Attribute("The page showing the "80 distributionsourcepackagerelease = Attribute("The page showing the "
8281
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2010-06-16 14:07:27 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2010-06-16 14:07:27 +0000
@@ -15,7 +15,7 @@
1515
16from zope.interface import implements16from zope.interface import implements
17from zope.component import getUtility17from zope.component import getUtility
18from zope.security.proxy import removeSecurityProxy18from zope.security.proxy import ProxyFactory, removeSecurityProxy
19from storm.expr import (19from storm.expr import (
20 Desc, In, Join, LeftJoin)20 Desc, In, Join, LeftJoin)
21from storm.store import Store21from storm.store import Store
@@ -67,11 +67,6 @@
6767
68def get_binary_build_for_build_farm_job(build_farm_job):68def get_binary_build_for_build_farm_job(build_farm_job):
69 """Factory method to returning a binary for a build farm job."""69 """Factory method to returning a binary for a build farm job."""
70 # No need to query the db if the build_farm_job doesn't have
71 # the correct job type.
72 if build_farm_job.job_type != BuildFarmJobType.PACKAGEBUILD:
73 return None
74
75 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)70 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
76 find_spec = (BinaryPackageBuild, PackageBuild, BuildFarmJob)71 find_spec = (BinaryPackageBuild, PackageBuild, BuildFarmJob)
77 resulting_tuple = store.find(72 resulting_tuple = store.find(
@@ -83,7 +78,9 @@
83 if resulting_tuple is None:78 if resulting_tuple is None:
84 return None79 return None
8580
86 return resulting_tuple[0]81 # We specifically return a proxied BinaryPackageBuild so that we can
82 # be sure it has the correct proxy.
83 return ProxyFactory(resulting_tuple[0])
8784
8885
89class BinaryPackageBuild(PackageBuildDerived, SQLBase):86class BinaryPackageBuild(PackageBuildDerived, SQLBase):
@@ -716,6 +713,12 @@
716713
717 raise NotFoundError(filename)714 raise NotFoundError(filename)
718715
716 def getSpecificJob(self):
717 """See `IBuildFarmJob`."""
718 # If we are asked to adapt an object that is already a binary
719 # package build, then don't hit the db.
720 return self
721
719722
720class BinaryPackageBuildSet:723class BinaryPackageBuildSet:
721 implements(IBinaryPackageBuildSet)724 implements(IBinaryPackageBuildSet)
722725
=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
--- lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-16 14:07:27 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-16 14:07:27 +0000
@@ -15,7 +15,6 @@
15from canonical.testing import LaunchpadZopelessLayer15from canonical.testing import LaunchpadZopelessLayer
16from lp.services.job.model.job import Job16from lp.services.job.model.job import Job
17from lp.buildmaster.interfaces.buildbase import BuildStatus17from lp.buildmaster.interfaces.buildbase import BuildStatus
18from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
19from lp.buildmaster.interfaces.builder import IBuilderSet18from lp.buildmaster.interfaces.builder import IBuilderSet
20from lp.buildmaster.interfaces.buildqueue import IBuildQueue19from lp.buildmaster.interfaces.buildqueue import IBuildQueue
21from lp.buildmaster.interfaces.packagebuild import IPackageBuild20from lp.buildmaster.interfaces.packagebuild import IPackageBuild
@@ -128,17 +127,7 @@
128 store = Store.of(build_farm_job)127 store = Store.of(build_farm_job)
129 store.flush()128 store.flush()
130129
131 binary_package_build = IBinaryPackageBuild(build_farm_job)130 self.failUnlessEqual(self.build, build_farm_job.getSpecificJob())
132 self.failUnlessEqual(self.build, binary_package_build)
133
134 def test_adapt_from_build_farm_job_wrong_type(self):
135 # An `IBuildFarmJob` of the wrong type results is None.
136 build_farm_job = self.build.build_farm_job
137 removeSecurityProxy(build_farm_job).job_type = (
138 BuildFarmJobType.RECIPEBRANCHBUILD)
139
140 binary_package_build = IBinaryPackageBuild(build_farm_job)
141 self.failUnlessEqual(None, binary_package_build)
142131
143 def test_adapt_from_build_farm_job_prefetching(self):132 def test_adapt_from_build_farm_job_prefetching(self):
144 # The package_build is prefetched for efficiency.133 # The package_build is prefetched for efficiency.
@@ -150,13 +139,20 @@
150 store.flush()139 store.flush()
151 store.reset()140 store.reset()
152141
153 binary_package_build = IBinaryPackageBuild(build_farm_job)142 binary_package_build = build_farm_job.getSpecificJob()
154143
155 self.assertStatementCount(144 self.assertStatementCount(
156 0, getattr, binary_package_build, "package_build")145 0, getattr, binary_package_build, "package_build")
157 self.assertStatementCount(146 self.assertStatementCount(
158 0, getattr, binary_package_build, "build_farm_job")147 0, getattr, binary_package_build, "build_farm_job")
159148
149 def test_getSpecificJob_noop(self):
150 # If getSpecificJob is called on the binary build it is a noop.
151 store = Store.of(self.build)
152 store.flush()
153 build, statements = record_statements(self.build.getSpecificJob)
154 self.assertEqual(0, len(statements))
155
160156
161class TestBuildUpdateDependencies(TestCaseWithFactory):157class TestBuildUpdateDependencies(TestCaseWithFactory):
162158