Merge lp:~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context into lp:launchpad/db-devel
- 536700-present-packagebuilds-in-ppa-context
- Merge into db-devel
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 9485 | ||||
Proposed branch: | lp:~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context | ||||
Merge into: | lp:launchpad/db-devel | ||||
Diff against target: |
1137 lines (+424/-145) (has conflicts) 28 files modified
lib/canonical/buildd/apply-ogre-model (+0/-39) lib/canonical/buildd/binarypackage.py (+7/-17) lib/canonical/buildd/debian.py (+1/-40) lib/canonical/buildd/debian/changelog (+7/-0) lib/canonical/buildd/debian/rules (+2/-2) lib/canonical/buildd/debian/upgrade-config (+11/-0) lib/canonical/buildd/template-buildd-slave.conf (+0/-1) lib/canonical/buildd/tests/buildd-slave-test.conf (+0/-1) lib/canonical/launchpad/templates/launchpad-form.pt (+6/-3) lib/canonical/widgets/product.py (+3/-1) lib/lp/buildmaster/configure.zcml (+6/-0) lib/lp/buildmaster/interfaces/packagebuild.py (+26/-1) lib/lp/buildmaster/model/packagebuild.py (+45/-2) lib/lp/buildmaster/tests/test_packagebuild.py (+49/-6) lib/lp/code/browser/sourcepackagerecipe.py (+12/-3) lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+56/-7) lib/lp/code/model/sourcepackagerecipedata.py (+2/-0) lib/lp/registry/doc/product-widgets.txt (+45/-2) lib/lp/registry/model/sourcepackage.py (+5/-3) lib/lp/soyuz/configure.zcml (+5/-0) lib/lp/soyuz/interfaces/archive.py (+6/-0) lib/lp/soyuz/interfaces/buildrecords.py (+11/-6) lib/lp/soyuz/model/archive.py (+14/-4) lib/lp/soyuz/model/binarypackagebuild.py (+21/-0) lib/lp/soyuz/tests/test_archive.py (+1/-1) lib/lp/soyuz/tests/test_binarypackagebuild.py (+37/-0) lib/lp/soyuz/tests/test_hasbuildrecords.py (+31/-0) lib/lp/testing/factory.py (+15/-6) Text conflict in lib/lp/code/browser/tests/test_sourcepackagerecipe.py |
||||
To merge this branch: | bzr merge lp:~michael.nelson/launchpad/536700-present-packagebuilds-in-ppa-context | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Henning Eggers (community) | code | Approve | |
Review via email: mp+27477@code.launchpad.net |
Commit message
Description of the change
Please review my branch:
bzr+
revision 11006.
Test command: bin/test -vv -m test_packagebuild -m test_binarypack
test_hasbuildre
This branch does some initial work to enable PPA's to present general
IPackageBuilds rather than IBinaryPackageB
It adds and tests an IPackageBuildSe
optional param to the getBuildRecords() method - binary_only - which defaults
to true (current behaivour). It then updates IArchive.
support binary_only=False.
Additionally, it adds an adapter for IBuildFarmJob-
preparation for the UI (so each build can present its title correctly).
Michael Nelson (michael.nelson) wrote : | # |
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Hi Michael,
> thank you for your fix and sorry for the delay...
No problem... it was worth all the improvements below :)
>
> Am 14.06.2010 10:35, schrieb Michael Nelson:
> > Test command: bin/test -vv -m test_packagebuild -m test_binarypack
> -m
> > test_hasbuildre
>
> Yup, they pass. Thanks.
>
> > This branch does some initial work to enable PPA's to present general
> > IPackageBuilds rather than IBinaryPackageB
>
> So, I assume that is the reason that no bug is attached to this branch?
There are two now :P
>
> >
> > It adds and tests an IPackageBuildSe
> > optional param to the getBuildRecords() method - binary_only - which
> defaults
> > to true (current behaivour). It then updates IArchive.
> > support binary_only=False.
> >
> > Additionally, it adds an adapter for IBuildFarmJob-
> > preparation for the UI (so each build can present its title correctly).
> >
>
> Very clever. ;-)
>
> Here are my comments:
>
...
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -214,3 +218,40 @@
> > def _handleStatus_
> > return BuildBase.
> > self, librarian, slave_status, logger)
> > +
> > +
> > +class PackageBuildSet:
> > + implements(
> > +
> > + def getBuildsForArc
> > + """See `IPackageBuildS
> > +
> > + extra_exprs = []
> > +
> > + # Add query clause that filters on build status if the latter is
> > + # provided.
>
> I had been told that comments are not there to reiterate what the code is
> doing - the code should speak for itself. This looks like such a case, does it
> not?
/me must stop doing that. Removed.
>
> > + if status is not None:
> > + extra_exprs.
> > +
> > + # Add query clause that filters on pocket if the latter is
> provided.
>
> Same here. The comment is just chatter. Maybe its pseudo code that you forgot
> in here?
Ditto.
>
> > + if pocket:
> > + extra_exprs.
> > +
> > + store = getUtility(
> > + result_set = store.find(
> > + PackageBuild.
> > + PackageBuild.
> > + *extra_exprs)
> > +
> > + # Ordering according status
> > + # * SUPERSEDED & All by -datecreated
>
> What does "& All" refer to"?
I updated this to: When we have a set of builds that may include pending or superseded builds, we order by -date_created (as we won't always have a date_finished), otherwise we can order by -date_finished.
>
> > + # * FULLYBUILT & FAILURES by -datebuilt
> I would put this near the "else" to expla...
Henning Eggers (henninge) wrote : | # |
As discussed on mumble:
- Please make sure the indention is less confusing by using an intermediate variable for the states, e.g. "unfinished_
+ if status is None or status in [
+ BuildStatus.
+ BuildStatus.
+ BuildStatus.
- AFAICT PackageBuildSet is missing from __all__ (not TestPackageBuil
Thanks for you good work and patience with me. ;-)
Preview Diff
1 | === removed file 'lib/canonical/buildd/apply-ogre-model' |
2 | --- lib/canonical/buildd/apply-ogre-model 2009-06-30 21:06:27 +0000 |
3 | +++ lib/canonical/buildd/apply-ogre-model 1970-01-01 00:00:00 +0000 |
4 | @@ -1,39 +0,0 @@ |
5 | -#!/bin/sh |
6 | -# |
7 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
8 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
9 | -# |
10 | -# Author: Daniel Silverstone <daniel.silverstone@canonical.com> |
11 | - |
12 | -# Buildd Slave tool to apply the ogre model to the chroot |
13 | - |
14 | -# Expects build id as arg 1, makes build-id to contain the build |
15 | -# Expects ogre component name as arg 2 |
16 | - |
17 | -# Needs SUDO to be set to a sudo instance for passwordless access |
18 | -# LN must be an ln instance which can take -s and -f at the same time |
19 | - |
20 | -SUDO=/usr/bin/sudo |
21 | - |
22 | -BUILDID="$1" |
23 | -COMPONENT="$2" |
24 | - |
25 | -set -e |
26 | - |
27 | -exec 2>&1 |
28 | - |
29 | -cd $HOME |
30 | -cd "build-$BUILDID/chroot-autobuild/etc/apt" |
31 | - |
32 | -echo "Attempting OGRE for $COMPONENT in build-$BUILDID" |
33 | - |
34 | -if [ -e "sources.list.$COMPONENT" ]; then |
35 | - if [ -h "sources.list" ]; then |
36 | - $SUDO ln -sf "sources.list.$COMPONENT" sources.list |
37 | - else |
38 | - echo "OGRE sources.list found, but sources.list is not a symlink" |
39 | - fi |
40 | -else |
41 | - echo "No OGRE sources.list found." |
42 | -fi |
43 | - |
44 | |
45 | === modified file 'lib/canonical/buildd/binarypackage.py' |
46 | --- lib/canonical/buildd/binarypackage.py 2010-01-14 02:55:22 +0000 |
47 | +++ lib/canonical/buildd/binarypackage.py 2010-06-16 14:06:25 +0000 |
48 | @@ -55,22 +55,12 @@ |
49 | self._dscfile = f |
50 | if self._dscfile is None: |
51 | raise ValueError, files |
52 | - if 'arch_indep' in extra_args: |
53 | - self.arch_indep = extra_args['arch_indep'] |
54 | - else: |
55 | - self.arch_indep = False |
56 | - if 'suite' in extra_args: |
57 | - self.suite = extra_args['suite'] |
58 | - else: |
59 | - self.suite = False |
60 | - if 'archive_purpose' in extra_args: |
61 | - self.archive_purpose = extra_args['archive_purpose'] |
62 | - else: |
63 | - self.archive_purpose = False |
64 | - if 'build_debug_symbols' in extra_args: |
65 | - self.build_debug_symbols = extra_args['build_debug_symbols'] |
66 | - else: |
67 | - self.build_debug_symbols = False |
68 | + |
69 | + self.archive_purpose = extra_args.get('archive_purpose') |
70 | + self.suite = extra_args.get('suite') |
71 | + self.component = extra_args['ogrecomponent'] |
72 | + self.arch_indep = extra_args.get('arch_indep', False) |
73 | + self.build_debug_symbols = extra_args.get('build_debug_symbols', False) |
74 | |
75 | super(BinaryPackageBuildManager, self).initiate( |
76 | files, chroot, extra_args) |
77 | @@ -92,7 +82,7 @@ |
78 | args.extend(["--purpose=" + self.archive_purpose]) |
79 | if self.build_debug_symbols: |
80 | args.extend(["--build-debug-symbols"]) |
81 | - args.extend(["--comp=" + self.ogre]) |
82 | + args.extend(["--comp=" + self.component]) |
83 | args.extend([self._dscfile]) |
84 | self.runSubProcess( self._sbuildpath, args ) |
85 | |
86 | |
87 | === modified file 'lib/canonical/buildd/debian.py' |
88 | --- lib/canonical/buildd/debian.py 2010-01-14 20:19:27 +0000 |
89 | +++ lib/canonical/buildd/debian.py 2010-06-16 14:06:25 +0000 |
90 | @@ -20,7 +20,6 @@ |
91 | INIT = "INIT" |
92 | UNPACK = "UNPACK" |
93 | MOUNT = "MOUNT" |
94 | - OGRE = "OGRE" |
95 | SOURCES = "SOURCES" |
96 | UPDATE = "UPDATE" |
97 | REAP = "REAP" |
98 | @@ -35,7 +34,6 @@ |
99 | BuildManager.__init__(self, slave, buildid) |
100 | self._updatepath = slave._config.get("debianmanager", "updatepath") |
101 | self._scanpath = slave._config.get("debianmanager", "processscanpath") |
102 | - self._ogrepath = slave._config.get("debianmanager", "ogrepath") |
103 | self._sourcespath = slave._config.get("debianmanager", "sourcespath") |
104 | self._cachepath = slave._config.get("slave","filecache") |
105 | self._state = DebianBuildState.INIT |
106 | @@ -49,32 +47,15 @@ |
107 | def initiate(self, files, chroot, extra_args): |
108 | """Initiate a build with a given set of files and chroot.""" |
109 | |
110 | - if 'ogrecomponent' in extra_args: |
111 | - # Ubuntu refers to the concept that "main sees only main |
112 | - # while building" etc as "The Ogre Model" (onions, layers |
113 | - # and all). If we're given an ogre component, use it |
114 | - self.ogre = extra_args['ogrecomponent'] |
115 | - else: |
116 | - self.ogre = False |
117 | - if 'archives' in extra_args and extra_args['archives']: |
118 | - self.sources_list = extra_args['archives'] |
119 | - else: |
120 | - self.sources_list = None |
121 | + self.sources_list = extra_args.get('archives') |
122 | |
123 | BuildManager.initiate(self, files, chroot, extra_args) |
124 | |
125 | - def doOgreModel(self): |
126 | - """Perform the ogre model activation.""" |
127 | - self.runSubProcess(self._ogrepath, |
128 | - ["apply-ogre-model", self._buildid, self.ogre]) |
129 | - |
130 | def doSourcesList(self): |
131 | """Override apt/sources.list. |
132 | |
133 | Mainly used for PPA builds. |
134 | """ |
135 | - # XXX cprov 2007-05-17: It 'undo' ogre-component changes. |
136 | - # for PPAs it must be re-implemented on builddmaster side. |
137 | args = ["override-sources-list", self._buildid] |
138 | args.extend(self.sources_list) |
139 | self.runSubProcess(self._sourcespath, args) |
140 | @@ -181,26 +162,6 @@ |
141 | self._state = DebianBuildState.UMOUNT |
142 | self.doUnmounting() |
143 | else: |
144 | - # Run OGRE if we need to, else run UPDATE |
145 | - if self.ogre: |
146 | - self._state = DebianBuildState.OGRE |
147 | - self.doOgreModel() |
148 | - elif self.sources_list is not None: |
149 | - self._state = DebianBuildState.SOURCES |
150 | - self.doSourcesList() |
151 | - else: |
152 | - self._state = DebianBuildState.UPDATE |
153 | - self.doUpdateChroot() |
154 | - |
155 | - def iterate_OGRE(self, success): |
156 | - """Just finished running the ogre applicator.""" |
157 | - if success != 0: |
158 | - if not self.alreadyfailed: |
159 | - self._slave.chrootFail() |
160 | - self.alreadyfailed = True |
161 | - self._state = DebianBuildState.REAP |
162 | - self.doReapProcesses() |
163 | - else: |
164 | if self.sources_list is not None: |
165 | self._state = DebianBuildState.SOURCES |
166 | self.doSourcesList() |
167 | |
168 | === modified file 'lib/canonical/buildd/debian/changelog' |
169 | --- lib/canonical/buildd/debian/changelog 2010-06-10 16:52:04 +0000 |
170 | +++ lib/canonical/buildd/debian/changelog 2010-06-16 14:06:25 +0000 |
171 | @@ -1,3 +1,10 @@ |
172 | +launchpad-buildd (63) hardy-cat; urgency=low |
173 | + |
174 | + * Drop apply-ogre-model, since override-sources-list replaced it three years |
175 | + ago. Also clean up extra_args parsing a bit. |
176 | + |
177 | + -- William Grant <wgrant@ubuntu.com> Sat, 12 Jun 2010 11:33:11 +1000 |
178 | + |
179 | launchpad-buildd (62) hardy-cat; urgency=low |
180 | |
181 | * Make the buildds cope with not having a sourcepackagename LP#587109 |
182 | |
183 | === modified file 'lib/canonical/buildd/debian/rules' |
184 | --- lib/canonical/buildd/debian/rules 2010-03-10 21:23:17 +0000 |
185 | +++ lib/canonical/buildd/debian/rules 2010-06-16 14:06:25 +0000 |
186 | @@ -24,8 +24,8 @@ |
187 | pyfiles = debian.py slave.py binarypackage.py utils.py __init__.py \ |
188 | sourcepackagerecipe.py translationtemplates.py |
189 | slavebins = unpack-chroot mount-chroot update-debian-chroot sbuild-package \ |
190 | -scan-for-processes umount-chroot remove-build apply-ogre-model \ |
191 | -override-sources-list buildrecipe generate-translation-templates |
192 | + scan-for-processes umount-chroot remove-build override-sources-list \ |
193 | + buildrecipe generate-translation-templates |
194 | |
195 | BUILDDUID=65500 |
196 | BUILDDGID=65500 |
197 | |
198 | === modified file 'lib/canonical/buildd/debian/upgrade-config' |
199 | --- lib/canonical/buildd/debian/upgrade-config 2010-04-01 03:47:51 +0000 |
200 | +++ lib/canonical/buildd/debian/upgrade-config 2010-06-16 14:06:25 +0000 |
201 | @@ -92,6 +92,15 @@ |
202 | 'generatepath = /usr/share/launchpad-buildd/slavebin/generate-translation-templates\n' |
203 | 'resultarchive = translation-templates.tar.gz\n') |
204 | |
205 | +def upgrade_to_63(): |
206 | + print "Upgrading %s to version 63" % conf_file |
207 | + subprocess.call(["mv", conf_file, conf_file+"-prev63~"]) |
208 | + in_file = open(conf_file+"-prev63~", "r") |
209 | + out_file = open(conf_file, "w") |
210 | + for line in in_file: |
211 | + if not line.startswith('ogrepath'): |
212 | + out_file.write(line) |
213 | + |
214 | |
215 | if __name__ == "__main__": |
216 | if old_version.find("~") > 0: |
217 | @@ -108,4 +117,6 @@ |
218 | upgrade_to_58() |
219 | if int(old_version) < 59: |
220 | upgrade_to_59() |
221 | + if int(old_version) < 63: |
222 | + upgrade_to_63() |
223 | |
224 | |
225 | === modified file 'lib/canonical/buildd/template-buildd-slave.conf' |
226 | --- lib/canonical/buildd/template-buildd-slave.conf 2010-03-12 08:32:39 +0000 |
227 | +++ lib/canonical/buildd/template-buildd-slave.conf 2010-06-16 14:06:25 +0000 |
228 | @@ -18,7 +18,6 @@ |
229 | [debianmanager] |
230 | updatepath = /usr/share/launchpad-buildd/slavebin/update-debian-chroot |
231 | processscanpath = /usr/share/launchpad-buildd/slavebin/scan-for-processes |
232 | -ogrepath = /usr/share/launchpad-buildd/slavebin/apply-ogre-model |
233 | sourcespath = /usr/share/launchpad-buildd/slavebin/override-sources-list |
234 | |
235 | [binarypackagemanager] |
236 | |
237 | === modified file 'lib/canonical/buildd/tests/buildd-slave-test.conf' |
238 | --- lib/canonical/buildd/tests/buildd-slave-test.conf 2007-06-05 00:53:06 +0000 |
239 | +++ lib/canonical/buildd/tests/buildd-slave-test.conf 2010-06-16 14:06:25 +0000 |
240 | @@ -17,5 +17,4 @@ |
241 | sbuildargs = -dautobuild --nolog --batch |
242 | updatepath = /var/tmp/buildd/slavebin/update-debian-chroot |
243 | processscanpath = /var/tmp/buildd/slavebin/scan-for-processes |
244 | -ogrepath = /var/tmp/buildd/slavebin/apply-ogre-model |
245 | sourcespath = /usr/share/launchpad-buildd/slavebin/override-sources-list |
246 | |
247 | === modified file 'lib/canonical/launchpad/templates/launchpad-form.pt' |
248 | --- lib/canonical/launchpad/templates/launchpad-form.pt 2010-03-15 16:58:49 +0000 |
249 | +++ lib/canonical/launchpad/templates/launchpad-form.pt 2010-06-16 14:06:25 +0000 |
250 | @@ -171,9 +171,12 @@ |
251 | </td> |
252 | </tr> |
253 | </tal:is-visible> |
254 | - <tal:not-visible condition="not: widget/visible"> |
255 | - <tr> |
256 | - <td tal:content="structure widget/hidden" /> |
257 | + <tal:not-visible |
258 | + condition="not: widget/visible"> |
259 | + <tr |
260 | + tal:define="markup widget/hidden" |
261 | + tal:condition="markup"> |
262 | + <td tal:content="structure markup" /> |
263 | </tr> |
264 | </tal:not-visible> |
265 | </metal:macro> |
266 | |
267 | === modified file 'lib/canonical/widgets/product.py' |
268 | --- lib/canonical/widgets/product.py 2009-07-21 03:56:03 +0000 |
269 | +++ lib/canonical/widgets/product.py 2010-06-16 14:06:25 +0000 |
270 | @@ -408,7 +408,7 @@ |
271 | |
272 | class GhostWidget(TextWidget): |
273 | """A simple widget that has no HTML.""" |
274 | - |
275 | + visible = False |
276 | # This suppresses the stuff above the widget. |
277 | display_label = False |
278 | # This suppresses the stuff underneath the widget. |
279 | @@ -418,3 +418,5 @@ |
280 | def __call__(self): |
281 | """See `SimpleInputWidget`.""" |
282 | return '' |
283 | + |
284 | + hidden = __call__ |
285 | |
286 | === modified file 'lib/lp/buildmaster/configure.zcml' |
287 | --- lib/lp/buildmaster/configure.zcml 2010-05-20 13:12:04 +0000 |
288 | +++ lib/lp/buildmaster/configure.zcml 2010-06-16 14:06:25 +0000 |
289 | @@ -75,6 +75,12 @@ |
290 | <allow |
291 | interface="lp.buildmaster.interfaces.packagebuild.IPackageBuildSource" /> |
292 | </securedutility> |
293 | + <securedutility |
294 | + class="lp.buildmaster.model.packagebuild.PackageBuildSet" |
295 | + provides="lp.buildmaster.interfaces.packagebuild.IPackageBuildSet"> |
296 | + <allow |
297 | + interface="lp.buildmaster.interfaces.packagebuild.IPackageBuildSet" /> |
298 | + </securedutility> |
299 | |
300 | <!-- BuildQueue --> |
301 | <class |
302 | |
303 | === modified file 'lib/lp/buildmaster/interfaces/packagebuild.py' |
304 | --- lib/lp/buildmaster/interfaces/packagebuild.py 2010-05-21 09:42:21 +0000 |
305 | +++ lib/lp/buildmaster/interfaces/packagebuild.py 2010-06-16 14:06:25 +0000 |
306 | @@ -6,6 +6,7 @@ |
307 | __all__ = [ |
308 | 'IPackageBuild', |
309 | 'IPackageBuildSource', |
310 | + 'IPackageBuildSet', |
311 | ] |
312 | |
313 | |
314 | @@ -16,6 +17,7 @@ |
315 | |
316 | from canonical.launchpad import _ |
317 | from canonical.launchpad.interfaces.librarian import ILibraryFileAlias |
318 | +from lp.buildmaster.interfaces.buildbase import BuildStatus |
319 | from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob |
320 | from lp.registry.interfaces.distribution import IDistribution |
321 | from lp.registry.interfaces.distroseries import IDistroSeries |
322 | @@ -162,10 +164,33 @@ |
323 | class IPackageBuildSource(Interface): |
324 | """A utility of this interface used to create _things_.""" |
325 | |
326 | - def new(archive, pocket, dependencies=None): |
327 | + def new(job_type, virtualized, archive, pocket, processor=None, |
328 | + status=BuildStatus.NEEDSBUILD, dependencies=None): |
329 | """Create a new `IPackageBuild`. |
330 | |
331 | + :param job_type: A `BuildFarmJobType` item. |
332 | + :param virtualized: A boolean indicating whether this build was |
333 | + virtualized. |
334 | :param archive: An `IArchive`. |
335 | :param pocket: An item of `PackagePublishingPocket`. |
336 | + :param processor: An `IProcessor` required to run this build farm |
337 | + job. Default is None (processor-independent). |
338 | + :param status: A `BuildStatus` item defaulting to NEEDSBUILD. |
339 | :param dependencies: An optional debian-like dependency line. |
340 | """ |
341 | + |
342 | + |
343 | +class IPackageBuildSet(Interface): |
344 | + """A utility representing a set of package builds.""" |
345 | + |
346 | + def getBuildsForArchive(archive, status=None, pocket=None): |
347 | + """Return package build records targeted to a given IArchive. |
348 | + |
349 | + :param archive: The archive for which builds will be returned. |
350 | + :param status: If status is provided, only builders with that |
351 | + status will be returned. |
352 | + :param pocket: If pocket is provided only builds for that pocket |
353 | + will be returned. |
354 | + :return: a `ResultSet` representing the requested package builds. |
355 | + """ |
356 | + |
357 | |
358 | === modified file 'lib/lp/buildmaster/model/packagebuild.py' |
359 | --- lib/lp/buildmaster/model/packagebuild.py 2010-06-07 10:43:01 +0000 |
360 | +++ lib/lp/buildmaster/model/packagebuild.py 2010-06-16 14:06:25 +0000 |
361 | @@ -5,12 +5,14 @@ |
362 | __all__ = [ |
363 | 'PackageBuild', |
364 | 'PackageBuildDerived', |
365 | + 'PackageBuildSet', |
366 | ] |
367 | |
368 | |
369 | from lazr.delegates import delegates |
370 | |
371 | from storm.locals import Int, Reference, Storm, Unicode |
372 | +from storm.expr import Desc |
373 | |
374 | from zope.component import getUtility |
375 | from zope.interface import classProvides, implements |
376 | @@ -19,13 +21,16 @@ |
377 | from canonical.launchpad.browser.librarian import ( |
378 | ProxiedLibraryFileAlias) |
379 | from canonical.launchpad.interfaces.lpstorm import IMasterStore |
380 | +from canonical.launchpad.webapp.interfaces import ( |
381 | + IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR) |
382 | |
383 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
384 | from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSource |
385 | from lp.buildmaster.interfaces.packagebuild import ( |
386 | - IPackageBuild, IPackageBuildSource) |
387 | + IPackageBuild, IPackageBuildSet, IPackageBuildSource) |
388 | from lp.buildmaster.model.buildbase import handle_status_for_build, BuildBase |
389 | -from lp.buildmaster.model.buildfarmjob import BuildFarmJobDerived |
390 | +from lp.buildmaster.model.buildfarmjob import ( |
391 | + BuildFarmJob, BuildFarmJobDerived) |
392 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
393 | from lp.soyuz.adapters.archivedependencies import ( |
394 | default_component_dependency_name) |
395 | @@ -214,3 +219,41 @@ |
396 | def _handleStatus_GIVENBACK(self, librarian, slave_status, logger): |
397 | return BuildBase._handleStatus_GIVENBACK( |
398 | self, librarian, slave_status, logger) |
399 | + |
400 | + |
401 | +class PackageBuildSet: |
402 | + implements(IPackageBuildSet) |
403 | + |
404 | + def getBuildsForArchive(self, archive, status=None, pocket=None): |
405 | + """See `IPackageBuildSet`.""" |
406 | + |
407 | + extra_exprs = [] |
408 | + |
409 | + if status is not None: |
410 | + extra_exprs.append(BuildFarmJob.status == status) |
411 | + |
412 | + if pocket: |
413 | + extra_exprs.append(PackageBuild.pocket == pocket) |
414 | + |
415 | + store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
416 | + result_set = store.find(PackageBuild, |
417 | + PackageBuild.archive == archive, |
418 | + PackageBuild.build_farm_job == BuildFarmJob.id, |
419 | + *extra_exprs) |
420 | + |
421 | + # When we have a set of builds that may include pending or |
422 | + # superseded builds, we order by -date_created (as we won't |
423 | + # always have a date_finished). Otherwise we can order by |
424 | + # -date_finished. |
425 | + unfinished_states = [ |
426 | + BuildStatus.NEEDSBUILD, |
427 | + BuildStatus.BUILDING, |
428 | + BuildStatus.SUPERSEDED] |
429 | + if status is None or status in unfinished_states: |
430 | + result_set.order_by( |
431 | + Desc(BuildFarmJob.date_created), BuildFarmJob.id) |
432 | + else: |
433 | + result_set.order_by( |
434 | + Desc(BuildFarmJob.date_finished), BuildFarmJob.id) |
435 | + |
436 | + return result_set |
437 | |
438 | === modified file 'lib/lp/buildmaster/tests/test_packagebuild.py' |
439 | --- lib/lp/buildmaster/tests/test_packagebuild.py 2010-05-21 12:48:44 +0000 |
440 | +++ lib/lp/buildmaster/tests/test_packagebuild.py 2010-06-16 14:06:25 +0000 |
441 | @@ -15,9 +15,10 @@ |
442 | |
443 | from canonical.testing.layers import LaunchpadFunctionalLayer |
444 | |
445 | +from lp.buildmaster.interfaces.buildbase import BuildStatus |
446 | from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType |
447 | from lp.buildmaster.interfaces.packagebuild import ( |
448 | - IPackageBuild, IPackageBuildSource) |
449 | + IPackageBuild, IPackageBuildSet, IPackageBuildSource) |
450 | from lp.buildmaster.model.packagebuild import PackageBuild |
451 | from lp.buildmaster.tests.test_buildbase import TestBuildBaseMixin |
452 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
453 | @@ -31,15 +32,16 @@ |
454 | only classes deriving from PackageBuild should be used. |
455 | """ |
456 | |
457 | - def makePackageBuild(self, archive=None): |
458 | + def makePackageBuild( |
459 | + self, archive=None, job_type=BuildFarmJobType.PACKAGEBUILD, |
460 | + status=BuildStatus.NEEDSBUILD, |
461 | + pocket=PackagePublishingPocket.RELEASE): |
462 | if archive is None: |
463 | archive = self.factory.makeArchive() |
464 | |
465 | return getUtility(IPackageBuildSource).new( |
466 | - job_type=BuildFarmJobType.PACKAGEBUILD, |
467 | - virtualized=True, |
468 | - archive=archive, |
469 | - pocket=PackagePublishingPocket.RELEASE) |
470 | + job_type=job_type, virtualized=True, archive=archive, |
471 | + status=status, pocket=pocket) |
472 | |
473 | |
474 | class TestBuildBaseMethods(TestPackageBuildBase, TestBuildBaseMixin): |
475 | @@ -176,5 +178,46 @@ |
476 | u'My deps', self.package_build.dependencies) |
477 | |
478 | |
479 | +class TestPackageBuildSet(TestPackageBuildBase): |
480 | + |
481 | + layer = LaunchpadFunctionalLayer |
482 | + |
483 | + def setUp(self): |
484 | + super(TestPackageBuildSet, self).setUp() |
485 | + person = self.factory.makePerson() |
486 | + self.archive = self.factory.makeArchive(owner=person) |
487 | + self.package_builds = [] |
488 | + self.package_builds.append( |
489 | + self.makePackageBuild(archive=self.archive, |
490 | + pocket=PackagePublishingPocket.UPDATES)) |
491 | + self.package_builds.append( |
492 | + self.makePackageBuild(archive=self.archive, |
493 | + status=BuildStatus.BUILDING)) |
494 | + self.package_build_set = getUtility(IPackageBuildSet) |
495 | + |
496 | + def test_getBuildsForArchive_all(self): |
497 | + # The default call without arguments returns all builds for the |
498 | + # archive. |
499 | + self.assertContentEqual( |
500 | + self.package_builds, self.package_build_set.getBuildsForArchive( |
501 | + self.archive)) |
502 | + |
503 | + def test_getBuildsForArchive_by_status(self): |
504 | + # If the status arg is used, the results will be filtered by |
505 | + # status. |
506 | + self.assertContentEqual( |
507 | + self.package_builds[1:], |
508 | + self.package_build_set.getBuildsForArchive( |
509 | + self.archive, status=BuildStatus.BUILDING)) |
510 | + |
511 | + def test_getBuildsForArchive_by_pocket(self): |
512 | + # If the pocket arg is used, the results will be filtered by |
513 | + # pocket. |
514 | + self.assertContentEqual( |
515 | + self.package_builds[:1], |
516 | + self.package_build_set.getBuildsForArchive( |
517 | + self.archive, pocket=PackagePublishingPocket.UPDATES)) |
518 | + |
519 | + |
520 | def test_suite(): |
521 | return unittest.TestLoader().loadTestsFromName(__name__) |
522 | |
523 | === modified file 'lib/lp/code/browser/sourcepackagerecipe.py' |
524 | --- lib/lp/code/browser/sourcepackagerecipe.py 2010-06-12 13:39:48 +0000 |
525 | +++ lib/lp/code/browser/sourcepackagerecipe.py 2010-06-16 14:06:25 +0000 |
526 | @@ -37,9 +37,11 @@ |
527 | LaunchpadView, Link, Navigation, NavigationMenu, stepthrough) |
528 | from canonical.launchpad.webapp.authorization import check_permission |
529 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
530 | +from canonical.launchpad.webapp.sorting import sorted_dotted_numbers |
531 | from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget |
532 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
533 | from lp.code.errors import ForbiddenInstruction |
534 | +from lp.code.interfaces.branch import NoSuchBranch |
535 | from lp.code.interfaces.sourcepackagerecipe import ( |
536 | ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT) |
537 | from lp.code.interfaces.sourcepackagerecipebuild import ( |
538 | @@ -170,9 +172,12 @@ |
539 | ppas = getUtility(IArchiveSet).getPPAsForUser(getUtility(ILaunchBag).user) |
540 | supported_distros = [ppa.distribution for ppa in ppas] |
541 | dsset = getUtility(IDistroSeriesSet).search() |
542 | - terms = [SimpleTerm(distro, distro.id, distro.displayname) |
543 | - for distro in dsset if ( |
544 | - distro.active and distro.distribution in supported_distros)] |
545 | + terms = sorted_dotted_numbers( |
546 | + [SimpleTerm(distro, distro.id, distro.displayname) |
547 | + for distro in dsset if ( |
548 | + distro.active and distro.distribution in supported_distros)], |
549 | + key=lambda term: term.value.version) |
550 | + terms.reverse() |
551 | return SimpleVocabulary(terms) |
552 | |
553 | def target_ppas_vocabulary(context): |
554 | @@ -375,6 +380,10 @@ |
555 | 'recipe_text', |
556 | 'The bzr-builder instruction "run" is not permitted here.') |
557 | return |
558 | + except NoSuchBranch, e: |
559 | + self.setFieldError( |
560 | + 'recipe_text', '%s is not a branch on Launchpad.' % e.name) |
561 | + return |
562 | |
563 | self.next_url = canonical_url(source_package_recipe) |
564 | |
565 | |
566 | === modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py' |
567 | --- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-14 20:14:15 +0000 |
568 | +++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-16 14:06:25 +0000 |
569 | @@ -42,7 +42,7 @@ |
570 | self.ppa = self.factory.makeArchive( |
571 | displayname='Secret PPA', owner=self.chef, name='ppa') |
572 | self.squirrel = self.factory.makeDistroSeries( |
573 | - displayname='Secret Squirrel', name='secret', |
574 | + displayname='Secret Squirrel', name='secret', version='100.04', |
575 | distribution=self.ppa.distribution) |
576 | self.squirrel.nominatedarchindep = self.squirrel.newArch( |
577 | 'i386', ProcessorFamily.get(1), False, self.chef, |
578 | @@ -65,6 +65,12 @@ |
579 | return extract_text(find_main_content(browser.contents)) |
580 | |
581 | |
582 | +def get_message_text(browser, index): |
583 | + """Return the text of a message, specified by index.""" |
584 | + tags = find_tags_by_class(browser.contents, 'message')[index] |
585 | + return extract_text(tags) |
586 | + |
587 | + |
588 | class TestSourcePackageRecipeAddView(TestCaseForRecipe): |
589 | |
590 | layer = DatabaseFunctionalLayer |
591 | @@ -149,7 +155,7 @@ |
592 | browser.getControl('Create Recipe').click() |
593 | |
594 | self.assertEqual( |
595 | - extract_text(find_tags_by_class(browser.contents, 'message')[1]), |
596 | + get_message_text(browser, 1), |
597 | 'The bzr-builder instruction "run" is not permitted here.') |
598 | |
599 | def test_create_new_recipe_empty_name(self): |
600 | @@ -169,12 +175,29 @@ |
601 | browser.getControl('Create Recipe').click() |
602 | |
603 | self.assertEqual( |
604 | - extract_text(find_tags_by_class(browser.contents, 'message')[1]), |
605 | - 'Required input is missing.') |
606 | + get_message_text(browser, 1), 'Required input is missing.') |
607 | + |
608 | + def createRecipe(self, recipe_text, branch=None): |
609 | + if branch is None: |
610 | + product = self.factory.makeProduct( |
611 | + name='ratatouille', displayname='Ratatouille') |
612 | + branch = self.factory.makeBranch( |
613 | + owner=self.chef, product=product, name='veggies') |
614 | + |
615 | + # A new recipe can be created from the branch page. |
616 | + browser = self.getUserBrowser(canonical_url(branch), user=self.chef) |
617 | + browser.getLink('Create packaging recipe').click() |
618 | + |
619 | + browser.getControl(name='field.name').value = 'daily' |
620 | + browser.getControl('Description').value = 'Make some food!' |
621 | + browser.getControl('Recipe text').value = recipe_text |
622 | + browser.getControl('Create Recipe').click() |
623 | + return browser |
624 | |
625 | def test_create_recipe_bad_text(self): |
626 | # If a user tries to create source package recipe with bad text, they |
627 | # should get an error. |
628 | +<<<<<<< TREE |
629 | branch = self.makeBranch() |
630 | |
631 | # A new recipe can be created from the branch page. |
632 | @@ -186,10 +209,14 @@ |
633 | browser.getControl('Recipe text').value = 'Foo bar baz' |
634 | browser.getControl('Create Recipe').click() |
635 | |
636 | +======= |
637 | + browser = self.createRecipe('Foo bar baz') |
638 | +>>>>>>> MERGE-SOURCE |
639 | self.assertEqual( |
640 | - extract_text(find_tags_by_class(browser.contents, 'message')[1]), |
641 | + get_message_text(browser, 1), |
642 | 'The recipe text is not a valid bzr-builder recipe.') |
643 | |
644 | +<<<<<<< TREE |
645 | def test_create_recipe_no_distroseries(self): |
646 | browser = self.getViewBrowser(self.makeBranch(), '+new-recipe') |
647 | browser.getControl(name='field.name').value = 'daily' |
648 | @@ -201,6 +228,28 @@ |
649 | extract_text(find_tags_by_class(browser.contents, 'message')[1]), |
650 | 'You must specify at least one series for daily builds.') |
651 | |
652 | +======= |
653 | + def test_create_recipe_bad_base_branch(self): |
654 | + # If a user tries to create source package recipe with a bad base |
655 | + # branch location, they should get an error. |
656 | + browser = self.createRecipe(MINIMAL_RECIPE_TEXT % 'foo') |
657 | + self.assertEqual( |
658 | + get_message_text(browser, 1), 'foo is not a branch on Launchpad.') |
659 | + |
660 | + def test_create_recipe_bad_instruction_branch(self): |
661 | + # If a user tries to create source package recipe with a bad |
662 | + # instruction branch location, they should get an error. |
663 | + product = self.factory.makeProduct( |
664 | + name='ratatouille', displayname='Ratatouille') |
665 | + branch = self.factory.makeBranch( |
666 | + owner=self.chef, product=product, name='veggies') |
667 | + recipe = MINIMAL_RECIPE_TEXT % branch.bzr_identity |
668 | + recipe += 'nest packaging foo debian' |
669 | + browser = self.createRecipe(recipe, branch) |
670 | + self.assertEqual( |
671 | + get_message_text(browser, 1), 'foo is not a branch on Launchpad.') |
672 | + |
673 | +>>>>>>> MERGE-SOURCE |
674 | def test_create_dupe_recipe(self): |
675 | # You shouldn't be able to create a duplicate recipe owned by the same |
676 | # person with the same name. |
677 | @@ -221,7 +270,7 @@ |
678 | browser.getControl('Create Recipe').click() |
679 | |
680 | self.assertEqual( |
681 | - extract_text(find_tags_by_class(browser.contents, 'message')[1]), |
682 | + get_message_text(browser, 1), |
683 | 'There is already a recipe owned by Master Chef with this name.') |
684 | |
685 | |
686 | @@ -516,8 +565,8 @@ |
687 | Secret PPA (chef/ppa) |
688 | Distribution series: |
689 | Secret Squirrel |
690 | + Hoary |
691 | Warty |
692 | - Hoary |
693 | or |
694 | Cancel""") |
695 | main_text = self.getMainText(recipe, '+request-builds') |
696 | |
697 | === modified file 'lib/lp/code/model/sourcepackagerecipedata.py' |
698 | --- lib/lp/code/model/sourcepackagerecipedata.py 2010-06-11 04:28:38 +0000 |
699 | +++ lib/lp/code/model/sourcepackagerecipedata.py 2010-06-16 14:06:25 +0000 |
700 | @@ -211,6 +211,8 @@ |
701 | line_number += 1 |
702 | comment = None |
703 | db_branch = branch_map[instruction.recipe_branch.url] |
704 | + if db_branch is None: |
705 | + raise NoSuchBranch(instruction.recipe_branch.url) |
706 | insn = _SourcePackageRecipeDataInstruction( |
707 | instruction.recipe_branch.name, type, comment, |
708 | line_number, db_branch, instruction.recipe_branch.revspec, |
709 | |
710 | === modified file 'lib/lp/registry/doc/product-widgets.txt' |
711 | --- lib/lp/registry/doc/product-widgets.txt 2009-08-13 19:36:01 +0000 |
712 | +++ lib/lp/registry/doc/product-widgets.txt 2010-06-16 14:06:25 +0000 |
713 | @@ -216,7 +216,6 @@ |
714 | |
715 | A custom widget is used to display a link to the license policy. |
716 | |
717 | - >>> from canonical.launchpad.interfaces import License |
718 | >>> from canonical.widgets.product import LicenseWidget |
719 | |
720 | >>> form = {'field.licenses': []} |
721 | @@ -414,7 +413,7 @@ |
722 | >>> print_checked_items(license_widget(), links=True) |
723 | [ ] Apache License ... <http://www.opensource.org/licenses/apache2.0.php> |
724 | [ ] GNU LGPL v2.1 ... <http://www.opensource.org/licenses/lgpl-2.1.php> |
725 | - [ ] GNU Affero GPL v3 ... <http://www.opensource.org/licenses/agpl-v3.html> |
726 | + [ ] GNU Affero GPL v3 ... <http://www.opensource.org/licenses/agpl-v3...> |
727 | ... |
728 | |
729 | But not all of them. |
730 | @@ -425,3 +424,47 @@ |
731 | [ ] I don't know yet |
732 | [ ] Other/Proprietary |
733 | [ ] Other/Open Source |
734 | + |
735 | + |
736 | +GhostWidget |
737 | +----------- |
738 | + |
739 | +The GhostWidget is used to suppress the markup of a field in cases where |
740 | +another mechanism is used to insert the field's markup into the page. Some |
741 | +widget for example generate the markup for subordinate fields, but they |
742 | +do not manage the field itself. The LicenseWidget widget for example |
743 | +generates the markup for the license_info field; the view must use a |
744 | +GhostWidget to suppress the markup. |
745 | + |
746 | + >>> from canonical.widgets.product import GhostWidget |
747 | + |
748 | + >>> license_info = IProduct['license_info'].bind(firefox) |
749 | + >>> ghost_widget = GhostWidget(license_info, LaunchpadTestRequest()) |
750 | + >>> ghost_widget.visible |
751 | + False |
752 | + >>> ghost_widget.hidden() |
753 | + '' |
754 | + >>> ghost_widget() |
755 | + '' |
756 | + |
757 | +Launchpad form macros do not generate table rows for the GhostWidget. |
758 | + |
759 | + >>> from z3c.ptcompat import ViewPageTemplateFile |
760 | + >>> from canonical.config import config |
761 | + >>> from canonical.launchpad.webapp.launchpadform import ( |
762 | + ... custom_widget, LaunchpadFormView) |
763 | + |
764 | + >>> class GhostWidgetView(LaunchpadFormView): |
765 | + ... page_title = 'Test' |
766 | + ... template = ViewPageTemplateFile( |
767 | + ... config.root + '/lib/lp/app/templates/generic-edit.pt') |
768 | + ... schema = IProduct |
769 | + ... field_names = ['license_info'] |
770 | + ... custom_widget('license_info', GhostWidget) |
771 | + |
772 | + >>> request = LaunchpadTestRequest() |
773 | + >>> request.setPrincipal(factory.makePerson()) |
774 | + >>> view = GhostWidgetView(firefox, request) |
775 | + >>> view.initialize() |
776 | + >>> extract_text(find_tag_by_id(view.render(), 'launchpad-form-widgets')) |
777 | + u'' |
778 | |
779 | === modified file 'lib/lp/registry/model/sourcepackage.py' |
780 | --- lib/lp/registry/model/sourcepackage.py 2010-06-09 08:26:26 +0000 |
781 | +++ lib/lp/registry/model/sourcepackage.py 2010-06-16 14:06:25 +0000 |
782 | @@ -508,15 +508,17 @@ |
783 | return not self.__eq__(other) |
784 | |
785 | def getBuildRecords(self, build_state=None, name=None, pocket=None, |
786 | - arch_tag=None, user=None): |
787 | + arch_tag=None, user=None, binary_only=True): |
788 | + """See `IHasBuildRecords`""" |
789 | # Ignore "user", since it would not make any difference to the |
790 | # records returned here (private builds are only in PPA right |
791 | # now and this method only returns records for SPRs in a |
792 | # distribution). |
793 | # We also ignore the name parameter (required as part of the |
794 | - # IHasBuildRecords interface) and use our own name. |
795 | + # IHasBuildRecords interface) and use our own name and the |
796 | + # binary_only parameter as a source package can only have |
797 | + # binary builds. |
798 | |
799 | - """See `IHasBuildRecords`""" |
800 | clauseTables = ['SourcePackageRelease', |
801 | 'SourcePackagePublishingHistory'] |
802 | |
803 | |
804 | === modified file 'lib/lp/soyuz/configure.zcml' |
805 | --- lib/lp/soyuz/configure.zcml 2010-06-14 15:58:46 +0000 |
806 | +++ lib/lp/soyuz/configure.zcml 2010-06-16 14:06:25 +0000 |
807 | @@ -515,6 +515,11 @@ |
808 | for="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild" |
809 | factory="lp.soyuz.browser.build.BuildBreadcrumb" |
810 | permission="zope.Public"/> |
811 | + <adapter |
812 | + provides="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild" |
813 | + for="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob" |
814 | + factory="lp.soyuz.model.binarypackagebuild.get_binary_build_for_build_farm_job" |
815 | + permission="zope.Public"/> |
816 | |
817 | <!-- BinaryPackageBuildSet --> |
818 | |
819 | |
820 | === modified file 'lib/lp/soyuz/interfaces/archive.py' |
821 | --- lib/lp/soyuz/interfaces/archive.py 2010-06-15 14:36:47 +0000 |
822 | +++ lib/lp/soyuz/interfaces/archive.py 2010-06-16 14:06:25 +0000 |
823 | @@ -29,6 +29,7 @@ |
824 | 'IArchivePublic', |
825 | 'IArchiveSet', |
826 | 'IDistributionArchive', |
827 | + 'IncompatibleArguments', |
828 | 'InsufficientUploadRights', |
829 | 'InvalidComponent', |
830 | 'InvalidPocketForPartnerArchive', |
831 | @@ -89,6 +90,11 @@ |
832 | webservice_error(400) #Bad request. |
833 | |
834 | |
835 | +class IncompatibleArguments(Exception): |
836 | + """Raised when incompatible arguments are passed to a method.""" |
837 | + webservice_error(400) # Bad request. |
838 | + |
839 | + |
840 | class CannotSwitchPrivacy(Exception): |
841 | """Raised when switching the privacy of an archive that has |
842 | publishing records.""" |
843 | |
844 | === modified file 'lib/lp/soyuz/interfaces/buildrecords.py' |
845 | --- lib/lp/soyuz/interfaces/buildrecords.py 2009-07-17 00:26:05 +0000 |
846 | +++ lib/lp/soyuz/interfaces/buildrecords.py 2010-06-16 14:06:25 +0000 |
847 | @@ -5,7 +5,7 @@ |
848 | |
849 | """IHasBuildRecords interface. |
850 | |
851 | -Implemented by any object that can have `IBuild` records related to it. |
852 | +Implemented by any object that can have `IPackageBuild` records related to it. |
853 | """ |
854 | |
855 | __metaclass__ = type |
856 | @@ -45,7 +45,7 @@ |
857 | @operation_returns_collection_of(Interface) |
858 | @export_read_operation() |
859 | def getBuildRecords(build_state=None, name=None, pocket=None, |
860 | - arch_tag=None, user=None): |
861 | + arch_tag=None, user=None, binary_only=True): |
862 | """Return build records in the context it is implemented. |
863 | |
864 | It excludes build records generated by Gina (imported from a external |
865 | @@ -66,9 +66,14 @@ |
866 | :param user: optional `IPerson` corresponding to the user performing |
867 | the request. It will filter out build records for which the user |
868 | have no 'view' permission. |
869 | + :param binary_only: optional boolean indicating whether only |
870 | + `BinaryPackageBuild` objects should be returned, or more general |
871 | + `PackageBuild` objects (which may include, for example, |
872 | + `SourcePackageRecipeBuild` objects. |
873 | |
874 | - :return: a result set containing `IBuild` records ordered by descending |
875 | - `IBuild.datebuilt` except when builds are filtered by |
876 | - `BuildStatus.NEEDSBUILD`, in this case records are ordered by |
877 | - descending `BuildQueue.lastscore` (dispatching order). |
878 | + :return: a result set containing `IPackageBuild` records ordered by |
879 | + descending `IPackageBuild.date_finished` except when builds are |
880 | + filtered by `BuildStatus.NEEDSBUILD`, in this case records |
881 | + are ordered by descending `BuildQueue.lastscore` |
882 | + (dispatching order). |
883 | """ |
884 | |
885 | === modified file 'lib/lp/soyuz/model/archive.py' |
886 | --- lib/lp/soyuz/model/archive.py 2010-06-14 12:19:10 +0000 |
887 | +++ lib/lp/soyuz/model/archive.py 2010-06-16 14:06:25 +0000 |
888 | @@ -34,6 +34,7 @@ |
889 | cursor, quote, quote_like, sqlvalues, SQLBase) |
890 | from canonical.launchpad.interfaces.lpstorm import ISlaveStore |
891 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
892 | +from lp.buildmaster.interfaces.packagebuild import IPackageBuildSet |
893 | from lp.buildmaster.model.buildfarmjob import BuildFarmJob |
894 | from lp.buildmaster.model.packagebuild import PackageBuild |
895 | from lp.services.job.interfaces.job import JobStatus |
896 | @@ -65,7 +66,7 @@ |
897 | ArchiveNotPrivate, ArchivePurpose, ArchiveStatus, CannotCopy, |
898 | CannotSwitchPrivacy, CannotUploadToPPA, CannotUploadToPocket, |
899 | DistroSeriesNotFound, IArchive, IArchiveSet, IDistributionArchive, |
900 | - InsufficientUploadRights, InvalidPocketForPPA, |
901 | + IncompatibleArguments, InsufficientUploadRights, InvalidPocketForPPA, |
902 | InvalidPocketForPartnerArchive, InvalidComponent, IPPA, |
903 | MAIN_ARCHIVE_PURPOSES, NoRightsForArchive, NoRightsForComponent, |
904 | NoSuchPPA, NoTokensForTeams, PocketNotFound, VersionRequiresName, |
905 | @@ -369,12 +370,21 @@ |
906 | return None |
907 | |
908 | def getBuildRecords(self, build_state=None, name=None, pocket=None, |
909 | - arch_tag=None, user=None): |
910 | + arch_tag=None, user=None, binary_only=True): |
911 | """See IHasBuildRecords""" |
912 | # Ignore "user", since anyone already accessing this archive |
913 | # will implicitly have permission to see it. |
914 | - return getUtility(IBinaryPackageBuildSet).getBuildsForArchive( |
915 | - self, build_state, name, pocket, arch_tag) |
916 | + |
917 | + if binary_only: |
918 | + return getUtility(IBinaryPackageBuildSet).getBuildsForArchive( |
919 | + self, build_state, name, pocket, arch_tag) |
920 | + else: |
921 | + if arch_tag is not None or name is not None: |
922 | + raise IncompatibleArguments( |
923 | + "The 'arch_tag' and 'name' parameters can be used only " |
924 | + "with binary_only=True.") |
925 | + return getUtility(IPackageBuildSet).getBuildsForArchive( |
926 | + self, status=build_state, pocket=pocket) |
927 | |
928 | def getPublishedSources(self, name=None, version=None, status=None, |
929 | distroseries=None, pocket=None, |
930 | |
931 | === modified file 'lib/lp/soyuz/model/binarypackagebuild.py' |
932 | --- lib/lp/soyuz/model/binarypackagebuild.py 2010-06-09 12:13:53 +0000 |
933 | +++ lib/lp/soyuz/model/binarypackagebuild.py 2010-06-16 14:06:25 +0000 |
934 | @@ -65,6 +65,27 @@ |
935 | PackageUpload, PackageUploadBuild) |
936 | |
937 | |
938 | +def get_binary_build_for_build_farm_job(build_farm_job): |
939 | + """Factory method to returning a binary for a build farm job.""" |
940 | + # No need to query the db if the build_farm_job doesn't have |
941 | + # the correct job type. |
942 | + if build_farm_job.job_type != BuildFarmJobType.PACKAGEBUILD: |
943 | + return None |
944 | + |
945 | + store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
946 | + find_spec = (BinaryPackageBuild, PackageBuild, BuildFarmJob) |
947 | + resulting_tuple = store.find( |
948 | + find_spec, |
949 | + BinaryPackageBuild.package_build == PackageBuild.id, |
950 | + PackageBuild.build_farm_job == BuildFarmJob.id, |
951 | + BuildFarmJob.id == build_farm_job.id).one() |
952 | + |
953 | + if resulting_tuple is None: |
954 | + return None |
955 | + |
956 | + return resulting_tuple[0] |
957 | + |
958 | + |
959 | class BinaryPackageBuild(PackageBuildDerived, SQLBase): |
960 | implements(IBinaryPackageBuild) |
961 | _table = 'BinaryPackageBuild' |
962 | |
963 | === modified file 'lib/lp/soyuz/tests/test_archive.py' |
964 | --- lib/lp/soyuz/tests/test_archive.py 2010-06-14 15:58:46 +0000 |
965 | +++ lib/lp/soyuz/tests/test_archive.py 2010-06-16 14:06:25 +0000 |
966 | @@ -240,7 +240,7 @@ |
967 | ubuntu_test = breezy_autotest.distribution |
968 | self.series = [breezy_autotest] |
969 | self.series.append(self.factory.makeDistroRelease( |
970 | - distribution=ubuntu_test, name="foo-series")) |
971 | + distribution=ubuntu_test, name="foo-series", version='1.0')) |
972 | |
973 | self.sources = [] |
974 | gedit_src_hist = self.publisher.getPubSource( |
975 | |
976 | === modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py' |
977 | --- lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-09 12:07:58 +0000 |
978 | +++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-16 14:06:25 +0000 |
979 | @@ -15,6 +15,7 @@ |
980 | from canonical.testing import LaunchpadZopelessLayer |
981 | from lp.services.job.model.job import Job |
982 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
983 | +from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType |
984 | from lp.buildmaster.interfaces.builder import IBuilderSet |
985 | from lp.buildmaster.interfaces.buildqueue import IBuildQueue |
986 | from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
987 | @@ -120,6 +121,42 @@ |
988 | self.build.build_farm_job.id), |
989 | self.build.log_url) |
990 | |
991 | + def test_adapt_from_build_farm_job(self): |
992 | + # An `IBuildFarmJob` can be adapted to an IBinaryPackageBuild |
993 | + # if it has the correct job type. |
994 | + build_farm_job = self.build.build_farm_job |
995 | + store = Store.of(build_farm_job) |
996 | + store.flush() |
997 | + |
998 | + binary_package_build = IBinaryPackageBuild(build_farm_job) |
999 | + self.failUnlessEqual(self.build, binary_package_build) |
1000 | + |
1001 | + def test_adapt_from_build_farm_job_wrong_type(self): |
1002 | + # An `IBuildFarmJob` of the wrong type results is None. |
1003 | + build_farm_job = self.build.build_farm_job |
1004 | + removeSecurityProxy(build_farm_job).job_type = ( |
1005 | + BuildFarmJobType.RECIPEBRANCHBUILD) |
1006 | + |
1007 | + binary_package_build = IBinaryPackageBuild(build_farm_job) |
1008 | + self.failUnlessEqual(None, binary_package_build) |
1009 | + |
1010 | + def test_adapt_from_build_farm_job_prefetching(self): |
1011 | + # The package_build is prefetched for efficiency. |
1012 | + build_farm_job = self.build.build_farm_job |
1013 | + |
1014 | + # We clear the cache to avoid getting cached objects where |
1015 | + # they would normally be queries. |
1016 | + store = Store.of(build_farm_job) |
1017 | + store.flush() |
1018 | + store.reset() |
1019 | + |
1020 | + binary_package_build = IBinaryPackageBuild(build_farm_job) |
1021 | + |
1022 | + self.assertStatementCount( |
1023 | + 0, getattr, binary_package_build, "package_build") |
1024 | + self.assertStatementCount( |
1025 | + 0, getattr, binary_package_build, "build_farm_job") |
1026 | + |
1027 | |
1028 | class TestBuildUpdateDependencies(TestCaseWithFactory): |
1029 | |
1030 | |
1031 | === modified file 'lib/lp/soyuz/tests/test_hasbuildrecords.py' |
1032 | --- lib/lp/soyuz/tests/test_hasbuildrecords.py 2010-05-06 10:35:17 +0000 |
1033 | +++ lib/lp/soyuz/tests/test_hasbuildrecords.py 2010-06-16 14:06:25 +0000 |
1034 | @@ -12,6 +12,11 @@ |
1035 | |
1036 | from lp.registry.model.sourcepackage import SourcePackage |
1037 | from lp.buildmaster.interfaces.builder import IBuilderSet |
1038 | +from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType |
1039 | +from lp.buildmaster.interfaces.packagebuild import ( |
1040 | + IPackageBuildSource) |
1041 | +from lp.registry.interfaces.pocket import PackagePublishingPocket |
1042 | +from lp.soyuz.interfaces.archive import IncompatibleArguments |
1043 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
1044 | from lp.soyuz.model.processor import ProcessorFamilySet |
1045 | from lp.soyuz.tests.test_binarypackagebuild import ( |
1046 | @@ -81,6 +86,32 @@ |
1047 | |
1048 | self.context = self.publisher.distroseries.main_archive |
1049 | |
1050 | + def test_binary_only_false(self): |
1051 | + # An archive can optionally return the more general |
1052 | + # package build objects. |
1053 | + |
1054 | + # Until we have different IBuildFarmJob types implemented, we |
1055 | + # can only test this by creating a lone PackageBuild of a |
1056 | + # different type. |
1057 | + getUtility(IPackageBuildSource).new( |
1058 | + job_type=BuildFarmJobType.RECIPEBRANCHBUILD, virtualized=True, |
1059 | + archive=self.context, pocket=PackagePublishingPocket.RELEASE) |
1060 | + |
1061 | + builds = self.context.getBuildRecords(binary_only=True) |
1062 | + self.failUnlessEqual(3, builds.count()) |
1063 | + |
1064 | + builds = self.context.getBuildRecords(binary_only=False) |
1065 | + self.failUnlessEqual(4, builds.count()) |
1066 | + |
1067 | + def test_incompatible_arguments(self): |
1068 | + # binary_only=False is incompatible with arch_tag and name. |
1069 | + self.failUnlessRaises( |
1070 | + IncompatibleArguments, self.context.getBuildRecords, |
1071 | + binary_only=False, arch_tag="anything") |
1072 | + self.failUnlessRaises( |
1073 | + IncompatibleArguments, self.context.getBuildRecords, |
1074 | + binary_only=False, name="anything") |
1075 | + |
1076 | |
1077 | class TestBuilderHasBuildRecords(TestHasBuildRecordsInterface): |
1078 | """Test the Builder implementation of IHasBuildRecords.""" |
1079 | |
1080 | === modified file 'lib/lp/testing/factory.py' |
1081 | --- lib/lp/testing/factory.py 2010-06-13 05:56:31 +0000 |
1082 | +++ lib/lp/testing/factory.py 2010-06-16 14:06:25 +0000 |
1083 | @@ -22,8 +22,10 @@ |
1084 | from email.mime.text import MIMEText |
1085 | from email.mime.multipart import MIMEMultipart |
1086 | from itertools import count |
1087 | +import os.path |
1088 | +from random import randint |
1089 | from StringIO import StringIO |
1090 | -import os.path |
1091 | +from threading import local |
1092 | |
1093 | import pytz |
1094 | |
1095 | @@ -56,7 +58,6 @@ |
1096 | from canonical.launchpad.webapp.dbpolicy import MasterDatabasePolicy |
1097 | from canonical.launchpad.webapp.interfaces import ( |
1098 | IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR) |
1099 | -from canonical.uuid import generate_uuid |
1100 | |
1101 | from lp.blueprints.interfaces.specification import ( |
1102 | ISpecificationSet, SpecificationDefinitionStatus) |
1103 | @@ -213,14 +214,22 @@ |
1104 | |
1105 | def __init__(self): |
1106 | # Initialise the unique identifier. |
1107 | - self._integer = count(1) |
1108 | + self._local = local() |
1109 | |
1110 | def getUniqueEmailAddress(self): |
1111 | return "%s@example.com" % self.getUniqueString('email') |
1112 | |
1113 | def getUniqueInteger(self): |
1114 | - """Return an integer unique to this factory instance.""" |
1115 | - return self._integer.next() |
1116 | + """Return an integer unique to this factory instance. |
1117 | + |
1118 | + For each thread, this will be a series of increasing numbers, but the |
1119 | + starting point will be unique per thread. |
1120 | + """ |
1121 | + counter = getattr(self._local, 'integer', None) |
1122 | + if counter is None: |
1123 | + counter = count(randint(0, 1000000)) |
1124 | + self._local.integer = counter |
1125 | + return counter.next() |
1126 | |
1127 | def getUniqueHexString(self, digits=None): |
1128 | """Return a unique hexadecimal string. |
1129 | @@ -245,7 +254,7 @@ |
1130 | """ |
1131 | if prefix is None: |
1132 | prefix = "generic-string" |
1133 | - string = "%s%s" % (prefix, generate_uuid()) |
1134 | + string = "%s%s" % (prefix, self.getUniqueInteger()) |
1135 | return string.replace('_', '-').lower() |
1136 | |
1137 | def getUniqueUnicode(self): |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi Michael,
thank you for your fix and sorry for the delay...
Am 14.06.2010 10:35, schrieb Michael Nelson: agebuild -m cords
> Test command: bin/test -vv -m test_packagebuild -m test_binarypack
> test_hasbuildre
Yup, they pass. Thanks.
> This branch does some initial work to enable PPA's to present general uilds (bug 536700).
> IPackageBuilds rather than IBinaryPackageB
So, I assume that is the reason that no bug is attached to this branch?
> t.getBuildsForA rchive( ). It adds an getBuildRecords () to >IBinaryPackage Build in
> It adds and tests an IPackageBuildSe
> optional param to the getBuildRecords() method - binary_only - which defaults
> to true (current behaivour). It then updates IArchive.
> support binary_only=False.
>
> Additionally, it adds an adapter for IBuildFarmJob-
> preparation for the UI (so each build can present its title correctly).
>
Very clever. ;-)
Here are my comments:
> === modified file 'lib/lp/ buildmaster/ configure. zcml'
OK.
> === modified file 'lib/lp/ buildmaster/ interfaces/ packagebuild. py'
OK.
> === modified file 'lib/lp/ buildmaster/ model/packagebu ild.py' buildmaster/ model/packagebu ild.py 2010-06-07 10:43:01 +0000 buildmaster/ model/packagebu ild.py 2010-06-14 08:35:42 +0000 launchpad. browser. librarian import ( ileAlias) launchpad. interfaces. lpstorm import IMasterStore launchpad. webapp. interfaces import ( interfaces. buildbase import BuildStatus interfaces. buildfarmjob import IBuildFarmJobSource interfaces. packagebuild import ( urce) urce) model.buildbase import handle_ status_ for_build, BuildBase model.buildfarm job import BuildFarmJobDerived model.buildfarm job import ( ived) interfaces. pocket import PackagePublishi ngPocket adapters. archivedependen cies import ( component_ dependency_ name) GIVENBACK( self, librarian, slave_status, logger): _handleStatus_ GIVENBACK( IPackageBuildSe t) hive(self, archive, status=None, pocket=None): et`."""
> --- lib/lp/
> +++ lib/lp/
> @@ -11,6 +11,7 @@
> from lazr.delegates import delegates
>
> from storm.locals import Int, Reference, Storm, Unicode
> +from storm.expr import Desc
>
> from zope.component import getUtility
> from zope.interface import classProvides, implements
> @@ -19,13 +20,16 @@
> from canonical.
> ProxiedLibraryF
> from canonical.
> +from canonical.
> + IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
>
> from lp.buildmaster.
> from lp.buildmaster.
> from lp.buildmaster.
> - IPackageBuild, IPackageBuildSo
> + IPackageBuild, IPackageBuildSet, IPackageBuildSo
> from lp.buildmaster.
> -from lp.buildmaster.
> +from lp.buildmaster.
> + BuildFarmJob, BuildFarmJobDer
> from lp.registry.
> from lp.soyuz.
> default_
> @@ -214,3 +218,40 @@
> def _handleStatus_
> return BuildBase.
> self, librarian, slave_status, logger)
> +
> +
> +class PackageBuildSet:
> + implements(
> +
> + def getBuildsForArc
> + """See `IPackageBuildS
> +
> + extra_exprs = []
> +
> + # Add query clause that filters on build status if the latter is
> + # provided.
I had been told that comments are not there to reiterate what the code is
doing - the code should speak for itself. Thi...