← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wallyworld/launchpad/recipe-build-now into lp:launchpad

 

Ian Booth has proposed merging lp:~wallyworld/launchpad/recipe-build-now into lp:launchpad with lp:~wallyworld/launchpad/request-build-popup as a prerequisite.

Requested reviews:
  Launchpad UI Reviewers (launchpad-ui-reviewers): ui
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/recipe-build-now/+merge/49968

This change provides a way to allow users to manually request that a daily build of a recipe should be queued "now". This feature is only available if the recipe is configured to build daily and is marked as stale.

= =Implementation ==

A new view was written - SourcePackageRecipeRequestDailyBuildView
This view handles both the ajax and non-ajax cases. The request.is_ajax flag is used to see if it's an ajax call. If so, a different page template is used to just load the recipe builds and this info is returned to be inserted directly into the page without a refresh. For the non-ajax case, a page refresh is used to reload the entire recipe page and hence show the new pending builds.

In the case where there is already a build for one or more of the distroseries pending, no error is shown. Any new builds are simply displayed.

A new sprite was added - sourcepackage-recipe-build.png. This is used as the icon to display next to the ajax "Build now" link.

A drive by fix was done to the recipe vocabularies for archives and distroseries since they borked when the recipe index page was shown without a user logged in.

== Demo and QA ==

The following screenshots show how the link/button looks for the script and no script case

http://people.canonical.com/~ianb/request-daily-build.png
http://people.canonical.com/~ianb/request-daily-build-no-ajax.png

To QA, you just need to ensure there's an appropriate recipe available (stale, build daily etc) and click the "Build now" link. The link should then disappear and the Recent Builds table be updated.

== Tests ==

A Windmill test was written to test that the ajax enabled "Build now" link works.
Unit tests were written to check that the "Build now" feature only shows when relevant and to check the no script version.

bin/test -vvt test_sourcepackagerecipe
bin/test -vvt test_recipe_request_build

== Lint ==

bin/lint.sh
= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/canonical/launchpad/icing/icon-sprites
  lib/canonical/launchpad/icing/icon-sprites.positioning
  lib/canonical/launchpad/icing/style-3-0.css.in
  lib/lp/code/browser/configure.zcml
  lib/lp/code/browser/sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py
  lib/lp/code/interfaces/sourcepackagerecipe.py
  lib/lp/code/javascript/requestbuild_overlay.js
  lib/lp/code/model/sourcepackagerecipe.py
  lib/lp/code/templates/sourcepackagerecipe-index.pt
  lib/lp/code/vocabularies/sourcepackagerecipe.py
  lib/lp/code/windmill/tests/test_recipe_request_build.py

