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

Proposed by Colin Watson
Status: Merged
Merged at revision: 18967
Proposed branch: lp:~cjwatson/launchpad/snap-request-build-select-channels
Merge into: lp:launchpad
Diff against target: 220 lines (+80/-5)
6 files modified
lib/lp/snappy/browser/snap.py (+10/-2)
lib/lp/snappy/browser/tests/test_snap.py (+39/-2)
lib/lp/snappy/interfaces/snap.py (+4/-0)
lib/lp/snappy/model/snap.py (+11/-1)
lib/lp/snappy/templates/snap-request-builds.pt (+3/-0)
lib/lp/snappy/tests/test_snap.py (+13/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-request-build-select-channels
Reviewer Review Type Date Requested Status
Adam Collard (community) Approve
Launchpad code reviewers Pending
Review via email: mp+367518@code.launchpad.net

Commit message

Allow selecting source snap channels when requesting manual snap builds.

To post a comment you must log in.
Revision history for this message
Adam Collard (adam-collard) wrote :

LGTM, +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/snappy/browser/snap.py'
--- lib/lp/snappy/browser/snap.py 2019-04-01 09:03:46 +0000
+++ lib/lp/snappy/browser/snap.py 2019-05-16 11:33:34 +0000
@@ -29,6 +29,7 @@
29from zope.interface import Interface29from zope.interface import Interface
30from zope.schema import (30from zope.schema import (
31 Choice,31 Choice,
32 Dict,
32 List,33 List,
33 TextLine,34 TextLine,
34 )35 )
@@ -294,10 +295,14 @@
294 description=(295 description=(
295 u'The package stream within the source distribution series '296 u'The package stream within the source distribution series '
296 u'to use when building the snap package.'))297 u'to use when building the snap package.'))
298 channels = Dict(
299 title=u'Source snap channels', key_type=TextLine(), required=True,
300 description=ISnap['auto_build_channels'].description)
297301
298 custom_widget_archive = SnapArchiveWidget302 custom_widget_archive = SnapArchiveWidget
299 custom_widget_distro_arch_series = LabeledMultiCheckBoxWidget303 custom_widget_distro_arch_series = LabeledMultiCheckBoxWidget
300 custom_widget_pocket = LaunchpadDropdownWidget304 custom_widget_pocket = LaunchpadDropdownWidget
305 custom_widget_channels = SnapBuildChannelsWidget
301306
302 help_links = {307 help_links = {
303 "pocket": u"/+help-snappy/snap-build-pocket.html",308 "pocket": u"/+help-snappy/snap-build-pocket.html",
@@ -320,6 +325,7 @@
320 else self.context.distro_series.main_archive),325 else self.context.distro_series.main_archive),
321 'distro_arch_series': [],326 'distro_arch_series': [],
322 'pocket': PackagePublishingPocket.UPDATES,327 'pocket': PackagePublishingPocket.UPDATES,
328 'channels': self.context.auto_build_channels,
323 }329 }
324330
325 def requestBuild(self, data):331 def requestBuild(self, data):
@@ -336,7 +342,8 @@
336 for arch in data['distro_arch_series']:342 for arch in data['distro_arch_series']:
337 try:343 try:
338 build = self.context.requestBuild(344 build = self.context.requestBuild(
339 self.user, data['archive'], arch, data['pocket'])345 self.user, data['archive'], arch, data['pocket'],
346 channels=data['channels'])
340 builds.append(build)347 builds.append(build)
341 except SnapBuildAlreadyPending:348 except SnapBuildAlreadyPending:
342 already_pending.append(arch)349 already_pending.append(arch)
@@ -356,7 +363,8 @@
356 self.request.response.addNotification(notification_text)363 self.request.response.addNotification(notification_text)
357 else:364 else:
358 self.context.requestBuilds(365 self.context.requestBuilds(
359 self.user, data['archive'], data['pocket'])366 self.user, data['archive'], data['pocket'],
367 channels=data['channels'])
360 self.request.response.addNotification(368 self.request.response.addNotification(
361 _('Builds will be dispatched soon.'))369 _('Builds will be dispatched soon.'))
362 self.next_url = self.cancel_url370 self.next_url = self.cancel_url
363371
=== modified file 'lib/lp/snappy/browser/tests/test_snap.py'
--- lib/lp/snappy/browser/tests/test_snap.py 2019-04-01 09:03:46 +0000
+++ lib/lp/snappy/browser/tests/test_snap.py 2019-05-16 11:33:34 +0000
@@ -1668,6 +1668,14 @@
1668 \(\?\)1668 \(\?\)
1669 The package stream within the source distribution series to use1669 The package stream within the source distribution series to use
1670 when building the snap package.1670 when building the snap package.
1671 Source snap channels:
1672 core
1673 snapcraft
1674 The channels to use for build tools when building the snap
1675 package.
1676 If unset, or if the channel for snapcraft is set to "apt", the
1677 default is to install snapcraft from the source archive using
1678 apt.
1671 or1679 or
1672 Cancel1680 Cancel
1673 """,1681 """,
@@ -1717,6 +1725,21 @@
1717 builds = self.snap.pending_builds1725 builds = self.snap.pending_builds
1718 self.assertEqual([ppa], [build.archive for build in builds])1726 self.assertEqual([ppa], [build.archive for build in builds])
17191727
1728 def test_request_builds_with_architectures_channels(self):
1729 # Selecting different channels with architectures selected creates
1730 # builds using those channels.
1731 browser = self.getViewBrowser(
1732 self.snap, "+request-builds", user=self.person)
1733 browser.getControl(name="field.channels.core").value = "edge"
1734 browser.getControl("amd64").selected = True
1735 self.assertFalse(browser.getControl("i386").selected)
1736 browser.getControl("Request builds").click()
1737
1738 login_person(self.person)
1739 builds = self.snap.pending_builds
1740 self.assertEqual(
1741 [{"core": "edge"}], [build.channels for build in builds])
1742
1720 def test_request_builds_with_architectures_rejects_duplicate(self):1743 def test_request_builds_with_architectures_rejects_duplicate(self):
1721 # A duplicate build request with architectures selected causes a1744 # A duplicate build request with architectures selected causes a
1722 # notification.1745 # notification.
@@ -1753,7 +1776,7 @@
1753 _job=MatchesStructure(1776 _job=MatchesStructure(
1754 requester=Equals(self.person),1777 requester=Equals(self.person),
1755 pocket=Equals(PackagePublishingPocket.UPDATES),1778 pocket=Equals(PackagePublishingPocket.UPDATES),
1756 channels=Is(None))))1779 channels=Equals({}))))
17571780
1758 def test_request_builds_no_architectures_ppa(self):1781 def test_request_builds_no_architectures_ppa(self):
1759 # Selecting a different archive with no architectures selected1782 # Selecting a different archive with no architectures selected
@@ -1772,6 +1795,20 @@
1772 [request] = self.snap.pending_build_requests1795 [request] = self.snap.pending_build_requests
1773 self.assertEqual(ppa, request.archive)1796 self.assertEqual(ppa, request.archive)
17741797
1798 def test_request_builds_no_architectures_channels(self):
1799 # Selecting different channels with no architectures selected
1800 # creates a build request using those channels.
1801 browser = self.getViewBrowser(
1802 self.snap, "+request-builds", user=self.person)
1803 browser.getControl(name="field.channels.core").value = "edge"
1804 self.assertFalse(browser.getControl("amd64").selected)
1805 self.assertFalse(browser.getControl("i386").selected)
1806 browser.getControl("Request builds").click()
1807
1808 login_person(self.person)
1809 [request] = self.snap.pending_build_requests
1810 self.assertEqual({"core": "edge"}, request.channels)
1811
1775 def test_request_builds_no_distro_series(self):1812 def test_request_builds_no_distro_series(self):
1776 # Requesting builds of a snap configured to infer an appropriate1813 # Requesting builds of a snap configured to infer an appropriate
1777 # distro series from snapcraft.yaml creates a pending build request.1814 # distro series from snapcraft.yaml creates a pending build request.
@@ -1792,4 +1829,4 @@
1792 _job=MatchesStructure(1829 _job=MatchesStructure(
1793 requester=Equals(self.person),1830 requester=Equals(self.person),
1794 pocket=Equals(PackagePublishingPocket.UPDATES),1831 pocket=Equals(PackagePublishingPocket.UPDATES),
1795 channels=Is(None))))1832 channels=Equals({}))))
17961833
=== modified file 'lib/lp/snappy/interfaces/snap.py'
--- lib/lp/snappy/interfaces/snap.py 2019-04-16 13:05:56 +0000
+++ lib/lp/snappy/interfaces/snap.py 2019-05-16 11:33:34 +0000
@@ -332,6 +332,10 @@
332 title=u"The source archive for builds produced by this request",332 title=u"The source archive for builds produced by this request",
333 required=True, readonly=True)333 required=True, readonly=True)
334334
335 channels = Dict(
336 title=_("Source snap channels for builds produced by this request"),
337 key_type=TextLine(), required=False, readonly=True)
338
335339
336class ISnapView(Interface):340class ISnapView(Interface):
337 """`ISnap` attributes that require launchpad.View permission."""341 """`ISnap` attributes that require launchpad.View permission."""
338342
=== modified file 'lib/lp/snappy/model/snap.py'
--- lib/lp/snappy/model/snap.py 2019-04-16 13:05:56 +0000
+++ lib/lp/snappy/model/snap.py 2019-05-16 11:33:34 +0000
@@ -244,6 +244,11 @@
244 """See `ISnapBuildRequest`."""244 """See `ISnapBuildRequest`."""
245 return self._job.archive245 return self._job.archive
246246
247 @property
248 def channels(self):
249 """See `ISnapBuildRequest`."""
250 return self._job.channels
251
247252
248@implementer(ISnap, IHasOwner)253@implementer(ISnap, IHasOwner)
249class Snap(Storm, WebhookTargetMixin):254class Snap(Storm, WebhookTargetMixin):
@@ -627,13 +632,18 @@
627 if not self._isArchitectureAllowed(distro_arch_series, pocket):632 if not self._isArchitectureAllowed(distro_arch_series, pocket):
628 raise SnapBuildDisallowedArchitecture(distro_arch_series, pocket)633 raise SnapBuildDisallowedArchitecture(distro_arch_series, pocket)
629634
635 if not channels:
636 channels_clause = Or(
637 SnapBuild.channels == None, SnapBuild.channels == {})
638 else:
639 channels_clause = SnapBuild.channels == channels
630 pending = IStore(self).find(640 pending = IStore(self).find(
631 SnapBuild,641 SnapBuild,
632 SnapBuild.snap_id == self.id,642 SnapBuild.snap_id == self.id,
633 SnapBuild.archive_id == archive.id,643 SnapBuild.archive_id == archive.id,
634 SnapBuild.distro_arch_series_id == distro_arch_series.id,644 SnapBuild.distro_arch_series_id == distro_arch_series.id,
635 SnapBuild.pocket == pocket,645 SnapBuild.pocket == pocket,
636 SnapBuild.channels == channels,646 channels_clause,
637 SnapBuild.status == BuildStatus.NEEDSBUILD)647 SnapBuild.status == BuildStatus.NEEDSBUILD)
638 if pending.any() is not None:648 if pending.any() is not None:
639 raise SnapBuildAlreadyPending649 raise SnapBuildAlreadyPending
640650
=== modified file 'lib/lp/snappy/templates/snap-request-builds.pt'
--- lib/lp/snappy/templates/snap-request-builds.pt 2019-02-07 10:34:20 +0000
+++ lib/lp/snappy/templates/snap-request-builds.pt 2019-05-16 11:33:34 +0000
@@ -28,6 +28,9 @@
28 <tal:widget define="widget nocall:view/widgets/pocket">28 <tal:widget define="widget nocall:view/widgets/pocket">
29 <metal:block use-macro="context/@@launchpad_form/widget_row" />29 <metal:block use-macro="context/@@launchpad_form/widget_row" />
30 </tal:widget>30 </tal:widget>
31 <tal:widget define="widget nocall:view/widgets/channels">
32 <metal:block use-macro="context/@@launchpad_form/widget_row" />
33 </tal:widget>
31 </table>34 </table>
32 </metal:formbody>35 </metal:formbody>
33 </div>36 </div>
3437
=== modified file 'lib/lp/snappy/tests/test_snap.py'
--- lib/lp/snappy/tests/test_snap.py 2019-04-16 13:05:56 +0000
+++ lib/lp/snappy/tests/test_snap.py 2019-05-16 11:33:34 +0000
@@ -308,6 +308,19 @@
308 snap.requestBuild(308 snap.requestBuild(
309 snap.owner, snap.distro_series.main_archive, arches[1],309 snap.owner, snap.distro_series.main_archive, arches[1],
310 PackagePublishingPocket.UPDATES)310 PackagePublishingPocket.UPDATES)
311 # channels=None and channels={} are treated as equivalent, but
312 # anything else allows a new build.
313 self.assertRaises(
314 SnapBuildAlreadyPending, snap.requestBuild,
315 snap.owner, snap.distro_series.main_archive, arches[0],
316 PackagePublishingPocket.UPDATES, channels={})
317 snap.requestBuild(
318 snap.owner, snap.distro_series.main_archive, arches[0],
319 PackagePublishingPocket.UPDATES, channels={"core": "edge"})
320 self.assertRaises(
321 SnapBuildAlreadyPending, snap.requestBuild,
322 snap.owner, snap.distro_series.main_archive, arches[0],
323 PackagePublishingPocket.UPDATES, channels={"core": "edge"})
311 # Changing the status of the old build allows a new build.324 # Changing the status of the old build allows a new build.
312 old_build.updateStatus(BuildStatus.BUILDING)325 old_build.updateStatus(BuildStatus.BUILDING)
313 old_build.updateStatus(BuildStatus.FULLYBUILT)326 old_build.updateStatus(BuildStatus.FULLYBUILT)