Merge lp:~cjwatson/launchpad/snap-auto-build-ui into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18129
Proposed branch: lp:~cjwatson/launchpad/snap-auto-build-ui
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/snap-auto-build-archive-widget
Diff against target: 797 lines (+426/-43)
10 files modified
lib/lp/snappy/browser/snap.py (+58/-5)
lib/lp/snappy/browser/tests/test_snap.py (+77/-1)
lib/lp/snappy/configure.zcml (+2/-0)
lib/lp/snappy/help/snap-build-frequency.html (+42/-0)
lib/lp/snappy/javascript/snap.edit.js (+25/-6)
lib/lp/snappy/javascript/tests/test_snap.edit.html (+90/-1)
lib/lp/snappy/javascript/tests/test_snap.edit.js (+35/-2)
lib/lp/snappy/templates/snap-edit.pt (+36/-18)
lib/lp/snappy/templates/snap-index.pt (+25/-0)
lib/lp/snappy/templates/snap-new.pt (+36/-10)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-auto-build-ui
Reviewer Review Type Date Requested Status
Thomi Richards (community) Approve
Launchpad code reviewers Pending
Review via email: mp+297962@code.launchpad.net

Commit message

Add UI for auto-building snap packages.

Description of the change

Add UI for auto-building snap packages. It's a little basic and could probably use more JS niceties in a few places, but it's functional.

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