./lib/canonical/launchpad/icing/icon-sprites
       5: Line exceeds 78 characters.
       6: Line exceeds 78 characters.
       8: Line exceeds 78 characters.
       9: Line exceeds 78 characters.
      10: Line exceeds 78 characters.
      12: Line exceeds 78 characters.
      13: Line exceeds 78 characters.
      15: Line exceeds 78 characters.
      17: Line exceeds 78 characters.
      18: Line exceeds 78 characters.
      20: Line exceeds 78 characters.
      25: Line exceeds 78 characters.
      26: Line exceeds 78 characters.
      27: Line exceeds 78 characters.
      28: Line exceeds 78 characters.
      29: Line exceeds 78 characters.
      31: Line exceeds 78 characters.
      32: Line exceeds 78 characters.
      33: Line exceeds 78 characters.
      34: Line exceeds 78 characters.
      36: Line exceeds 78 characters.
      37: Line exceeds 78 characters.
      42: Line exceeds 78 characters.
      44: Line exceeds 78 characters.
      45: Line exceeds 78 characters.
      48: Line exceeds 78 characters.
      50: Line exceeds 78 characters.
      51: Line exceeds 78 characters.
      52: Line exceeds 78 characters.
      53: Line exceeds 78 characters.
      54: Line exceeds 78 characters.
      55: Line exceeds 78 characters.
      56: Line exceeds 78 characters.
      57: Line exceeds 78 characters.
      58: Line exceeds 78 characters.
      59: Line exceeds 78 characters.
      60: Line exceeds 78 characters.
      62: Line exceeds 78 characters.
      65: Line exceeds 78 characters.
      66: Line exceeds 78 characters.
      67: Line exceeds 78 characters.
      69: Line exceeds 78 characters.
      70: Line exceeds 78 characters.
      72: Line exceeds 78 characters.
      73: Line exceeds 78 characters.
      74: Line exceeds 78 characters.
      75: Line exceeds 78 characters.
      76: Line exceeds 78 characters.
      78: Line exceeds 78 characters.
      81: Line exceeds 78 characters.
      84: Line exceeds 78 characters.
      85: Line exceeds 78 characters.
      87: Line exceeds 78 characters.
      88: Line exceeds 78 characters.
      89: Line exceeds 78 characters.
      90: Line exceeds 78 characters.
      92: Line exceeds 78 characters.
      93: Line exceeds 78 characters.
      94: Line exceeds 78 characters.
      96: Line exceeds 78 characters.
      99: Line exceeds 78 characters.
     101: Line exceeds 78 characters.
     102: Line exceeds 78 characters.
     103: Line exceeds 78 characters.
     106: Line exceeds 78 characters.
     108: Line exceeds 78 characters.
     109: Line exceeds 78 characters.
     113: Line exceeds 78 characters.
     116: Line exceeds 78 characters.
     117: Line exceeds 78 characters.
     119: Line exceeds 78 characters.
     120: Line exceeds 78 characters.
     124: Line exceeds 78 characters.
     125: Line exceeds 78 characters.
     126: Line exceeds 78 characters.
     127: Line exceeds 78 characters.
     128: Line exceeds 78 characters.
     134: Line exceeds 78 characters.
     137: Line exceeds 78 characters.
     138: Line exceeds 78 characters.
     139: Line exceeds 78 characters.
     141: Line exceeds 78 characters.
     146: Line exceeds 78 characters.
     147: Line exceeds 78 characters.
     149: Line exceeds 78 characters.
     150: Line exceeds 78 characters.
     152: Line exceeds 78 characters.
     153: Line exceeds 78 characters.
     154: Line exceeds 78 characters.
     158: Line exceeds 78 characters.
     159: Line exceeds 78 characters.
     161: Line exceeds 78 characters.
     162: Line exceeds 78 characters.
     163: Line exceeds 78 characters.
     166: Line exceeds 78 characters.
     167: Line exceeds 78 characters.
     170: Line exceeds 78 characters.
     171: Line exceeds 78 characters.
     172: Line exceeds 78 characters.
     175: Line exceeds 78 characters.
     177: Line exceeds 78 characters.
     178: Line exceeds 78 characters.
     180: Line exceeds 78 characters.
     180: Line has trailing whitespace.
     185: Line exceeds 78 characters.
     186: Line exceeds 78 characters.
     187: Line exceeds 78 characters.
     188: Line exceeds 78 characters.
     189: Line exceeds 78 characters.
     191: Line exceeds 78 characters.
     193: Line exceeds 78 characters.
     195: Line exceeds 78 characters.
     196: Line exceeds 78 characters.
     197: Line exceeds 78 characters.
     199: Line exceeds 78 characters.
     201: Line exceeds 78 characters.
     202: Line exceeds 78 characters.
     204: Line exceeds 78 characters.
     205: Line exceeds 78 characters.
     209: Line exceeds 78 characters.
     210: Line exceeds 78 characters.
     212: Line exceeds 78 characters.
     214: Line exceeds 78 characters.
     215: Line exceeds 78 characters.
     216: Line exceeds 78 characters.
     217: Line exceeds 78 characters.
     218: Line exceeds 78 characters.
     220: Line exceeds 78 characters.
     222: Line exceeds 78 characters.
     223: Line exceeds 78 characters.
     224: Line exceeds 78 characters.
     226: Line exceeds 78 characters.
     231: Line exceeds 78 characters.
     232: Line exceeds 78 characters.
     233: Line exceeds 78 characters.
     235: Line exceeds 78 characters.
     236: Line exceeds 78 characters.
     240: Line exceeds 78 characters.
     244: Line exceeds 78 characters.
     246: Line exceeds 78 characters.
     248: Line exceeds 78 characters.
     249: Line exceeds 78 characters.
     250: Line exceeds 78 characters.
     253: Line exceeds 78 characters.
     254: Line exceeds 78 characters.
     256: Line exceeds 78 characters.
     258: Line exceeds 78 characters.
     260: Line exceeds 78 characters.
     261: Line exceeds 78 characters.
     262: Line exceeds 78 characters.
     265: Line exceeds 78 characters.
     266: Line exceeds 78 characters.
     267: Line exceeds 78 characters.
     268: Line exceeds 78 characters.
     270: Line exceeds 78 characters.
     271: Line exceeds 78 characters.
     272: Line exceeds 78 characters.
     274: Line exceeds 78 characters.
     278: Line exceeds 78 characters.
     280: Line exceeds 78 characters.
     282: Line exceeds 78 characters.
     283: Line exceeds 78 characters.
     284: Line exceeds 78 characters.
     287: Line exceeds 78 characters.
     288: Line exceeds 78 characters.
     289: Line exceeds 78 characters.
     290: Line exceeds 78 characters.
     293: Line exceeds 78 characters.
     298: Line exceeds 78 characters.
     300: Line exceeds 78 characters.
     302: Line exceeds 78 characters.
     303: Line exceeds 78 characters.
     304: Line exceeds 78 characters.
     305: Line exceeds 78 characters.
     307: Line exceeds 78 characters.
     308: Line exceeds 78 characters.
     309: Line exceeds 78 characters.
     310: Line exceeds 78 characters.
     312: Line exceeds 78 characters.
     313: Line exceeds 78 characters.
     314: Line exceeds 78 characters.
     318: Line exceeds 78 characters.
     319: Line exceeds 78 characters.
     320: Line exceeds 78 characters.
     321: Line exceeds 78 characters.
     323: Line exceeds 78 characters.
     324: Line exceeds 78 characters.
     325: Line exceeds 78 characters.
     326: Line exceeds 78 characters.
     327: Line exceeds 78 characters.
     328: Line exceeds 78 characters.
     330: Line exceeds 78 characters.
     332: Line exceeds 78 characters.
     333: Line exceeds 78 characters.
     334: Line exceeds 78 characters.
     337: Line exceeds 78 characters.
     338: Line exceeds 78 characters.
     342: Line exceeds 78 characters.
     343: Line exceeds 78 characters.
     347: Line exceeds 78 characters.
     348: Line exceeds 78 characters.
     349: Line exceeds 78 characters.
     350: Line exceeds 78 characters.
     352: Line exceeds 78 characters.
     355: Line exceeds 78 characters.
     357: Line exceeds 78 characters.
     358: Line exceeds 78 characters.
     360: Line exceeds 78 characters.
     361: Line exceeds 78 characters.
     365: Line exceeds 78 characters.
     366: Line exceeds 78 characters.
     369: Line exceeds 78 characters.
     370: Line exceeds 78 characters.
     371: Line exceeds 78 characters.
     372: Line exceeds 78 characters.
     373: Line exceeds 78 characters.
     377: Line exceeds 78 characters.
     378: Line exceeds 78 characters.
     379: Line exceeds 78 characters.
     381: Line exceeds 78 characters.
     382: Line exceeds 78 characters.
     383: Line exceeds 78 characters.
     384: Line exceeds 78 characters.
     385: Line exceeds 78 characters.
     386: Line exceeds 78 characters.
     387: Line exceeds 78 characters.
     388: Line exceeds 78 characters.
     389: Line exceeds 78 characters.
     391: Line exceeds 78 characters.
     393: Line exceeds 78 characters.
     394: Line exceeds 78 characters.
     395: Line exceeds 78 characters.
     396: Line exceeds 78 characters.
     397: Line exceeds 78 characters.
     398: Line exceeds 78 characters.
     399: Line exceeds 78 characters.
     400: Line exceeds 78 characters.
     403: Line exceeds 78 characters.
     404: Line exceeds 78 characters.
     406: Line exceeds 78 characters.
     408: Line exceeds 78 characters.
     413: Line exceeds 78 characters.
     414: Line exceeds 78 characters.
     415: Line exceeds 78 characters.
     416: Line exceeds 78 characters.
     418: Line exceeds 78 characters.
     419: Line exceeds 78 characters.
     421: Line exceeds 78 characters.
     422: Line exceeds 78 characters.
     423: Line exceeds 78 characters.
     424: Line exceeds 78 characters.
     425: Line exceeds 78 characters.
     427: Line exceeds 78 characters.
     428: Line exceeds 78 characters.
     429: Line exceeds 78 characters.
     430: Line exceeds 78 characters.
     432: Line exceeds 78 characters.
     433: Line exceeds 78 characters.
     435: Line exceeds 78 characters.
     436: Line exceeds 78 characters.
     437: Line exceeds 78 characters.
     438: Line exceeds 78 characters.
     439: Line exceeds 78 characters.
     441: Line exceeds 78 characters.
     442: Line exceeds 78 characters.
     443: Line exceeds 78 characters.
     445: Line exceeds 78 characters.
     447: Line exceeds 78 characters.
     448: Line exceeds 78 characters.
     450: Line exceeds 78 characters.
     451: Line exceeds 78 characters.
     453: Line exceeds 78 characters.
     454: Line exceeds 78 characters.
     455: Line exceeds 78 characters.
     457: Line exceeds 78 characters.
     458: Line exceeds 78 characters.
     461: Line exceeds 78 characters.
     462: Line exceeds 78 characters.
     464: Line exceeds 78 characters.
     467: Line exceeds 78 characters.
     469: Line exceeds 78 characters.
     470: Line exceeds 78 characters.
     471: Line exceeds 78 characters.
     472: Line exceeds 78 characters.
     476: Line exceeds 78 characters.
     477: Line exceeds 78 characters.
     480: Line exceeds 78 characters.
     481: Line exceeds 78 characters.
     483: Line exceeds 78 characters.
     484: Line exceeds 78 characters.
     485: Line exceeds 78 characters.
     486: Line exceeds 78 characters.
     487: Line exceeds 78 characters.
     488: Line exceeds 78 characters.
     490: Line exceeds 78 characters.
     491: Line exceeds 78 characters.
     492: Line exceeds 78 characters.
     494: Line exceeds 78 characters.
     495: Line exceeds 78 characters.
     496: Line exceeds 78 characters.
     497: Line exceeds 78 characters.
     498: Line exceeds 78 characters.
     499: Line exceeds 78 characters.
     501: Line exceeds 78 characters.
     502: Line exceeds 78 characters.
     504: Line exceeds 78 characters.
     506: Line exceeds 78 characters.
     507: Line exceeds 78 characters.
     508: Line exceeds 78 characters.
     510: Line exceeds 78 characters.
     511: Line exceeds 78 characters.
     513: Line exceeds 78 characters.
     514: Line exceeds 78 characters.
     515: Line exceeds 78 characters.
     517: Line exceeds 78 characters.
     523: Line exceeds 78 characters.
     524: Line exceeds 78 characters.
     525: Line exceeds 78 characters.
     531: Line exceeds 78 characters.
     532: Line exceeds 78 characters.
     533: Line exceeds 78 characters.
     534: Line exceeds 78 characters.
     537: Line exceeds 78 characters.
     539: Line exceeds 78 characters.
     540: Line exceeds 78 characters.
     541: Line exceeds 78 characters.
     545: Line exceeds 78 characters.
     549: Line exceeds 78 characters.
     550: Line exceeds 78 characters.
     551: Line exceeds 78 characters.
     552: Line exceeds 78 characters.
     553: Line has trailing whitespace.
     554: Line exceeds 78 characters.
     555: Line exceeds 78 characters.
     558: Line exceeds 78 characters.
     560: Line exceeds 78 characters.
     562: Line exceeds 78 characters.
     563: Line exceeds 78 characters.
     564: Line exceeds 78 characters.
     565: Line exceeds 78 characters.
     568: Line exceeds 78 characters.
     573: Line exceeds 78 characters.
     575: Line exceeds 78 characters.
     576: Line exceeds 78 characters.
     578: Line exceeds 78 characters.
     579: Line exceeds 78 characters.
     580: Line exceeds 78 characters.
     583: Line exceeds 78 characters.
     587: Line exceeds 78 characters.
     588: Line exceeds 78 characters.
     589: Line exceeds 78 characters.
     591: Line exceeds 78 characters.
     592: Line exceeds 78 characters.
     593: Line exceeds 78 characters.
     595: Line exceeds 78 characters.
     597: Line exceeds 78 characters.
     598: Line exceeds 78 characters.
     603: Line exceeds 78 characters.
     605: Line exceeds 78 characters.
     606: Line exceeds 78 characters.
     608: Line exceeds 78 characters.
     609: Line exceeds 78 characters.
     610: Line exceeds 78 characters.
     611: Line exceeds 78 characters.
     612: Line exceeds 78 characters.
     613: Line exceeds 78 characters.
     617: Line exceeds 78 characters.
     619: Line exceeds 78 characters.
     621: Line exceeds 78 characters.
     625: Line exceeds 78 characters.
     626: Line exceeds 78 characters.
     627: Line exceeds 78 characters.
     629: Line exceeds 78 characters.
     631: Line exceeds 78 characters.
     633: Line exceeds 78 characters.
     634: Line exceeds 78 characters.
     635: Line exceeds 78 characters.
     639: Line exceeds 78 characters.
     640: Line exceeds 78 characters.
     642: Line exceeds 78 characters.
     644: Line exceeds 78 characters.
     647: Line exceeds 78 characters.
     655: Line exceeds 78 characters.
     657: Line exceeds 78 characters.
     658: Line exceeds 78 characters.
     659: Line exceeds 78 characters.
     661: Line exceeds 78 characters.
     662: Line exceeds 78 characters.
     663: Line exceeds 78 characters.
     666: Line exceeds 78 characters.
     667: Line exceeds 78 characters.
     671: Line exceeds 78 characters.
     674: Line exceeds 78 characters.
     675: Line exceeds 78 characters.
     676: Line exceeds 78 characters.
     680: Line exceeds 78 characters.
     681: Line exceeds 78 characters.
     686: Line exceeds 78 characters.
     687: Line exceeds 78 characters.
     690: Line exceeds 78 characters.
     694: Line exceeds 78 characters.
     695: Line exceeds 78 characters.
     696: Line exceeds 78 characters.
     697: Line exceeds 78 characters.
     698: Line exceeds 78 characters.
     701: Line exceeds 78 characters.
     702: Line exceeds 78 characters.
     703: Line exceeds 78 characters.
     705: Line exceeds 78 characters.
     710: Line exceeds 78 characters.
     712: Line exceeds 78 characters.
     714: Line exceeds 78 characters.
     716: Line exceeds 78 characters.
     717: Line exceeds 78 characters.
     719: Line exceeds 78 characters.
     720: Line exceeds 78 characters.
     721: Line exceeds 78 characters.
     722: Line exceeds 78 characters.
     723: Line exceeds 78 characters.
     725: Line exceeds 78 characters.
     727: Line exceeds 78 characters.
     730: Line exceeds 78 characters.
     732: Line exceeds 78 characters.
     734: Line exceeds 78 characters.
     735: Line exceeds 78 characters.
     739: Line exceeds 78 characters.
     740: Line exceeds 78 characters.
