Merge lp:~cjwatson/launchpad/snap-build-behaviour into lp:launchpad
- snap-build-behaviour
- Merge into devel
Proposed by
Colin Watson
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 17677 | ||||
Proposed branch: | lp:~cjwatson/launchpad/snap-build-behaviour | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
648 lines (+457/-40) 10 files modified
database/schema/security.cfg (+2/-0) lib/lp/code/model/recipebuilder.py (+4/-26) lib/lp/code/model/tests/test_recipebuilder.py (+3/-3) lib/lp/services/config/schema-lazr.conf (+7/-0) lib/lp/snappy/configure.zcml (+7/-0) lib/lp/snappy/model/snapbuildbehaviour.py (+110/-0) lib/lp/snappy/tests/test_snapbuildbehaviour.py (+242/-0) lib/lp/soyuz/adapters/archivedependencies.py (+36/-11) lib/lp/soyuz/doc/archive-dependencies.txt (+44/-0) lib/lp/soyuz/interfaces/archive.py (+2/-0) |
||||
To merge this branch: | bzr merge lp:~cjwatson/launchpad/snap-build-behaviour | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+266736@code.launchpad.net |
Commit message
Add a build behaviour for snap packages.
Description of the change
Add a build behaviour for snap packages.
This involves adding support for a build tools archive which can be set in a config variable for individual build behaviours, since snapcraft is currently only available in a PPA. We should eventually make this more fine-grained, but that isn't urgent.
You may want to review this alongside https:/
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) : | # |
review:
Approve
(code)
Revision history for this message
William Grant (wgrant) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'database/schema/security.cfg' |
2 | --- database/schema/security.cfg 2015-07-30 12:43:33 +0000 |
3 | +++ database/schema/security.cfg 2015-08-05 12:26:41 +0000 |
4 | @@ -967,6 +967,8 @@ |
5 | public.distroseriesparent = SELECT |
6 | public.emailaddress = SELECT |
7 | public.flatpackagesetinclusion = SELECT |
8 | +public.gitref = SELECT |
9 | +public.gitrepository = SELECT |
10 | public.gpgkey = SELECT |
11 | public.libraryfilealias = SELECT, INSERT |
12 | public.libraryfilecontent = SELECT, INSERT |
13 | |
14 | === modified file 'lib/lp/code/model/recipebuilder.py' |
15 | --- lib/lp/code/model/recipebuilder.py 2015-07-09 12:18:51 +0000 |
16 | +++ lib/lp/code/model/recipebuilder.py 2015-08-05 12:26:41 +0000 |
17 | @@ -8,8 +8,6 @@ |
18 | 'RecipeBuildBehaviour', |
19 | ] |
20 | |
21 | -import traceback |
22 | - |
23 | from zope.component import adapter |
24 | from zope.interface import implementer |
25 | from zope.security.proxy import removeSecurityProxy |
26 | @@ -67,31 +65,11 @@ |
27 | args["ogrecomponent"] = get_primary_current_component( |
28 | self.build.archive, self.build.distroseries, |
29 | None) |
30 | - args['archives'] = get_sources_list_for_building(self.build, |
31 | - distroarchseries, None) |
32 | + args['archives'] = get_sources_list_for_building( |
33 | + self.build, distroarchseries, None, |
34 | + tools_source=config.builddmaster.bzr_builder_sources_list, |
35 | + logger=logger) |
36 | args['archive_private'] = self.build.archive.private |
37 | - |
38 | - # config.builddmaster.bzr_builder_sources_list can contain a |
39 | - # sources.list entry for an archive that will contain a |
40 | - # bzr-builder package that needs to be used to build this |
41 | - # recipe. |
42 | - try: |
43 | - extra_archive = config.builddmaster.bzr_builder_sources_list |
44 | - except AttributeError: |
45 | - extra_archive = None |
46 | - |
47 | - if extra_archive is not None: |
48 | - try: |
49 | - sources_line = extra_archive % ( |
50 | - {'series': self.build.distroseries.name}) |
51 | - args['archives'].append(sources_line) |
52 | - except StandardError: |
53 | - # Someone messed up the config, don't add it. |
54 | - if logger: |
55 | - logger.error( |
56 | - "Exception processing bzr_builder_sources_list:\n%s" |
57 | - % traceback.format_exc()) |
58 | - |
59 | args['distroseries_name'] = self.build.distroseries.name |
60 | return args |
61 | |
62 | |
63 | === modified file 'lib/lp/code/model/tests/test_recipebuilder.py' |
64 | --- lib/lp/code/model/tests/test_recipebuilder.py 2015-04-20 09:48:57 +0000 |
65 | +++ lib/lp/code/model/tests/test_recipebuilder.py 2015-08-05 12:26:41 +0000 |
66 | @@ -137,8 +137,8 @@ |
67 | distroarchseries = job.build.distroseries.architectures[0] |
68 | expected_archives = get_sources_list_for_building( |
69 | job.build, distroarchseries, None) |
70 | - expected_archives.append( |
71 | - "deb http://foo %s main" % job.build.distroseries.name) |
72 | + expected_archives.insert( |
73 | + 0, "deb http://foo %s main" % job.build.distroseries.name) |
74 | self.assertEqual({ |
75 | 'archive_private': False, |
76 | 'arch_tag': 'i386', |
77 | @@ -238,7 +238,7 @@ |
78 | 'distroseries_name': job.build.distroseries.name, |
79 | }, job._extraBuildArgs(distroarchseries, logger)) |
80 | self.assertIn( |
81 | - "Exception processing bzr_builder_sources_list:", |
82 | + "Exception processing build tools sources.list entry:", |
83 | logger.getLogBuffer()) |
84 | |
85 | def test_extraBuildArgs_withNoBZrBuilderConfigSet(self): |
86 | |
87 | === modified file 'lib/lp/services/config/schema-lazr.conf' |
88 | --- lib/lp/services/config/schema-lazr.conf 2015-08-03 08:18:09 +0000 |
89 | +++ lib/lp/services/config/schema-lazr.conf 2015-08-05 12:26:41 +0000 |
90 | @@ -1722,6 +1722,13 @@ |
91 | # credentials, so the proxy needs to be very carefully secured. |
92 | http_proxy: none |
93 | |
94 | +[snappy] |
95 | +# Optional sources.list entry to send to build slaves when doing snap |
96 | +# package builds. Use this form so that the series is set: |
97 | +# deb http://foo %(series)s main |
98 | +# datatype: string |
99 | +tools_source: none |
100 | + |
101 | [process-job-source-groups] |
102 | # This section is used by cronscripts/process-job-source-groups.py. |
103 | dbuser: process-job-source-groups |
104 | |
105 | === modified file 'lib/lp/snappy/configure.zcml' |
106 | --- lib/lp/snappy/configure.zcml 2015-07-30 15:19:51 +0000 |
107 | +++ lib/lp/snappy/configure.zcml 2015-08-05 12:26:41 +0000 |
108 | @@ -66,6 +66,13 @@ |
109 | <allow interface="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJobSource" /> |
110 | </securedutility> |
111 | |
112 | + <!-- SnapBuildBehaviour --> |
113 | + <adapter |
114 | + for="lp.snappy.interfaces.snapbuild.ISnapBuild" |
115 | + provides="lp.buildmaster.interfaces.buildfarmjobbehaviour.IBuildFarmJobBehaviour" |
116 | + factory="lp.snappy.model.snapbuildbehaviour.SnapBuildBehaviour" |
117 | + permission="zope.Public" /> |
118 | + |
119 | <!-- SnapFile --> |
120 | <class class="lp.snappy.model.snapbuild.SnapFile"> |
121 | <allow interface="lp.snappy.interfaces.snapbuild.ISnapFile" /> |
122 | |
123 | === added file 'lib/lp/snappy/model/snapbuildbehaviour.py' |
124 | --- lib/lp/snappy/model/snapbuildbehaviour.py 1970-01-01 00:00:00 +0000 |
125 | +++ lib/lp/snappy/model/snapbuildbehaviour.py 2015-08-05 12:26:41 +0000 |
126 | @@ -0,0 +1,110 @@ |
127 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
128 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
129 | + |
130 | +"""An `IBuildFarmJobBehaviour` for `SnapBuild`. |
131 | + |
132 | +Dispatches snap package build jobs to build-farm slaves. |
133 | +""" |
134 | + |
135 | +__metaclass__ = type |
136 | +__all__ = [ |
137 | + 'SnapBuildBehaviour', |
138 | + ] |
139 | + |
140 | +from zope.component import adapter |
141 | +from zope.interface import implementer |
142 | + |
143 | +from lp.buildmaster.interfaces.builder import CannotBuild |
144 | +from lp.buildmaster.interfaces.buildfarmjobbehaviour import ( |
145 | + IBuildFarmJobBehaviour, |
146 | + ) |
147 | +from lp.buildmaster.model.buildfarmjobbehaviour import ( |
148 | + BuildFarmJobBehaviourBase, |
149 | + ) |
150 | +from lp.registry.interfaces.series import SeriesStatus |
151 | +from lp.services.config import config |
152 | +from lp.snappy.interfaces.snap import SnapBuildArchiveOwnerMismatch |
153 | +from lp.snappy.interfaces.snapbuild import ISnapBuild |
154 | +from lp.soyuz.adapters.archivedependencies import ( |
155 | + get_sources_list_for_building, |
156 | + ) |
157 | +from lp.soyuz.interfaces.archive import ArchiveDisabled |
158 | + |
159 | + |
160 | +@adapter(ISnapBuild) |
161 | +@implementer(IBuildFarmJobBehaviour) |
162 | +class SnapBuildBehaviour(BuildFarmJobBehaviourBase): |
163 | + """Dispatches `SnapBuild` jobs to slaves.""" |
164 | + |
165 | + def getLogFileName(self): |
166 | + das = self.build.distro_arch_series |
167 | + |
168 | + # Examples: |
169 | + # buildlog_snap_ubuntu_wily_amd64_name_FULLYBUILT.txt |
170 | + return 'buildlog_snap_%s_%s_%s_%s_%s.txt' % ( |
171 | + das.distroseries.distribution.name, das.distroseries.name, |
172 | + das.architecturetag, self.build.snap.name, self.build.status.name) |
173 | + |
174 | + def verifyBuildRequest(self, logger): |
175 | + """Assert some pre-build checks. |
176 | + |
177 | + The build request is checked: |
178 | + * Virtualized builds can't build on a non-virtual builder |
179 | + * The source archive may not be disabled |
180 | + * If the source archive is private, the snap owner must match the |
181 | + archive owner (see `SnapBuildArchiveOwnerMismatch` docstring) |
182 | + * Ensure that we have a chroot |
183 | + """ |
184 | + build = self.build |
185 | + if build.virtualized and not self._builder.virtualized: |
186 | + raise AssertionError( |
187 | + "Attempt to build virtual item on a non-virtual builder.") |
188 | + |
189 | + if not build.archive.enabled: |
190 | + raise ArchiveDisabled(build.archive.displayname) |
191 | + if build.archive.private and build.snap.owner != build.archive.owner: |
192 | + raise SnapBuildArchiveOwnerMismatch() |
193 | + |
194 | + chroot = build.distro_arch_series.getChroot() |
195 | + if chroot is None: |
196 | + raise CannotBuild( |
197 | + "Missing chroot for %s" % build.distro_arch_series.displayname) |
198 | + |
199 | + def _extraBuildArgs(self, logger=None): |
200 | + """ |
201 | + Return the extra arguments required by the slave for the given build. |
202 | + """ |
203 | + build = self.build |
204 | + args = {} |
205 | + args["name"] = build.snap.name |
206 | + args["arch_tag"] = build.distro_arch_series.architecturetag |
207 | + # XXX cjwatson 2015-08-03: Allow tools_source to be overridden at |
208 | + # some more fine-grained level. |
209 | + args["archives"] = get_sources_list_for_building( |
210 | + build, build.distro_arch_series, None, |
211 | + tools_source=config.snappy.tools_source, logger=logger) |
212 | + args["archive_private"] = build.archive.private |
213 | + if build.snap.branch is not None: |
214 | + args["branch"] = build.snap.branch.bzr_identity |
215 | + elif build.snap.git_repository is not None: |
216 | + args["git_repository"] = build.snap.git_repository.git_https_url |
217 | + args["git_path"] = build.snap.git_path |
218 | + else: |
219 | + raise CannotBuild( |
220 | + "Source branch/repository for ~%s/%s has been deleted." % |
221 | + (build.snap.owner.name, build.snap.name)) |
222 | + return args |
223 | + |
224 | + def composeBuildRequest(self, logger): |
225 | + return ( |
226 | + "snap", self.build.distro_arch_series, {}, |
227 | + self._extraBuildArgs(logger=logger)) |
228 | + |
229 | + def verifySuccessfulBuild(self): |
230 | + """See `IBuildFarmJobBehaviour`.""" |
231 | + # The implementation in BuildFarmJobBehaviourBase checks whether the |
232 | + # target suite is modifiable in the target archive. However, a |
233 | + # `SnapBuild`'s archive is a source rather than a target, so that |
234 | + # check does not make sense. We do, however, refuse to build for |
235 | + # obsolete series. |
236 | + assert self.build.distro_series.status != SeriesStatus.OBSOLETE |
237 | |
238 | === added file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py' |
239 | --- lib/lp/snappy/tests/test_snapbuildbehaviour.py 1970-01-01 00:00:00 +0000 |
240 | +++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2015-08-05 12:26:41 +0000 |
241 | @@ -0,0 +1,242 @@ |
242 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
243 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
244 | + |
245 | +"""Test snap package build behaviour.""" |
246 | + |
247 | +__metaclass__ = type |
248 | + |
249 | +import fixtures |
250 | +import transaction |
251 | +from twisted.trial.unittest import TestCase as TrialTestCase |
252 | +from zope.component import getUtility |
253 | + |
254 | +from lp.buildmaster.enums import BuildStatus |
255 | +from lp.buildmaster.interfaces.builder import CannotBuild |
256 | +from lp.buildmaster.interfaces.buildfarmjobbehaviour import ( |
257 | + IBuildFarmJobBehaviour, |
258 | + ) |
259 | +from lp.buildmaster.interfaces.processor import IProcessorSet |
260 | +from lp.buildmaster.tests.mock_slaves import ( |
261 | + MockBuilder, |
262 | + OkSlave, |
263 | + ) |
264 | +from lp.buildmaster.tests.test_buildfarmjobbehaviour import ( |
265 | + TestGetUploadMethodsMixin, |
266 | + TestHandleStatusMixin, |
267 | + TestVerifySuccessfulBuildMixin, |
268 | + ) |
269 | +from lp.registry.interfaces.pocket import PackagePublishingPocket |
270 | +from lp.registry.interfaces.series import SeriesStatus |
271 | +from lp.services.features.testing import FeatureFixture |
272 | +from lp.services.log.logger import BufferLogger |
273 | +from lp.snappy.interfaces.snap import ( |
274 | + SNAP_FEATURE_FLAG, |
275 | + SnapBuildArchiveOwnerMismatch, |
276 | + ) |
277 | +from lp.snappy.model.snapbuildbehaviour import SnapBuildBehaviour |
278 | +from lp.soyuz.adapters.archivedependencies import ( |
279 | + get_sources_list_for_building, |
280 | + ) |
281 | +from lp.soyuz.interfaces.archive import ArchiveDisabled |
282 | +from lp.testing import TestCaseWithFactory |
283 | +from lp.testing.layers import LaunchpadZopelessLayer |
284 | + |
285 | + |
286 | +class TestSnapBuildBehaviour(TestCaseWithFactory): |
287 | + |
288 | + layer = LaunchpadZopelessLayer |
289 | + |
290 | + def setUp(self): |
291 | + super(TestSnapBuildBehaviour, self).setUp() |
292 | + self.useFixture(FeatureFixture({SNAP_FEATURE_FLAG: u"on"})) |
293 | + |
294 | + def makeJob(self, pocket=PackagePublishingPocket.RELEASE, **kwargs): |
295 | + """Create a sample `ISnapBuildBehaviour`.""" |
296 | + distribution = self.factory.makeDistribution(name="distro") |
297 | + distroseries = self.factory.makeDistroSeries( |
298 | + distribution=distribution, name="unstable") |
299 | + processor = getUtility(IProcessorSet).getByName("386") |
300 | + distroarchseries = self.factory.makeDistroArchSeries( |
301 | + distroseries=distroseries, architecturetag="i386", |
302 | + processor=processor) |
303 | + build = self.factory.makeSnapBuild( |
304 | + distroarchseries=distroarchseries, pocket=pocket, |
305 | + name=u"test-snap", **kwargs) |
306 | + return IBuildFarmJobBehaviour(build) |
307 | + |
308 | + def test_provides_interface(self): |
309 | + # SnapBuildBehaviour provides IBuildFarmJobBehaviour. |
310 | + job = SnapBuildBehaviour(None) |
311 | + self.assertProvides(job, IBuildFarmJobBehaviour) |
312 | + |
313 | + def test_adapts_ISnapBuild(self): |
314 | + # IBuildFarmJobBehaviour adapts an ISnapBuild. |
315 | + build = self.factory.makeSnapBuild() |
316 | + job = IBuildFarmJobBehaviour(build) |
317 | + self.assertProvides(job, IBuildFarmJobBehaviour) |
318 | + |
319 | + def test_verifyBuildRequest_valid(self): |
320 | + # verifyBuildRequest doesn't raise any exceptions when called with a |
321 | + # valid builder set. |
322 | + job = self.makeJob() |
323 | + lfa = self.factory.makeLibraryFileAlias() |
324 | + transaction.commit() |
325 | + job.build.distro_arch_series.addOrUpdateChroot(lfa) |
326 | + builder = MockBuilder() |
327 | + job.setBuilder(builder, OkSlave()) |
328 | + logger = BufferLogger() |
329 | + job.verifyBuildRequest(logger) |
330 | + self.assertEqual("", logger.getLogBuffer()) |
331 | + |
332 | + def test_verifyBuildRequest_virtual_mismatch(self): |
333 | + # verifyBuildRequest raises on an attempt to build a virtualized |
334 | + # build on a non-virtual builder. |
335 | + job = self.makeJob() |
336 | + lfa = self.factory.makeLibraryFileAlias() |
337 | + transaction.commit() |
338 | + job.build.distro_arch_series.addOrUpdateChroot(lfa) |
339 | + builder = MockBuilder(virtualized=False) |
340 | + job.setBuilder(builder, OkSlave()) |
341 | + logger = BufferLogger() |
342 | + e = self.assertRaises(AssertionError, job.verifyBuildRequest, logger) |
343 | + self.assertEqual( |
344 | + "Attempt to build virtual item on a non-virtual builder.", str(e)) |
345 | + |
346 | + def test_verifyBuildRequest_archive_disabled(self): |
347 | + archive = self.factory.makeArchive( |
348 | + enabled=False, displayname="Disabled Archive") |
349 | + job = self.makeJob(archive=archive) |
350 | + lfa = self.factory.makeLibraryFileAlias() |
351 | + transaction.commit() |
352 | + job.build.distro_arch_series.addOrUpdateChroot(lfa) |
353 | + builder = MockBuilder() |
354 | + job.setBuilder(builder, OkSlave()) |
355 | + logger = BufferLogger() |
356 | + e = self.assertRaises(ArchiveDisabled, job.verifyBuildRequest, logger) |
357 | + self.assertEqual("Disabled Archive is disabled.", str(e)) |
358 | + |
359 | + def test_verifyBuildRequest_archive_private_owners_match(self): |
360 | + archive = self.factory.makeArchive(private=True) |
361 | + job = self.makeJob( |
362 | + archive=archive, registrant=archive.owner, owner=archive.owner) |
363 | + lfa = self.factory.makeLibraryFileAlias() |
364 | + transaction.commit() |
365 | + job.build.distro_arch_series.addOrUpdateChroot(lfa) |
366 | + builder = MockBuilder() |
367 | + job.setBuilder(builder, OkSlave()) |
368 | + logger = BufferLogger() |
369 | + job.verifyBuildRequest(logger) |
370 | + self.assertEqual("", logger.getLogBuffer()) |
371 | + |
372 | + def test_verifyBuildRequest_archive_private_owners_mismatch(self): |
373 | + archive = self.factory.makeArchive(private=True) |
374 | + job = self.makeJob(archive=archive) |
375 | + lfa = self.factory.makeLibraryFileAlias() |
376 | + transaction.commit() |
377 | + job.build.distro_arch_series.addOrUpdateChroot(lfa) |
378 | + builder = MockBuilder() |
379 | + job.setBuilder(builder, OkSlave()) |
380 | + logger = BufferLogger() |
381 | + e = self.assertRaises( |
382 | + SnapBuildArchiveOwnerMismatch, job.verifyBuildRequest, logger) |
383 | + self.assertEqual( |
384 | + "Snap package builds against private archives are only allowed " |
385 | + "if the snap package owner and the archive owner are equal.", |
386 | + str(e)) |
387 | + |
388 | + def test_verifyBuildRequest_no_chroot(self): |
389 | + # verifyBuildRequest raises when the DAS has no chroot. |
390 | + job = self.makeJob() |
391 | + builder = MockBuilder() |
392 | + job.setBuilder(builder, OkSlave()) |
393 | + logger = BufferLogger() |
394 | + e = self.assertRaises(CannotBuild, job.verifyBuildRequest, logger) |
395 | + self.assertIn("Missing chroot", str(e)) |
396 | + |
397 | + def test_extraBuildArgs_bzr(self): |
398 | + # _extraBuildArgs returns appropriate arguments if asked to build a |
399 | + # job for a Bazaar branch. |
400 | + branch = self.factory.makeBranch() |
401 | + job = self.makeJob(branch=branch) |
402 | + expected_archives = get_sources_list_for_building( |
403 | + job.build, job.build.distro_arch_series, None) |
404 | + self.assertEqual({ |
405 | + "archive_private": False, |
406 | + "archives": expected_archives, |
407 | + "arch_tag": "i386", |
408 | + "branch": branch.bzr_identity, |
409 | + "name": u"test-snap", |
410 | + }, job._extraBuildArgs()) |
411 | + |
412 | + def test_extraBuildArgs_git(self): |
413 | + # _extraBuildArgs returns appropriate arguments if asked to build a |
414 | + # job for a Git branch. |
415 | + [ref] = self.factory.makeGitRefs() |
416 | + job = self.makeJob(git_ref=ref) |
417 | + expected_archives = get_sources_list_for_building( |
418 | + job.build, job.build.distro_arch_series, None) |
419 | + self.assertEqual({ |
420 | + "archive_private": False, |
421 | + "archives": expected_archives, |
422 | + "arch_tag": "i386", |
423 | + "git_repository": ref.repository.git_https_url, |
424 | + "git_path": ref.path, |
425 | + "name": u"test-snap", |
426 | + }, job._extraBuildArgs()) |
427 | + |
428 | + def test_composeBuildRequest(self): |
429 | + job = self.makeJob() |
430 | + lfa = self.factory.makeLibraryFileAlias(db_only=True) |
431 | + job.build.distro_arch_series.addOrUpdateChroot(lfa) |
432 | + self.assertEqual( |
433 | + ('snap', job.build.distro_arch_series, {}, |
434 | + job._extraBuildArgs()), |
435 | + job.composeBuildRequest(None)) |
436 | + |
437 | + def test_composeBuildRequest_deleted(self): |
438 | + # If the source branch/repository has been deleted, |
439 | + # composeBuildRequest raises CannotBuild. |
440 | + branch = self.factory.makeBranch() |
441 | + owner = self.factory.makePerson(name="snap-owner") |
442 | + job = self.makeJob(registrant=owner, owner=owner, branch=branch) |
443 | + branch.destroySelf(break_references=True) |
444 | + self.assertIsNone(job.build.snap.branch) |
445 | + self.assertIsNone(job.build.snap.git_repository) |
446 | + self.assertRaisesWithContent( |
447 | + CannotBuild, |
448 | + "Source branch/repository for ~snap-owner/test-snap has been " |
449 | + "deleted.", |
450 | + job.composeBuildRequest, None) |
451 | + |
452 | + |
453 | +class MakeSnapBuildMixin: |
454 | + """Provide the common makeBuild method returning a queued build.""" |
455 | + |
456 | + def makeBuild(self): |
457 | + self.useFixture(FeatureFixture({SNAP_FEATURE_FLAG: u"on"})) |
458 | + build = self.factory.makeSnapBuild(status=BuildStatus.BUILDING) |
459 | + build.queueBuild() |
460 | + return build |
461 | + |
462 | + def makeUnmodifiableBuild(self): |
463 | + self.useFixture(FeatureFixture({SNAP_FEATURE_FLAG: u"on"})) |
464 | + build = self.factory.makeSnapBuild(status=BuildStatus.BUILDING) |
465 | + build.distro_series.status = SeriesStatus.OBSOLETE |
466 | + build.queueBuild() |
467 | + return build |
468 | + |
469 | + |
470 | +class TestGetUploadMethodsForSnapBuild( |
471 | + MakeSnapBuildMixin, TestGetUploadMethodsMixin, TestCaseWithFactory): |
472 | + """IPackageBuild.getUpload-related methods work with Snap builds.""" |
473 | + |
474 | + |
475 | +class TestVerifySuccessfulBuildForSnapBuild( |
476 | + MakeSnapBuildMixin, TestVerifySuccessfulBuildMixin, TestCaseWithFactory): |
477 | + """IBuildFarmJobBehaviour.verifySuccessfulBuild works.""" |
478 | + |
479 | + |
480 | +class TestHandleStatusForSnapBuild( |
481 | + MakeSnapBuildMixin, TestHandleStatusMixin, TrialTestCase, |
482 | + fixtures.TestWithFixtures): |
483 | + """IPackageBuild.handleStatus works with Snap builds.""" |
484 | |
485 | === modified file 'lib/lp/soyuz/adapters/archivedependencies.py' |
486 | --- lib/lp/soyuz/adapters/archivedependencies.py 2014-12-11 21:24:19 +0000 |
487 | +++ lib/lp/soyuz/adapters/archivedependencies.py 2015-08-05 12:26:41 +0000 |
488 | @@ -133,7 +133,7 @@ |
489 | |
490 | |
491 | def expand_dependencies(archive, distro_arch_series, pocket, component, |
492 | - source_package_name): |
493 | + source_package_name, tools_source=None, logger=None): |
494 | """Return the set of dependency archives, pockets and components. |
495 | |
496 | :param archive: the context `IArchive`. |
497 | @@ -141,6 +141,10 @@ |
498 | :param pocket: the context `PackagePublishingPocket`. |
499 | :param component: the context `IComponent`. |
500 | :param source_package_name: A source package name (as text) |
501 | + :param tools_source: if not None, a sources.list entry to use as an |
502 | + additional dependency for build tools, just before the default |
503 | + primary archive. |
504 | + :param logger: an optional logger. |
505 | :return: a list of (archive, distro_arch_series, pocket, [component]), |
506 | representing the dependencies defined by the given build context. |
507 | """ |
508 | @@ -172,6 +176,17 @@ |
509 | (archive_dependency.dependency, distro_arch_series, pocket, |
510 | components)) |
511 | |
512 | + # Consider build tools archive dependencies. |
513 | + if tools_source is not None: |
514 | + try: |
515 | + deps.append(tools_source % {'series': distro_series.name}) |
516 | + except Exception: |
517 | + # Someone messed up the configuration; don't add it. |
518 | + if logger is not None: |
519 | + logger.error( |
520 | + "Exception processing build tools sources.list entry:\n%s" |
521 | + % traceback.format_exc()) |
522 | + |
523 | # Consider primary archive dependency override. Add the default |
524 | # primary archive dependencies if it's not present. |
525 | if archive.getArchiveDependency( |
526 | @@ -201,7 +216,8 @@ |
527 | return deps |
528 | |
529 | |
530 | -def get_sources_list_for_building(build, distroarchseries, sourcepackagename): |
531 | +def get_sources_list_for_building(build, distroarchseries, sourcepackagename, |
532 | + tools_source=None, logger=None): |
533 | """Return the sources_list entries required to build the given item. |
534 | |
535 | The entries are returned in the order that is most useful; |
536 | @@ -213,11 +229,16 @@ |
537 | :param build: a context `IBuild`. |
538 | :param distroarchseries: A `IDistroArchSeries` |
539 | :param sourcepackagename: A source package name (as text) |
540 | + :param tools_source: if not None, a sources.list entry to use as an |
541 | + additional dependency for build tools, just before the default |
542 | + primary archive. |
543 | + :param logger: an optional logger. |
544 | :return: a deb sources_list entries (lines). |
545 | """ |
546 | deps = expand_dependencies( |
547 | build.archive, distroarchseries, build.pocket, |
548 | - build.current_component, sourcepackagename) |
549 | + build.current_component, sourcepackagename, |
550 | + tools_source=tools_source, logger=logger) |
551 | sources_list_lines = \ |
552 | _get_sources_list_for_dependencies(deps) |
553 | |
554 | @@ -296,14 +317,18 @@ |
555 | :return: a list of sources_list formatted lines. |
556 | """ |
557 | sources_list_lines = [] |
558 | - for archive, distro_arch_series, pocket, components in dependencies: |
559 | - has_published_binaries = _has_published_binaries( |
560 | - archive, distro_arch_series, pocket) |
561 | - if not has_published_binaries: |
562 | - continue |
563 | - sources_list_line = _get_binary_sources_list_line( |
564 | - archive, distro_arch_series, pocket, components) |
565 | - sources_list_lines.append(sources_list_line) |
566 | + for dep in dependencies: |
567 | + if isinstance(dep, basestring): |
568 | + sources_list_lines.append(dep) |
569 | + else: |
570 | + archive, distro_arch_series, pocket, components = dep |
571 | + has_published_binaries = _has_published_binaries( |
572 | + archive, distro_arch_series, pocket) |
573 | + if not has_published_binaries: |
574 | + continue |
575 | + sources_list_line = _get_binary_sources_list_line( |
576 | + archive, distro_arch_series, pocket, components) |
577 | + sources_list_lines.append(sources_list_line) |
578 | |
579 | return sources_list_lines |
580 | |
581 | |
582 | === modified file 'lib/lp/soyuz/doc/archive-dependencies.txt' |
583 | --- lib/lp/soyuz/doc/archive-dependencies.txt 2014-12-11 21:52:15 +0000 |
584 | +++ lib/lp/soyuz/doc/archive-dependencies.txt 2015-08-05 12:26:41 +0000 |
585 | @@ -499,6 +499,50 @@ |
586 | deb http://archive.launchpad.dev/ubuntu hoary-updates |
587 | main restricted universe multiverse |
588 | |
589 | + >>> cprov.archive.external_dependencies = None |
590 | + |
591 | + |
592 | +== Build tools sources.list entries == |
593 | + |
594 | +We can force an extra build tools line to be added to the sources.list, |
595 | +which is useful for specialised build types. |
596 | + |
597 | + >>> for line in get_sources_list_for_building( |
598 | + ... a_build, a_build.distro_arch_series, |
599 | + ... a_build.source_package_release.name, |
600 | + ... tools_source="deb http://example.org %(series)s main"): |
601 | + ... print line |
602 | + deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main |
603 | + deb http://example.org hoary main |
604 | + deb http://archive.launchpad.dev/ubuntu hoary |
605 | + main restricted universe multiverse |
606 | + deb http://archive.launchpad.dev/ubuntu hoary-security |
607 | + main restricted universe multiverse |
608 | + deb http://archive.launchpad.dev/ubuntu hoary-updates |
609 | + main restricted universe multiverse |
610 | + |
611 | +If tools_source is badly formatted, we log the error but don't blow up. |
612 | +(Note the missing "s" at the end of "%(series)".) |
613 | + |
614 | + >>> from lp.services.log.logger import BufferLogger |
615 | + >>> logger = BufferLogger() |
616 | + >>> for line in get_sources_list_for_building( |
617 | + ... a_build, a_build.distro_arch_series, |
618 | + ... a_build.source_package_release.name, |
619 | + ... tools_source="deb http://example.org %(series) main", |
620 | + ... logger=logger): |
621 | + ... print line |
622 | + deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main |
623 | + deb http://archive.launchpad.dev/ubuntu hoary |
624 | + main restricted universe multiverse |
625 | + deb http://archive.launchpad.dev/ubuntu hoary-security |
626 | + main restricted universe multiverse |
627 | + deb http://archive.launchpad.dev/ubuntu hoary-updates |
628 | + main restricted universe multiverse |
629 | + >>> print logger.getLogBuffer() |
630 | + ERROR Exception processing build tools sources.list entry: |
631 | + ... |
632 | + |
633 | |
634 | == Overlays == |
635 | |
636 | |
637 | === modified file 'lib/lp/soyuz/interfaces/archive.py' |
638 | --- lib/lp/soyuz/interfaces/archive.py 2015-05-18 06:36:51 +0000 |
639 | +++ lib/lp/soyuz/interfaces/archive.py 2015-08-05 12:26:41 +0000 |
640 | @@ -2290,6 +2290,8 @@ |
641 | :param ext_deps: The dependencies form field to check. |
642 | """ |
643 | errors = [] |
644 | + if ext_deps is None: |
645 | + return errors |
646 | # The field can consist of multiple entries separated by |
647 | # newlines, so process each in turn. |
648 | for dep in ext_deps.splitlines(): |
What happens if the Git repository or Bazaar branch is deleted? I think it'll raise an AssertionError and be failure-counted, but we should check.