← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~thumper/launchpad/recipe-binary-builds into lp:launchpad/devel

 

Tim Penhey has proposed merging lp:~thumper/launchpad/recipe-binary-builds into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #602508 Ignoring binary build outcomes violates user expectations
  https://bugs.launchpad.net/bugs/602508


Adds binary build information to the latest builds section on the recipe index page.

http://people.canonical.com/~tim/recipe-latest-builds.png shows an example of how it looks.

I made some of the properties of the view that were accessed multiple times
cached properties.  This required a few tweaks in the tests too.

The binary builds for the successful recipe builds are indented slightly and
show underneath the recipe build that they relate to.

During interactive hacking, I found that I wanted some nicer repr methods
for the processors.

A drive-by fix on the soyuz build-index.pt to just specify the content as
buildlog instead of using tal:content="string: buildlog"

-- 
https://code.launchpad.net/~thumper/launchpad/recipe-binary-builds/+merge/40686
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~thumper/launchpad/recipe-binary-builds into lp:launchpad/devel.
=== modified file 'lib/lp/code/browser/sourcepackagerecipebuild.py'
--- lib/lp/code/browser/sourcepackagerecipebuild.py	2010-08-30 11:51:40 +0000
+++ lib/lp/code/browser/sourcepackagerecipebuild.py	2010-11-12 04:23:29 +0000
@@ -32,6 +32,7 @@
     ISourcePackageRecipeBuild,
     )
 from lp.services.job.interfaces.job import JobStatus
+from lp.services.propertycache import cachedproperty
 
 
 UNEDITABLE_BUILD_STATES = (
@@ -93,7 +94,7 @@
             BuildStatus.FAILEDTOUPLOAD: 'Could not be uploaded correctly',
             }.get(self.context.status, self.context.status.title)
 
-    @property
+    @cachedproperty
     def eta(self):
         """The datetime when the build job is estimated to complete.
 
@@ -112,14 +113,14 @@
         duration = queue_record.estimated_duration
         return start_time + duration
 
-    @property
+    @cachedproperty
     def date(self):
         """The date when the build completed or is estimated to complete."""
         if self.estimate:
             return self.eta
         return self.context.date_finished
 
-    @property
+    @cachedproperty
     def estimate(self):
         """If true, the date value is an estimate."""
         if self.context.date_finished is not None:

=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2010-11-10 20:53:17 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2010-11-12 04:23:29 +0000
@@ -42,6 +42,7 @@
 from lp.code.interfaces.sourcepackagerecipe import MINIMAL_RECIPE_TEXT
 from lp.code.tests.helpers import recipe_parser_newest_version
 from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.services.propertycache import clear_property_cache
 from lp.soyuz.model.processor import ProcessorFamily
 from lp.testing import (
     ANONYMOUS,
@@ -640,7 +641,7 @@
 
 class TestSourcePackageRecipeView(TestCaseForRecipe):
 
-    layer = DatabaseFunctionalLayer
+    layer = LaunchpadFunctionalLayer
 
     def test_index(self):
         recipe = self.makeRecipe()
@@ -673,6 +674,80 @@
             # bzr-builder format 0.2 deb-version 0\+\{revno\}
             lp://dev/~chef/chocolate/cake""", self.getMainText(recipe))
 