./lib/canonical/launchpad/icing/icon-sprites.positioning
       5: Line has trailing whitespace.
       7: Line has trailing whitespace.
       9: Line has trailing whitespace.
      11: Line has trailing whitespace.
      13: Line has trailing whitespace.
      15: Line has trailing whitespace.
      17: Line has trailing whitespace.
      19: Line has trailing whitespace.
      21: Line has trailing whitespace.
      23: Line has trailing whitespace.
      25: Line has trailing whitespace.
      27: Line has trailing whitespace.
      29: Line has trailing whitespace.
      31: Line has trailing whitespace.
      33: Line has trailing whitespace.
      35: Line has trailing whitespace.
      37: Line has trailing whitespace.
      39: Line has trailing whitespace.
      41: Line has trailing whitespace.
      43: Line has trailing whitespace.
      45: Line has trailing whitespace.
      47: Line has trailing whitespace.
      49: Line has trailing whitespace.
      51: Line has trailing whitespace.
      53: Line has trailing whitespace.
      55: Line has trailing whitespace.
      57: Line has trailing whitespace.
      59: Line has trailing whitespace.
      61: Line has trailing whitespace.
      63: Line has trailing whitespace.
      65: Line has trailing whitespace.
      67: Line has trailing whitespace.
      69: Line has trailing whitespace.
      71: Line has trailing whitespace.
      73: Line has trailing whitespace.
      75: Line has trailing whitespace.
      77: Line has trailing whitespace.
      79: Line has trailing whitespace.
      81: Line has trailing whitespace.
      83: Line has trailing whitespace.
      85: Line has trailing whitespace.
      87: Line has trailing whitespace.
      89: Line has trailing whitespace.
      91: Line has trailing whitespace.
      93: Line has trailing whitespace.
      95: Line has trailing whitespace.
      97: Line has trailing whitespace.
      99: Line has trailing whitespace.
     101: Line has trailing whitespace.
     103: Line has trailing whitespace.
     105: Line has trailing whitespace.
     107: Line has trailing whitespace.
     109: Line has trailing whitespace.
     111: Line has trailing whitespace.
     113: Line has trailing whitespace.
     115: Line has trailing whitespace.
     117: Line has trailing whitespace.
     119: Line has trailing whitespace.
     121: Line has trailing whitespace.
     123: Line has trailing whitespace.
     125: Line has trailing whitespace.
     127: Line has trailing whitespace.
     129: Line has trailing whitespace.
     131: Line has trailing whitespace.
     133: Line has trailing whitespace.
     135: Line has trailing whitespace.
     137: Line has trailing whitespace.
     139: Line has trailing whitespace.
     141: Line has trailing whitespace.
     143: Line has trailing whitespace.
     145: Line has trailing whitespace.
     147: Line has trailing whitespace.
     149: Line has trailing whitespace.
     151: Line has trailing whitespace.
     153: Line has trailing whitespace.
     155: Line has trailing whitespace.
     157: Line has trailing whitespace.
     159: Line has trailing whitespace.
     161: Line has trailing whitespace.
     163: Line has trailing whitespace.
     165: Line has trailing whitespace.
     167: Line has trailing whitespace.
     169: Line has trailing whitespace.
     171: Line has trailing whitespace.
     173: Line has trailing whitespace.
     175: Line has trailing whitespace.
     177: Line has trailing whitespace.
     179: Line has trailing whitespace.
     181: Line has trailing whitespace.
     183: Line has trailing whitespace.
     185: Line has trailing whitespace.
     187: Line has trailing whitespace.
     189: Line has trailing whitespace.
     191: Line has trailing whitespace.
     193: Line has trailing whitespace.
     195: Line has trailing whitespace.
     197: Line has trailing whitespace.
     199: Line has trailing whitespace.
     201: Line has trailing whitespace.
     203: Line has trailing whitespace.
     205: Line has trailing whitespace.
     207: Line has trailing whitespace.
     209: Line has trailing whitespace.
     211: Line has trailing whitespace.
     213: Line has trailing whitespace.
     215: Line has trailing whitespace.
     217: Line has trailing whitespace.
     219: Line has trailing whitespace.
     221: Line has trailing whitespace.
     223: Line has trailing whitespace.
     225: Line has trailing whitespace.
     227: Line has trailing whitespace.
     229: Line has trailing whitespace.
     231: Line has trailing whitespace.
     233: Line has trailing whitespace.
     235: Line has trailing whitespace.
     237: Line has trailing whitespace.
     239: Line has trailing whitespace.
     241: Line has trailing whitespace.
     243: Line has trailing whitespace.
     245: Line has trailing whitespace.
     247: Line has trailing whitespace.
     249: Line has trailing whitespace.
     251: Line has trailing whitespace.
     253: Line has trailing whitespace.
     255: Line has trailing whitespace.
     257: Line has trailing whitespace.
     259: Line has trailing whitespace.
     261: Line has trailing whitespace.
     263: Line has trailing whitespace.
     265: Line has trailing whitespace.
     267: Line has trailing whitespace.
     269: Line has trailing whitespace.
     271: Line has trailing whitespace.
     273: Line has trailing whitespace.
     275: Line has trailing whitespace.
     277: Line has trailing whitespace.
     279: Line has trailing whitespace.
     281: Line has trailing whitespace.
     283: Line has trailing whitespace.
     285: Line has trailing whitespace.
     287: Line has trailing whitespace.
     289: Line has trailing whitespace.
     291: Line has trailing whitespace.
     293: Line has trailing whitespace.
     295: Line has trailing whitespace.
     297: Line has trailing whitespace.
     299: Line has trailing whitespace.
     301: Line has trailing whitespace.
     303: Line has trailing whitespace.
     305: Line has trailing whitespace.
     307: Line has trailing whitespace.
     309: Line has trailing whitespace.
     311: Line has trailing whitespace.
     313: Line has trailing whitespace.
     315: Line has trailing whitespace.
     317: Line has trailing whitespace.
     319: Line has trailing whitespace.
     321: Line has trailing whitespace.
     323: Line has trailing whitespace.
     325: Line has trailing whitespace.
     327: Line has trailing whitespace.
     329: Line has trailing whitespace.
     331: Line has trailing whitespace.
     333: Line has trailing whitespace.
     335: Line has trailing whitespace.
     337: Line has trailing whitespace.
     339: Line has trailing whitespace.
     341: Line has trailing whitespace.
     343: Line has trailing whitespace.
     345: Line has trailing whitespace.
     347: Line has trailing whitespace.
     349: Line has trailing whitespace.
     351: Line has trailing whitespace.
     353: Line has trailing whitespace.
     355: Line has trailing whitespace.
     357: Line has trailing whitespace.
     359: Line has trailing whitespace.
     361: Line has trailing whitespace.
     363: Line has trailing whitespace.
     365: Line has trailing whitespace.
     367: Line has trailing whitespace.
     369: Line has trailing whitespace.
     371: Line has trailing whitespace.
     373: Line has trailing whitespace.
     375: Line has trailing whitespace.
     377: Line has trailing whitespace.
     379: Line has trailing whitespace.
     381: Line has trailing whitespace.
     383: Line has trailing whitespace.
     385: Line has trailing whitespace.
     387: Line has trailing whitespace.
     389: Line has trailing whitespace.
     391: Line has trailing whitespace.
     393: Line has trailing whitespace.
     395: Line has trailing whitespace.
     397: Line has trailing whitespace.
     399: Line has trailing whitespace.
     401: Line has trailing whitespace.
     403: Line has trailing whitespace.
     405: Line has trailing whitespace.
     407: Line has trailing whitespace.
     409: Line has trailing whitespace.
     411: Line has trailing whitespace.
     413: Line has trailing whitespace.
     415: Line has trailing whitespace.
     417: Line has trailing whitespace.
     419: Line has trailing whitespace.
     421: Line has trailing whitespace.
     423: Line has trailing whitespace.
     425: Line has trailing whitespace.
     427: Line has trailing whitespace.
     429: Line has trailing whitespace.
     431: Line has trailing whitespace.
     433: Line has trailing whitespace.
     435: Line has trailing whitespace.
     437: Line has trailing whitespace.
     439: Line has trailing whitespace.
     441: Line has trailing whitespace.
     443: Line has trailing whitespace.
     445: Line has trailing whitespace.
     447: Line has trailing whitespace.
     449: Line has trailing whitespace.
     451: Line has trailing whitespace.
     453: Line has trailing whitespace.
     455: Line has trailing whitespace.
     457: Line has trailing whitespace.
     459: Line has trailing whitespace.
     461: Line has trailing whitespace.
     463: Line has trailing whitespace.
     465: Line has trailing whitespace.
     467: Line has trailing whitespace.
     469: Line has trailing whitespace.
     471: Line has trailing whitespace.
     473: Line has trailing whitespace.