LGTM, but it might be worth asking someone with more LP UI experience to take a look.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/snappy/browser/snap.py'
2--- lib/lp/snappy/browser/snap.py 2016-07-05 10:20:51 +0000
3+++ lib/lp/snappy/browser/snap.py 2016-07-12 13:40:07 +0000
4@@ -179,6 +179,13 @@
5 self.context, field, format_link(self.context.owner),
6 header='Change owner', step_title='Select a new owner')
7
8+ @property
9+ def build_frequency(self):
10+ if self.context.auto_build:
11+ return 'Built automatically'
12+ else:
13+ return 'Built on request'
14+
15
16 def builds_for_snap(snap):
17 """A list of interesting builds.
18@@ -303,6 +310,7 @@
19 'name',
20 'private',
21 'require_virtualized',
22+ 'auto_build',
23 'store_upload',
24 ])
25 store_distro_series = Choice(
26@@ -315,8 +323,13 @@
27 branch = copy_field(ISnap['branch'], required=True)
28 git_ref = copy_field(ISnap['git_ref'], required=True)
29
30- # These are only required if store_upload is True. Later validation
31- # takes care of adjusting the required attribute.
32+ # These are only required if auto_build is True. Later validation takes
33+ # care of adjusting the required attribute.
34+ auto_build_archive = copy_field(ISnap['auto_build_archive'], required=True)
35+ auto_build_pocket = copy_field(ISnap['auto_build_pocket'], required=True)
36+
37+ # This is only required if store_upload is True. Later validation takes
38+ # care of adjusting the required attribute.
39 store_name = copy_field(ISnap['store_name'], required=True)
40
41
42@@ -350,10 +363,14 @@
43 'owner',
44 'name',
45 'store_distro_series',
46+ 'auto_build',
47+ 'auto_build_archive',
48+ 'auto_build_pocket',
49 'store_upload',
50 'store_name',
51 ]
52 custom_widget('store_distro_series', LaunchpadRadioWidget)
53+ custom_widget('auto_build_archive', SnapArchiveWidget)
54
55 def initialize(self):
56 """See `LaunchpadView`."""
57@@ -417,6 +434,8 @@
58 'processors': [
59 p for p in getUtility(IProcessorSet).getAll()
60 if p.build_by_default],
61+ 'auto_build_archive': series.main_archive,
62+ 'auto_build_pocket': PackagePublishingPocket.UPDATES,
63 }
64
65 @property
66@@ -425,6 +444,13 @@
67
68 def validate_widgets(self, data, names=None):
69 """See `LaunchpadFormView`."""
70+ if self.widgets.get('auto_build') is not None:
71+ # Set widgets as required or optional depending on the
72+ # auto_build field.
73+ super(SnapAddView, self).validate_widgets(data, ['auto_build'])
74+ auto_build = data.get('auto_build', False)
75+ self.widgets['auto_build_archive'].context.required = auto_build
76+ self.widgets['auto_build_pocket'].context.required = auto_build
77 if self.widgets.get('store_upload') is not None:
78 # Set widgets as required or optional depending on the
79 # store_upload field.
80@@ -441,9 +467,15 @@
81 kwargs = {'branch': self.context}
82 private = not getUtility(
83 ISnapSet).isValidPrivacy(False, data['owner'], **kwargs)
84+ if not data.get('auto_build', False):
85+ data['auto_build_archive'] = None
86+ data['auto_build_pocket'] = None
87 snap = getUtility(ISnapSet).new(
88 self.user, data['owner'],
89 data['store_distro_series'].distro_series, data['name'],
90+ auto_build=data['auto_build'],
91+ auto_build_archive=data['auto_build_archive'],
92+ auto_build_pocket=data['auto_build_pocket'],
93 private=private, store_upload=data['store_upload'],
94 store_series=data['store_distro_series'].snappy_series,
95 store_name=data['store_name'], processors=data['processors'],
96@@ -502,6 +534,14 @@
97 self.widgets['git_ref'].context.required = True
98 else:
99 raise AssertionError("Unknown branch type %s" % vcs)
100+ if self.widgets.get('auto_build') is not None:
101+ # Set widgets as required or optional depending on the
102+ # auto_build field.
103+ super(BaseSnapEditView, self).validate_widgets(
104+ data, ['auto_build'])
105+ auto_build = data.get('auto_build', False)
106+ self.widgets['auto_build_archive'].context.required = auto_build
107+ self.widgets['auto_build_pocket'].context.required = auto_build
108 if self.widgets.get('store_upload') is not None:
109 # Set widgets as required or optional depending on the
110 # store_upload field.
111@@ -538,9 +578,15 @@
112 self.context.setProcessors(
113 new_processors, check_permissions=True, user=self.user)
114 del data['processors']
115+ if not data.get('auto_build', False):
116+ if 'auto_build_archive' in data:
117+ del data['auto_build_archive']
118+ if 'auto_build_pocket' in data:
119+ del data['auto_build_pocket']
120 store_upload = data.get('store_upload', False)
121 if not store_upload:
122- data['store_name'] = None
123+ if 'store_name' in data:
124+ del data['store_name']
125 need_store_reauth = self._needStoreReauth(data)
126 self.updateContextFromData(data)
127 if need_store_reauth:
128@@ -592,15 +638,19 @@
129 'owner',
130 'name',
131 'store_distro_series',
132- 'store_upload',
133- 'store_name',
134 'vcs',
135 'branch',
136 'git_ref',
137+ 'auto_build',
138+ 'auto_build_archive',
139+ 'auto_build_pocket',
140+ 'store_upload',
141+ 'store_name',
142 ]
143 custom_widget('store_distro_series', LaunchpadRadioWidget)
144 custom_widget('vcs', LaunchpadRadioWidget)
145 custom_widget('git_ref', GitRefWidget)
146+ custom_widget('auto_build_archive', SnapArchiveWidget)
147
148 def setUpFields(self):
149 """See `LaunchpadFormView`."""
150@@ -624,6 +674,9 @@
151 initial_values['vcs'] = VCSType.GIT
152 else:
153 initial_values['vcs'] = VCSType.BZR
154+ if self.context.auto_build_pocket is None:
155+ initial_values['auto_build_pocket'] = (
156+ PackagePublishingPocket.UPDATES)
157 return initial_values
158
159 def validate(self, data):
160
161=== modified file 'lib/lp/snappy/browser/tests/test_snap.py'
162--- lib/lp/snappy/browser/tests/test_snap.py 2016-07-05 10:20:51 +0000
163+++ lib/lp/snappy/browser/tests/test_snap.py 2016-07-12 13:40:07 +0000
164@@ -42,6 +42,7 @@
165 from lp.buildmaster.enums import BuildStatus
166 from lp.buildmaster.interfaces.processor import IProcessorSet
167 from lp.code.errors import GitRepositoryScanFault
168+from lp.code.interfaces.githosting import IGitHostingClient
169 from lp.registry.enums import PersonVisibility
170 from lp.registry.interfaces.pocket import PackagePublishingPocket
171 from lp.registry.interfaces.series import SeriesStatus
172@@ -72,6 +73,7 @@
173 time_counter,
174 )
175 from lp.testing.fakemethod import FakeMethod
176+from lp.testing.fixture import ZopeUtilityFixture
177 from lp.testing.layers import (
178 DatabaseFunctionalLayer,
179 LaunchpadFunctionalLayer,
180@@ -220,11 +222,23 @@
181 "Source:\n%s\nEdit snap package" % source_display,
182 MatchesTagText(content, "source"))
183 self.assertThat(
184+ "Build schedule:\n(?)\nBuilt on request\nEdit snap package\n",
185+ MatchesTagText(content, "auto_build"))
186+ self.assertThat(
187+ "Source archive for automatic builds:\n\nEdit snap package\n",
188+ MatchesTagText(content, "auto_build_archive"))
189+ self.assertThat(
190+ "Pocket for automatic builds:\n\nEdit snap package",
191+ MatchesTagText(content, "auto_build_pocket"))
192+ self.assertThat(
193 "Builds of this snap package are not automatically uploaded to "
194 "the store.\nEdit snap package",
195 MatchesTagText(content, "store_upload"))
196
197 def test_create_new_snap_git(self):
198+ hosting_client = FakeMethod()
199+ hosting_client.getBlob = FakeMethod(result="")
200+ self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
201 [git_ref] = self.factory.makeGitRefs()
202 source_display = git_ref.display_name
203 browser = self.getViewBrowser(
204@@ -244,6 +258,15 @@
205 "Source:\n%s\nEdit snap package" % source_display,
206 MatchesTagText(content, "source"))
207 self.assertThat(
208+ "Build schedule:\n(?)\nBuilt on request\nEdit snap package\n",
209+ MatchesTagText(content, "auto_build"))
210+ self.assertThat(
211+ "Source archive for automatic builds:\n\nEdit snap package\n",
212+ MatchesTagText(content, "auto_build_archive"))
213+ self.assertThat(
214+ "Pocket for automatic builds:\n\nEdit snap package",
215+ MatchesTagText(content, "auto_build_pocket"))
216+ self.assertThat(
217 "Builds of this snap package are not automatically uploaded to "
218 "the store.\nEdit snap package",
219 MatchesTagText(content, "store_upload"))
220@@ -309,6 +332,34 @@
221 extract_text(find_tag_by_id(browser.contents, "privacy"))
222 )
223
224+ def test_create_new_snap_auto_build(self):
225+ # Creating a new snap and asking for it to be automatically built
226+ # sets all the appropriate fields.
227+ branch = self.factory.makeAnyBranch()
228+ archive = self.factory.makeArchive()
229+ browser = self.getViewBrowser(
230+ branch, view_name="+new-snap", user=self.person)
231+ browser.getControl(name="field.name").value = "snap-name"
232+ browser.getControl(
233+ "Automatically build when branch changes").selected = True
234+ browser.getControl("PPA").click()
235+ browser.getControl(name="field.auto_build_archive.ppa").value = (
236+ archive.reference)
237+ browser.getControl("Pocket for automatic builds").value = ["SECURITY"]
238+ browser.getControl("Create snap package").click()
239+
240+ content = find_main_content(browser.contents)
241+ self.assertThat(
242+ "Build schedule:\n(?)\nBuilt automatically\nEdit snap package\n",
243+ MatchesTagText(content, "auto_build"))
244+ self.assertThat(
245+ "Source archive for automatic builds:\n%s\nEdit snap package\n" %
246+ archive.displayname,
247+ MatchesTagText(content, "auto_build_archive"))
248+ self.assertThat(
249+ "Pocket for automatic builds:\nSecurity\nEdit snap package",
250+ MatchesTagText(content, "auto_build_pocket"))
251+
252 def test_create_new_snap_store_upload(self):
253 # Creating a new snap and asking for it to be automatically uploaded
254 # to the store sets all the appropriate fields and redirects to SSO
255@@ -441,7 +492,7 @@
256 view = create_initialized_view(git_ref, "+new-snap")
257 with mock.patch('yaml.load') as unsafe_load:
258 with mock.patch('yaml.safe_load') as safe_load:
259- initial_values = view.initial_values
260+ view.initial_values
261 self.assertEqual(0, unsafe_load.call_count)
262 self.assertEqual(1, safe_load.call_count)
263
264@@ -574,6 +625,7 @@
265 new_snappy_series = self.factory.makeSnappySeries(
266 usable_distro_series=[new_series])
267 [new_git_ref] = self.factory.makeGitRefs()
268+ archive = self.factory.makeArchive()
269
270 browser = self.getViewBrowser(snap, user=self.person)
271 browser.getLink("Edit snap package").click()
272@@ -585,6 +637,12 @@
273 browser.getControl("Git repository").value = (
274 new_git_ref.repository.identity)
275 browser.getControl("Git branch").value = new_git_ref.path
276+ browser.getControl(
277+ "Automatically build when branch changes").selected = True
278+ browser.getControl("PPA").click()
279+ browser.getControl(name="field.auto_build_archive.ppa").value = (
280+ archive.reference)
281+ browser.getControl("Pocket for automatic builds").value = ["SECURITY"]
282 browser.getControl("Update snap package").click()
283
284 content = find_main_content(browser.contents)
285@@ -598,6 +656,16 @@
286 "Source:\n%s\nEdit snap package" % new_git_ref.display_name,
287 MatchesTagText(content, "source"))
288 self.assertThat(
289+ "Build schedule:\n(?)\nBuilt automatically\nEdit snap package\n",
290+ MatchesTagText(content, "auto_build"))
291+ self.assertThat(
292+ "Source archive for automatic builds:\n%s\nEdit snap package\n" %
293+ archive.displayname,
294+ MatchesTagText(content, "auto_build_archive"))
295+ self.assertThat(
296+ "Pocket for automatic builds:\nSecurity\nEdit snap package",
297+ MatchesTagText(content, "auto_build_pocket"))
298+ self.assertThat(
299 "Builds of this snap package are not automatically uploaded to "
300 "the store.\nEdit snap package",
301 MatchesTagText(content, "store_upload"))
302@@ -1083,6 +1151,10 @@
303 Owner: Test Person
304 Distribution series: Ubuntu Shiny
305 Source: lp://dev/~test-person/\\+junk/snap-branch
306+ Build schedule: \(\?\)
307+ Built on request
308+ Source archive for automatic builds:
309+ Pocket for automatic builds:
310 Builds of this snap package are not automatically uploaded to
311 the store.
312 Latest builds
313@@ -1106,6 +1178,10 @@
314 Owner: Test Person
315 Distribution series: Ubuntu Shiny
316 Source: ~test-person/\\+git/snap-repository:master
317+ Build schedule: \(\?\)
318+ Built on request
319+ Source archive for automatic builds:
320+ Pocket for automatic builds:
321 Builds of this snap package are not automatically uploaded to
322 the store.
323 Latest builds
324
325=== modified file 'lib/lp/snappy/configure.zcml'
326--- lib/lp/snappy/configure.zcml 2016-05-06 16:34:21 +0000
327+++ lib/lp/snappy/configure.zcml 2016-07-12 13:40:07 +0000
328@@ -14,6 +14,8 @@
329 <include package=".browser" />
330 <include file="vocabularies.zcml" />
331
332+ <lp:help-folder folder="help" name="+help-snappy" />
333+
334 <!-- Snap -->
335 <class class="lp.snappy.model.snap.Snap">
336 <require
337
338=== added directory 'lib/lp/snappy/help'
339=== added file 'lib/lp/snappy/help/snap-build-frequency.html'
340--- lib/lp/snappy/help/snap-build-frequency.html 1970-01-01 00:00:00 +0000
341+++ lib/lp/snappy/help/snap-build-frequency.html 2016-07-12 13:40:07 +0000
342@@ -0,0 +1,42 @@
343+<html>
344+ <head>
345+ <title>Snap package build schedule</title>
346+ <link rel="stylesheet" type="text/css"
347+ href="/+icing/yui/cssreset/reset.css" />
348+ <link rel="stylesheet" type="text/css"
349+ href="/+icing/yui/cssfonts/fonts.css" />
350+ <link rel="stylesheet" type="text/css"
351+ href="/+icing/yui/cssbase/base.css" />
352+ <style type="text/css">
353+ dt { font-weight: bold }
354+ dd p { margin-bottom: 0.5em }
355+ </style>
356+ </head>
357+ <body>
358+ <h1>Snap package build schedule</h1>
359+
360+ <p>There are two options for when snap packages get built:</p>
361+ <dl>
362+ <dt>Built automatically</dt>
363+ <dd>
364+ <p>A build will be scheduled automatically once a change to the
365+ top-level source branch for the snap package is detected.</p>
366+ <p>If there has been a build of the snap package within the previous
367+ hour from the source archive, the build will not be scheduled
368+ until an hour since the last build from the source archive.</p>
369+ <p>If the snap package has been built within the last hour from a
370+ different archive using the "Request builds" action, this will not
371+ delay the automatic build.</p>
372+ <p>If you really want the build to happen before the one-hour period
373+ is up, you can use the "Request builds" action.</p>
374+ </dd>
375+ <dt>Built on request</dt>
376+ <dd>
377+ <p>Builds of the snap package have to be manually requested using
378+ the "Request builds" action.</p>
379+ </dd>
380+ </dl>
381+
382+ </body>
383+</html>
384+
385
386=== modified file 'lib/lp/snappy/javascript/snap.edit.js'
387--- lib/lp/snappy/javascript/snap.edit.js 2015-09-09 14:17:46 +0000
388+++ lib/lp/snappy/javascript/snap.edit.js 2016-07-12 13:40:07 +0000
389@@ -1,7 +1,8 @@
390-/* Copyright 2015 Canonical Ltd. This software is licensed under the
391+/* Copyright 2015-2016 Canonical Ltd. This software is licensed under the
392 * GNU Affero General Public License version 3 (see the file LICENSE).
393 *
394- * Control enabling/disabling form elements on the Snap:+edit page.
395+ * Control enabling/disabling form elements on the {Branch,GitRef}:+new-snap
396+ * and Snap:+edit pages.
397 *
398 * @module Y.lp.snappy.snap.edit
399 * @requires node, DOM
400@@ -12,7 +13,9 @@
401
402 module.set_enabled = function(field_id, is_enabled) {
403 var field = Y.DOM.byId(field_id);
404- field.disabled = !is_enabled;
405+ if (field !== null) {
406+ field.disabled = !is_enabled;
407+ }
408 };
409
410 module.onclick_vcs = function(e) {
411@@ -22,15 +25,31 @@
412 selected_vcs = node.get('value');
413 }
414 });
415- module.set_enabled('field.branch', selected_vcs === 'BZR');
416- module.set_enabled('field.git_ref.repository', selected_vcs === 'GIT');
417- module.set_enabled('field.git_ref.path', selected_vcs === 'GIT');
418+ if (selected_vcs !== null) {
419+ module.set_enabled('field.branch', selected_vcs === 'BZR');
420+ module.set_enabled(
421+ 'field.git_ref.repository', selected_vcs === 'GIT');
422+ module.set_enabled('field.git_ref.path', selected_vcs === 'GIT');
423+ }
424+ };
425+
426+ module.onclick_auto_build = function(e) {
427+ var auto_build = Y.one(
428+ 'input[name="field.auto_build"]').get('checked');
429+ module.set_enabled(
430+ 'field.auto_build_archive.option.primary', auto_build);
431+ module.set_enabled('field.auto_build_archive.option.ppa', auto_build);
432+ module.set_enabled('field.auto_build_archive.ppa', auto_build);
433+ module.set_enabled('field.auto_build_pocket', auto_build);
434 };
435
436 module.setup = function() {
437 Y.all('input[name="field.vcs"]').on('click', module.onclick_vcs);
438+ Y.all('input[name="field.auto_build"]').on(
439+ 'click', module.onclick_auto_build);
440
441 // Set the initial state.
442 module.onclick_vcs();
443+ module.onclick_auto_build();
444 };
445 }, '0.1', {'requires': ['node', 'DOM']});
446
447=== modified file 'lib/lp/snappy/javascript/tests/test_snap.edit.html'
448--- lib/lp/snappy/javascript/tests/test_snap.edit.html 2015-09-09 14:17:46 +0000
449+++ lib/lp/snappy/javascript/tests/test_snap.edit.html 2016-07-12 13:40:07 +0000
450@@ -1,6 +1,6 @@
451 <!DOCTYPE html>
452 <!--
453-Copyright 2015 Canonical Ltd. This software is licensed under the
454+Copyright 2015-2016 Canonical Ltd. This software is licensed under the
455 GNU Affero General Public License version 3 (see the file LICENSE).
456 -->
457
458@@ -133,6 +133,95 @@
459 </div>
460 </td>
461 </tr>
462+ <tr>
463+ <td colspan="2">
464+ <div>
465+ <label for="field.auto_build">Automatically build when branch changes</label>
466+ <input class="checkboxType"
467+ id="field.auto_build"
468+ name="field.auto_build"
469+ type="checkbox"
470+ value="" />
471+ </div>
472+ </td>
473+ </tr>
474+ <tr>
475+ <td>
476+ <table class="subordinate">
477+ <tr>
478+ <td colspan="2">
479+ <div>
480+ <label for="field.auto_build_archive">Source archive for automatic builds:</label>
481+ <div>
482+ <table>
483+ <tr>
484+ <td colspan="2">
485+ <label>
486+ <input class="radioType"
487+ id="field.auto_build_archive.option.primary"
488+ name="field.auto_build_archive"
489+ type="radio"
490+ value="primary" />
491+ Primary Archive for Ubuntu
492+ </label>
493+ </td>
494+ </tr>
495+ <tr>
496+ <td>
497+ <label>
498+ <input class="radioType"
499+ id="field.auto_build_archive.option.ppa"
500+ name="field.auto_build_archive"
501+ type="radio"
502+ value="ppa" />
503+ PPA
504+ </label>
505+ </td>
506+ <td>
507+ <input type="text"
508+ value=""
509+ id="field.auto_build_archive.ppa"
510+ name="field.auto_build_archive.ppa"
511+ size="20"
512+ maxlength=""
513+ onKeyPress=""
514+ style=""
515+ class="" />
516+ </td>
517+ </tr>
518+ </table>
519+ </div>
520+ </div>
521+ </td>
522+ </tr>
523+ <tr>
524+ <td colspan="2">
525+ <div>
526+ <label for="field.auto_build_pocket">Pocket for automatic builds:</label>
527+ <div>
528+ <div>
529+ <div class="value">
530+ <select id="field.auto_build_pocket"
531+ name="field.auto_build_pocket"
532+ size="1">
533+ <option value="RELEASE">Release</option>
534+ <option value="SECURITY">Security</option>
535+ <option selected="selected" value="UPDATES">Updates</option>
536+ <option value="PROPOSED">Proposed</option>
537+ <option value="BACKPORTS">Backports</option>
538+ </select>
539+ </div>
540+ <input name="field.auto_build_pocket-empty-marker"
541+ type="hidden"
542+ value="1" />
543+ </div>
544+ </div>
545+ </div>
546+ </td>
547+ </tr>
548+ </table>
549+ </td>
550+ </tr>
551 </table>
552
553 <input type="submit" id="field.actions.update"
554
555=== modified file 'lib/lp/snappy/javascript/tests/test_snap.edit.js'
556--- lib/lp/snappy/javascript/tests/test_snap.edit.js 2015-09-09 14:17:46 +0000
557+++ lib/lp/snappy/javascript/tests/test_snap.edit.js 2016-07-12 13:40:07 +0000
558@@ -1,4 +1,4 @@
559-/* Copyright 2015 Canonical Ltd. This software is licensed under the
560+/* Copyright 2015-2016 Canonical Ltd. This software is licensed under the
561 * GNU Affero General Public License version 3 (see the file LICENSE).
562 *
563 * Test driver for snap.edit.js.
564@@ -14,9 +14,10 @@
565 setUp: function() {
566 this.tbody = Y.one('#snap.edit');
567
568- // Get the individual VCS type radio buttons.
569+ // Get the widgets with event handlers attached.
570 this.vcs_bzr = Y.DOM.byId('field.vcs.Bazaar');
571 this.vcs_git = Y.DOM.byId('field.vcs.Git');
572+ this.auto_build = Y.DOM.byId('field.auto_build');
573
574 // Get the input widgets.
575 this.input_branch = Y.DOM.byId('field.branch');
576@@ -45,6 +46,7 @@
577
578 check_handler(this.vcs_bzr, module.onclick_vcs);
579 check_handler(this.vcs_git, module.onclick_vcs);
580+ check_handler(this.auto_build, module.onclick_auto_build);
581 },
582
583 test_select_vcs_bzr: function() {
584@@ -73,7 +75,38 @@
585 'git_ref.repository field disabled');
586 Y.Assert.isFalse(this.input_git_path.disabled,
587 'git_ref.path field disabled');
588+ },
589+
590+ test_check_auto_build: function() {
591+ var fields = [
592+ 'auto_build_archive.option.primary',
593+ 'auto_build_archive.option.ppa',
594+ 'auto_build_archive.ppa',
595+ 'auto_build_pocket'
596+ ];
597+ var field;
598+ module.setup();
599+ for (i = 0; i < fields.length; i++) {
600+ field = Y.DOM.byId('field.' + fields[i]);
601+ Y.Assert.isTrue(
602+ field.disabled, fields[i] + ' field not disabled');
603+ }
604+ this.auto_build.checked = true;
605+ module.onclick_auto_build();
606+ for (i = 0; i < fields.length; i++) {
607+ field = Y.DOM.byId('field.' + fields[i]);
608+ Y.Assert.isFalse(
609+ field.disabled, fields[i] + ' field disabled');
610+ }
611+ this.auto_build.checked = false;
612+ module.onclick_auto_build();
613+ for (i = 0; i < fields.length; i++) {
614+ field = Y.DOM.byId('field.' + fields[i]);
615+ Y.Assert.isTrue(
616+ field.disabled, fields[i] + ' field not disabled');
617+ }
618 }
619+
620 }));
621 }, '0.1', {
622 requires: ['lp.testing.runner', 'test', 'test-console',
623
624=== modified file 'lib/lp/snappy/templates/snap-edit.pt'
625--- lib/lp/snappy/templates/snap-edit.pt 2016-05-24 05:15:50 +0000
626+++ lib/lp/snappy/templates/snap-edit.pt 2016-07-12 13:40:07 +0000
627@@ -29,24 +29,6 @@
628 <metal:block use-macro="context/@@launchpad_form/widget_row" />
629 </tal:widget>
630
631- <tr tal:condition="view/has_snappy_distro_series">
632- <td>
633- <tal:widget define="widget nocall:view/widgets/store_upload">
634- <metal:block use-macro="context/@@launchpad_form/widget_row" />
635- </tal:widget>
636- <table class="subordinate">
637- <tal:widget define="widget nocall:view/widgets/store_name">
638- <metal:block use-macro="context/@@launchpad_form/widget_row" />
639- </tal:widget>
640- </table>
641- <p class="formHelp">
642- If you change any settings related to automatically uploading
643- builds of this snap to the store, then the login service will
644- prompt you to authorize this request.
645- </p>
646- </td>
647- </tr>
648-
649 <tr>
650 <td>
651 <div>
652@@ -78,6 +60,42 @@
653 </td>
654 </tr>
655
656+ <tal:widget define="widget nocall:view/widgets/auto_build">
657+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
658+ </tal:widget>
659+ <tr>
660+ <td>
661+ <table class="subordinate">
662+ <tal:widget define="widget nocall:view/widgets/auto_build_archive">
663+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
664+ </tal:widget>
665+ <tal:widget define="widget nocall:view/widgets/auto_build_pocket">
666+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
667+ </tal:widget>
668+ </table>
669+ </td>
670+ </tr>
671+
672+ <tal:has-sds tal:condition="view/has_snappy_distro_series">
673+ <tal:widget define="widget nocall:view/widgets/store_upload">
674+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
675+ </tal:widget>
676+ <tr>
677+ <td>
678+ <table class="subordinate">
679+ <tal:widget define="widget nocall:view/widgets/store_name">
680+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
681+ </tal:widget>
682+ </table>
683+ <p class="formHelp">
684+ If you change any settings related to automatically uploading
685+ builds of this snap to the store, then the login service will
686+ prompt you to authorize this request.
687+ </p>
688+ </td>
689+ </tr>
690+ </tal:has-sds>
691+
692 <tal:widget define="widget nocall:view/widgets/processors">
693 <metal:block use-macro="context/@@launchpad_form/widget_row" />
694 </tal:widget>
695
696=== modified file 'lib/lp/snappy/templates/snap-index.pt'
697--- lib/lp/snappy/templates/snap-index.pt 2016-05-28 00:21:40 +0000
698+++ lib/lp/snappy/templates/snap-index.pt 2016-07-12 13:40:07 +0000
699@@ -61,6 +61,31 @@
700 <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
701 </dd>
702 </dl>
703+
704+ <dl id="auto_build">
705+ <dt>Build schedule:
706+ <a href="/+help-snappy/snap-build-frequency.html" target="help"
707+ class="sprite maybe action-icon">(?)</a>
708+ </dt>
709+ <dd>
710+ <span tal:replace="view/build_frequency"/>
711+ <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
712+ </dd>
713+ </dl>
714+ <dl id="auto_build_archive">
715+ <dt>Source archive for automatic builds:</dt>
716+ <dd>
717+ <a tal:replace="structure context/auto_build_archive/fmt:link"/>
718+ <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
719+ </dd>
720+ </dl>
721+ <dl id="auto_build_pocket">
722+ <dt>Pocket for automatic builds:</dt>
723+ <dd>
724+ <span tal:replace="context/auto_build_pocket/title|nothing"/>
725+ <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
726+ </dd>
727+ </dl>
728 </div>
729
730 <div id="store_upload" class="two-column-list"
731
732=== modified file 'lib/lp/snappy/templates/snap-new.pt'
733--- lib/lp/snappy/templates/snap-new.pt 2016-06-30 19:19:20 +0000
734+++ lib/lp/snappy/templates/snap-new.pt 2016-07-12 13:40:07 +0000
735@@ -35,26 +35,52 @@
736 <metal:block use-macro="context/@@launchpad_form/widget_row" />
737 </tal:widget>
738
739- <tr tal:condition="view/has_snappy_distro_series">
740+ <tal:widget define="widget nocall:view/widgets/auto_build">
741+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
742+ </tal:widget>
743+ <tr>
744 <td>
745- <tal:widget define="widget nocall:view/widgets/store_upload">
746- <metal:block use-macro="context/@@launchpad_form/widget_row" />
747- </tal:widget>
748 <table class="subordinate">
749- <tal:widget define="widget nocall:view/widgets/store_name">
750+ <tal:widget define="widget nocall:view/widgets/auto_build_archive">
751+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
752+ </tal:widget>
753+ <tal:widget define="widget nocall:view/widgets/auto_build_pocket">
754 <metal:block use-macro="context/@@launchpad_form/widget_row" />
755 </tal:widget>
756 </table>
757- <p class="formHelp">
758- If you ask Launchpad to automatically upload builds of this
759- snap to the store on your behalf, then the login service
760- will prompt you to authorize this request.
761- </p>
762 </td>
763 </tr>
764+
765+ <tal:has-sds condition="view/has_snappy_distro_series">
766+ <tal:widget define="widget nocall:view/widgets/store_upload">
767+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
768+ </tal:widget>
769+ <tr>
770+ <td>
771+ <table class="subordinate">
772+ <tal:widget define="widget nocall:view/widgets/store_name">
773+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
774+ </tal:widget>
775+ </table>
776+ <p class="formHelp">
777+ If you ask Launchpad to automatically upload builds of this
778+ snap to the store on your behalf, then the login service
779+ will prompt you to authorize this request.
780+ </p>
781+ </td>
782+ </tr>
783+ </tal:has-sds>
784 </table>
785 </metal:formbody>
786 </div>
787+
788+ <script type="text/javascript">
789+ LPJS.use('lp.snappy.snap.edit', function(Y) {
790+ Y.on('domready', function(e) {
791+ Y.lp.snappy.snap.edit.setup();
792+ }, window);
793+ });
794+ </script>
795 </div>
796
797 </body>