+    def test_index_success_with_buildlog(self):
+        # The buildlog is shown if it is there.
+        recipe = self.makeRecipe()
+        build = removeSecurityProxy(self.factory.makeSourcePackageRecipeBuild(
+            recipe=recipe, distroseries=self.squirrel, archive=self.ppa))
+        build.status = BuildStatus.FULLYBUILT
+        build.date_started = datetime(2010, 03, 16, tzinfo=utc)
+        build.date_finished = datetime(2010, 03, 16, tzinfo=utc)
+        build.log = self.factory.makeLibraryFileAlias()
+
+        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+            Latest builds
+            Status Time Distribution series Archive
+            Successful build on 2010-03-16 buildlog \(.*\) Secret Squirrel Secret PPA
+            Request build\(s\)""", self.getMainText(recipe))
+
+    def test_index_success_with_binary_builds(self):
+        # Binary builds are shown after the recipe builds if there are any.
+        recipe = self.makeRecipe()
+        build = removeSecurityProxy(self.factory.makeSourcePackageRecipeBuild(
+            recipe=recipe, distroseries=self.squirrel, archive=self.ppa))
+        build.status = BuildStatus.FULLYBUILT
+        build.date_started = datetime(2010, 03, 16, tzinfo=utc)
+        build.date_finished = datetime(2010, 03, 16, tzinfo=utc)
+        build.log = self.factory.makeLibraryFileAlias()
+        package_name = self.factory.getOrMakeSourcePackageName('chocolate')
+        source_package_release = self.factory.makeSourcePackageRelease(
+            archive=self.ppa, sourcepackagename=package_name, distroseries=self.squirrel,
+            source_package_recipe_build=build, version='0+r42')
+        builder = self.factory.makeBuilder()
+        binary_build = self.factory.makeBinaryPackageBuild(
+            source_package_release=source_package_release,
+            distroarchseries=self.squirrel.nominatedarchindep,
+            processor=builder.processor)
+        binary_build.queueBuild()
+
+        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+            Latest builds
+            Status Time Distribution series Archive
+            Successful build on 2010-03-16 buildlog \(.*\) Secret Squirrel Secret PPA
+              chocolate - 0\+r42 starting in .* \(estimated\) i386
+            Request build\(s\)""", self.getMainText(recipe))
+
+    def test_index_success_with_completed_binary_build(self):
+        # Binary builds show their buildlog too.
+        recipe = self.makeRecipe()
+        build = removeSecurityProxy(self.factory.makeSourcePackageRecipeBuild(
+            recipe=recipe, distroseries=self.squirrel, archive=self.ppa))
+        build.status = BuildStatus.FULLYBUILT
+        build.date_started = datetime(2010, 03, 16, tzinfo=utc)
+        build.date_finished = datetime(2010, 03, 16, tzinfo=utc)
+        build.log = self.factory.makeLibraryFileAlias()
+        package_name = self.factory.getOrMakeSourcePackageName('chocolate')
+        source_package_release = self.factory.makeSourcePackageRelease(
+            archive=self.ppa, sourcepackagename=package_name, distroseries=self.squirrel,
+            source_package_recipe_build=build, version='0+r42')
+        builder = self.factory.makeBuilder()
+        binary_build = removeSecurityProxy(self.factory.makeBinaryPackageBuild(
+            source_package_release=source_package_release,
+            distroarchseries=self.squirrel.nominatedarchindep,
+            processor=builder.processor))
+        binary_build.queueBuild()
+        binary_build.status = BuildStatus.FULLYBUILT
+        binary_build.date_started = datetime(2010, 04, 16, tzinfo=utc)
+        binary_build.date_finished = datetime(2010, 04, 16, tzinfo=utc)
+        binary_build.log = self.factory.makeLibraryFileAlias()
+
+        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+            Latest builds
+            Status Time Distribution series Archive
+            Successful build on 2010-03-16 buildlog \(.*\) Secret Squirrel Secret PPA
+              chocolate - 0\+r42 on 2010-04-16 buildlog \(.*\) i386
+            Request build\(s\)""", self.getMainText(recipe))
+
     def test_index_no_builds(self):
         """A message should be shown when there are no builds."""
         recipe = self.makeRecipe()
@@ -706,7 +781,7 @@
         pattern = """\
             Latest builds
             Status Time Distribution series Archive
