Merge lp:~abentley/launchpad/daily-builds into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: 11004
Proposed branch: lp:~abentley/launchpad/daily-builds
Merge into: lp:launchpad
Diff against target: 502 lines (+250/-14) (has conflicts)
14 files modified
configs/testrunner/launchpad-lazr.conf (+4/-0)
cronscripts/request_daily_builds.py (+44/-0)
database/schema/security.cfg (+21/-0)
lib/canonical/config/schema-lazr.conf (+5/-0)
lib/lp/code/browser/sourcepackagerecipe.py (+6/-0)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+2/-1)
lib/lp/code/interfaces/sourcepackagerecipebuild.py (+4/-1)
lib/lp/code/model/sourcepackagerecipe.py (+32/-5)
lib/lp/code/model/sourcepackagerecipebuild.py (+21/-0)
lib/lp/code/model/tests/test_sourcepackagerecipe.py (+34/-3)
lib/lp/code/model/tests/test_sourcepackagerecipebuild.py (+12/-0)
lib/lp/code/scripts/tests/test_request_daily_builds.py (+56/-0)
lib/lp/registry/model/person.py (+1/-1)
lib/lp/testing/factory.py (+8/-3)
Text conflict in lib/lp/code/browser/sourcepackagerecipe.py
Text conflict in lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
To merge this branch: bzr merge lp:~abentley/launchpad/daily-builds
Reviewer Review Type Date Requested Status
Paul Hummer (community) code Approve
Review via email: mp+26232@code.launchpad.net

Commit message

Implement request_daily_builds script.

Description of the change

= Summary =
Fix bug #585905: Launchpad should provide daily builds

== Proposed fix ==
Implement cronscripts/request_daily_builds

== Pre-implementation notes ==
Discussed with thumper and rockstar

== Implementation details ==
As a preliminary step, stale recipes are detected according to whether the base
branch has been modified since the build completed.

Work has already begun on improving detection of stale recipes, but database
patches are required for this.

== Tests ==
bin/test -vt test_request_daily_builds -t test_makeDailyBuilds -t test_findStaleDailyBuilds_requires_build_daily -t test_findStaleDailyBuilds_for_modified_base_branch

== Demo and Q/A ==
None

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  cronscripts/request_daily_builds.py
  lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
  lib/lp/code/interfaces/sourcepackagerecipebuild.py
  lib/canonical/config/schema-lazr.conf
  lib/lp/code/scripts/tests/test_request_daily_builds.py
  database/schema/security.cfg
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/testing/factory.py
  lib/lp/code/model/sourcepackagerecipebuild.py
  lib/lp/code/browser/sourcepackagerecipe.py
  lib/lp/code/model/sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py
  configs/testrunner/launchpad-lazr.conf
  lib/lp/registry/model/person.py

== Pyflakes notices ==

cronscripts/request_daily_builds.py
    19: 'canonical' imported but unused

    ^^^ fixes circular import problem

== Pylint notices ==

cronscripts/request_daily_builds.py
    19: [W0611] Unused import canonical

    ^^^ fixes circular import problem

lib/lp/code/model/sourcepackagerecipebuild.py
    207: [W0702, SourcePackageRecipeBuild.makeDailyBuilds] No exception type(s) specified

    ^^^ Indeed; this is a catch-all exception handler.

lib/lp/registry/model/person.py
    1265: [W0104, Person.addMember] Statement seems to have no effect

    ^^^ Existing code uses this to force a flush.

To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) wrote :