./lib/canonical/launchpad/icing/style-3-0.css.in
     144: Line exceeds 78 characters.
    1342: Line exceeds 78 characters.
    1391: Line exceeds 78 characters.
    1947: Line exceeds 78 characters.
    1963: Line exceeds 78 characters.
    1967: Line exceeds 78 characters.
    1975: Line exceeds 78 characters.
    1979: Line exceeds 78 characters.
    2007: Line exceeds 78 characters.
    2023: Line exceeds 78 characters.
    2031: Line exceeds 78 characters.
    2035: Line exceeds 78 characters.
    2039: Line exceeds 78 characters.
    2043: Line exceeds 78 characters.
    2047: Line exceeds 78 characters.
    2051: Line exceeds 78 characters.
    2064: Line exceeds 78 characters.
    2068: Line exceeds 78 characters.
    2096: Line exceeds 78 characters.
    2108: Line exceeds 78 characters.
    2112: Line exceeds 78 characters.
    2116: Line exceeds 78 characters.
    2124: Line exceeds 78 characters.
    2128: Line exceeds 78 characters.
    2136: Line exceeds 78 characters.
    2164: Line exceeds 78 characters.
    2168: Line exceeds 78 characters.
    2196: Line exceeds 78 characters.
    2204: Line exceeds 78 characters.
    2208: Line exceeds 78 characters.
    2212: Line exceeds 78 characters.
    2216: Line exceeds 78 characters.
    2221: Line exceeds 78 characters.
    2225: Line exceeds 78 characters.
    2229: Line exceeds 78 characters.
    2233: Line exceeds 78 characters.
    2237: Line exceeds 78 characters.
    2289: Line exceeds 78 characters.
    2297: Line exceeds 78 characters.
    2301: Line exceeds 78 characters.
    2309: Line exceeds 78 characters.
    2321: Line exceeds 78 characters.
    2329: Line exceeds 78 characters.
    2333: Line exceeds 78 characters.
    2341: Line exceeds 78 characters.
    2345: Line exceeds 78 characters.
    2349: Line exceeds 78 characters.
    2353: Line exceeds 78 characters.
    2357: Line exceeds 78 characters.
    2366: Line exceeds 78 characters.
    2374: Line exceeds 78 characters.
    2506: Line exceeds 78 characters.
    2507: Line exceeds 78 characters.
    2576: Line exceeds 78 characters.
    2577: Line exceeds 78 characters.
