Merge lp:~cjwatson/launchpad/snap-channels-ui into lp:launchpad
- snap-channels-ui
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 18144 |
Proposed branch: | lp:~cjwatson/launchpad/snap-channels-ui |
Merge into: | lp:launchpad |
Prerequisite: | lp:~cjwatson/launchpad/snap-channels-job |
Diff against target: |
351 lines (+122/-8) 10 files modified
lib/lp/scripts/utilities/importfascist.py (+3/-1) lib/lp/snappy/browser/snap.py (+15/-3) lib/lp/snappy/browser/snapbuild.py (+5/-0) lib/lp/snappy/browser/tests/test_snap.py (+33/-3) lib/lp/snappy/browser/tests/test_snapbuild.py (+19/-0) lib/lp/snappy/templates/snap-edit.pt (+4/-0) lib/lp/snappy/templates/snap-index.pt (+10/-0) lib/lp/snappy/templates/snap-new.pt (+4/-0) lib/lp/snappy/vocabularies.py (+18/-1) lib/lp/snappy/vocabularies.zcml (+11/-0) |
To merge this branch: | bzr merge lp:~cjwatson/launchpad/snap-channels-ui |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thomi Richards (community) | Approve | ||
Launchpad code reviewers | Pending | ||
Review via email: mp+298811@code.launchpad.net |
Commit message
Add UI for automatically releasing snap packages.
Description of the change
Add UI for automatically releasing snap packages.
This will require firewall changes first to let the appservers talk to search.
Thomi Richards (thomir-deactivatedaccount) wrote : | # |
Colin Watson (cjwatson) wrote : | # |
That implies that you haven't upgraded the database schema to its latest
revision. You can do "make schema" to burn your current database down
and start afresh, or use upgrade.py and security.py from
database/schema/ to apply patches in place. I'm not quite sure why the
"make schema" that you said you ran didn't apply
database/
the database that you're running tests against is somewhere else?
upgrade.py gives you a bit more control and it should be easier to see
what's going on there, anyway.
After that, you'll run into Snap.store_channels similarly not existing.
The top three items in this branch stack (of which this branch is
currently the deepest) also depend on
https:/
It's not listed as a prerequisite because database patches are targeted
at a separate trunk (lp:launchpad/db-devel), but db-snap-channels will
need to be merged, deployed in a fastdowntime, and then db-devel merged
into devel before it's possible to land snap-channels-
above.
Preview Diff
1 | === modified file 'lib/lp/scripts/utilities/importfascist.py' | |||
2 | --- lib/lp/scripts/utilities/importfascist.py 2016-05-11 10:45:12 +0000 | |||
3 | +++ lib/lp/scripts/utilities/importfascist.py 2016-07-16 07:54:57 +0000 | |||
4 | @@ -1,4 +1,4 @@ | |||
6 | 1 | # Copyright 2009-2012 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2016 Canonical Ltd. This software is licensed under the |
7 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | 3 | 3 | ||
9 | 4 | import __builtin__ | 4 | import __builtin__ |
10 | @@ -27,6 +27,8 @@ | |||
11 | 27 | valid_imports_not_in_all = { | 27 | valid_imports_not_in_all = { |
12 | 28 | 'bzrlib.lsprof': set(['BzrProfiler']), | 28 | 'bzrlib.lsprof': set(['BzrProfiler']), |
13 | 29 | 'cookielib': set(['domain_match']), | 29 | 'cookielib': set(['domain_match']), |
14 | 30 | # Exported in Python 3, but missing and so not exported in Python 2. | ||
15 | 31 | 'json.decoder': set(['JSONDecodeError']), | ||
16 | 30 | 'openid.fetchers': set(['Urllib2Fetcher']), | 32 | 'openid.fetchers': set(['Urllib2Fetcher']), |
17 | 31 | 'openid.message': set(['NamespaceAliasRegistrationError']), | 33 | 'openid.message': set(['NamespaceAliasRegistrationError']), |
18 | 32 | 'storm.database': set(['STATE_DISCONNECTED']), | 34 | 'storm.database': set(['STATE_DISCONNECTED']), |
19 | 33 | 35 | ||
20 | === modified file 'lib/lp/snappy/browser/snap.py' | |||
21 | --- lib/lp/snappy/browser/snap.py 2016-07-12 13:38:58 +0000 | |||
22 | +++ lib/lp/snappy/browser/snap.py 2016-07-16 07:54:57 +0000 | |||
23 | @@ -331,6 +331,9 @@ | |||
24 | 331 | # This is only required if store_upload is True. Later validation takes | 331 | # This is only required if store_upload is True. Later validation takes |
25 | 332 | # care of adjusting the required attribute. | 332 | # care of adjusting the required attribute. |
26 | 333 | store_name = copy_field(ISnap['store_name'], required=True) | 333 | store_name = copy_field(ISnap['store_name'], required=True) |
27 | 334 | store_channels = copy_field( | ||
28 | 335 | ISnap['store_channels'], | ||
29 | 336 | value_type=Choice(vocabulary='SnapStoreChannel'), required=True) | ||
30 | 334 | 337 | ||
31 | 335 | 338 | ||
32 | 336 | def log_oops(error, request): | 339 | def log_oops(error, request): |
33 | @@ -368,9 +371,11 @@ | |||
34 | 368 | 'auto_build_pocket', | 371 | 'auto_build_pocket', |
35 | 369 | 'store_upload', | 372 | 'store_upload', |
36 | 370 | 'store_name', | 373 | 'store_name', |
37 | 374 | 'store_channels', | ||
38 | 371 | ] | 375 | ] |
39 | 372 | custom_widget('store_distro_series', LaunchpadRadioWidget) | 376 | custom_widget('store_distro_series', LaunchpadRadioWidget) |
40 | 373 | custom_widget('auto_build_archive', SnapArchiveWidget) | 377 | custom_widget('auto_build_archive', SnapArchiveWidget) |
41 | 378 | custom_widget('store_channels', LabeledMultiCheckBoxWidget) | ||
42 | 374 | 379 | ||
43 | 375 | def initialize(self): | 380 | def initialize(self): |
44 | 376 | """See `LaunchpadView`.""" | 381 | """See `LaunchpadView`.""" |
45 | @@ -457,6 +462,7 @@ | |||
46 | 457 | super(SnapAddView, self).validate_widgets(data, ['store_upload']) | 462 | super(SnapAddView, self).validate_widgets(data, ['store_upload']) |
47 | 458 | store_upload = data.get('store_upload', False) | 463 | store_upload = data.get('store_upload', False) |
48 | 459 | self.widgets['store_name'].context.required = store_upload | 464 | self.widgets['store_name'].context.required = store_upload |
49 | 465 | self.widgets['store_channels'].context.required = store_upload | ||
50 | 460 | super(SnapAddView, self).validate_widgets(data, names=names) | 466 | super(SnapAddView, self).validate_widgets(data, names=names) |
51 | 461 | 467 | ||
52 | 462 | @action('Create snap package', name='create') | 468 | @action('Create snap package', name='create') |
53 | @@ -476,10 +482,11 @@ | |||
54 | 476 | auto_build=data['auto_build'], | 482 | auto_build=data['auto_build'], |
55 | 477 | auto_build_archive=data['auto_build_archive'], | 483 | auto_build_archive=data['auto_build_archive'], |
56 | 478 | auto_build_pocket=data['auto_build_pocket'], | 484 | auto_build_pocket=data['auto_build_pocket'], |
58 | 479 | private=private, store_upload=data['store_upload'], | 485 | processors=data['processors'], private=private, |
59 | 486 | store_upload=data['store_upload'], | ||
60 | 480 | store_series=data['store_distro_series'].snappy_series, | 487 | store_series=data['store_distro_series'].snappy_series, |
63 | 481 | store_name=data['store_name'], processors=data['processors'], | 488 | store_name=data['store_name'], |
64 | 482 | **kwargs) | 489 | store_channels=data.get('store_channels'), **kwargs) |
65 | 483 | if data['store_upload']: | 490 | if data['store_upload']: |
66 | 484 | self.requestAuthorization(snap) | 491 | self.requestAuthorization(snap) |
67 | 485 | else: | 492 | else: |
68 | @@ -549,6 +556,7 @@ | |||
69 | 549 | data, ['store_upload']) | 556 | data, ['store_upload']) |
70 | 550 | store_upload = data.get('store_upload', False) | 557 | store_upload = data.get('store_upload', False) |
71 | 551 | self.widgets['store_name'].context.required = store_upload | 558 | self.widgets['store_name'].context.required = store_upload |
72 | 559 | self.widgets['store_channels'].context.required = store_upload | ||
73 | 552 | super(BaseSnapEditView, self).validate_widgets(data, names=names) | 560 | super(BaseSnapEditView, self).validate_widgets(data, names=names) |
74 | 553 | 561 | ||
75 | 554 | def _needStoreReauth(self, data): | 562 | def _needStoreReauth(self, data): |
76 | @@ -587,6 +595,8 @@ | |||
77 | 587 | if not store_upload: | 595 | if not store_upload: |
78 | 588 | if 'store_name' in data: | 596 | if 'store_name' in data: |
79 | 589 | del data['store_name'] | 597 | del data['store_name'] |
80 | 598 | if 'store_channels' in data: | ||
81 | 599 | del data['store_channels'] | ||
82 | 590 | need_store_reauth = self._needStoreReauth(data) | 600 | need_store_reauth = self._needStoreReauth(data) |
83 | 591 | self.updateContextFromData(data) | 601 | self.updateContextFromData(data) |
84 | 592 | if need_store_reauth: | 602 | if need_store_reauth: |
85 | @@ -646,8 +656,10 @@ | |||
86 | 646 | 'auto_build_pocket', | 656 | 'auto_build_pocket', |
87 | 647 | 'store_upload', | 657 | 'store_upload', |
88 | 648 | 'store_name', | 658 | 'store_name', |
89 | 659 | 'store_channels', | ||
90 | 649 | ] | 660 | ] |
91 | 650 | custom_widget('store_distro_series', LaunchpadRadioWidget) | 661 | custom_widget('store_distro_series', LaunchpadRadioWidget) |
92 | 662 | custom_widget('store_channels', LabeledMultiCheckBoxWidget) | ||
93 | 651 | custom_widget('vcs', LaunchpadRadioWidget) | 663 | custom_widget('vcs', LaunchpadRadioWidget) |
94 | 652 | custom_widget('git_ref', GitRefWidget) | 664 | custom_widget('git_ref', GitRefWidget) |
95 | 653 | custom_widget('auto_build_archive', SnapArchiveWidget) | 665 | custom_widget('auto_build_archive', SnapArchiveWidget) |
96 | 654 | 666 | ||
97 | === modified file 'lib/lp/snappy/browser/snapbuild.py' | |||
98 | --- lib/lp/snappy/browser/snapbuild.py 2016-07-14 15:19:03 +0000 | |||
99 | +++ lib/lp/snappy/browser/snapbuild.py 2016-07-16 07:54:57 +0000 | |||
100 | @@ -95,6 +95,11 @@ | |||
101 | 95 | return structured( | 95 | return structured( |
102 | 96 | '<a href="%s">Manage this package in the store</a>', | 96 | '<a href="%s">Manage this package in the store</a>', |
103 | 97 | job.store_url).escapedtext | 97 | job.store_url).escapedtext |
104 | 98 | elif job.store_url: | ||
105 | 99 | return structured( | ||
106 | 100 | '<a href="%s">Manage this package in the store</a><br />' | ||
107 | 101 | 'Releasing package to channels failed: %s', | ||
108 | 102 | job.store_url, job.error_message).escapedtext | ||
109 | 98 | else: | 103 | else: |
110 | 99 | return structured( | 104 | return structured( |
111 | 100 | "Store upload failed: %s", job.error_message).escapedtext | 105 | "Store upload failed: %s", job.error_message).escapedtext |
112 | 101 | 106 | ||
113 | === modified file 'lib/lp/snappy/browser/tests/test_snap.py' | |||
114 | --- lib/lp/snappy/browser/tests/test_snap.py 2016-07-12 13:38:58 +0000 | |||
115 | +++ lib/lp/snappy/browser/tests/test_snap.py 2016-07-16 07:54:57 +0000 | |||
116 | @@ -63,6 +63,7 @@ | |||
117 | 63 | SNAP_TESTING_FLAGS, | 63 | SNAP_TESTING_FLAGS, |
118 | 64 | SnapPrivateFeatureDisabled, | 64 | SnapPrivateFeatureDisabled, |
119 | 65 | ) | 65 | ) |
120 | 66 | from lp.snappy.interfaces.snapstoreclient import ISnapStoreClient | ||
121 | 66 | from lp.testing import ( | 67 | from lp.testing import ( |
122 | 67 | admin_logged_in, | 68 | admin_logged_in, |
123 | 68 | BrowserTestCase, | 69 | BrowserTestCase, |
124 | @@ -125,6 +126,10 @@ | |||
125 | 125 | def test_private_feature_flag_disabled(self): | 126 | def test_private_feature_flag_disabled(self): |
126 | 126 | # Without a private_snap feature flag, we will not create Snaps for | 127 | # Without a private_snap feature flag, we will not create Snaps for |
127 | 127 | # private contexts. | 128 | # private contexts. |
128 | 129 | self.snap_store_client = FakeMethod() | ||
129 | 130 | self.snap_store_client.listChannels = FakeMethod(result=[]) | ||
130 | 131 | self.useFixture( | ||
131 | 132 | ZopeUtilityFixture(self.snap_store_client, ISnapStoreClient)) | ||
132 | 128 | owner = self.factory.makePerson() | 133 | owner = self.factory.makePerson() |
133 | 129 | branch = self.factory.makeAnyBranch( | 134 | branch = self.factory.makeAnyBranch( |
134 | 130 | owner=owner, information_type=InformationType.USERDATA) | 135 | owner=owner, information_type=InformationType.USERDATA) |
135 | @@ -149,6 +154,15 @@ | |||
136 | 149 | with admin_logged_in(): | 154 | with admin_logged_in(): |
137 | 150 | self.snappyseries = self.factory.makeSnappySeries( | 155 | self.snappyseries = self.factory.makeSnappySeries( |
138 | 151 | usable_distro_series=[self.distroseries]) | 156 | usable_distro_series=[self.distroseries]) |
139 | 157 | self.snap_store_client = FakeMethod() | ||
140 | 158 | self.snap_store_client.listChannels = FakeMethod(result=[ | ||
141 | 159 | {"name": "stable", "display_name": "Stable"}, | ||
142 | 160 | {"name": "edge", "display_name": "Edge"}, | ||
143 | 161 | ]) | ||
144 | 162 | self.snap_store_client.requestPackageUploadPermission = ( | ||
145 | 163 | getUtility(ISnapStoreClient).requestPackageUploadPermission) | ||
146 | 164 | self.useFixture( | ||
147 | 165 | ZopeUtilityFixture(self.snap_store_client, ISnapStoreClient)) | ||
148 | 152 | 166 | ||
149 | 153 | def setUpDistroSeries(self): | 167 | def setUpDistroSeries(self): |
150 | 154 | """Set up a distroseries with some available processors.""" | 168 | """Set up a distroseries with some available processors.""" |
151 | @@ -371,6 +385,8 @@ | |||
152 | 371 | browser.getControl("Automatically upload to store").selected = True | 385 | browser.getControl("Automatically upload to store").selected = True |
153 | 372 | browser.getControl("Registered store package name").value = ( | 386 | browser.getControl("Registered store package name").value = ( |
154 | 373 | "store-name") | 387 | "store-name") |
155 | 388 | self.assertFalse(browser.getControl("Stable").selected) | ||
156 | 389 | browser.getControl("Edge").selected = True | ||
157 | 374 | root_macaroon = Macaroon() | 390 | root_macaroon = Macaroon() |
158 | 375 | root_macaroon.add_third_party_caveat( | 391 | root_macaroon.add_third_party_caveat( |
159 | 376 | urlsplit(config.launchpad.openid_provider_root).netloc, "", | 392 | urlsplit(config.launchpad.openid_provider_root).netloc, "", |
160 | @@ -395,7 +411,8 @@ | |||
161 | 395 | owner=self.person, distro_series=self.distroseries, | 411 | owner=self.person, distro_series=self.distroseries, |
162 | 396 | name=u"snap-name", source=branch, store_upload=True, | 412 | name=u"snap-name", source=branch, store_upload=True, |
163 | 397 | store_series=self.snappyseries, store_name=u"store-name", | 413 | store_series=self.snappyseries, store_name=u"store-name", |
165 | 398 | store_secrets={"root": root_macaroon_raw})) | 414 | store_secrets={"root": root_macaroon_raw}, |
166 | 415 | store_channels=["edge"])) | ||
167 | 399 | self.assertThat(self.request, MatchesStructure.byEquality( | 416 | self.assertThat(self.request, MatchesStructure.byEquality( |
168 | 400 | url="http://sca.example/dev/api/acl/", method="POST")) | 417 | url="http://sca.example/dev/api/acl/", method="POST")) |
169 | 401 | expected_body = { | 418 | expected_body = { |
170 | @@ -590,6 +607,15 @@ | |||
171 | 590 | with admin_logged_in(): | 607 | with admin_logged_in(): |
172 | 591 | self.snappyseries = self.factory.makeSnappySeries( | 608 | self.snappyseries = self.factory.makeSnappySeries( |
173 | 592 | usable_distro_series=[self.distroseries]) | 609 | usable_distro_series=[self.distroseries]) |
174 | 610 | self.snap_store_client = FakeMethod() | ||
175 | 611 | self.snap_store_client.listChannels = FakeMethod(result=[ | ||
176 | 612 | {"name": "stable", "display_name": "Stable"}, | ||
177 | 613 | {"name": "edge", "display_name": "Edge"}, | ||
178 | 614 | ]) | ||
179 | 615 | self.snap_store_client.requestPackageUploadPermission = ( | ||
180 | 616 | getUtility(ISnapStoreClient).requestPackageUploadPermission) | ||
181 | 617 | self.useFixture( | ||
182 | 618 | ZopeUtilityFixture(self.snap_store_client, ISnapStoreClient)) | ||
183 | 593 | 619 | ||
184 | 594 | def test_initial_store_series(self): | 620 | def test_initial_store_series(self): |
185 | 595 | # The initial store_series is the newest that is usable for the | 621 | # The initial store_series is the newest that is usable for the |
186 | @@ -839,10 +865,13 @@ | |||
187 | 839 | snap = self.factory.makeSnap( | 865 | snap = self.factory.makeSnap( |
188 | 840 | registrant=self.person, owner=self.person, | 866 | registrant=self.person, owner=self.person, |
189 | 841 | distroseries=self.distroseries, store_upload=True, | 867 | distroseries=self.distroseries, store_upload=True, |
191 | 842 | store_series=self.snappyseries, store_name=u"one") | 868 | store_series=self.snappyseries, store_name=u"one", |
192 | 869 | store_channels=["edge"]) | ||
193 | 843 | view_url = canonical_url(snap, view_name="+edit") | 870 | view_url = canonical_url(snap, view_name="+edit") |
194 | 844 | browser = self.getNonRedirectingBrowser(url=view_url, user=self.person) | 871 | browser = self.getNonRedirectingBrowser(url=view_url, user=self.person) |
195 | 845 | browser.getControl("Registered store package name").value = "two" | 872 | browser.getControl("Registered store package name").value = "two" |
196 | 873 | browser.getControl("Stable").selected = True | ||
197 | 874 | self.assertTrue(browser.getControl("Edge").selected) | ||
198 | 846 | root_macaroon = Macaroon() | 875 | root_macaroon = Macaroon() |
199 | 847 | root_macaroon.add_third_party_caveat( | 876 | root_macaroon.add_third_party_caveat( |
200 | 848 | urlsplit(config.launchpad.openid_provider_root).netloc, "", | 877 | urlsplit(config.launchpad.openid_provider_root).netloc, "", |
201 | @@ -863,7 +892,8 @@ | |||
202 | 863 | HTTPError, browser.getControl("Update snap package").click) | 892 | HTTPError, browser.getControl("Update snap package").click) |
203 | 864 | login_person(self.person) | 893 | login_person(self.person) |
204 | 865 | self.assertThat(snap, MatchesStructure.byEquality( | 894 | self.assertThat(snap, MatchesStructure.byEquality( |
206 | 866 | store_name=u"two", store_secrets={"root": root_macaroon_raw})) | 895 | store_name=u"two", store_secrets={"root": root_macaroon_raw}, |
207 | 896 | store_channels=["stable", "edge"])) | ||
208 | 867 | self.assertThat(self.request, MatchesStructure.byEquality( | 897 | self.assertThat(self.request, MatchesStructure.byEquality( |
209 | 868 | url="http://sca.example/dev/api/acl/", method="POST")) | 898 | url="http://sca.example/dev/api/acl/", method="POST")) |
210 | 869 | expected_body = { | 899 | expected_body = { |
211 | 870 | 900 | ||
212 | === modified file 'lib/lp/snappy/browser/tests/test_snapbuild.py' | |||
213 | --- lib/lp/snappy/browser/tests/test_snapbuild.py 2016-07-14 15:19:03 +0000 | |||
214 | +++ lib/lp/snappy/browser/tests/test_snapbuild.py 2016-07-16 07:54:57 +0000 | |||
215 | @@ -113,6 +113,25 @@ | |||
216 | 113 | attrs={"id": "store-upload-status"}, | 113 | attrs={"id": "store-upload-status"}, |
217 | 114 | text="Store upload failed: Scan failed."))) | 114 | text="Store upload failed: Scan failed."))) |
218 | 115 | 115 | ||
219 | 116 | def test_store_upload_status_release_failed(self): | ||
220 | 117 | build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT) | ||
221 | 118 | job = getUtility(ISnapStoreUploadJobSource).create(build) | ||
222 | 119 | naked_job = removeSecurityProxy(job) | ||
223 | 120 | naked_job.job._status = JobStatus.FAILED | ||
224 | 121 | naked_job.store_url = "http://sca.example/dev/click-apps/1/rev/1/" | ||
225 | 122 | naked_job.error_message = "Failed to publish" | ||
226 | 123 | build_view = create_initialized_view(build, "+index") | ||
227 | 124 | self.assertThat(build_view(), soupmatchers.HTMLContains( | ||
228 | 125 | soupmatchers.Within( | ||
229 | 126 | soupmatchers.Tag( | ||
230 | 127 | "store upload status", "li", | ||
231 | 128 | attrs={"id": "store-upload-status"}, | ||
232 | 129 | text=( | ||
233 | 130 | "Releasing package to channels failed: " | ||
234 | 131 | "Failed to publish")), | ||
235 | 132 | soupmatchers.Tag( | ||
236 | 133 | "store link", "a", attrs={"href": job.store_url})))) | ||
237 | 134 | |||
238 | 116 | 135 | ||
239 | 117 | class TestSnapBuildOperations(BrowserTestCase): | 136 | class TestSnapBuildOperations(BrowserTestCase): |
240 | 118 | 137 | ||
241 | 119 | 138 | ||
242 | === modified file 'lib/lp/snappy/templates/snap-edit.pt' | |||
243 | --- lib/lp/snappy/templates/snap-edit.pt 2016-06-20 21:17:58 +0000 | |||
244 | +++ lib/lp/snappy/templates/snap-edit.pt 2016-07-16 07:54:57 +0000 | |||
245 | @@ -87,6 +87,10 @@ | |||
246 | 87 | <metal:block use-macro="context/@@launchpad_form/widget_row" /> | 87 | <metal:block use-macro="context/@@launchpad_form/widget_row" /> |
247 | 88 | </tal:widget> | 88 | </tal:widget> |
248 | 89 | </table> | 89 | </table> |
249 | 90 | <tal:widget define="widget nocall:view/widgets/store_channels" | ||
250 | 91 | condition="widget/context/value_type/vocabulary"> | ||
251 | 92 | <metal:block use-macro="context/@@launchpad_form/widget_row" /> | ||
252 | 93 | </tal:widget> | ||
253 | 90 | <p class="formHelp"> | 94 | <p class="formHelp"> |
254 | 91 | If you change any settings related to automatically uploading | 95 | If you change any settings related to automatically uploading |
255 | 92 | builds of this snap to the store, then the login service will | 96 | builds of this snap to the store, then the login service will |
256 | 93 | 97 | ||
257 | === modified file 'lib/lp/snappy/templates/snap-index.pt' | |||
258 | --- lib/lp/snappy/templates/snap-index.pt 2016-06-20 21:17:58 +0000 | |||
259 | +++ lib/lp/snappy/templates/snap-index.pt 2016-07-16 07:54:57 +0000 | |||
260 | @@ -104,6 +104,16 @@ | |||
261 | 104 | <a tal:replace="structure view/menu:overview/edit/fmt:icon"/> | 104 | <a tal:replace="structure view/menu:overview/edit/fmt:icon"/> |
262 | 105 | </dd> | 105 | </dd> |
263 | 106 | </dl> | 106 | </dl> |
264 | 107 | <dl id="store_channels" tal:condition="context/store_channels"> | ||
265 | 108 | <dt>Store channels:</dt> | ||
266 | 109 | <dd> | ||
267 | 110 | <span tal:content="context/store_channels"/> | ||
268 | 111 | <a tal:replace="structure view/menu:overview/edit/fmt:icon"/> | ||
269 | 112 | </dd> | ||
270 | 113 | </dl> | ||
271 | 114 | <p id="store_channels" tal:condition="not: context/store_channels"> | ||
272 | 115 | This snap package will not be released to any channels on the store. | ||
273 | 116 | </p> | ||
274 | 107 | </div> | 117 | </div> |
275 | 108 | <p id="store_upload" tal:condition="not: context/store_upload"> | 118 | <p id="store_upload" tal:condition="not: context/store_upload"> |
276 | 109 | Builds of this snap package are not automatically uploaded to the store. | 119 | Builds of this snap package are not automatically uploaded to the store. |
277 | 110 | 120 | ||
278 | === modified file 'lib/lp/snappy/templates/snap-new.pt' | |||
279 | --- lib/lp/snappy/templates/snap-new.pt 2016-07-12 13:38:58 +0000 | |||
280 | +++ lib/lp/snappy/templates/snap-new.pt 2016-07-16 07:54:57 +0000 | |||
281 | @@ -61,6 +61,10 @@ | |||
282 | 61 | <tal:widget define="widget nocall:view/widgets/store_name"> | 61 | <tal:widget define="widget nocall:view/widgets/store_name"> |
283 | 62 | <metal:block use-macro="context/@@launchpad_form/widget_row" /> | 62 | <metal:block use-macro="context/@@launchpad_form/widget_row" /> |
284 | 63 | </tal:widget> | 63 | </tal:widget> |
285 | 64 | <tal:widget define="widget nocall:view/widgets/store_channels" | ||
286 | 65 | condition="widget/context/value_type/vocabulary"> | ||
287 | 66 | <metal:block use-macro="context/@@launchpad_form/widget_row" /> | ||
288 | 67 | </tal:widget> | ||
289 | 64 | </table> | 68 | </table> |
290 | 65 | <p class="formHelp"> | 69 | <p class="formHelp"> |
291 | 66 | If you ask Launchpad to automatically upload builds of this | 70 | If you ask Launchpad to automatically upload builds of this |
292 | 67 | 71 | ||
293 | === modified file 'lib/lp/snappy/vocabularies.py' | |||
294 | --- lib/lp/snappy/vocabularies.py 2016-05-09 09:28:58 +0000 | |||
295 | +++ lib/lp/snappy/vocabularies.py 2016-07-16 07:54:57 +0000 | |||
296 | @@ -11,13 +11,18 @@ | |||
297 | 11 | ] | 11 | ] |
298 | 12 | 12 | ||
299 | 13 | from storm.locals import Desc | 13 | from storm.locals import Desc |
301 | 14 | from zope.schema.vocabulary import SimpleTerm | 14 | from zope.component import getUtility |
302 | 15 | from zope.schema.vocabulary import ( | ||
303 | 16 | SimpleTerm, | ||
304 | 17 | SimpleVocabulary, | ||
305 | 18 | ) | ||
306 | 15 | 19 | ||
307 | 16 | from lp.registry.model.distribution import Distribution | 20 | from lp.registry.model.distribution import Distribution |
308 | 17 | from lp.registry.model.distroseries import DistroSeries | 21 | from lp.registry.model.distroseries import DistroSeries |
309 | 18 | from lp.registry.model.series import ACTIVE_STATUSES | 22 | from lp.registry.model.series import ACTIVE_STATUSES |
310 | 19 | from lp.services.database.interfaces import IStore | 23 | from lp.services.database.interfaces import IStore |
311 | 20 | from lp.services.webapp.vocabulary import StormVocabularyBase | 24 | from lp.services.webapp.vocabulary import StormVocabularyBase |
312 | 25 | from lp.snappy.interfaces.snapstoreclient import ISnapStoreClient | ||
313 | 21 | from lp.snappy.model.snappyseries import ( | 26 | from lp.snappy.model.snappyseries import ( |
314 | 22 | SnappyDistroSeries, | 27 | SnappyDistroSeries, |
315 | 23 | SnappySeries, | 28 | SnappySeries, |
316 | @@ -109,3 +114,15 @@ | |||
317 | 109 | _clauses = SnappyDistroSeriesVocabulary._clauses + [ | 114 | _clauses = SnappyDistroSeriesVocabulary._clauses + [ |
318 | 110 | SnappySeries.status.is_in(ACTIVE_STATUSES), | 115 | SnappySeries.status.is_in(ACTIVE_STATUSES), |
319 | 111 | ] | 116 | ] |
320 | 117 | |||
321 | 118 | |||
322 | 119 | class SnapStoreChannelVocabulary(SimpleVocabulary): | ||
323 | 120 | """A vocabulary for searching store channels.""" | ||
324 | 121 | |||
325 | 122 | def __init__(self, context=None): | ||
326 | 123 | channels = getUtility(ISnapStoreClient).listChannels() | ||
327 | 124 | terms = [ | ||
328 | 125 | self.createTerm( | ||
329 | 126 | channel["name"], channel["name"], channel["display_name"]) | ||
330 | 127 | for channel in channels] | ||
331 | 128 | super(SnapStoreChannelVocabulary, self).__init__(terms) | ||
332 | 112 | 129 | ||
333 | === modified file 'lib/lp/snappy/vocabularies.zcml' | |||
334 | --- lib/lp/snappy/vocabularies.zcml 2016-05-05 20:02:01 +0000 | |||
335 | +++ lib/lp/snappy/vocabularies.zcml 2016-07-16 07:54:57 +0000 | |||
336 | @@ -48,4 +48,15 @@ | |||
337 | 48 | <allow interface="lp.services.webapp.vocabulary.IHugeVocabulary" /> | 48 | <allow interface="lp.services.webapp.vocabulary.IHugeVocabulary" /> |
338 | 49 | </class> | 49 | </class> |
339 | 50 | 50 | ||
340 | 51 | <securedutility | ||
341 | 52 | name="SnapStoreChannel" | ||
342 | 53 | component="lp.snappy.vocabularies.SnapStoreChannelVocabulary" | ||
343 | 54 | provides="zope.schema.interfaces.IVocabularyFactory"> | ||
344 | 55 | <allow interface="zope.schema.interfaces.IVocabularyFactory" /> | ||
345 | 56 | </securedutility> | ||
346 | 57 | |||
347 | 58 | <class class="lp.snappy.vocabularies.SnapStoreChannelVocabulary"> | ||
348 | 59 | <allow interface="zope.schema.interfaces.IVocabularyTokenized" /> | ||
349 | 60 | </class> | ||
350 | 61 | |||
351 | 51 | </configure> | 62 | </configure> |
Hi Colin,
The code looks fine, but when running './bin/test -cvvt snap' I get this message printed out while running many of the tests:
Traceback (most recent call last): thomi/launchpad /lp-branches/ snap-channels- ui/lib/ lp/snappy/ tests/test_ snap.py" , line 422, in test_getBuildSu mmariesForSnapB uildIds_ log_size_ field makeSnap( ) thomi/launchpad /lp-branches/ snap-channels- ui/lib/ lp/testing/ factory. py", line 390, in with_default_ master_ store thomi/launchpad /lp-branches/ snap-channels- ui/lib/ lp/testing/ factory. py", line 4664, in makeSnap channels= store_channels) thomi/launchpad /lp-branches/ snap-channels- ui/lib/ lp/snappy/ model/snap. py", line 531, in new thomi/launchpad /lp-branches/ snap-channels- ui/lib/ lp/snappy/ model/snap. py", line 583, in exists (owner, name) is not None thomi/launchpad /lp-branches/ snap-channels- ui/lib/ lp/snappy/ model/snap. py", line 579, in _getByName thomi/launchpad /lp-sourcedeps/ eggs/storm- 0.19.0. 99_lpwithnodate time_r408- py2.7-linux- i686.egg/ storm/store. py", line 1162, in one _connection. execute( select) thomi/launchpad /lp-sourcedeps/ eggs/storm- 0.19.0. 99_lpwithnodate time_r408- py2.7-linux- i686.egg/ storm/databases /postgres. py", line 266, in execute execute( self, statement, params, noresult) thomi/launchpad /lp-sourcedeps/ eggs/storm- 0.19.0. 99_lpwithnodate time_r408- py2.7-linux- i686.egg/ storm/database. py", line 238, in execute execute( statement, params) thomi/launchpad /lp-sourcedeps/ eggs/storm- 0.19.0. 99_lpwithnodate time_r408- py2.7-linux- i686.egg/ storm/databases /postgres. py", line 276, in raw_execute raw_execute( self, statement, params) thomi/launchpad /lp-sourcedeps/ eggs/storm- 0.19.0. 99_lpwithnodate time_r408- py2.7-linux- i686.egg/ storm/database. py", line 322, in raw_execute _check_ disconnect( raw_cursor. execute, *args) thomi/launchpad /lp-sourcedeps/ eggs/storm- 0.19.0. 99_lpwithnodate time_r408- py2.7-linux- i686.egg/ storm/database. py", line 371, in _check_disconnect thomi/launchpad /lp-branches/ snap-channels- ui/lib/ lp/testing/ pgsql.py" , line 118, in execute cursor. execute( *args, **kwargs) build_archive, Snap.auto_b...
File "/home/
snap = self.factory.
File "/home/
return func(*args, **kw)
File "/home/
store_
File "/home/
if self.exists(owner, name):
File "/home/
return self._getByName
File "/home/
Snap, Snap.owner == owner, Snap.name == name).one()
File "/home/
result = self._store.
File "/home/
return Connection.
File "/home/
raw_cursor = self.raw_
File "/home/
return Connection.
File "/home/
self.
File "/home/
return function(*args, **kwargs)
File "/home/
return self.real_
ProgrammingError: column snap.auto_build does not exist
LINE 1: SELECT Snap.auto_build, Snap.auto_
I'm not convinced this has anything to do with your branch.... any ideas? I've run the clean, build and schema targets. Not sure what else could be causing this?
I'll leave a provisional +1 here in any case.