This all looks good generally. However, you should merge db-devel in and run the tests again. I suspect when you merge db-devel, my sourcepackagename removal work is going to make a few headaches for you. Just a heads up.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'configs/testrunner/launchpad-lazr.conf'
--- configs/testrunner/launchpad-lazr.conf 2010-06-01 14:28:40 +0000
+++ configs/testrunner/launchpad-lazr.conf 2010-06-12 06:36:32 +0000
@@ -198,6 +198,10 @@
198oops_prefix: TAPPORTBLOB198oops_prefix: TAPPORTBLOB
199error_dir: /var/tmp/lperr.test199error_dir: /var/tmp/lperr.test
200200
201[request_daily_builds]
202oops_prefix: TRDB
203error_dir: /var/tmp/lperr.test
204
201[rosetta]205[rosetta]
202generate_templates: True206generate_templates: True
203207
204208
=== added file 'cronscripts/request_daily_builds.py'
--- cronscripts/request_daily_builds.py 1970-01-01 00:00:00 +0000
+++ cronscripts/request_daily_builds.py 2010-06-12 06:36:32 +0000
@@ -0,0 +1,44 @@
1#!/usr/bin/python -S
2#
3# Copyright 2010 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).
5
6# pylint: disable-msg=W0403
7
8"""Request builds for stale daily build recipes."""
9
10__metaclass__ = type
11
12import _pythonpath
13
14import transaction
15from zope.component import getUtility
16
17from canonical.config import config
18# fix circular import issue
19import canonical.launchpad.interfaces
20from lp.code.interfaces.sourcepackagerecipebuild import (
21 ISourcePackageRecipeBuildSource,)
22from lp.services.scripts.base import LaunchpadCronScript
23from canonical.launchpad.webapp.errorlog import globalErrorUtility
24
25
26class RequestDailyBuilds(LaunchpadCronScript):
27 """Run create merge proposal jobs."""
28
29 def __init__(self):
30 name = 'request_daily_builds'
31 dbuser = config.request_daily_builds.dbuser
32 LaunchpadCronScript.__init__(self, name, dbuser)
33
34 def main(self):
35 globalErrorUtility.configure(self.name)
36 source = getUtility(ISourcePackageRecipeBuildSource)
37 builds = source.makeDailyBuilds()
38 self.logger.info('Requested %d daily builds.' % len(builds))
39 transaction.commit()
40
41
42if __name__ == '__main__':
43 script = RequestDailyBuilds()
44 script.lock_and_run()
045
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2010-06-07 10:24:03 +0000
+++ database/schema/security.cfg 2010-06-12 06:36:32 +0000
@@ -687,6 +687,27 @@
687public.validpersoncache = SELECT687public.validpersoncache = SELECT
688public.validpersonorteamcache = SELECT688public.validpersonorteamcache = SELECT
689689
690[request-daily-builds]
691type=user
692groups=script
693public.archive = SELECT
694public.buildqueue = SELECT, INSERT, UPDATE
695public.branch = SELECT
696public.component = SELECT
697public.distroseries = SELECT
698public.distroarchseries = SELECT
699public.job = SELECT, INSERT
700public.person = SELECT
701public.processor = SELECT
702public.processorfamily = SELECT
703public.sourcepackagerecipe = SELECT
704public.sourcepackagename = SELECT
705public.sourcepackagerecipebuild = SELECT, INSERT
706public.sourcepackagerecipebuildjob = SELECT, INSERT
707public.sourcepackagerecipedata = SELECT
708public.sourcepackagerecipedistroseries = SELECT
709public.teamparticipation = SELECT
710
690[revisionkarma]711[revisionkarma]
691# Allocate karma for revisions.712# Allocate karma for revisions.
692type=user713type=user
693714
=== modified file 'lib/canonical/config/schema-lazr.conf'
--- lib/canonical/config/schema-lazr.conf 2010-06-04 07:20:19 +0000
+++ lib/canonical/config/schema-lazr.conf 2010-06-12 06:36:32 +0000
@@ -1646,6 +1646,11 @@
1646# See [error_reports].1646# See [error_reports].
1647copy_to_zlog: false1647copy_to_zlog: false
16481648
1649[request_daily_builds]
1650dbuser: request-daily-builds
1651error_dir: none
1652oops_prefix: none
1653copy_to_zlog: false
16491654
1650[revisionkarma]1655[revisionkarma]
1651# The database user which will be used by this process.1656# The database user which will be used by this process.
16521657
=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py 2010-06-11 05:05:52 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2010-06-12 06:36:32 +0000
@@ -353,6 +353,7 @@
353 def request_action(self, action, data):353 def request_action(self, action, data):
354 parser = RecipeParser(data['recipe_text'])354 parser = RecipeParser(data['recipe_text'])
355 recipe = parser.parse()355 recipe = parser.parse()
356<<<<<<< TREE
356 try:357 try:
357 source_package_recipe = getUtility(358 source_package_recipe = getUtility(
358 ISourcePackageRecipeSource).new(359 ISourcePackageRecipeSource).new(
@@ -365,6 +366,11 @@
365 'The bzr-builder instruction "run" is not permitted here.')366 'The bzr-builder instruction "run" is not permitted here.')
366 return367 return
367368
369=======
370 source_package_recipe = getUtility(ISourcePackageRecipeSource).new(
371 self.user, self.user, data['name'], recipe, data['description'],
372 data['distros'])
373>>>>>>> MERGE-SOURCE
368 self.next_url = canonical_url(source_package_recipe)374 self.next_url = canonical_url(source_package_recipe)
369375
370 def validate(self, data):376 def validate(self, data):
371377
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-11 19:03:35 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-12 06:36:32 +0000
@@ -56,7 +56,8 @@
56 return self.factory.makeSourcePackageRecipe(56 return self.factory.makeSourcePackageRecipe(
57 owner=self.chef, distroseries=self.squirrel, name=u'cake_recipe',57 owner=self.chef, distroseries=self.squirrel, name=u'cake_recipe',
58 description=u'This recipe builds a foo for disto bar, with my'58 description=u'This recipe builds a foo for disto bar, with my'
59 ' Secret Squirrel changes.', branches=[cake_branch])59 ' Secret Squirrel changes.', branches=[cake_branch],
60 daily_build_archive=self.ppa)
6061
61 def getMainText(self, recipe, view_name=None):62 def getMainText(self, recipe, view_name=None):
62 """Return the main text of a recipe page, as seen by Chef."""63 """Return the main text of a recipe page, as seen by Chef."""
6364
=== modified file 'lib/lp/code/interfaces/sourcepackagerecipebuild.py'
--- lib/lp/code/interfaces/sourcepackagerecipebuild.py 2010-05-28 03:27:18 +0000
+++ lib/lp/code/interfaces/sourcepackagerecipebuild.py 2010-06-12 06:36:32 +0000
@@ -1,7 +1,7 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=E02134# pylint: disable-msg=E0213,E0211
55
6"""Interfaces for source package builds."""6"""Interfaces for source package builds."""
77
@@ -83,6 +83,9 @@
83 :return: `ISourcePackageRecipeBuild`.83 :return: `ISourcePackageRecipeBuild`.
84 """84 """
8585
86 def makeDailyBuilds():
87 """Create and return builds for stale ISourcePackageRecipes."""
88
86 def getById(build_id):89 def getById(build_id):
87 """Return the `ISourcePackageRecipeBuild` for the given build id.90 """Return the `ISourcePackageRecipeBuild` for the given build id.
8891
8992
=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
--- lib/lp/code/model/sourcepackagerecipe.py 2010-06-11 05:05:52 +0000
+++ lib/lp/code/model/sourcepackagerecipe.py 2010-06-12 06:36:32 +0000
@@ -14,7 +14,8 @@
14from lazr.delegates import delegates14from lazr.delegates import delegates
1515
16from storm.locals import (16from storm.locals import (
17 Bool, Desc, Int, Reference, ReferenceSet, Store, Storm, Unicode)17 And, Bool, Desc, Int, Not, Reference, ReferenceSet, Select, Store, Storm,
18 Unicode)
1819
19from zope.component import getUtility20from zope.component import getUtility
20from zope.interface import classProvides, implements21from zope.interface import classProvides, implements
@@ -29,6 +30,7 @@
29 ISourcePackageRecipeData)30 ISourcePackageRecipeData)
30from lp.code.interfaces.sourcepackagerecipebuild import (31from lp.code.interfaces.sourcepackagerecipebuild import (
31 ISourcePackageRecipeBuildSource)32 ISourcePackageRecipeBuildSource)
33from lp.code.model.branch import Branch
32from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild34from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild
33from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData35from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData
34from lp.registry.model.distroseries import DistroSeries36from lp.registry.model.distroseries import DistroSeries
@@ -47,7 +49,10 @@
47 __storm_table__ = "SourcePackageRecipeDistroSeries"49 __storm_table__ = "SourcePackageRecipeDistroSeries"
48 id = Int(primary=True)50 id = Int(primary=True)
49 sourcepackagerecipe_id = Int(name='sourcepackagerecipe', allow_none=False)51 sourcepackagerecipe_id = Int(name='sourcepackagerecipe', allow_none=False)
52 sourcepackage_recipe = Reference(
53 sourcepackagerecipe_id, 'SourcePackageRecipe.id')
50 distroseries_id = Int(name='distroseries', allow_none=False)54 distroseries_id = Int(name='distroseries', allow_none=False)
55 distroseries = Reference(distroseries_id, 'DistroSeries.id')
5156
5257
53class SourcePackageRecipe(Storm):58class SourcePackageRecipe(Storm):
@@ -119,8 +124,8 @@
119 return str(self.builder_recipe)124 return str(self.builder_recipe)
120125
121 @staticmethod126 @staticmethod
122 def new(registrant, owner, distroseries, name, builder_recipe,127 def new(registrant, owner, name, builder_recipe, description,
123 description):128 distroseries=None, daily_build_archive=None, build_daily=False):
124 """See `ISourcePackageRecipeSource.new`."""129 """See `ISourcePackageRecipeSource.new`."""
125 store = IMasterStore(SourcePackageRecipe)130 store = IMasterStore(SourcePackageRecipe)
126 sprecipe = SourcePackageRecipe()131 sprecipe = SourcePackageRecipe()
@@ -128,12 +133,34 @@
128 sprecipe.registrant = registrant133 sprecipe.registrant = registrant
129 sprecipe.owner = owner134 sprecipe.owner = owner
130 sprecipe.name = name135 sprecipe.name = name
131 for distroseries_item in distroseries:136 if distroseries is not None:
132 sprecipe.distroseries.add(distroseries_item)137 for distroseries_item in distroseries:
138 sprecipe.distroseries.add(distroseries_item)
133 sprecipe.description = description139 sprecipe.description = description
140 sprecipe.daily_build_archive = daily_build_archive
141 sprecipe.build_daily = build_daily
134 store.add(sprecipe)142 store.add(sprecipe)
135 return sprecipe143 return sprecipe
136144
145 @classmethod
146 def findStaleDailyBuilds(cls):
147 store = IStore(cls)
148 # Distroseries which have been built since the base branch was
149 # modified.
150 up_to_date_distroseries = Select(
151 SourcePackageRecipeBuild.distroseries_id,
152 And(
153 SourcePackageRecipeData.sourcepackage_recipe_id ==
154 SourcePackageRecipeBuild.recipe_id,
155 SourcePackageRecipeData.base_branch_id == Branch.id,
156 Branch.date_last_modified <
157 SourcePackageRecipeBuild.datebuilt))
158 return store.find(
159 _SourcePackageRecipeDistroSeries, cls.build_daily == True,
160 _SourcePackageRecipeDistroSeries.sourcepackagerecipe_id == cls.id,
161 Not(_SourcePackageRecipeDistroSeries.distroseries_id.is_in(
162 up_to_date_distroseries)))
163
137 @staticmethod164 @staticmethod
138 def exists(owner, name):165 def exists(owner, name):
139 """See `ISourcePackageRecipeSource.new`."""166 """See `ISourcePackageRecipeSource.new`."""
140167
=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py 2010-06-10 15:25:52 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py 2010-06-12 06:36:32 +0000
@@ -11,6 +11,7 @@
11 ]11 ]
1212
13import datetime13import datetime
14import sys
1415
15from pytz import utc16from pytz import utc
1617
@@ -26,6 +27,7 @@
26from zope.component import getUtility27from zope.component import getUtility
27from zope.interface import classProvides, implements28from zope.interface import classProvides, implements
2829
30from canonical.launchpad.webapp import errorlog
29from lp.buildmaster.interfaces.buildbase import BuildStatus, IBuildBase31from lp.buildmaster.interfaces.buildbase import BuildStatus, IBuildBase
30from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType32from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
31from lp.buildmaster.model.buildbase import BuildBase33from lp.buildmaster.model.buildbase import BuildBase
@@ -196,6 +198,25 @@
196 store.add(spbuild)198 store.add(spbuild)
197 return spbuild199 return spbuild
198200
201 @staticmethod
202 def makeDailyBuilds():
203 from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
204 candidates = SourcePackageRecipe.findStaleDailyBuilds()
205 builds = []
206 for candidate in candidates:
207 recipe = candidate.sourcepackage_recipe
208 try:
209 build = recipe.requestBuild(recipe.daily_build_archive,
210 recipe.owner, candidate.distroseries,
211 PackagePublishingPocket.RELEASE)
212 except:
213 info = sys.exc_info()
214 errorlog.globalErrorUtility.raising(info)
215 else:
216 builds.append(build)
217
218 return builds
219
199 def destroySelf(self):220 def destroySelf(self):
200 store = Store.of(self)221 store = Store.of(self)
201 job = self.buildqueue_record.job222 job = self.buildqueue_record.job
202223
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-06-11 05:05:52 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-06-12 06:36:32 +0000
@@ -38,7 +38,7 @@
38from lp.code.model.sourcepackagerecipebuild import (38from lp.code.model.sourcepackagerecipebuild import (
39 SourcePackageRecipeBuildJob)39 SourcePackageRecipeBuildJob)
40from lp.code.model.sourcepackagerecipe import (40from lp.code.model.sourcepackagerecipe import (
41 NonPPABuildRequest)41 NonPPABuildRequest, SourcePackageRecipe)
42from lp.registry.interfaces.pocket import PackagePublishingPocket42from lp.registry.interfaces.pocket import PackagePublishingPocket
43from lp.services.job.interfaces.job import (43from lp.services.job.interfaces.job import (
44 IJob, JobStatus)44 IJob, JobStatus)
@@ -354,6 +354,37 @@
354 # Show no database constraints were violated354 # Show no database constraints were violated
355 Store.of(recipe).flush()355 Store.of(recipe).flush()
356356
357 def test_findStaleDailyBuilds_requires_build_daily(self):
358 recipe = self.factory.makeSourcePackageRecipe()
359 self.assertContentEqual(
360 [], SourcePackageRecipe.findStaleDailyBuilds())
361 removeSecurityProxy(recipe).build_daily = True
362 self.assertEqual([recipe],
363 [sprd.sourcepackage_recipe for sprd
364 in SourcePackageRecipe.findStaleDailyBuilds()])
365
366 def test_findStaleDailyBuilds_for_modified_base_branch(self):
367 recipe = self.factory.makeSourcePackageRecipe(build_daily=True)
368 now = self.factory.getUniqueDate()
369 self.assertEqual([recipe],
370 [sprd.sourcepackage_recipe for sprd
371 in SourcePackageRecipe.findStaleDailyBuilds()])
372 recipe.base_branch.date_last_modified = now - timedelta(hours=1)
373 build = recipe.requestBuild(
374 archive=recipe.daily_build_archive, requester=recipe.owner,
375 distroseries=list(recipe.distroseries)[0],
376 pocket=PackagePublishingPocket.RELEASE)
377 removeSecurityProxy(build).datebuilt = now
378 self.assertContentEqual(
379 [], SourcePackageRecipe.findStaleDailyBuilds())
380 distro2 = self.factory.makeDistroSeries()
381 distro3 = self.factory.makeDistroSeries()
382 recipe.distroseries.add(distro2)
383 recipe.distroseries.add(distro3)
384 self.assertContentEqual(set([(recipe, distro2), (recipe, distro3)]),
385 set((sprd.sourcepackage_recipe, sprd.distroseries) for sprd
386 in SourcePackageRecipe.findStaleDailyBuilds()))
387
357 def test_getMedianBuildDuration(self):388 def test_getMedianBuildDuration(self):
358 recipe = removeSecurityProxy(self.factory.makeSourcePackageRecipe())389 recipe = removeSecurityProxy(self.factory.makeSourcePackageRecipe())
359 self.assertIs(None, recipe.getMedianBuildDuration())390 self.assertIs(None, recipe.getMedianBuildDuration())
@@ -584,8 +615,8 @@
584 distroseries = ws_object(launchpad, db_distroseries)615 distroseries = ws_object(launchpad, db_distroseries)
585 ws_owner = ws_object(launchpad, owner)616 ws_owner = ws_object(launchpad, owner)
586 recipe = ws_owner.createRecipe(617 recipe = ws_owner.createRecipe(
587 name='toaster-1', description='a recipe',618 name='toaster-1', description='a recipe', recipe_text=recipe_text,
588 distroseries=[distroseries.self_link], recipe_text=recipe_text)619 distroseries=[distroseries.self_link])
589 # at the moment, distroseries is not exposed in the API.620 # at the moment, distroseries is not exposed in the API.
590 transaction.commit()621 transaction.commit()
591 db_recipe = owner.getRecipe(name=u'toaster-1')622 db_recipe = owner.getRecipe(name=u'toaster-1')
592623
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-06-10 15:25:52 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-06-12 06:36:32 +0000
@@ -201,6 +201,14 @@
201 Store.of(binary).flush()201 Store.of(binary).flush()
202 self.assertEqual([binary], list(spb.binary_builds))202 self.assertEqual([binary], list(spb.binary_builds))
203203
204 def test_makeDailyBuilds(self):
205 self.assertEqual([],
206 SourcePackageRecipeBuild.makeDailyBuilds())
207 recipe = self.factory.makeSourcePackageRecipe(build_daily=True)
208 build = SourcePackageRecipeBuild.makeDailyBuilds()[0]
209 self.assertEqual(recipe, build.recipe)
210 self.assertEqual(list(recipe.distroseries), [build.distroseries])
211
204 def test_getRecentBuilds(self):212 def test_getRecentBuilds(self):
205 """Recent builds match the same person, series and receipe.213 """Recent builds match the same person, series and receipe.
206214
@@ -231,6 +239,7 @@
231 removeSecurityProxy(recent_build).datecreated += a_second239 removeSecurityProxy(recent_build).datecreated += a_second
232 self.assertContentEqual([recent_build], get_recent())240 self.assertContentEqual([recent_build], get_recent())
233241
242<<<<<<< TREE
234class TestAsBuildmaster(TestCaseWithFactory):243class TestAsBuildmaster(TestCaseWithFactory):
235244
236 layer = LaunchpadZopelessLayer245 layer = LaunchpadZopelessLayer
@@ -282,6 +291,9 @@
282 assertNotifyOnce('OK', prepare_build())291 assertNotifyOnce('OK', prepare_build())
283292
284293
294=======
295
296>>>>>>> MERGE-SOURCE
285class MakeSPRecipeBuildMixin:297class MakeSPRecipeBuildMixin:
286 """Provide the common makeBuild method returning a queued build."""298 """Provide the common makeBuild method returning a queued build."""
287299
288300
=== added file 'lib/lp/code/scripts/tests/test_request_daily_builds.py'
--- lib/lp/code/scripts/tests/test_request_daily_builds.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/scripts/tests/test_request_daily_builds.py 2010-06-12 06:36:32 +0000
@@ -0,0 +1,56 @@
1#! /usr/bin/python2.5
2#
3# Copyright 2010 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).
5
6"""Test the request_daily_builds script"""
7
8import unittest
9import transaction
10
11from canonical.testing import ZopelessAppServerLayer
12from canonical.launchpad.scripts.tests import run_script
13from canonical.launchpad.webapp.errorlog import ErrorReportingUtility
14from lp.soyuz.interfaces.archive import ArchivePurpose
15from lp.testing import TestCaseWithFactory
16
17
18class TestRequestDailyBuilds(TestCaseWithFactory):
19
20 layer = ZopelessAppServerLayer
21
22 def test_request_daily_builds(self):
23 """Ensure the request_daily_builds script requests daily builds."""
24 prod_branch = self.factory.makeProductBranch()
25 prod_recipe = self.factory.makeSourcePackageRecipe(build_daily=True,
26 branches=[prod_branch])
27 pack_branch = self.factory.makePackageBranch()
28 pack_recipe = self.factory.makeSourcePackageRecipe(build_daily=True,
29 branches=[pack_branch])
30 self.assertEqual(0, prod_recipe.getBuilds(True).count())
31 self.assertEqual(0, pack_recipe.getBuilds(True).count())
32 transaction.commit()
33 retcode, stdout, stderr = run_script(
34 'cronscripts/request_daily_builds.py', [])
35 self.assertIn('Requested 2 daily builds.', stderr)
36 self.assertEqual(1, prod_recipe.getBuilds(True).count())
37 self.assertEqual(1, pack_recipe.getBuilds(True).count())
38
39 def test_request_daily_builds_oops(self):
40 """Ensure errors are handled cleanly."""
41 archive = self.factory.makeArchive(purpose=ArchivePurpose.COPY)
42 recipe = self.factory.makeSourcePackageRecipe(
43 daily_build_archive=archive, build_daily=True)
44 transaction.commit()
45 retcode, stdout, stderr = run_script(
46 'cronscripts/request_daily_builds.py', [])
47 self.assertEqual(0, recipe.getBuilds(True).count())
48 self.assertIn('Requested 0 daily builds.', stderr)
49 utility = ErrorReportingUtility()
50 utility.configure('request_daily_builds')
51 oops = utility.getLastOopsReport()
52 self.assertIn('NonPPABuildRequest', oops.tb_text)
53
54
55def test_suite():
56 return unittest.TestLoader().loadTestsFromName(__name__)
057
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2010-05-22 01:42:59 +0000
+++ lib/lp/registry/model/person.py 2010-06-12 06:36:32 +0000
@@ -2269,7 +2269,7 @@
2269 builder_recipe = RecipeParser(recipe_text).parse()2269 builder_recipe = RecipeParser(recipe_text).parse()
2270 spnset = getUtility(ISourcePackageNameSet)2270 spnset = getUtility(ISourcePackageNameSet)
2271 return SourcePackageRecipe.new(2271 return SourcePackageRecipe.new(
2272 registrant, self, distroseries, name, builder_recipe, description)2272 registrant, self, name, builder_recipe, description, distroseries)
22732273
2274 def getRecipe(self, name):2274 def getRecipe(self, name):
2275 from lp.code.model.sourcepackagerecipe import SourcePackageRecipe2275 from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
22762276
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-06-12 04:06:45 +0000
+++ lib/lp/testing/factory.py 2010-06-12 06:36:32 +0000
@@ -1765,8 +1765,9 @@
1765 return parser.parse()1765 return parser.parse()
17661766
1767 def makeSourcePackageRecipe(self, registrant=None, owner=None,1767 def makeSourcePackageRecipe(self, registrant=None, owner=None,
1768 distroseries=None, name=None, description=None,1768 distroseries=None, name=None,
1769 branches=()):1769 description=None, branches=(),
1770 build_daily=False, daily_build_archive=None):
1770 """Make a `SourcePackageRecipe`."""1771 """Make a `SourcePackageRecipe`."""
1771 if registrant is None:1772 if registrant is None:
1772 registrant = self.makePerson()1773 registrant = self.makePerson()
@@ -1782,9 +1783,13 @@
1782 name = self.getUniqueString().decode('utf8')1783 name = self.getUniqueString().decode('utf8')
1783 if description is None:1784 if description is None:
1784 description = self.getUniqueString().decode('utf8')1785 description = self.getUniqueString().decode('utf8')
1786 if daily_build_archive is None:
1787 daily_build_archive = self.makeArchive(
1788 distribution=distroseries.distribution, owner=owner)
1785 recipe = self.makeRecipe(*branches)1789 recipe = self.makeRecipe(*branches)
1786 source_package_recipe = getUtility(ISourcePackageRecipeSource).new(1790 source_package_recipe = getUtility(ISourcePackageRecipeSource).new(
1787 registrant, owner, [distroseries], name, recipe, description)1791 registrant, owner, name, recipe, description, [distroseries],
1792 daily_build_archive, build_daily)
1788 IStore(source_package_recipe).flush()1793 IStore(source_package_recipe).flush()
1789 return source_package_recipe1794 return source_package_recipe
17901795