-            Pending build in .* \(estimated\) Secret Squirrel Secret PPA
+            Pending build starting in .* \(estimated\) Secret Squirrel Secret PPA
             Request build\(s\)
 
             Recipe contents"""
@@ -895,8 +970,10 @@
         view = self.makeBuildView()
         self.assertTrue(view.estimate)
         view.context.buildqueue_record.job.start()
+        clear_property_cache(view)
         self.assertTrue(view.estimate)
         removeSecurityProxy(view.context).date_finished = datetime.now(utc)
+        clear_property_cache(view)
         self.assertFalse(view.estimate)
 
     def test_eta(self):
@@ -915,11 +992,13 @@
             recipe_build=build)
         queue_entry._now = lambda: datetime(1970, 1, 1, 0, 0, 0, 0, utc)
         self.factory.makeBuilder()
+        clear_property_cache(view)
         self.assertIsNot(None, view.eta)
         self.assertEqual(
             queue_entry.getEstimatedJobStartTime() +
             queue_entry.estimated_duration, view.eta)
         queue_entry.job.start()
+        clear_property_cache(view)
         self.assertEqual(
             queue_entry.job.date_started + queue_entry.estimated_duration,
             view.eta)

=== modified file 'lib/lp/code/templates/sourcepackagerecipe-index.pt'
--- lib/lp/code/templates/sourcepackagerecipe-index.pt	2010-11-09 04:24:09 +0000
+++ lib/lp/code/templates/sourcepackagerecipe-index.pt	2010-11-12 04:23:29 +0000
@@ -7,6 +7,13 @@
   i18n:domain="launchpad"
 >
 
+<metal:block fill-slot="head_epilogue">
+  <style type="text/css">
+    .binary-build .indent {
+      padding-left: 2em;
+    }
+  </style>
+</metal:block>
 
 <body>
 
@@ -92,18 +99,36 @@
             </tr>
           </thead>
           <tbody>
-            <tr tal:repeat="build view/builds">
+            <tal:recipe-builds repeat="build view/builds">
               <tal:build-view define="buildview nocall:build/@@+index">
+            <tr>
               <td>
                 <span tal:replace="structure build/image:icon" />
                 <a tal:content="buildview/status"
                    tal:attributes="href build/fmt:url"></a>
               </td>
-              <td>
-                <tal:date replace="buildview/date/fmt:displaydate" />
-                <tal:estimate condition="buildview/estimate">
-                (estimated)
-                </tal:estimate>
+              <td tal:define="is_building build/status/enumvalue:BUILDING">
+                <tal:building condition="is_building">
+                  Started
+                  <span tal:attributes="title build/date_started/fmt:datetime"
+                        tal:content="build/date_started/fmt:approximatedate"
+                        >5 minutes ago</span>
+                </tal:building>
+                <tal:not-building condition="not: is_building">
+                  <tal:estimate condition="buildview/estimate">
+                    starting
+                  </tal:estimate>
+                  <tal:date replace="buildview/date/fmt:displaydate" />
+                  <tal:estimate condition="buildview/estimate">
+                    (estimated)
+                  </tal:estimate>
+                </tal:not-building>
+                <tal:build-log define="file build/log"
+                               tal:condition="file">
+                  <a class="sprite download"
+                     tal:attributes="href build/log_url">buildlog</a>
+                  (<span tal:replace="file/content/filesize/fmt:bytes" />)
+                </tal:build-log>
               </td>
               <td>
                 <tal:distro
@@ -112,8 +137,56 @@
               <td>
                 <tal:archive replace="structure build/archive/fmt:link"/>
               </td>
-              </tal:build-view>
             </tr>
+            <tal:binary-builds repeat="binary buildview/binary_builds">
+              <tr tal:define="binaryview nocall:binary/@@+index"
+                  class="binary-build">
+                <td class="indent">
+                  <span tal:replace="structure binary/image:icon"/>
+                  <a tal:content="binary/source_package_release/title"
+                     tal:attributes="href binary/fmt:url">package - version</a>
+                </td>
+                <td>
+
+                  <tal:pending condition="binaryview/dispatch_time_estimate_available"
+                               define="eta binary/buildqueue_record/getEstimatedJobStartTime;">
+                    starting <tal:eta
+                    replace="eta/fmt:approximatedate">in 3 hours</tal:eta>
+                    (estimated)
+                  </tal:pending>
+
+                  <tal:building condition="binary/status/enumvalue:BUILDING">
+                    <li>
+                      started
+                      <span tal:attributes="title binary/date_started/fmt:datetime"
+                            tal:content="binary/date_started/fmt:approximatedate"
+                            >5 minutes ago</span>
+                    </li>
+                  </tal:building>
+
+                  <tal:built condition="binary/date_finished">
+                    <span tal:attributes="title binary/date_finished/fmt:datetime"
+                        tal:content="binary/date_finished/fmt:displaydate">2008-01-01</span>
+                  </tal:built>
+
+                  <tal:build-log define="file binary/log"
+                                 tal:condition="file">
+                    <a class="sprite download"
+                       tal:attributes="href binary/log_url">buildlog</a>
+                    (<span tal:replace="file/content/filesize/fmt:bytes" />)
+                  </tal:build-log>
+
+                </td>
+                <td class="indent">
+                  <a class="sprite distribution"
+                     tal:define="archseries binary/distro_arch_series"
+                     tal:attributes="href archseries/fmt:url"
+                     tal:content="archseries/architecturetag">i386</a>
+                </td>
+              </tr>
+            </tal:binary-builds>
+            </tal:build-view>
+            </tal:recipe-builds>
           </tbody>
         </table>
         <p tal:condition="not: view/builds">

=== modified file 'lib/lp/soyuz/model/processor.py'
--- lib/lp/soyuz/model/processor.py	2010-08-20 20:31:18 +0000
+++ lib/lp/soyuz/model/processor.py	2010-11-12 04:23:29 +0000
@@ -38,6 +38,9 @@
     title = StringCol(dbName='title', notNull=True)
     description = StringCol(dbName='description', notNull=True)
 
+    def __repr__(self):
+        return "<Processor %r>" % self.title
+
 
 class ProcessorFamily(SQLBase):
     implements(IProcessorFamily)
@@ -55,6 +58,9 @@
         return Processor(family=self, name=name, title=title,
             description=description)
 
+    def __repr__(self):
+        return "<ProcessorFamily %r>" % self.title
+
 
 class ProcessorFamilySet:
     implements(IProcessorFamilySet)

=== modified file 'lib/lp/soyuz/templates/build-index.pt'
--- lib/lp/soyuz/templates/build-index.pt	2010-08-06 16:01:38 +0000
+++ lib/lp/soyuz/templates/build-index.pt	2010-11-12 04:23:29 +0000
@@ -190,8 +190,7 @@
       <li tal:define="file context/log"
           tal:condition="file">
         <a class="sprite download"
-           tal:attributes="href context/log_url"
-           tal:content="string: buildlog">BUILDLOG</a>
+           tal:attributes="href context/log_url">buildlog</a>
         (<span tal:replace="file/content/filesize/fmt:bytes" />)
       </li>
       <li tal:define="file context/upload_log"

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2010-11-05 08:17:29 +0000
+++ lib/lp/testing/factory.py	2010-11-12 04:23:29 +0000
@@ -2696,7 +2696,7 @@
 
     def makeBinaryPackageBuild(self, source_package_release=None,
             distroarchseries=None, archive=None, builder=None,
-            status=None, pocket=None):
+            status=None, pocket=None, date_created=None, processor=None):
         """Create a BinaryPackageBuild.
 
         If archive is not supplied, the source_package_release is used
@@ -2720,7 +2720,8 @@
             self.makeSourcePackagePublishingHistory(
                 distroseries=source_package_release.upload_distroseries,
                 archive=archive, sourcepackagerelease=source_package_release)
-        processor = self.makeProcessor()
+        if processor is None:
+            processor = self.makeProcessor()
         if distroarchseries is None:
             distroarchseries = self.makeDistroArchSeries(
                 distroseries=source_package_release.upload_distroseries,
@@ -2729,6 +2730,8 @@
             status = BuildStatus.NEEDSBUILD
         if pocket is None:
             pocket = PackagePublishingPocket.RELEASE
+        if date_created is None:
+            date_created = self.getUniqueDate()
         binary_package_build = getUtility(IBinaryPackageBuildSet).new(
             source_package_release=source_package_release,
             processor=processor,
@@ -2736,7 +2739,7 @@
             status=status,
             archive=archive,
             pocket=pocket,
-            date_created=self.getUniqueDate())
+            date_created=date_created)
         naked_build = removeSecurityProxy(binary_package_build)
         naked_build.builder = builder
         return binary_package_build


Follow ups