Merge lp:~cjwatson/launchpad/snap-build-channels into lp:launchpad

Proposed by Colin Watson on 2018-02-08
Status: Needs review
Proposed branch: lp:~cjwatson/launchpad/snap-build-channels
Merge into: lp:launchpad
Diff against target: 523 lines (+180/-26)
9 files modified
lib/lp/snappy/interfaces/snap.py (+18/-3)
lib/lp/snappy/interfaces/snapbuild.py (+9/-1)
lib/lp/snappy/model/snap.py (+28/-10)
lib/lp/snappy/model/snapbuild.py (+7/-3)
lib/lp/snappy/model/snapbuildbehaviour.py (+3/-1)
lib/lp/snappy/tests/test_snap.py (+93/-1)
lib/lp/snappy/tests/test_snapbuild.py (+1/-0)
lib/lp/snappy/tests/test_snapbuildbehaviour.py (+12/-1)
lib/lp/testing/factory.py (+9/-6)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-build-channels
Reviewer Review Type Date Requested Status
Launchpad code reviewers 2018-02-08 Pending
Review via email: mp+337360@code.launchpad.net

Commit Message

Add the ability to select source channels when building snaps.

Description of the Change

This will let us run snap builds where snapcraft is installed as a snap. It depends on https://code.launchpad.net/~cjwatson/launchpad-buildd/snapception/+merge/337126 and https://code.launchpad.net/~cjwatson/launchpad/db-snap-build-channels/+merge/337361.

We'll probably also want a feature flag to change the default, but that can be added a little later, once the basic mechanism works.

To post a comment you must log in.

Unmerged revisions

18545. By Colin Watson on 2018-02-08