./lib/lp/code/browser/tests/test_sourcepackagerecipe.py
     467: E501 line too long (80 characters)
    1131: E501 line too long (85 characters)
    1161: E501 line too long (85 characters)
    1196: E501 line too long (85 characters)
     467: Line exceeds 78 characters.
    1131: Line exceeds 78 characters.
    1161: Line exceeds 78 characters.
    1183: Line exceeds 78 characters.
    1196: Line exceeds 78 characters.
./lib/lp/code/interfaces/sourcepackagerecipe.py
     179: E501 line too long (83 characters)
     179: Line exceeds 78 characters.

Process finished with exit code 0

-- 
https://code.launchpad.net/~wallyworld/launchpad/recipe-build-now/+merge/49968
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wallyworld/launchpad/recipe-build-now into lp:launchpad.
=== modified file 'lib/canonical/launchpad/icing/icon-sprites'
Binary files lib/canonical/launchpad/icing/icon-sprites	2010-06-11 18:39:35 +0000 and lib/canonical/launchpad/icing/icon-sprites	2011-02-16 23:37:40 +0000 differ
=== modified file 'lib/canonical/launchpad/icing/icon-sprites.positioning'
--- lib/canonical/launchpad/icing/icon-sprites.positioning	2010-10-31 20:18:45 +0000
+++ lib/canonical/launchpad/icing/icon-sprites.positioning	2011-02-16 23:37:40 +0000
@@ -3,7 +3,7 @@
 {
     "../images/arrowLeft.png": [
         0, 
-        -14754
+        -14918
     ], 
     "../images/cancel.png": [
         0, 
@@ -13,9 +13,9 @@
         0, 
         -3440
     ], 
-    "../images/zoom-out.png": [
+    "../images/build-needed.png": [
         0, 
-        -11966
+        -13442
     ], 
     "../images/team.png": [
         0, 
@@ -43,7 +43,7 @@
     ], 
     "../images/arrowTop.png": [
         0, 
-        -14426
+        -14590
     ], 
     "../images/zoom-in.png": [
         0, 
@@ -55,23 +55,23 @@
     ], 
     "../images/blue-bar.png": [
         0, 
-        -14918
+        -15082
     ], 
     "../images/arrowStart.png": [
         0, 
-        -14098
+        -14262
     ], 
     "../images/ppa-icon-inactive.png": [
         0, 
         -12458
     ], 
-    "../images/build-needed.png": [
+    "../images/zoom-out.png": [
         0, 
-        -13278
+        -11966
     ], 
     "../images/purple-bar.png": [
         0, 
-        -15246
+        -15410
     ], 
     "../images/bullet.png": [
         0, 
@@ -79,11 +79,11 @@
     ], 
     "../images/info-large.png": [
         0, 
-        -17340
+        -17504
     ], 
     "../images/trash-logo.png": [
         0, 
-        -20358
+        -20340
     ], 
     "../images/warning.png": [
         0, 
@@ -95,35 +95,35 @@
     ], 
     "../images/build-failure.png": [
         0, 
-        -13442
+        -13606
     ], 
     "../images/branch-large.png": [
         0, 
-        -16066
+        -16230
     ], 
     "../images/download-large.png": [
         0, 
-        -17158
+        -17322
     ], 
     "../images/private-large.png": [
         0, 
-        -18250
+        -18232
     ], 
     "../images/launchpad-large.png": [
         0, 
-        -17522
+        -17686
     ], 
     "../images/translation-file.png": [
         0, 
         -10818
     ], 
-    "../images/read-only.png": [
+    "../images/source-package-recipe.png": [
         0, 
-        -9998
+        -12622
     ], 
     "../images/project-logo.png": [
         0, 
-        -18860
+        -18842
     ], 
     "../images/bug-medium.png": [
         0, 
@@ -135,7 +135,7 @@
     ], 
     "../images/tour-icon": [
         0, 
-        -15902
+        -16066
     ], 
     "../images/trash-icon.png": [
         0, 
@@ -147,7 +147,7 @@
     ], 
     "../images/arrowBottom.png": [
         0, 
-        -14590
+        -14754
     ], 
     "../images/project.png": [
         0, 
@@ -173,17 +173,13 @@
         0, 
         -3768
     ], 
-    "../images/stop.png": [
-        0, 
-        -11310
-    ], 
     "../images/person-logo.png": [
         0, 
-        -19288
+        -19270
     ], 
     "../images/distribution-logo.png": [
         0, 
-        -18646
+        -18628
     ], 
     "../images/retry.png": [
         0, 
@@ -199,7 +195,7 @@
     ], 
     "../images/merge-proposal-icon.png": [
         0, 
-        -12786
+        -12950
     ], 
     "../images/download.png": [
         0, 
@@ -207,7 +203,7 @@
     ], 
     "../images/arrowDown.png": [
         0, 
-        -13934
+        -14098
     ], 
     "../images/package-binary.png": [
         0, 
@@ -219,11 +215,11 @@
     ], 
     "../images/bug-status-expand.png": [
         0, 
-        -12622
+        -12786
     ], 
     "../images/crowd-large.png": [
         0, 
-        -16430
+        -16594
     ], 
     "../images/blueprint.png": [
         0, 
@@ -245,9 +241,13 @@
         0, 
         -6228
     ], 
+    "../images/stop.png": [
+        0, 
+        -11310
+    ], 
     "../images/flame-large.png": [
         0, 
-        -16976
+        -17140
     ], 
     "../images/bug-dupe-icon.png": [
         0, 
@@ -259,11 +259,11 @@
     ], 
     "../images/build-success.png": [
         0, 
-        -13114
+        -13278
     ], 
     "../images/haspatch-icon.png": [
         0, 
-        -15738
+        -15902
     ], 
     "../images/person-inactive-badge.png": [
         0, 
@@ -279,7 +279,7 @@
     ], 
     "../images/team-logo.png": [
         0, 
-        -19716
+        -19698
     ], 
     "../images/arrowRight.png": [
         0, 
@@ -311,7 +311,7 @@
     ], 
     "../images/arrowUp.png": [
         0, 
-        -13770
+        -13934
     ], 
     "../images/distribution.png": [
         0, 
@@ -319,11 +319,11 @@
     ], 
     "../images/error-large.png": [
         0, 
-        -16794
+        -16958
     ], 
     "../images/news.png": [
         0, 
-        -15574
+        -15738
     ], 
     "../images/treeExpanded.png": [
         0, 
@@ -331,7 +331,7 @@
     ], 
     "../images/build-depwait.png": [
         0, 
-        -13606
+        -13770
     ], 
     "../images/blueprint-essential.png": [
         0, 
@@ -351,7 +351,7 @@
     ], 
     "../images/product-logo.png": [
         0, 
-        -19074
+        -19056
     ], 
     "../images/blueprint-medium.png": [
         0, 
@@ -367,11 +367,11 @@
     ], 
     "../images/launchpad-logo.png": [
         0, 
-        -18432
+        -18414
     ], 
     "../images/flame-logo.png": [
         0, 
-        -20144
+        -20126
     ], 
     "../images/translation-template.png": [
         0, 
@@ -383,7 +383,7 @@
     ], 
     "../images/meeting-logo.png": [
         0, 
-        -19930
+        -19912
     ], 
     "../images/treeCollapsed.png": [
         0, 
@@ -391,19 +391,19 @@
     ], 
     "../images/green-bar.png": [
         0, 
-        -15082
+        -15246
     ], 
     "../images/build-superseded.png": [
         0, 
-        -12950
+        -13114
     ], 
     "../images/trash-large.png": [
         0, 
-        -18068
+        -18050
     ], 
     "../images/red-bar.png": [
         0, 
-        -15410
+        -15574
     ], 
     "../images/add.png": [
         0, 
@@ -413,9 +413,13 @@
         0, 
         -328
     ], 
+    "../images/read-only.png": [
+        0, 
+        -9998
+    ], 
     "../images/person-inactive-logo.png": [
         0, 
-        -19502
+        -19484
     ], 
     "../images/edit.png": [
         0, 
@@ -427,11 +431,11 @@
     ], 
     "../images/warning-large.png": [
         0, 
-        -16248
+        -16412
     ], 
     "../images/arrowEnd.png": [
         0, 
-        -14262
+        -14426
     ], 
     "../images/cve.png": [
         0, 
@@ -439,7 +443,7 @@
     ], 
     "../images/merge-proposal-large.png": [
         0, 
-        -17886
+        -17868
     ], 
     "../images/branch.png": [
         0, 
@@ -469,4 +473,4 @@
         0, 
         -1148
     ]
-}
+}
\ No newline at end of file

=== modified file 'lib/canonical/launchpad/icing/style-3-0.css.in'
--- lib/canonical/launchpad/icing/style-3-0.css.in	2011-02-14 00:58:22 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css.in	2011-02-16 23:37:40 +0000
@@ -1471,10 +1471,10 @@
    Colors and fonts
 */
 
-h1, h2, h3, 
-table.listing thead, 
-#homepage-stats strong, 
-#application-footer strong, 
+h1, h2, h3,
+table.listing thead,
+#homepage-stats strong,
+#application-footer strong,
 #application-summary strong {
     color: #5a5a5a;
 }
@@ -2204,7 +2204,10 @@
     background-image: url(/@@/ppa-icon-inactive.png); /* sprite-ref: icon-sprites */
     background-repeat: no-repeat;
     }
