← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/snap-auto-build-ui into lp:launchpad with lp:~cjwatson/launchpad/snap-auto-build-archive-widget as a prerequisite.

Commit message:
Add UI for auto-building snap packages.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1593359 in Launchpad itself: "Add option to trigger snap builds when top-level branch changes"
  https://bugs.launchpad.net/launchpad/+bug/1593359

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/snap-auto-build-ui/+merge/297962

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.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/snap-auto-build-ui into lp:launchpad.
=== modified file 'lib/lp/snappy/browser/snap.py'
--- lib/lp/snappy/browser/snap.py	2016-05-28 00:21:40 +0000
+++ lib/lp/snappy/browser/snap.py	2016-06-20 21:21:33 +0000
@@ -180,6 +180,13 @@
             self.context, field, format_link(self.context.owner),
             header='Change owner', step_title='Select a new owner')
 
+    @property
+    def build_frequency(self):
+        if self.context.auto_build:
+            return 'Built automatically'
+        else:
+            return 'Built on request'
+
 
 def builds_for_snap(snap):
     """A list of interesting builds.
@@ -304,6 +311,7 @@
         'name',
         'private',
         'require_virtualized',
+        'auto_build',
         'store_upload',
         ])
     store_distro_series = Choice(
@@ -316,8 +324,13 @@
     branch = copy_field(ISnap['branch'], required=True)
     git_ref = copy_field(ISnap['git_ref'], required=True)
 
-    # These are only required if store_upload is True.  Later validation
-    # takes care of adjusting the required attribute.
+    # These are only required if auto_build is True.  Later validation takes
+    # care of adjusting the required attribute.
+    auto_build_archive = copy_field(ISnap['auto_build_archive'], required=True)
+    auto_build_pocket = copy_field(ISnap['auto_build_pocket'], required=True)
+
+    # This is only required if store_upload is True.  Later validation takes
+    # care of adjusting the required attribute.
     store_name = copy_field(ISnap['store_name'], required=True)
 
 
@@ -350,10 +363,14 @@
         'owner',
         'name',
         'store_distro_series',
+        'auto_build',
+        'auto_build_archive',
+        'auto_build_pocket',
         'store_upload',
         'store_name',
         ]
     custom_widget('store_distro_series', LaunchpadRadioWidget)
+    custom_widget('auto_build_archive', SnapArchiveWidget)
 
     def initialize(self):
         """See `LaunchpadView`."""
@@ -403,6 +420,8 @@
             'store_name': store_name,
             'owner': self.user,
             'store_distro_series': sds_set.getByDistroSeries(series).first(),
+            'auto_build_archive': series.main_archive,
+            'auto_build_pocket': PackagePublishingPocket.UPDATES,
             }
 
     @property
@@ -411,6 +430,13 @@
 
     def validate_widgets(self, data, names=None):
         """See `LaunchpadFormView`."""
+        if self.widgets.get('auto_build') is not None:
+            # Set widgets as required or optional depending on the
+            # auto_build field.
+            super(SnapAddView, self).validate_widgets(data, ['auto_build'])
+            auto_build = data.get('auto_build', False)
+            self.widgets['auto_build_archive'].context.required = auto_build
+            self.widgets['auto_build_pocket'].context.required = auto_build
         if self.widgets.get('store_upload') is not None:
             # Set widgets as required or optional depending on the
             # store_upload field.
@@ -427,9 +453,15 @@
             kwargs = {'branch': self.context}
         private = not getUtility(
             ISnapSet).isValidPrivacy(False, data['owner'], **kwargs)
+        if not data.get('auto_build', False):
+            data['auto_build_archive'] = None
+            data['auto_build_pocket'] = None
         snap = getUtility(ISnapSet).new(
             self.user, data['owner'],
             data['store_distro_series'].distro_series, data['name'],
+            auto_build=data['auto_build'],
+            auto_build_archive=data['auto_build_archive'],
+            auto_build_pocket=data['auto_build_pocket'],
             private=private, store_upload=data['store_upload'],
             store_series=data['store_distro_series'].snappy_series,
             store_name=data['store_name'], **kwargs)
@@ -487,6 +519,14 @@
                 self.widgets['git_ref'].context.required = True
             else:
                 raise AssertionError("Unknown branch type %s" % vcs)
+        if self.widgets.get('auto_build') is not None:
+            # Set widgets as required or optional depending on the
+            # auto_build field.
+            super(BaseSnapEditView, self).validate_widgets(
+                data, ['auto_build'])
+            auto_build = data.get('auto_build', False)
+            self.widgets['auto_build_archive'].context.required = auto_build
+            self.widgets['auto_build_pocket'].context.required = auto_build
         if self.widgets.get('store_upload') is not None:
             # Set widgets as required or optional depending on the
             # store_upload field.
@@ -523,9 +563,15 @@
                 self.context.setProcessors(
                     new_processors, check_permissions=True, user=self.user)
             del data['processors']
+        if not data.get('auto_build', False):
+            if 'auto_build_archive' in data:
+                del data['auto_build_archive']
+            if 'auto_build_pocket' in data:
+                del data['auto_build_pocket']
         store_upload = data.get('store_upload', False)
         if not store_upload:
-            data['store_name'] = None
+            if 'store_name' in data:
+                del data['store_name']
         need_store_reauth = self._needStoreReauth(data)
         self.updateContextFromData(data)
         if need_store_reauth:
@@ -577,15 +623,19 @@
         'owner',
         'name',
         'store_distro_series',
-        'store_upload',
-        'store_name',
         'vcs',
         'branch',
         'git_ref',
+        'auto_build',
+        'auto_build_archive',
+        'auto_build_pocket',
+        'store_upload',
+        'store_name',
         ]
     custom_widget('store_distro_series', LaunchpadRadioWidget)
     custom_widget('vcs', LaunchpadRadioWidget)
     custom_widget('git_ref', GitRefWidget)
+    custom_widget('auto_build_archive', SnapArchiveWidget)
 
     def setUpFields(self):
         """See `LaunchpadFormView`."""
@@ -609,6 +659,9 @@
             initial_values['vcs'] = VCSType.GIT
         else:
             initial_values['vcs'] = VCSType.BZR
+        if self.context.auto_build_pocket is None:
+            initial_values['auto_build_pocket'] = (
+                PackagePublishingPocket.UPDATES)
         return initial_values
 
     def validate(self, data):

=== modified file 'lib/lp/snappy/browser/tests/test_snap.py'
--- lib/lp/snappy/browser/tests/test_snap.py	2016-05-28 00:21:40 +0000
+++ lib/lp/snappy/browser/tests/test_snap.py	2016-06-20 21:21:33 +0000
@@ -42,6 +42,7 @@
 from lp.buildmaster.enums import BuildStatus
 from lp.buildmaster.interfaces.processor import IProcessorSet
 from lp.code.errors import GitRepositoryScanFault
+from lp.code.interfaces.githosting import IGitHostingClient
 from lp.registry.enums import PersonVisibility
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
@@ -76,6 +77,7 @@
     time_counter,
     )
 from lp.testing.fakemethod import FakeMethod
+from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
@@ -210,11 +212,23 @@
             "Source:\n%s\nEdit snap package" % source_display,
             MatchesTagText(content, "source"))
         self.assertThat(
+            "Build schedule:\n(?)\nBuilt on request\nEdit snap package\n",
+            MatchesTagText(content, "auto_build"))
+        self.assertThat(
+            "Source archive for automatic builds:\n\nEdit snap package\n",
+            MatchesTagText(content, "auto_build_archive"))
+        self.assertThat(
+            "Pocket for automatic builds:\n\nEdit snap package",
+            MatchesTagText(content, "auto_build_pocket"))
+        self.assertThat(
             "Builds of this snap package are not automatically uploaded to "
             "the store.\nEdit snap package",
             MatchesTagText(content, "store_upload"))
 
     def test_create_new_snap_git(self):
+        hosting_client = FakeMethod()
+        hosting_client.getBlob = FakeMethod(result="")
+        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
         [git_ref] = self.factory.makeGitRefs()
         source_display = git_ref.display_name
         browser = self.getViewBrowser(
@@ -234,6 +248,15 @@
             "Source:\n%s\nEdit snap package" % source_display,
             MatchesTagText(content, "source"))
         self.assertThat(
+            "Build schedule:\n(?)\nBuilt on request\nEdit snap package\n",
+            MatchesTagText(content, "auto_build"))
+        self.assertThat(
+            "Source archive for automatic builds:\n\nEdit snap package\n",
+            MatchesTagText(content, "auto_build_archive"))
+        self.assertThat(
+            "Pocket for automatic builds:\n\nEdit snap package",
+            MatchesTagText(content, "auto_build_pocket"))
+        self.assertThat(
             "Builds of this snap package are not automatically uploaded to "
             "the store.\nEdit snap package",
             MatchesTagText(content, "store_upload"))
@@ -303,6 +326,34 @@
             extract_text(find_tag_by_id(browser.contents, "privacy"))
         )
 
+    def test_create_new_snap_auto_build(self):
+        # Creating a new snap and asking for it to be automatically built
+        # sets all the appropriate fields.
+        branch = self.factory.makeAnyBranch()
+        archive = self.factory.makeArchive()
+        browser = self.getViewBrowser(
+            branch, view_name="+new-snap", user=self.person)
+        browser.getControl(name="field.name").value = "snap-name"
+        browser.getControl(
+            "Automatically build when branch changes").selected = True
+        browser.getControl("PPA").click()
+        browser.getControl(name="field.auto_build_archive.ppa").value = (
+            archive.reference)
+        browser.getControl("Pocket for automatic builds").value = ["SECURITY"]
+        browser.getControl("Create snap package").click()
+
+        content = find_main_content(browser.contents)
+        self.assertThat(
+            "Build schedule:\n(?)\nBuilt automatically\nEdit snap package\n",
+            MatchesTagText(content, "auto_build"))
+        self.assertThat(
+            "Source archive for automatic builds:\n%s\nEdit snap package\n" %
+            archive.displayname,
+            MatchesTagText(content, "auto_build_archive"))
+        self.assertThat(
+            "Pocket for automatic builds:\nSecurity\nEdit snap package",
+            MatchesTagText(content, "auto_build_pocket"))
+
     def test_create_new_snap_store_upload(self):
         # Creating a new snap and asking for it to be automatically uploaded
         # to the store sets all the appropriate fields and redirects to SSO
@@ -394,7 +445,7 @@
         view = create_initialized_view(git_ref, "+new-snap")
         with mock.patch('yaml.load') as unsafe_load:
             with mock.patch('yaml.safe_load') as safe_load:
-                initial_values = view.initial_values
+                view.initial_values
         self.assertEqual(0, unsafe_load.call_count)
         self.assertEqual(1, safe_load.call_count)
 
@@ -527,6 +578,7 @@
             new_snappy_series = self.factory.makeSnappySeries(
                 usable_distro_series=[new_series])
         [new_git_ref] = self.factory.makeGitRefs()
+        archive = self.factory.makeArchive()
 
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
@@ -538,6 +590,12 @@
         browser.getControl("Git repository").value = (
             new_git_ref.repository.identity)
         browser.getControl("Git branch").value = new_git_ref.path
+        browser.getControl(
+            "Automatically build when branch changes").selected = True
+        browser.getControl("PPA").click()
+        browser.getControl(name="field.auto_build_archive.ppa").value = (
+            archive.reference)
+        browser.getControl("Pocket for automatic builds").value = ["SECURITY"]
         browser.getControl("Update snap package").click()
 
         content = find_main_content(browser.contents)
@@ -551,6 +609,16 @@
             "Source:\n%s\nEdit snap package" % new_git_ref.display_name,
             MatchesTagText(content, "source"))
         self.assertThat(
+            "Build schedule:\n(?)\nBuilt automatically\nEdit snap package\n",
+            MatchesTagText(content, "auto_build"))
+        self.assertThat(
+            "Source archive for automatic builds:\n%s\nEdit snap package\n" %
+            archive.displayname,
+            MatchesTagText(content, "auto_build_archive"))
+        self.assertThat(
+            "Pocket for automatic builds:\nSecurity\nEdit snap package",
+            MatchesTagText(content, "auto_build_pocket"))
+        self.assertThat(
             "Builds of this snap package are not automatically uploaded to "
             "the store.\nEdit snap package",
             MatchesTagText(content, "store_upload"))
@@ -1036,6 +1104,10 @@
             Owner: Test Person
             Distribution series: Ubuntu Shiny
             Source: lp://dev/~test-person/\\+junk/snap-branch
+            Build schedule: \(\?\)
+            Built on request
+            Source archive for automatic builds:
+            Pocket for automatic builds:
             Builds of this snap package are not automatically uploaded to
             the store.
             Latest builds
@@ -1059,6 +1131,10 @@
             Owner: Test Person
             Distribution series: Ubuntu Shiny
             Source: ~test-person/\\+git/snap-repository:master
+            Build schedule: \(\?\)
+            Built on request
+            Source archive for automatic builds:
+            Pocket for automatic builds:
             Builds of this snap package are not automatically uploaded to
             the store.
             Latest builds

=== modified file 'lib/lp/snappy/configure.zcml'
--- lib/lp/snappy/configure.zcml	2016-05-06 16:34:21 +0000
+++ lib/lp/snappy/configure.zcml	2016-06-20 21:21:33 +0000
@@ -14,6 +14,8 @@
     <include package=".browser" />
     <include file="vocabularies.zcml" />
 
+    <lp:help-folder folder="help" name="+help-snappy" />
+
     <!-- Snap -->
     <class class="lp.snappy.model.snap.Snap">
         <require

=== added directory 'lib/lp/snappy/help'
=== added file 'lib/lp/snappy/help/snap-build-frequency.html'
--- lib/lp/snappy/help/snap-build-frequency.html	1970-01-01 00:00:00 +0000
+++ lib/lp/snappy/help/snap-build-frequency.html	2016-06-20 21:21:33 +0000
@@ -0,0 +1,42 @@
+<html>
+  <head>
+    <title>Snap package build schedule</title>
+    <link rel="stylesheet" type="text/css"
+          href="/+icing/yui/cssreset/reset.css" />
+    <link rel="stylesheet" type="text/css"
+          href="/+icing/yui/cssfonts/fonts.css" />
+    <link rel="stylesheet" type="text/css"
+          href="/+icing/yui/cssbase/base.css" />
+    <style type="text/css">
+      dt { font-weight: bold }
+      dd p { margin-bottom: 0.5em }
+    </style>
+  </head>
+  <body>
+    <h1>Snap package build schedule</h1>
+
+    <p>There are two options for when snap packages get built:</p>
+    <dl>
+      <dt>Built automatically</dt>
+      <dd>
+        <p>A build will be scheduled automatically once a change to the
+          top-level source branch for the snap package is detected.</p>
+        <p>If there has been a build of the snap package within the previous
+          hour from the source archive, the build will not be scheduled
+          until an hour since the last build from the source archive.</p>
+        <p>If the snap package has been built within the last hour from a
+          different archive using the "Request builds" action, this will not
+          delay the automatic build.</p>
+        <p>If you really want the build to happen before the one-hour period
+          is up, you can use the "Request builds" action.</p>
+      </dd>
+      <dt>Built on request</dt>
+      <dd>
+        <p>Builds of the snap package have to be manually requested using
+          the "Request builds" action.</p>
+      </dd>
+    </dl>
+
+  </body>
+</html>
+

=== modified file 'lib/lp/snappy/javascript/snap.edit.js'
--- lib/lp/snappy/javascript/snap.edit.js	2015-09-09 14:17:46 +0000
+++ lib/lp/snappy/javascript/snap.edit.js	2016-06-20 21:21:33 +0000
@@ -1,7 +1,8 @@
-/* Copyright 2015 Canonical Ltd.  This software is licensed under the
+/* Copyright 2015-2016 Canonical Ltd.  This software is licensed under the
  * GNU Affero General Public License version 3 (see the file LICENSE).
  *
- * Control enabling/disabling form elements on the Snap:+edit page.
+ * Control enabling/disabling form elements on the {Branch,GitRef}:+new-snap
+ * and Snap:+edit pages.
  *
  * @module Y.lp.snappy.snap.edit
  * @requires node, DOM
@@ -12,7 +13,9 @@
 
     module.set_enabled = function(field_id, is_enabled) {
         var field = Y.DOM.byId(field_id);
-        field.disabled = !is_enabled;
+        if (field !== null) {
+            field.disabled = !is_enabled;
+        }
     };
 
     module.onclick_vcs = function(e) {
@@ -22,15 +25,31 @@
                 selected_vcs = node.get('value');
             }
         });
-        module.set_enabled('field.branch', selected_vcs === 'BZR');
-        module.set_enabled('field.git_ref.repository', selected_vcs === 'GIT');
-        module.set_enabled('field.git_ref.path', selected_vcs === 'GIT');
+        if (selected_vcs !== null) {
+            module.set_enabled('field.branch', selected_vcs === 'BZR');
+            module.set_enabled(
+                'field.git_ref.repository', selected_vcs === 'GIT');
+            module.set_enabled('field.git_ref.path', selected_vcs === 'GIT');
+        }
+    };
+
+    module.onclick_auto_build = function(e) {
+        var auto_build = Y.one(
+            'input[name="field.auto_build"]').get('checked');
+        module.set_enabled(
+            'field.auto_build_archive.option.primary', auto_build);
+        module.set_enabled('field.auto_build_archive.option.ppa', auto_build);
+        module.set_enabled('field.auto_build_archive.ppa', auto_build);
+        module.set_enabled('field.auto_build_pocket', auto_build);
     };
 
     module.setup = function() {
         Y.all('input[name="field.vcs"]').on('click', module.onclick_vcs);
+        Y.all('input[name="field.auto_build"]').on(
+            'click', module.onclick_auto_build);
 
         // Set the initial state.
         module.onclick_vcs();
+        module.onclick_auto_build();
     };
 }, '0.1', {'requires': ['node', 'DOM']});

=== modified file 'lib/lp/snappy/javascript/tests/test_snap.edit.html'
--- lib/lp/snappy/javascript/tests/test_snap.edit.html	2015-09-09 14:17:46 +0000
+++ lib/lp/snappy/javascript/tests/test_snap.edit.html	2016-06-20 21:21:33 +0000
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <!--
-Copyright 2015 Canonical Ltd.  This software is licensed under the
+Copyright 2015-2016 Canonical Ltd.  This software is licensed under the
 GNU Affero General Public License version 3 (see the file LICENSE).
 -->
 
@@ -133,6 +133,95 @@
                 </div>
               </td>
             </tr>
+            <tr>
+              <td colspan="2">
+                <div>
+                  <label for="field.auto_build">Automatically build when branch changes</label>
+                  <input class="checkboxType"
+                         id="field.auto_build"
+                         name="field.auto_build"
+                         type="checkbox"
+                         value="" />
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <td>
+                <table class="subordinate">
+                  <tr>
+                    <td colspan="2">
+                      <div>
+                        <label for="field.auto_build_archive">Source archive for automatic builds:</label>
+                        <div>
+                          <table>
+                            <tr>
+                              <td colspan="2">
+                                <label>
+                                  <input class="radioType"
+                                         id="field.auto_build_archive.option.primary"
+                                         name="field.auto_build_archive"
+                                         type="radio"
+                                         value="primary" />
+                                  Primary Archive for Ubuntu
+                                </label>
+                              </td>
+                            </tr>
+                            <tr>
+                              <td>
+                                <label>
+                                  <input class="radioType"
+                                         id="field.auto_build_archive.option.ppa"
+                                         name="field.auto_build_archive"
+                                         type="radio"
+                                         value="ppa" />
+                                  PPA
+                                </label>
+                              </td>
+                              <td>
+                                <input type="text"
+                                       value=""
+                                       id="field.auto_build_archive.ppa"
+                                       name="field.auto_build_archive.ppa"
+                                       size="20"
+                                       maxlength=""
+                                       onKeyPress=""
+                                       style=""
+                                       class="" />
+                              </td>
+                            </tr>
+                          </table>
+                        </div>
+                      </div>
+                    </td>
+                  </tr>
+                  <tr>
+                    <td colspan="2">
+                      <div>
+                        <label for="field.auto_build_pocket">Pocket for automatic builds:</label>
+                        <div>
+                          <div>
+                            <div class="value">
+                              <select id="field.auto_build_pocket"
+                                      name="field.auto_build_pocket"
+                                      size="1">
+                                <option value="RELEASE">Release</option>
+                                <option value="SECURITY">Security</option>
+                                <option selected="selected" value="UPDATES">Updates</option>
+                                <option value="PROPOSED">Proposed</option>
+                                <option value="BACKPORTS">Backports</option>
+                              </select>
+                            </div>
+                            <input name="field.auto_build_pocket-empty-marker"
+                                   type="hidden"
+                                   value="1" />
+                          </div>
+                        </div>
+                      </div>
+                    </td>
+                  </tr>
+                </table>
+              </td>
+            </tr>
           </table>
 
           <input type="submit" id="field.actions.update"

=== modified file 'lib/lp/snappy/javascript/tests/test_snap.edit.js'
--- lib/lp/snappy/javascript/tests/test_snap.edit.js	2015-09-09 14:17:46 +0000
+++ lib/lp/snappy/javascript/tests/test_snap.edit.js	2016-06-20 21:21:33 +0000
@@ -1,4 +1,4 @@
-/* Copyright 2015 Canonical Ltd.  This software is licensed under the
+/* Copyright 2015-2016 Canonical Ltd.  This software is licensed under the
  * GNU Affero General Public License version 3 (see the file LICENSE).
  *
  * Test driver for snap.edit.js.
@@ -14,9 +14,10 @@
         setUp: function() {
             this.tbody = Y.one('#snap.edit');
 
-            // Get the individual VCS type radio buttons.
+            // Get the widgets with event handlers attached.
             this.vcs_bzr = Y.DOM.byId('field.vcs.Bazaar');
             this.vcs_git = Y.DOM.byId('field.vcs.Git');
+            this.auto_build = Y.DOM.byId('field.auto_build');
 
             // Get the input widgets.
             this.input_branch = Y.DOM.byId('field.branch');
@@ -45,6 +46,7 @@
 
             check_handler(this.vcs_bzr, module.onclick_vcs);
             check_handler(this.vcs_git, module.onclick_vcs);
+            check_handler(this.auto_build, module.onclick_auto_build);
         },
 
         test_select_vcs_bzr: function() {
@@ -73,7 +75,38 @@
                              'git_ref.repository field disabled');
             Y.Assert.isFalse(this.input_git_path.disabled,
                              'git_ref.path field disabled');
+        },
+
+        test_check_auto_build: function() {
+            var fields = [
+                'auto_build_archive.option.primary',
+                'auto_build_archive.option.ppa',
+                'auto_build_archive.ppa',
+                'auto_build_pocket'
+                ];
+            var field;
+            module.setup();
+            for (i = 0; i < fields.length; i++) {
+                field = Y.DOM.byId('field.' + fields[i]);
+                Y.Assert.isTrue(
+                    field.disabled, fields[i] + ' field not disabled');
+            }
+            this.auto_build.checked = true;
+            module.onclick_auto_build();
+            for (i = 0; i < fields.length; i++) {
+                field = Y.DOM.byId('field.' + fields[i]);
+                Y.Assert.isFalse(
+                    field.disabled, fields[i] + ' field disabled');
+            }
+            this.auto_build.checked = false;
+            module.onclick_auto_build();
+            for (i = 0; i < fields.length; i++) {
+                field = Y.DOM.byId('field.' + fields[i]);
+                Y.Assert.isTrue(
+                    field.disabled, fields[i] + ' field not disabled');
+            }
         }
+
     }));
 }, '0.1', {
     requires: ['lp.testing.runner', 'test', 'test-console',

=== modified file 'lib/lp/snappy/templates/snap-edit.pt'
--- lib/lp/snappy/templates/snap-edit.pt	2016-05-24 05:15:50 +0000
+++ lib/lp/snappy/templates/snap-edit.pt	2016-06-20 21:21:33 +0000
@@ -29,24 +29,6 @@
           <metal:block use-macro="context/@@launchpad_form/widget_row" />
         </tal:widget>
 
-        <tr tal:condition="view/has_snappy_distro_series">
-          <td>
-            <tal:widget define="widget nocall:view/widgets/store_upload">
-              <metal:block use-macro="context/@@launchpad_form/widget_row" />
-            </tal:widget>
-            <table class="subordinate">
-              <tal:widget define="widget nocall:view/widgets/store_name">
-                <metal:block use-macro="context/@@launchpad_form/widget_row" />
-              </tal:widget>
-            </table>
-            <p class="formHelp">
-              If you change any settings related to automatically uploading
-              builds of this snap to the store, then the login service will
-              prompt you to authorize this request.
-            </p>
-          </td>
-        </tr>
-
         <tr>
           <td>
             <div>
@@ -78,6 +60,42 @@
           </td>
         </tr>
 
+        <tal:widget define="widget nocall:view/widgets/auto_build">
+          <metal:block use-macro="context/@@launchpad_form/widget_row" />
+        </tal:widget>
+        <tr>
+          <td>
+            <table class="subordinate">
+              <tal:widget define="widget nocall:view/widgets/auto_build_archive">
+                <metal:block use-macro="context/@@launchpad_form/widget_row" />
+              </tal:widget>
+              <tal:widget define="widget nocall:view/widgets/auto_build_pocket">
+                <metal:block use-macro="context/@@launchpad_form/widget_row" />
+              </tal:widget>
+            </table>
+          </td>
+        </tr>
+
+        <tal:has-sds tal:condition="view/has_snappy_distro_series">
+          <tal:widget define="widget nocall:view/widgets/store_upload">
+            <metal:block use-macro="context/@@launchpad_form/widget_row" />
+          </tal:widget>
+          <tr>
+            <td>
+              <table class="subordinate">
+                <tal:widget define="widget nocall:view/widgets/store_name">
+                  <metal:block use-macro="context/@@launchpad_form/widget_row" />
+                </tal:widget>
+              </table>
+              <p class="formHelp">
+                If you change any settings related to automatically uploading
+                builds of this snap to the store, then the login service will
+                prompt you to authorize this request.
+              </p>
+            </td>
+          </tr>
+        </tal:has-sds>
+
         <tal:widget define="widget nocall:view/widgets/processors">
           <metal:block use-macro="context/@@launchpad_form/widget_row" />
         </tal:widget>

=== modified file 'lib/lp/snappy/templates/snap-index.pt'
--- lib/lp/snappy/templates/snap-index.pt	2016-05-28 00:21:40 +0000
+++ lib/lp/snappy/templates/snap-index.pt	2016-06-20 21:21:33 +0000
@@ -61,6 +61,31 @@
           <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
         </dd>
       </dl>
+
+      <dl id="auto_build">
+        <dt>Build schedule:
+          <a href="/+help-snappy/snap-build-frequency.html" target="help"
+             class="sprite maybe action-icon">(?)</a>
+        </dt>
+        <dd>
+          <span tal:replace="view/build_frequency"/>
+          <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
+        </dd>
+      </dl>
+      <dl id="auto_build_archive">
+        <dt>Source archive for automatic builds:</dt>
+        <dd>
+          <a tal:replace="structure context/auto_build_archive/fmt:link"/>
+          <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
+        </dd>
+      </dl>
+      <dl id="auto_build_pocket">
+        <dt>Pocket for automatic builds:</dt>
+        <dd>
+          <span tal:replace="context/auto_build_pocket/title|nothing"/>
+          <a tal:replace="structure view/menu:overview/edit/fmt:icon"/>
+        </dd>
+      </dl>
     </div>
 
     <div id="store_upload" class="two-column-list"

=== modified file 'lib/lp/snappy/templates/snap-new.pt'
--- lib/lp/snappy/templates/snap-new.pt	2016-05-24 05:15:50 +0000
+++ lib/lp/snappy/templates/snap-new.pt	2016-06-20 21:21:33 +0000
@@ -32,26 +32,52 @@
           <metal:block use-macro="context/@@launchpad_form/widget_row" />
         </tal:widget>
 
-        <tr tal:condition="view/has_snappy_distro_series">
+        <tal:widget define="widget nocall:view/widgets/auto_build">
+          <metal:block use-macro="context/@@launchpad_form/widget_row" />
+        </tal:widget>
+        <tr>
           <td>
-            <tal:widget define="widget nocall:view/widgets/store_upload">
-              <metal:block use-macro="context/@@launchpad_form/widget_row" />
-            </tal:widget>
             <table class="subordinate">
-              <tal:widget define="widget nocall:view/widgets/store_name">
+              <tal:widget define="widget nocall:view/widgets/auto_build_archive">
+                <metal:block use-macro="context/@@launchpad_form/widget_row" />
+              </tal:widget>
+              <tal:widget define="widget nocall:view/widgets/auto_build_pocket">
                 <metal:block use-macro="context/@@launchpad_form/widget_row" />
               </tal:widget>
             </table>
-            <p class="formHelp">
-              If you ask Launchpad to automatically upload builds of this
-              snap to the store on your behalf, then the login service
-              will prompt you to authorize this request.
-            </p>
           </td>
         </tr>
+
+        <tal:has-sds condition="view/has_snappy_distro_series">
+          <tal:widget define="widget nocall:view/widgets/store_upload">
+            <metal:block use-macro="context/@@launchpad_form/widget_row" />
+          </tal:widget>
+          <tr>
+            <td>
+              <table class="subordinate">
+                <tal:widget define="widget nocall:view/widgets/store_name">
+                  <metal:block use-macro="context/@@launchpad_form/widget_row" />
+                </tal:widget>
+              </table>
+              <p class="formHelp">
+                If you ask Launchpad to automatically upload builds of this
+                snap to the store on your behalf, then the login service
+                will prompt you to authorize this request.
+              </p>
+            </td>
+          </tr>
+        </tal:has-sds>
       </table>
     </metal:formbody>
   </div>
+
+  <script type="text/javascript">
+    LPJS.use('lp.snappy.snap.edit', function(Y) {
+      Y.on('domready', function(e) {
+        Y.lp.snappy.snap.edit.setup();
+      }, window);
+    });
+  </script>
 </div>
 
 </body>


Follow ups