Add the ability to select source channels when building snaps.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/snappy/interfaces/snap.py'
2--- lib/lp/snappy/interfaces/snap.py 2017-08-22 11:36:30 +0000
3+++ lib/lp/snappy/interfaces/snap.py 2018-02-08 13:37:58 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
6+# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Snap package interfaces."""
10@@ -63,6 +63,7 @@
11 Bool,
12 Choice,
13 Datetime,
14+ Dict,
15 Int,
16 List,
17 Text,
18@@ -281,17 +282,23 @@
19 @operation_parameters(
20 archive=Reference(schema=IArchive),
21 distro_arch_series=Reference(schema=IDistroArchSeries),
22- pocket=Choice(vocabulary=PackagePublishingPocket))
23+ pocket=Choice(vocabulary=PackagePublishingPocket),
24+ channels=Dict(
25+ title=_("Source channels to use for this build."),
26+ key_type=TextLine(), required=False))
27 # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
28 @export_factory_operation(Interface, [])
29 @operation_for_version("devel")
30- def requestBuild(requester, archive, distro_arch_series, pocket):
31+ def requestBuild(requester, archive, distro_arch_series, pocket,
32+ channels=None):
33 """Request that the snap package be built.
34
35 :param requester: The person requesting the build.
36 :param archive: The IArchive to associate the build with.
37 :param distro_arch_series: The architecture to build for.
38 :param pocket: The pocket that should be targeted.
39+ :param channels: A dictionary mapping snap names to channels to use
40+ for this build.
41 :return: `ISnapBuild`.
42 """
43
44@@ -509,6 +516,14 @@
45 "The package stream within the source distribution series to use "
46 "when building the snap package.")))
47
48+ auto_build_channels = exported(Dict(
49+ title=_("Source channels for automatic builds"),
50+ key_type=TextLine(), required=False, readonly=False,
51+ description=_(
52+ "A dictionary mapping snap names to channels to use when building "
53+ "this snap package. Currently only 'core' and 'snapcraft' keys "
54+ "are supported.")))
55+
56 is_stale = Bool(
57 title=_("Snap package is stale and is due to be rebuilt."),
58 required=True, readonly=False)
59
60=== modified file 'lib/lp/snappy/interfaces/snapbuild.py'
61--- lib/lp/snappy/interfaces/snapbuild.py 2017-04-27 16:22:37 +0000
62+++ lib/lp/snappy/interfaces/snapbuild.py 2018-02-08 13:37:58 +0000
63@@ -1,4 +1,4 @@
64-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
65+# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
66 # GNU Affero General Public License version 3 (see the file LICENSE).
67
68 """Snap package build interfaces."""
69@@ -39,6 +39,7 @@
70 Bool,
71 Choice,
72 Datetime,
73+ Dict,
74 Int,
75 TextLine,
76 )
77@@ -147,6 +148,13 @@
78 title=_("The pocket for which to build."),
79 vocabulary=PackagePublishingPocket, required=True, readonly=True))
80
81+ channels = exported(Dict(
82+ title=_("Source channels to use for this build."),
83+ description=_(
84+ "A dictionary mapping snap names to channels to use for this "
85+ "build. Currently only 'core' and 'snapcraft' keys are "
86+ "supported.")))
87+
88 virtualized = Bool(
89 title=_("If True, this build is virtualized."), readonly=True)
90
91
92=== modified file 'lib/lp/snappy/model/snap.py'
93--- lib/lp/snappy/model/snap.py 2017-11-10 11:23:27 +0000
94+++ lib/lp/snappy/model/snap.py 2018-02-08 13:37:58 +0000
95@@ -1,4 +1,4 @@
96-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
97+# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
98 # GNU Affero General Public License version 3 (see the file LICENSE).
99
100 __metaclass__ = type
101@@ -189,6 +189,8 @@
102
103 auto_build_pocket = DBEnum(enum=PackagePublishingPocket, allow_none=True)
104
105+ auto_build_channels = JSON('auto_build_channels', allow_none=True)
106+
107 is_stale = Bool(name='is_stale', allow_none=False)
108
109 require_virtualized = Bool(name='require_virtualized')
110@@ -209,9 +211,10 @@
111 def __init__(self, registrant, owner, distro_series, name,
112 description=None, branch=None, git_ref=None, auto_build=False,
113 auto_build_archive=None, auto_build_pocket=None,
114- require_virtualized=True, date_created=DEFAULT,
115- private=False, store_upload=False, store_series=None,
116- store_name=None, store_secrets=None, store_channels=None):
117+ auto_build_channels=None, require_virtualized=True,
118+ date_created=DEFAULT, private=False, store_upload=False,
119+ store_series=None, store_name=None, store_secrets=None,
120+ store_channels=None):
121 """Construct a `Snap`."""
122 super(Snap, self).__init__()
123 self.registrant = registrant
124@@ -224,6 +227,7 @@
125 self.auto_build = auto_build
126 self.auto_build_archive = auto_build_archive
127 self.auto_build_pocket = auto_build_pocket
128+ self.auto_build_channels = auto_build_channels
129 self.require_virtualized = require_virtualized
130 self.date_created = date_created
131 self.date_last_modified = date_created
132@@ -439,7 +443,8 @@
133 return False
134 return True
135
136- def requestBuild(self, requester, archive, distro_arch_series, pocket):
137+ def requestBuild(self, requester, archive, distro_arch_series, pocket,
138+ channels=None):
139 """See `ISnap`."""
140 if not requester.inTeam(self.owner):
141 raise SnapNotOwner(
142@@ -459,12 +464,14 @@
143 SnapBuild.archive_id == archive.id,
144 SnapBuild.distro_arch_series_id == distro_arch_series.id,
145 SnapBuild.pocket == pocket,
146+ SnapBuild.channels == channels,
147 SnapBuild.status == BuildStatus.NEEDSBUILD)
148 if pending.any() is not None:
149 raise SnapBuildAlreadyPending
150
151 build = getUtility(ISnapBuildSet).new(
152- requester, self, archive, distro_arch_series, pocket)
153+ requester, self, archive, distro_arch_series, pocket,
154+ channels=channels)
155 build.queueBuild()
156 return build
157
158@@ -484,7 +491,7 @@
159 try:
160 build = self.requestBuild(
161 self.owner, self.auto_build_archive, arch,
162- self.auto_build_pocket)
163+ self.auto_build_pocket, self.auto_build_channels)
164 if logger is not None:
165 logger.debug(
166 " - %s/%s/%s: Build requested.",
167@@ -659,9 +666,10 @@
168 branch=None, git_repository=None, git_repository_url=None,
169 git_path=None, git_ref=None, auto_build=False,
170 auto_build_archive=None, auto_build_pocket=None,
171- require_virtualized=True, processors=None, date_created=DEFAULT,
172- private=False, store_upload=False, store_series=None,
173- store_name=None, store_secrets=None, store_channels=None):
174+ auto_build_channels=None, require_virtualized=True,
175+ processors=None, date_created=DEFAULT, private=False,
176+ store_upload=False, store_series=None, store_name=None,
177+ store_secrets=None, store_channels=None):
178 """See `ISnapSet`."""
179 if not registrant.inTeam(owner):
180 if owner.is_team:
181@@ -702,6 +710,7 @@
182 branch=branch, git_ref=git_ref, auto_build=auto_build,
183 auto_build_archive=auto_build_archive,
184 auto_build_pocket=auto_build_pocket,
185+ auto_build_channels=auto_build_channels,
186 require_virtualized=require_virtualized, date_created=date_created,
187 private=private, store_upload=store_upload,
188 store_series=store_series, store_name=store_name,
189@@ -917,6 +926,15 @@
190 SnapBuild.snap_id == Snap.id,
191 SnapBuild.archive_id == Snap.auto_build_archive_id,
192 SnapBuild.pocket == Snap.auto_build_pocket,
193+ # These columns are nullable so require some care, since
194+ # a straightforward equality check will compile to
195+ # "SnapBuild.channels = Snap.auto_build_channels" which
196+ # is false if both are NULL.
197+ Or(
198+ And(
199+ SnapBuild.channels == None,
200+ Snap.auto_build_channels == None),
201+ SnapBuild.channels == Snap.auto_build_channels),
202 # We only want Snaps that haven't had an automatic
203 # SnapBuild dispatched for them recently.
204 SnapBuild.date_created >= threshold_date)),
205
206=== modified file 'lib/lp/snappy/model/snapbuild.py'
207--- lib/lp/snappy/model/snapbuild.py 2018-01-23 10:59:44 +0000
208+++ lib/lp/snappy/model/snapbuild.py 2018-02-08 13:37:58 +0000
209@@ -22,6 +22,7 @@
210 DateTime,
211 Desc,
212 Int,
213+ JSON,
214 Reference,
215 Select,
216 SQL,
217@@ -142,6 +143,8 @@
218
219 pocket = DBEnum(enum=PackagePublishingPocket, allow_none=False)
220
221+ channels = JSON('channels', allow_none=True)
222+
223 processor_id = Int(name='processor', allow_none=False)
224 processor = Reference(processor_id, 'Processor.id')
225 virtualized = Bool(name='virtualized')
226@@ -171,7 +174,7 @@
227 failure_count = Int(name='failure_count', allow_none=False)
228
229 def __init__(self, build_farm_job, requester, snap, archive,
230- distro_arch_series, pocket, processor, virtualized,
231+ distro_arch_series, pocket, channels, processor, virtualized,
232 date_created):
233 """Construct a `SnapBuild`."""
234 super(SnapBuild, self).__init__()
235@@ -181,6 +184,7 @@
236 self.archive = archive
237 self.distro_arch_series = distro_arch_series
238 self.pocket = pocket
239+ self.channels = channels
240 self.processor = processor
241 self.virtualized = virtualized
242 self.date_created = date_created
243@@ -479,7 +483,7 @@
244 class SnapBuildSet(SpecificBuildFarmJobSourceMixin):
245
246 def new(self, requester, snap, archive, distro_arch_series, pocket,
247- date_created=DEFAULT):
248+ channels=None, date_created=DEFAULT):
249 """See `ISnapBuildSet`."""
250 store = IMasterStore(SnapBuild)
251 build_farm_job = getUtility(IBuildFarmJobSource).new(
252@@ -487,7 +491,7 @@
253 archive)
254 snapbuild = SnapBuild(
255 build_farm_job, requester, snap, archive, distro_arch_series,
256- pocket, distro_arch_series.processor,
257+ pocket, channels, distro_arch_series.processor,
258 not distro_arch_series.processor.supports_nonvirtualized
259 or snap.require_virtualized or archive.require_virtualized,
260 date_created)
261
262=== modified file 'lib/lp/snappy/model/snapbuildbehaviour.py'
263--- lib/lp/snappy/model/snapbuildbehaviour.py 2017-07-25 18:01:04 +0000
264+++ lib/lp/snappy/model/snapbuildbehaviour.py 2018-02-08 13:37:58 +0000
265@@ -1,4 +1,4 @@
266-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
267+# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
268 # GNU Affero General Public License version 3 (see the file LICENSE).
269
270 """An `IBuildFarmJobBehaviour` for `SnapBuild`.
271@@ -107,6 +107,8 @@
272 tools_fingerprint=config.snappy.tools_fingerprint,
273 logger=logger))
274 args["archive_private"] = build.archive.private
275+ if build.channels is not None:
276+ args["channels"] = build.channels
277 if build.snap.branch is not None:
278 args["branch"] = build.snap.branch.bzr_identity
279 elif build.snap.git_ref is not None:
280
281=== modified file 'lib/lp/snappy/tests/test_snap.py'
282--- lib/lp/snappy/tests/test_snap.py 2018-01-23 18:54:30 +0000
283+++ lib/lp/snappy/tests/test_snap.py 2018-02-08 13:37:58 +0000
284@@ -54,6 +54,7 @@
285 ONE_DAY_AGO,
286 UTC_NOW,
287 )
288+from lp.services.database.interfaces import IStore
289 from lp.services.database.sqlbase import flush_database_caches
290 from lp.services.features.testing import (
291 FeatureFixture,
292@@ -188,6 +189,7 @@
293 self.assertEqual(snap.distro_series.main_archive, build.archive)
294 self.assertEqual(distroarchseries, build.distro_arch_series)
295 self.assertEqual(PackagePublishingPocket.UPDATES, build.pocket)
296+ self.assertIsNone(build.channels)
297 self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
298 store = Store.of(build)
299 store.flush()
300@@ -232,6 +234,18 @@
301 queue_record.score()
302 self.assertEqual(2610, queue_record.lastscore)
303
304+ def test_requestBuild_channels(self):
305+ # requestBuild can select non-default channels.
306+ processor = self.factory.makeProcessor(supports_virtualized=True)
307+ distroarchseries = self.makeBuildableDistroArchSeries(
308+ processor=processor)
309+ snap = self.factory.makeSnap(
310+ distroseries=distroarchseries.distroseries, processors=[processor])
311+ build = snap.requestBuild(
312+ snap.owner, snap.distro_series.main_archive, distroarchseries,
313+ PackagePublishingPocket.UPDATES, channels={"snapcraft": "edge"})
314+ self.assertEqual({"snapcraft": "edge"}, build.channels)
315+
316 def test_requestBuild_rejects_repeats(self):
317 # requestBuild refuses if there is already a pending build.
318 distroseries = self.factory.makeDistroSeries()
319@@ -355,10 +369,36 @@
320 with person_logged_in(snap.owner):
321 builds = snap.requestAutoBuilds()
322 self.assertThat(builds, MatchesSetwise(
323+ *(MatchesStructure(
324+ requester=Equals(snap.owner), snap=Equals(snap),
325+ archive=Equals(archive), distro_arch_series=Equals(das),
326+ pocket=Equals(PackagePublishingPocket.PROPOSED),
327+ channels=Is(None))
328+ for das in dases[:2])))
329+
330+ def test_requestAutoBuilds_channels(self):
331+ # requestAutoBuilds honours Snap.auto_build_channels.
332+ distroseries = self.factory.makeDistroSeries()
333+ dases = []
334+ for _ in range(3):
335+ processor = self.factory.makeProcessor(supports_virtualized=True)
336+ dases.append(self.makeBuildableDistroArchSeries(
337+ distroseries=distroseries, processor=processor))
338+ archive = self.factory.makeArchive()
339+ snap = self.factory.makeSnap(
340+ distroseries=distroseries,
341+ processors=[das.processor for das in dases[:2]],
342+ auto_build_archive=archive,
343+ auto_build_pocket=PackagePublishingPocket.PROPOSED,
344+ auto_build_channels={"snapcraft": "edge"})
345+ with person_logged_in(snap.owner):
346+ builds = snap.requestAutoBuilds()
347+ self.assertThat(builds, MatchesSetwise(
348 *(MatchesStructure.byEquality(
349 requester=snap.owner, snap=snap, archive=archive,
350 distro_arch_series=das,
351- pocket=PackagePublishingPocket.PROPOSED)
352+ pocket=PackagePublishingPocket.PROPOSED,
353+ channels={"snapcraft": "edge"})
354 for das in dases[:2])))
355
356 def test_getBuilds(self):
357@@ -610,6 +650,7 @@
358 self.assertFalse(snap.auto_build)
359 self.assertIsNone(snap.auto_build_archive)
360 self.assertIsNone(snap.auto_build_pocket)
361+ self.assertIsNone(snap.auto_build_channels)
362 self.assertTrue(snap.require_virtualized)
363 self.assertFalse(snap.private)
364
365@@ -630,6 +671,7 @@
366 self.assertFalse(snap.auto_build)
367 self.assertIsNone(snap.auto_build_archive)
368 self.assertIsNone(snap.auto_build_pocket)
369+ self.assertIsNone(snap.auto_build_channels)
370 self.assertTrue(snap.require_virtualized)
371 self.assertFalse(snap.private)
372
373@@ -1008,6 +1050,56 @@
374 self.assertEqual([], builds)
375 self.assertEqual([], logger.getLogBuffer().splitlines())
376
377+ def test_makeAutoBuilds_skips_if_built_recently_matching_channels(self):
378+ # ISnapSet.makeAutoBuilds only considers recently-requested builds
379+ # to match a snap if they match its auto_build_channels.
380+ das1, snap1 = self.makeAutoBuildableSnap(is_stale=True)
381+ das2, snap2 = self.makeAutoBuildableSnap(
382+ is_stale=True, auto_build_channels={"snapcraft": "edge"})
383+ # Create some builds with mismatched channels.
384+ self.factory.makeSnapBuild(
385+ requester=snap1.owner, snap=snap1,
386+ archive=snap1.auto_build_archive, distroarchseries=das1,
387+ channels={"snapcraft": "edge"})
388+ self.factory.makeSnapBuild(
389+ requester=snap2.owner, snap=snap2,
390+ archive=snap2.auto_build_archive, distroarchseries=das2,
391+ channels={"snapcraft": "stable"})
392+
393+ logger = BufferLogger()
394+ builds = getUtility(ISnapSet).makeAutoBuilds(logger=logger)
395+ self.assertThat(builds, MatchesSetwise(
396+ MatchesStructure(
397+ requester=Equals(snap1.owner), snap=Equals(snap1),
398+ distro_arch_series=Equals(das1), channels=Is(None),
399+ status=Equals(BuildStatus.NEEDSBUILD)),
400+ MatchesStructure.byEquality(
401+ requester=snap2.owner, snap=snap2, distro_arch_series=das2,
402+ channels={"snapcraft": "edge"}, status=BuildStatus.NEEDSBUILD),
403+ ))
404+ log_entries = logger.getLogBuffer().splitlines()
405+ self.assertEqual(4, len(log_entries))
406+ for das, snap in (das1, snap1), (das2, snap2):
407+ self.assertIn(
408+ "DEBUG Scheduling builds of snap package %s/%s" % (
409+ snap.owner.name, snap.name),
410+ log_entries)
411+ self.assertIn(
412+ "DEBUG - %s/%s/%s: Build requested." % (
413+ snap.owner.name, snap.name, das.architecturetag),
414+ log_entries)
415+ self.assertFalse(snap.is_stale)
416+
417+ # Mark the two snaps stale and try again. There are now matching
418+ # builds so we don't try to request more.
419+ for snap in snap1, snap2:
420+ removeSecurityProxy(snap).is_stale = True
421+ IStore(snap).flush()
422+ logger = BufferLogger()
423+ builds = getUtility(ISnapSet).makeAutoBuilds(logger=logger)
424+ self.assertEqual([], builds)
425+ self.assertEqual([], logger.getLogBuffer().splitlines())
426+
427 def test_makeAutoBuilds_skips_non_stale_snaps(self):
428 # ISnapSet.makeAutoBuilds skips snap packages that are not stale.
429 das, snap = self.makeAutoBuildableSnap(is_stale=False)
430
431=== modified file 'lib/lp/snappy/tests/test_snapbuild.py'
432--- lib/lp/snappy/tests/test_snapbuild.py 2018-01-23 10:59:44 +0000
433+++ lib/lp/snappy/tests/test_snapbuild.py 2018-02-08 13:37:58 +0000
434@@ -597,6 +597,7 @@
435 self.assertEqual(
436 db_build.distro_arch_series.architecturetag, build["arch_tag"])
437 self.assertEqual("Updates", build["pocket"])
438+ self.assertIsNone(build["channels"])
439 self.assertIsNone(build["score"])
440 self.assertFalse(build["can_be_rescored"])
441 self.assertFalse(build["can_be_cancelled"])
442
443=== modified file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py'
444--- lib/lp/snappy/tests/test_snapbuildbehaviour.py 2017-10-20 13:35:42 +0000
445+++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2018-02-08 13:37:58 +0000
446@@ -1,4 +1,4 @@
447-# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
448+# Copyright 2015-2018 Canonical Ltd. This software is licensed under the
449 # GNU Affero General Public License version 3 (see the file LICENSE).
450
451 """Test snap package build behaviour."""
452@@ -396,6 +396,17 @@
453 ]))
454
455 @defer.inlineCallbacks
456+ def test_extraBuildArgs_channels(self):
457+ # If the build needs particular channels, _extraBuildArgs sends
458+ # them.
459+ job = self.makeJob(channels={"snapcraft": "edge"})
460+ expected_archives, expected_trusted_keys = (
461+ yield get_sources_list_for_building(
462+ job.build, job.build.distro_arch_series, None))
463+ args = yield job._extraBuildArgs()
464+ self.assertEqual({"snapcraft": "edge"}, args["channels"])
465+
466+ @defer.inlineCallbacks
467 def test_composeBuildRequest_proxy_url_set(self):
468 job = self.makeJob()
469 build_request = yield job.composeBuildRequest(None)
470
471=== modified file 'lib/lp/testing/factory.py'
472--- lib/lp/testing/factory.py 2017-04-25 11:36:10 +0000
473+++ lib/lp/testing/factory.py 2018-02-08 13:37:58 +0000
474@@ -2,7 +2,7 @@
475 # NOTE: The first line above must stay first; do not move the copyright
476 # notice to the top. See http://www.python.org/dev/peps/pep-0263/.
477 #
478-# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
479+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
480 # GNU Affero General Public License version 3 (see the file LICENSE).
481
482 """Testing infrastructure for the Launchpad application.
483@@ -4649,7 +4649,8 @@
484 def makeSnap(self, registrant=None, owner=None, distroseries=None,
485 name=None, branch=None, git_ref=None, auto_build=False,
486 auto_build_archive=None, auto_build_pocket=None,
487- is_stale=None, require_virtualized=True, processors=None,
488+ auto_build_channels=None, is_stale=None,
489+ require_virtualized=True, processors=None,
490 date_created=DEFAULT, private=False, store_upload=False,
491 store_series=None, store_name=None, store_secrets=None,
492 store_channels=None):
493@@ -4675,7 +4676,8 @@
494 require_virtualized=require_virtualized, processors=processors,
495 date_created=date_created, branch=branch, git_ref=git_ref,
496 auto_build=auto_build, auto_build_archive=auto_build_archive,
497- auto_build_pocket=auto_build_pocket, private=private,
498+ auto_build_pocket=auto_build_pocket,
499+ auto_build_channels=auto_build_channels, private=private,
500 store_upload=store_upload, store_series=store_series,
501 store_name=store_name, store_secrets=store_secrets,
502 store_channels=store_channels)
503@@ -4686,8 +4688,9 @@
504
505 def makeSnapBuild(self, requester=None, registrant=None, snap=None,
506 archive=None, distroarchseries=None, pocket=None,
507- date_created=DEFAULT, status=BuildStatus.NEEDSBUILD,
508- builder=None, duration=None, **kwargs):
509+ channels=None, date_created=DEFAULT,
510+ status=BuildStatus.NEEDSBUILD, builder=None,
511+ duration=None, **kwargs):
512 """Make a new SnapBuild."""
513 if requester is None:
514 requester = self.makePerson()
515@@ -4715,7 +4718,7 @@
516 pocket = PackagePublishingPocket.UPDATES
517 snapbuild = getUtility(ISnapBuildSet).new(
518 requester, snap, archive, distroarchseries, pocket,
519- date_created=date_created)
520+ channels=channels, date_created=date_created)
521 if duration is not None:
522 removeSecurityProxy(snapbuild).updateStatus(
523 BuildStatus.BUILDING, builder=builder,