-
+.source-package-recipe {
+    background-image: url(/@@/source-package-recipe.png); /* sprite-ref: icon-sprites */
+    background-repeat: no-repeat;
+    }
 .bug-status-expand {
     background-image: url(/@@/bug-status-expand.png); /* sprite-ref: icon-sprites */
     background-repeat: no-repeat;

=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml	2011-02-16 23:37:39 +0000
+++ lib/lp/code/browser/configure.zcml	2011-02-16 23:37:40 +0000
@@ -1239,6 +1239,12 @@
             name="+builds"
             template="../templates/sourcepackagerecipe-builds.pt"
             permission="launchpad.View"/>
+        <browser:page
+            for="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipe"
+            layer="lp.code.publisher.CodeLayer"
+            class="lp.code.browser.sourcepackagerecipe.SourcePackageRecipeRequestDailyBuildView"
+            name="+request-daily-build"
+            permission="launchpad.Edit"/>
     </facet>
     <facet facet="branches">
         <browser:defaultView

=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py	2011-02-16 23:37:39 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py	2011-02-16 23:37:40 +0000
@@ -174,12 +174,27 @@
 
     facet = 'branches'
 
-    links = ('request_builds',)
+    links = ('request_builds', 'request_daily_build',)
 
     def request_builds(self):
         """Provide a link for requesting builds of a recipe."""
         return Link('+request-builds', 'Request build(s)', icon='add')
 
+    def request_daily_build(self):
+        """Provide a link for requesting a daily build of a recipe."""
+        recipe = self.context
+        ppa = recipe.daily_build_archive
+        if (ppa is None or not recipe.build_daily or not recipe.is_stale
+                or not recipe.distroseries):
+            show_request_build = False
+        else:
+            has_upload = ppa.checkArchivePermission(recipe.owner)
+            show_request_build = has_upload
+
+        return Link(
+                '+request-daily-build', 'Build now',
+                enabled=show_request_build)
+
 
 class SourcePackageRecipeView(LaunchpadView):
     """Default view of a SourcePackageRecipe."""
@@ -382,6 +397,38 @@
         return builds_for_recipe(self.context)
 
 
+class SourcePackageRecipeRequestDailyBuildView(LaunchpadFormView):
+    """Supports requests to perform a daily build for a recipe.
+
+    Renders the recipe builds table so that the recipe index page can be
+    updated with the new build records.
+
+    This view works for both ajax and html form requests.
+    """
+
+    # Attributes for the html version
+    template="../../app/templates/generic-edit.pt"
+    page_title = label = "Build now"
+
+    class schema(Interface):
+        """Schema for requesting a build."""
+
+    @action('Build now', name='build')
+    def build_action(self, action, data):
+        recipe = self.context
+        recipe.performDailyBuild()
+        if self.request.is_ajax:
+            template = ViewPageTemplateFile(
+                    "../templates/sourcepackagerecipe-builds.pt")
+            return template(self)
+        else:
+            self.next_url = canonical_url(recipe)
+
+    @property
+    def builds(self):
+        return builds_for_recipe(self.context)
+
+
 class ISourcePackageEditSchema(Interface):
     """Schema for adding or editing a recipe."""
 

=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2011-02-15 11:31:56 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2011-02-16 23:37:40 +0000
@@ -25,6 +25,7 @@
 from canonical.launchpad.testing.pages import (
     extract_text,
     find_main_content,
+    find_tag_by_id,
     find_tags_by_class,
     get_feedback_messages,
     get_radio_button_text_for_field,
@@ -1288,6 +1289,89 @@
         self.assertEqual(
             [build1, build2, build3, build4, build5], view.builds)
 
+    def test_request_daily_builds_button(self):
+        """The Build now button should only be rendered when applicable."""
+
+        # Recipes that are stale and are built daily have a build now link
+        recipe = self.factory.makeSourcePackageRecipe(
+            owner=self.chef, daily_build_archive=self.ppa,
+            is_stale=True, build_daily=True)
+        browser = self.getViewBrowser(recipe)
+        build_button = find_tag_by_id(browser.contents, 'field.actions.build')
+        self.assertIsNot(None, build_button)
+
+        # Recipes that are not stale do not have a build now link
+        login(ANONYMOUS)
+        recipe = self.factory.makeSourcePackageRecipe(
+            owner=self.chef, daily_build_archive=self.ppa,
+            is_stale=False, build_daily=True)
+        browser = self.getViewBrowser(recipe)
+        build_button = find_tag_by_id(browser.contents, 'field.actions.build')
+        self.assertIs(None, build_button)
+
+        # Recipes that are not built daily do not have a build now link
+        login(ANONYMOUS)
+        recipe = self.factory.makeSourcePackageRecipe(
+            owner=self.chef, daily_build_archive=self.ppa,
+            is_stale=True, build_daily=False)
+        browser = self.getViewBrowser(recipe)
+        build_button = find_tag_by_id(browser.contents, 'field.actions.build')
+        self.assertIs(None, build_button)
+
+        # Recipes that have no daily build ppa do not have a build now link
+        login(ANONYMOUS)
+        recipe = self.factory.makeSourcePackageRecipe(
+            owner=self.chef, is_stale=True, build_daily=True)
+        naked_recipe = removeSecurityProxy(recipe)
+        naked_recipe.daily_build_archive = None
+        browser = self.getViewBrowser(recipe)
+        build_button = find_tag_by_id(browser.contents, 'field.actions.build')
+        self.assertIs(None, build_button)
+
+        # Recipes that have a daily build ppa without upload permissions
+        # do not have a build now link
+        login(ANONYMOUS)
+        distroseries = self.factory.makeSourcePackageRecipeDistroseries()
+        person = self.factory.makePerson()
+        daily_build_archive = self.factory.makeArchive(
+                distribution=distroseries.distribution, owner=person)
+        recipe = self.factory.makeSourcePackageRecipe(
+            owner=self.chef, daily_build_archive=daily_build_archive,
+            is_stale=True, build_daily=True)
+        browser = self.getViewBrowser(recipe)
+        build_button = find_tag_by_id(browser.contents, 'field.actions.build')
+        self.assertIs(None, build_button)
+
+    def test_request_daily_builds_ajax_link_not_rendered(self):
+        """The Build now link should not be rendered without javascript."""
+
+        # Recipes that are stale and are built daily have a build now link
+        recipe = self.factory.makeSourcePackageRecipe(
+            owner=self.chef, daily_build_archive=self.ppa,
+            is_stale=True, build_daily=True)
+        browser = self.getViewBrowser(recipe)
+        build_link = find_tag_by_id(browser.contents, 'request-daily-builds')
+        self.assertIs(None, build_link)
+
+    def test_request_daily_builds_action(self):
+        """Daily builds should be triggered when requested."""
+
+        recipe = self.factory.makeSourcePackageRecipe(
+            owner=self.chef, daily_build_archive=self.ppa,
+            is_stale=True, build_daily=True)
+        browser = self.getViewBrowser(recipe)
+        browser.getControl('Build now').click()
+        login(ANONYMOUS)
+        builds = recipe.getPendingBuilds()
+        build_distros = [
+            build.distroseries.displayname for build in builds]
+        build_distros.sort()
+        # Our recipe has a Warty distroseries
+        self.assertEqual(['Warty'], build_distros)
+        self.assertEqual(
+            set([2505]),
+            set(build.buildqueue_record.lastscore for build in builds))
+
     def test_request_builds_page(self):
         """Ensure the +request-builds page is sane."""
         recipe = self.makeRecipe()

=== modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py'
--- lib/lp/code/interfaces/sourcepackagerecipe.py	2011-02-14 01:48:57 +0000
+++ lib/lp/code/interfaces/sourcepackagerecipe.py	2011-02-16 23:37:40 +0000
@@ -131,6 +131,10 @@
             able to upload to the archive.
         """
 
+    @export_write_operation()
+    def performDailyBuild():
+        """Perform a build into the daily build archive."""
+
 
 class ISourcePackageRecipeEdit(Interface):
     """ISourcePackageRecipe methods that require launchpad.Edit permission."""

=== modified file 'lib/lp/code/javascript/requestbuild_overlay.js'
--- lib/lp/code/javascript/requestbuild_overlay.js	2011-02-16 23:37:39 +0000
+++ lib/lp/code/javascript/requestbuild_overlay.js	2011-02-16 23:37:40 +0000
@@ -13,7 +13,8 @@
 
 var lp_client;
 var request_build_overlay = null;
-var response_handler;
+var request_build_response_handler;
+var request_daily_build_response_handler;
 
 function set_up_lp_client() {
     if (lp_client === undefined) {
@@ -56,7 +57,7 @@
     }
 };
 
-namespace.connect_requestbuild = function() {
+namespace.connect_requestbuilds = function() {
 
     var request_build_handle = Y.one('#request-builds');
     request_build_handle.addClass('js-action');
@@ -81,26 +82,77 @@
             request_build_overlay.render();
         }
         request_build_overlay.clearError();
-        var temp_spinner = [
+        var loading_spinner = [
             '<div id="temp-spinner">',
             '<img src="/@@/spinner"/>Loading...',
             '</div>'].join('');
-        request_build_overlay.form_node.set("innerHTML", temp_spinner);
+        request_build_overlay.form_node.set("innerHTML", loading_spinner);
         request_build_overlay.loadFormContentAndRender('+builds/++form++');
         request_build_overlay.show();
     });
 
     // Wire up the processing hooks
-    response_handler = new RequestResponseHandler();
-    response_handler.clearProgressUI = function() {
+    request_build_response_handler = new RequestResponseHandler();
+    request_build_response_handler.clearProgressUI = function() {
         destroy_temporary_spinner();
     };
-    response_handler.showError = function(error) {
+    request_build_response_handler.showError = function(error) {
         request_build_overlay.showError(error);
         Y.log(error);
     };
 };
 
+namespace.connect_requestdailybuild = function() {
+
+    var request_daily_build_handle = Y.one('#request-daily-build');
+    request_daily_build_handle.on('click', function(e) {
+        e.preventDefault();
+
+        create_temporary_spinner(
+                "Requesting build...", request_daily_build_handle);
+        request_daily_build_handle.setStyle("display", "none");
+
+        var base_url = LP.client.cache.context.web_link;
+        var submit_url = base_url+"/+request-daily-build";
+        var current_builds = harvest_current_build_records();
+
+        var qs = LP.client.append_qs('', 'field.actions.build', 'Build now');
+        var y_config = {
+            method: "POST",
+            headers: {'Accept': 'application/xhtml'},
+            on: {
+                failure: request_daily_build_response_handler.getErrorHandler(
+                    function(handler, id, response) {
+                        request_daily_build_handle.setStyle(
+                                "display", "block");
+                        var server_error = 'Server error, ' +
+                                           'please contact an administrator.';
+                        handler.showError(server_error);
+                    }),
+                success:
+                    request_daily_build_response_handler.getSuccessHandler(
+                    function(handler, id, response) {
+                        display_build_records(
+                                response.responseText, current_builds)
+                    }
+                  )
+            },
+            data: qs
+        };
+        Y.io(submit_url, y_config);
+    });
+
+    // Wire up the processing hooks
+    request_daily_build_response_handler = new RequestResponseHandler();
+    request_daily_build_response_handler.clearProgressUI = function() {
+        destroy_temporary_spinner();
+    };
+    request_daily_build_response_handler.showError = function(error) {
+        alert(error);
+        Y.log(error);
+    };
+};
+
 /*
  * A function to return the current build records as displayed on the page
  */
@@ -119,14 +171,31 @@
 }
 
 /*
+ * Render build records and flash the new ones
+ */
+function display_build_records(build_records_markup, current_builds) {
+    var target = Y.one('#builds-target');
+    target.set('innerHTML', build_records_markup);
+    var new_builds = harvest_current_build_records();
+    Y.Array.each(new_builds, function(row_id) {
+        if( current_builds.indexOf(row_id)>=0 )
+            return;
+        var row = Y.one('#'+row_id);
+        var anim = Y.lazr.anim.green_flash({node: row});
+        anim.run();
+    });
+}
+
+/*
  * Perform any client side validation
  * Return: true if data is valid
  */
 function validate(data) {
     var distros = data['field.distros']
     if( Y.Object.size(distros) == 0 ) {
-        response_handler.showError("You need to specify at least one " +
-                "distro series for which to build.");
+        request_build_response_handler.showError(
+                "You need to specify at least one distro series for " +
+                "which to build.");
         return false;
     }
     return true;
@@ -138,7 +207,9 @@
 function do_request_builds(data) {
     if( !validate(data) )
         return;
-    create_temporary_spinner();
+    var spinner_location = Y.one('.yui3-lazr-formoverlay-actions');
+    create_temporary_spinner("Requesting builds...", spinner_location);
+
     var base_url = LP.client.cache.context.web_link;
     var submit_url = base_url+"/+builds";
     var current_builds = harvest_current_build_records();
@@ -146,7 +217,7 @@
         method: "POST",
         headers: {'Accept': 'application/json; application/xhtml'},
         on: {
-            failure: response_handler.getErrorHandler(
+            failure: request_build_response_handler.getErrorHandler(
                 function(handler, id, response) {
                     if( response.status >= 500 ) {
                         // There's some error content we need to display.
@@ -165,19 +236,11 @@
                         errors.push(error_info[field_name]);
                     handler.showError(errors);
                 }),
-            success: response_handler.getSuccessHandler(
+            success: request_build_response_handler.getSuccessHandler(
                 function(handler, id, response) {
                     request_build_overlay.hide();
-                    var target = Y.one('#builds-target');
-                    target.set('innerHTML', response.responseText);
-                    var new_builds = harvest_current_build_records();
-                    Y.Array.each(new_builds, function(row_id) {
-                        if( current_builds.indexOf(row_id)>=0 )
-                            return;
-                        var row = Y.one('#'+row_id);
-                        var anim = Y.lazr.anim.green_flash({node: row});
-                        anim.run();
-                    });
+                    display_build_records(
+                            response.responseText, current_builds)
                 })
         },
         form: {
@@ -191,14 +254,14 @@
 /*
  * Show the temporary "Requesting..." text
  */
-function create_temporary_spinner() {
+function create_temporary_spinner(text, node) {
     // Add the temp "Requesting build..." text
     var temp_spinner = Y.Node.create([
         '<div id="temp-spinner">',
-        '<img src="/@@/spinner"/>Requesting build...',
+        '<img src="/@@/spinner"/>',
+        text,
         '</div>'].join(''));
-    var request_build_handle = Y.one('.yui3-lazr-formoverlay-actions');
-    request_build_handle.insert(temp_spinner, request_build_handle);
+    node.insert(temp_spinner, node);
 }
 
 /*

=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
--- lib/lp/code/model/sourcepackagerecipe.py	2011-02-16 23:37:39 +0000
+++ lib/lp/code/model/sourcepackagerecipe.py	2011-02-16 23:37:40 +0000
@@ -272,6 +272,17 @@
             queue_record.manualScore(queue_record.lastscore + 100)
         return build
 
+    def performDailyBuild(self):
+        """See `ISourcePackageRecipe`."""
+        self.is_stale = False
+        for distroseries in self.distroseries:
+            try:
+                self.requestBuild(
+                    self.daily_build_archive, self.owner,
+                    distroseries, PackagePublishingPocket.RELEASE)
+            except BuildAlreadyPending:
+                continue
+
     def getBuilds(self):
         """See `ISourcePackageRecipe`."""
         where_clause = BuildFarmJob.status != BuildStatus.NEEDSBUILD

=== modified file 'lib/lp/code/templates/sourcepackagerecipe-index.pt'
--- lib/lp/code/templates/sourcepackagerecipe-index.pt	2011-02-16 23:37:39 +0000
+++ lib/lp/code/templates/sourcepackagerecipe-index.pt	2011-02-16 23:37:40 +0000
@@ -13,6 +13,17 @@
       padding-left: 2em;
     }
   </style>
+  <!-- If we have javascript, we want to show the "Build now" link.
+       Do it here so it's made visible as soon as possible.
+  -->
+  <!--<script type="text/javascript">-->
+    <!--build_link = document.getElementById('request-daily-build');-->
+    <!--alert("bb: " + build_link);-->
+    <!--if( build_link != null ) {-->
+      <!--alert('aa');-->
+      <!--build_link.setStyle('display', 'block');-->
+    <!--}-->
+  <!--</script>-->
 </metal:block>
 
 <body>
@@ -50,6 +61,25 @@
               </a>
             </dt>
             <dd tal:content="structure view/daily_build_widget"/>
+            <dd>
+              <div
+                tal:define="link context/menu:context/request_daily_build"
+                tal:condition="link/enabled"
+                >
+                <noscript>
+                  <form action="+request-daily-build"
+                        method="post"
+                        id="request-daily-build-form">
+                      <input type="submit" name="field.actions.build"
+                          id="field.actions.build" value="Build now" />
+                  </form>
+                </noscript>
+                <a id="request-daily-build" style="display: none"
+                   class="sprite source-package-recipe js-action"
+                   tal:attributes="href link/url"
+                   tal:content="link/text"/>
+              </div>
+            </dd>
           </dl>
 
           <dl id="owner">
@@ -106,11 +136,16 @@
         if(Y.UA.ie) {
             return;
         }
-
         Y.on('load', function() {
             var logged_in = LP.client.links['me'] !== undefined;
             if (logged_in) {
-                Y.lp.code.requestbuild_overlay.connect_requestbuild();
+                build_now_link = Y.one('#request-daily-build');
+                if( build_now_link != null ) {
+                  build_now_link.setStyle('display', 'block');
+                  Y.lp.code.requestbuild_overlay.connect_requestdailybuild();
+                }
+                Y.lp.code.requestbuild_overlay.connect_requestbuilds();
+
             }
         }, window);
       });"

=== modified file 'lib/lp/code/vocabularies/sourcepackagerecipe.py'
--- lib/lp/code/vocabularies/sourcepackagerecipe.py	2010-09-30 22:28:37 +0000
+++ lib/lp/code/vocabularies/sourcepackagerecipe.py	2011-02-16 23:37:40 +0000
@@ -24,7 +24,10 @@
 
 def buildable_distroseries_vocabulary(context):
     """Return a vocabulary of buildable distroseries."""
-    distros = get_buildable_distroseries_set(getUtility(ILaunchBag).user)
+    user = getUtility(ILaunchBag).user
+    if user is None:
+        return SimpleVocabulary([])
+    distros = get_buildable_distroseries_set(user)
     terms = sorted_dotted_numbers(
         [SimpleTerm(distro, distro.id, distro.displayname)
          for distro in distros],
@@ -35,7 +38,10 @@
 
 def target_ppas_vocabulary(context):
     """Return a vocabulary of ppas that the current user can target."""
-    ppas = getUtility(IArchiveSet).getPPAsForUser(getUtility(ILaunchBag).user)
+    user = getUtility(ILaunchBag).user
+    if user is None:
+        return SimpleVocabulary([])
+    ppas = getUtility(IArchiveSet).getPPAsForUser(user)
     return make_archive_vocabulary(
         ppa for ppa in ppas
         if check_permission('launchpad.Append', ppa))

=== modified file 'lib/lp/code/windmill/tests/test_recipe_request_build.py'
--- lib/lp/code/windmill/tests/test_recipe_request_build.py	2011-02-16 23:37:39 +0000
+++ lib/lp/code/windmill/tests/test_recipe_request_build.py	2011-02-16 23:37:40 +0000
@@ -50,7 +50,7 @@
             owner=self.chef, distroseries=self.squirrel, name=u'cake_recipe',
             description=u'This recipe builds a foo for disto bar, with my'
             ' Secret Squirrel changes.', branches=[cake_branch],
-            daily_build_archive=self.ppa)
+            daily_build_archive=self.ppa, build_daily=True, is_stale=True)
         transaction.commit()
         login_person(self.chef, "chef@xxxxxxxxxxx", "test", self.client)
 
@@ -94,3 +94,20 @@
                 '/ul/li'),
             validator=u'An identical build is already pending for %s %s.'
                         % (self.ppa.distribution.name, self.squirrel.name))
+
+    def test_recipe_daily_build_request(self):
+        """Request a recipe build."""
+
+        client = self.client
+        client.open(url=canonical_url(self.recipe))
+        client.waits.forElement(
+            id=u'request-daily-build', timeout=PAGE_LOAD)
+
+        # Request a daily build.
+        client.click(id=u'request-daily-build')
+
+        # Ensure it shows up.
+        client.waits.forElement(
+            xpath = (u'//tr[contains(@class, "package-build")]/td[4]'
+                     '/a[@href="%s"]') % PPAFormatterAPI(self.ppa).url(),
+            timeout=SLEEP)


Follow ups