Merge lp:~sergiusens/snapcraft/config into lp:~snappy-dev/snapcraft/core
- config
- Merge into core
Status: | Merged |
---|---|
Approved by: | Sergio Schvezov |
Approved revision: | no longer in the source branch. |
Merged at revision: | 166 |
Proposed branch: | lp:~sergiusens/snapcraft/config |
Merge into: | lp:~snappy-dev/snapcraft/core |
Prerequisite: | lp:~sergiusens/snapcraft/collisions |
Diff against target: |
456 lines (+231/-65) 9 files modified
examples/webcam-webui/config.py (+57/-0) examples/webcam-webui/setup.py (+16/-0) examples/webcam-webui/snapcraft.yaml (+11/-1) examples/webcam-webui/webcam-webui (+7/-1) integration-tests/data/assemble/binary1.after (+1/-0) schema/snapcraft.yaml (+3/-0) snapcraft/meta.py (+33/-9) snapcraft/sources.py (+3/-1) snapcraft/tests/test_meta.py (+100/-53) |
To merge this branch: | bzr merge lp:~sergiusens/snapcraft/config |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sergio Schvezov | Approve | ||
John Lenton (community) | Approve | ||
Review via email: mp+270989@code.launchpad.net |
Commit message
Support config
Description of the change
This branch ends up being more complicated than necessary because I chose the config hook to be python based and setuptools rewrites the shebang. (We probably need to do the reverse in our plugin).
- 158. By Björn Tillenius
-
Add support for setuptools based python projects. by bjornt approved by sergiusens,ted
- 159. By Sergio Schvezov
-
Filesets based filtering for stage and snap by sergiusens approved by sergiusens,mvo
- 160. By Sergio Schvezov
-
snapcraft init with templated values by sergiusens approved by mvo
- 161. By Sergio Schvezov
-
regex for binary and service names by sergiusens approved by mvo
Sergio Schvezov (sergiusens) wrote : | # |
- 162. By Sergio Schvezov
-
docs refresh by sergiusens approved by chipaca
- 163. By Sergio Schvezov
-
Collision logic updates with an introduction of _BUILTIN_OPTIONS so all parts have a certain set of options by default even if not declared. by sergiusens approved by mvo
- 164. By Daniel Holbach
-
fix crash when stage_packages is defined in the yaml for the plugin, but empty (getattr does not use [] as default return value in that case) by dholbach approved by sergiusens
- 165. By Daniel Holbach
-
Fix small typo. by dholbach approved by sergiusens
Sergio Schvezov (sergiusens) wrote : | # |
I fixed the nit
- 166. By Sergio Schvezov
-
Support config by sergiusens approved by sergiusens,chipaca
Preview Diff
1 | === added file 'examples/webcam-webui/config.py' |
2 | --- examples/webcam-webui/config.py 1970-01-01 00:00:00 +0000 |
3 | +++ examples/webcam-webui/config.py 2015-09-15 20:53:59 +0000 |
4 | @@ -0,0 +1,57 @@ |
5 | +#!/usr/bin/env python3 |
6 | + |
7 | +import os |
8 | +import os.path |
9 | +import sys |
10 | +import yaml |
11 | + |
12 | +_CONFIG = 'configuration' |
13 | + |
14 | +_DEFAULT_INTERVAL = 10 |
15 | + |
16 | + |
17 | +def main(): |
18 | + config_file = os.path.join(os.environ['SNAP_APP_DATA_PATH'], _CONFIG) |
19 | + |
20 | + config_yaml = yaml.load(sys.stdin) |
21 | + if config_yaml: |
22 | + set_config(config_file, config_yaml) |
23 | + |
24 | + yaml.dump(get_config(config_file), stream=sys.stdout, default_flow_style=False) |
25 | + |
26 | + |
27 | +def set_config(config_file, config_yaml={}): |
28 | + with open(config_file, 'w') as f: |
29 | + yaml.dump(_config(config_yaml), stream=f, default_flow_style=False) |
30 | + |
31 | + return config_yaml |
32 | + |
33 | + |
34 | +def get_config(config_file): |
35 | + try: |
36 | + with open(config_file) as f: |
37 | + return yaml.load(f) |
38 | + except FileNotFoundError: |
39 | + return _config() |
40 | + |
41 | + |
42 | +def _config(config_yaml={}): |
43 | + try: |
44 | + interval_value = config_yaml['config'][os.environ['SNAP_NAME']]['interval'] |
45 | + if not isinstance(interval_value, int): |
46 | + config_yaml['config'][os.environ['SNAP_NAME']]['interval'] = _DEFAULT_INTERVAL |
47 | + except KeyError: |
48 | + interval = { |
49 | + 'config': { |
50 | + os.environ['SNAP_NAME']: { |
51 | + 'interval': _DEFAULT_INTERVAL |
52 | + } |
53 | + } |
54 | + } |
55 | + config_yaml.update(interval) |
56 | + |
57 | + return config_yaml |
58 | + |
59 | + |
60 | +if __name__ == '__main__': |
61 | + main() |
62 | |
63 | === added file 'examples/webcam-webui/setup.py' |
64 | --- examples/webcam-webui/setup.py 1970-01-01 00:00:00 +0000 |
65 | +++ examples/webcam-webui/setup.py 2015-09-15 20:53:59 +0000 |
66 | @@ -0,0 +1,16 @@ |
67 | +#!/usr/bin/env python3 |
68 | + |
69 | +from setuptools import setup |
70 | + |
71 | +setup( |
72 | + name='config', |
73 | + description='config for webcam-webui', |
74 | + author='Sergio Schvezov <sergio.schvezov@canonical.com>', |
75 | + license='GPLv3', |
76 | + install_requires=[ |
77 | + 'pyyaml', |
78 | + ], |
79 | + scripts=[ |
80 | + 'config.py', |
81 | + ], |
82 | +) |
83 | |
84 | === modified file 'examples/webcam-webui/snapcraft.yaml' |
85 | --- examples/webcam-webui/snapcraft.yaml 2015-09-11 14:20:10 +0000 |
86 | +++ examples/webcam-webui/snapcraft.yaml 2015-09-15 20:53:59 +0000 |
87 | @@ -7,6 +7,7 @@ |
88 | services: |
89 | - name: webcam-webui |
90 | start: bin/webcam-webui |
91 | +config: python3 usr/bin/config.py |
92 | |
93 | parts: |
94 | cam: |
95 | @@ -21,11 +22,20 @@ |
96 | - usr/lib |
97 | go-server: |
98 | - bin/golang-* |
99 | + ignore: |
100 | + - -lib/x86_64-linux-gnu/libexpat* |
101 | + - -usr/lib/x86_64-linux-gnu/libexpat* |
102 | + - -usr/share/doc |
103 | + stage: |
104 | + - $ignore |
105 | snap: |
106 | + - $ignore |
107 | - $fswebcam |
108 | - $go-server |
109 | glue: |
110 | type: copy |
111 | files: |
112 | webcam-webui: bin/webcam-webui |
113 | - |
114 | + config: |
115 | + type: python3-project |
116 | + source: . |
117 | |
118 | === modified file 'examples/webcam-webui/webcam-webui' |
119 | --- examples/webcam-webui/webcam-webui 2015-09-11 14:20:10 +0000 |
120 | +++ examples/webcam-webui/webcam-webui 2015-09-15 20:53:59 +0000 |
121 | @@ -3,9 +3,15 @@ |
122 | |
123 | cd "$SNAP_APP_DATA_PATH" |
124 | |
125 | +[ -f configuration ] && interval=$(sed -n 's/.*interval: `\([0-9.]\+\).*/\1/p' configuration) |
126 | + |
127 | +[ -z "$interval" ] && interval=10 |
128 | + |
129 | +echo "Snapping every $interval seconds" |
130 | + |
131 | golang-static-http & |
132 | |
133 | while :; do |
134 | fswebcam shot.jpeg |
135 | - sleep 10 |
136 | + sleep $interval |
137 | done |
138 | |
139 | === modified file 'integration-tests/data/assemble/binary1.after' |
140 | --- integration-tests/data/assemble/binary1.after 2015-08-05 15:39:18 +0000 |
141 | +++ integration-tests/data/assemble/binary1.after 2015-09-15 20:53:59 +0000 |
142 | @@ -1,4 +1,5 @@ |
143 | #!/bin/sh |
144 | export PATH="$SNAP_APP_PATH/bin:$SNAP_APP_PATH/usr/bin:$PATH" |
145 | export LD_LIBRARY_PATH="$SNAP_APP_PATH/lib:$SNAP_APP_PATH/usr/lib:$SNAP_APP_PATH/lib/@MULTIARCH@:$SNAP_APP_PATH/usr/lib/@MULTIARCH@:$LD_LIBRARY_PATH" |
146 | + |
147 | exec "$SNAP_APP_PATH/binary1" $* |
148 | |
149 | === modified file 'schema/snapcraft.yaml' |
150 | --- schema/snapcraft.yaml 2015-09-14 19:22:59 +0000 |
151 | +++ schema/snapcraft.yaml 2015-09-15 20:53:59 +0000 |
152 | @@ -38,6 +38,9 @@ |
153 | uniqueItems: true |
154 | items: |
155 | - type: string |
156 | + config: |
157 | + type: string |
158 | + description: path to a configure hook to expose configuration for the package |
159 | services: |
160 | type: array |
161 | items: |
162 | |
163 | === modified file 'snapcraft/meta.py' |
164 | --- snapcraft/meta.py 2015-08-27 21:33:56 +0000 |
165 | +++ snapcraft/meta.py 2015-09-15 20:53:59 +0000 |
166 | @@ -57,6 +57,9 @@ |
167 | _write_package_yaml(meta_dir, config_data, arches) |
168 | _write_readme_md(meta_dir, config_data) |
169 | |
170 | + if 'config' in config_data: |
171 | + _setup_config_hook(meta_dir, config_data['config']) |
172 | + |
173 | return meta_dir |
174 | |
175 | |
176 | @@ -76,6 +79,18 @@ |
177 | f.write(readme_md) |
178 | |
179 | |
180 | +def _setup_config_hook(meta_dir, config): |
181 | + hooks_dir = os.path.join(meta_dir, 'hooks') |
182 | + |
183 | + os.makedirs(hooks_dir) |
184 | + |
185 | + execparts = shlex.split(config) |
186 | + args = execparts[1:] if len(execparts) > 1 else [] |
187 | + |
188 | + config_hook_path = os.path.join(hooks_dir, 'config') |
189 | + _write_wrap_exe(execparts[0], config_hook_path, args=args, cwd='$SNAP_APP_PATH') |
190 | + |
191 | + |
192 | def _copy_icon(meta_dir, icon_path): |
193 | new_icon_path = os.path.join(meta_dir, os.path.basename(icon_path)) |
194 | shutil.copyfile(icon_path, new_icon_path) |
195 | @@ -122,6 +137,23 @@ |
196 | return ' '.join([shlex.quote(x) for x in newparts]) |
197 | |
198 | |
199 | +def _write_wrap_exe(wrapexec, wrappath, args=[], cwd=None): |
200 | + args = ' '.join(args) + ' $*' if args else '$*' |
201 | + cwd = 'cd {}'.format(cwd) if cwd else '' |
202 | + |
203 | + snap_dir = common.get_snapdir() |
204 | + assembled_env = common.assemble_env().replace(snap_dir, '$SNAP_APP_PATH') |
205 | + script = ('#!/bin/sh\n' + |
206 | + '{}\n'.format(assembled_env) + |
207 | + '{}\n'.format(cwd) + |
208 | + 'exec "{}" {}\n'.format(wrapexec, args)) |
209 | + |
210 | + with open(wrappath, 'w+') as f: |
211 | + f.write(script) |
212 | + |
213 | + os.chmod(wrappath, 0o755) |
214 | + |
215 | + |
216 | def _wrap_exe(relexepath): |
217 | snap_dir = common.get_snapdir() |
218 | exepath = os.path.join(snap_dir, relexepath) |
219 | @@ -149,15 +181,7 @@ |
220 | else: |
221 | logger.warning('Warning: unable to find "{}" in the path'.format(relexepath)) |
222 | |
223 | - assembled_env = common.assemble_env().replace(snap_dir, '$SNAP_APP_PATH') |
224 | - script = ('#!/bin/sh\n' + |
225 | - '{}\n'.format(assembled_env) + |
226 | - 'exec "{}" $*\n'.format(wrapexec)) |
227 | - |
228 | - with open(wrappath, 'w+') as f: |
229 | - f.write(script) |
230 | - |
231 | - os.chmod(wrappath, 0o755) |
232 | + _write_wrap_exe(wrapexec, wrappath) |
233 | |
234 | return os.path.relpath(wrappath, snap_dir) |
235 | |
236 | |
237 | === modified file 'snapcraft/sources.py' |
238 | --- snapcraft/sources.py 2015-09-01 10:09:30 +0000 |
239 | +++ snapcraft/sources.py 2015-09-15 20:53:59 +0000 |
240 | @@ -177,7 +177,9 @@ |
241 | |
242 | def provision(self, dst): |
243 | path = os.path.abspath(self.source) |
244 | - if os.path.isdir(dst): |
245 | + if os.path.islink(dst): |
246 | + os.remove(dst) |
247 | + elif os.path.isdir(dst): |
248 | os.rmdir(dst) |
249 | else: |
250 | os.remove(dst) |
251 | |
252 | === modified file 'snapcraft/tests/test_meta.py' |
253 | --- snapcraft/tests/test_meta.py 2015-08-27 21:33:56 +0000 |
254 | +++ snapcraft/tests/test_meta.py 2015-09-15 20:53:59 +0000 |
255 | @@ -17,7 +17,6 @@ |
256 | import os |
257 | from unittest.mock import ( |
258 | call, |
259 | - mock_open, |
260 | patch, |
261 | ) |
262 | |
263 | @@ -45,7 +44,6 @@ |
264 | } |
265 | |
266 | def test_plain_no_binaries_or_services(self): |
267 | - |
268 | y = meta._compose_package_yaml(self.config_data, ['armhf', 'amd64']) |
269 | |
270 | expected = { |
271 | @@ -59,7 +57,6 @@ |
272 | self.assertEqual(y, expected) |
273 | |
274 | def test_plain_no_binaries_or_services_or_arches(self): |
275 | - |
276 | y = meta._compose_package_yaml(self.config_data, None) |
277 | |
278 | expected = { |
279 | @@ -186,6 +183,15 @@ |
280 | self.mock_copyfile = patcher_copyfile.start() |
281 | self.addCleanup(patcher_copyfile.stop) |
282 | |
283 | + patcher_move = patch('shutil.move') |
284 | + self.mock_move = patcher_move.start() |
285 | + self.addCleanup(patcher_move.stop) |
286 | + |
287 | + patcher_exists = patch('os.path.exists') |
288 | + self.mock_exists = patcher_exists.start() |
289 | + self.mock_exists.return_value = True |
290 | + self.addCleanup(patcher_exists.stop) |
291 | + |
292 | self.config_data = { |
293 | 'name': 'my-package', |
294 | 'version': '1.0', |
295 | @@ -193,62 +199,103 @@ |
296 | 'description': 'my description', |
297 | 'summary': 'my summary', |
298 | 'icon': 'my-icon.png', |
299 | + 'config': 'bin/config' |
300 | } |
301 | |
302 | - def test_create_meta(self): |
303 | - mock_the_open = mock_open() |
304 | - |
305 | - with patch('snapcraft.meta.open', mock_the_open, create=True): |
306 | - meta.create(self.config_data, ['amd64']) |
307 | - |
308 | - meta_dir = os.path.join(os.path.abspath(os.curdir), 'snap', 'meta') |
309 | - |
310 | - self.mock_makedirs.assert_called_once_with(meta_dir, exist_ok=True) |
311 | - |
312 | - mock_the_open.assert_has_calls([ |
313 | - call(os.path.join(meta_dir, 'package.yaml'), 'w'), |
314 | + self.meta_dir = os.path.join(os.path.abspath(os.curdir), 'snap', 'meta') |
315 | + |
316 | + self.expected_open_calls = [ |
317 | + call(os.path.join(self.meta_dir, 'package.yaml'), 'w'), |
318 | call().__enter__(), |
319 | - call().write('architectures'), |
320 | - call().write(':'), |
321 | - call().write('\n'), |
322 | - call().write('-'), |
323 | - call().write(' '), |
324 | - call().write('amd64'), |
325 | - call().write('\n'), |
326 | - call().write('icon'), |
327 | - call().write(':'), |
328 | - call().write(' '), |
329 | - call().write('meta/my-icon.png'), |
330 | - call().write('\n'), |
331 | - call().write('name'), |
332 | - call().write(':'), |
333 | - call().write(' '), |
334 | - call().write('my-package'), |
335 | - call().write('\n'), |
336 | - call().write('vendor'), |
337 | - call().write(':'), |
338 | - call().write(' '), |
339 | - call().write('Sergio'), |
340 | - call().write(' '), |
341 | - call().write('Schvezov'), |
342 | - call().write(' '), |
343 | - call().write('<sergio.schvezov@canonical.com>'), |
344 | - call().write('\n'), |
345 | - call().write('version'), |
346 | - call().write(':'), |
347 | - call().write(" '"), |
348 | - call().write('1.0'), |
349 | - call().write("'"), |
350 | - call().write('\n'), |
351 | - call().flush(), |
352 | - call().flush(), |
353 | + call().__enter__().write('architectures'), |
354 | + call().__enter__().write(':'), |
355 | + call().__enter__().write('\n'), |
356 | + call().__enter__().write('-'), |
357 | + call().__enter__().write(' '), |
358 | + call().__enter__().write('amd64'), |
359 | + call().__enter__().write('\n'), |
360 | + call().__enter__().write('icon'), |
361 | + call().__enter__().write(':'), |
362 | + call().__enter__().write(' '), |
363 | + call().__enter__().write('meta/my-icon.png'), |
364 | + call().__enter__().write('\n'), |
365 | + call().__enter__().write('name'), |
366 | + call().__enter__().write(':'), |
367 | + call().__enter__().write(' '), |
368 | + call().__enter__().write('my-package'), |
369 | + call().__enter__().write('\n'), |
370 | + call().__enter__().write('vendor'), |
371 | + call().__enter__().write(':'), |
372 | + call().__enter__().write(' '), |
373 | + call().__enter__().write('Sergio'), |
374 | + call().__enter__().write(' '), |
375 | + call().__enter__().write('Schvezov'), |
376 | + call().__enter__().write(' '), |
377 | + call().__enter__().write('<sergio.schvezov@canonical.com>'), |
378 | + call().__enter__().write('\n'), |
379 | + call().__enter__().write('version'), |
380 | + call().__enter__().write(':'), |
381 | + call().__enter__().write(" '"), |
382 | + call().__enter__().write('1.0'), |
383 | + call().__enter__().write("'"), |
384 | + call().__enter__().write('\n'), |
385 | + call().__enter__().flush(), |
386 | + call().__enter__().flush(), |
387 | call().__exit__(None, None, None), |
388 | - call(os.path.join(meta_dir, 'readme.md'), 'w'), |
389 | + call(os.path.join(self.meta_dir, 'readme.md'), 'w'), |
390 | call().__enter__(), |
391 | - call().write('my summary\nmy description\n'), |
392 | + call().__enter__().write('my summary\nmy description\n'), |
393 | call().__exit__(None, None, None), |
394 | ] |
395 | - ) |
396 | + |
397 | + self.hooks_dir = os.path.join(self.meta_dir, 'hooks') |
398 | + |
399 | + @patch('snapcraft.meta._write_wrap_exe') |
400 | + @patch('snapcraft.meta.open', create=True) |
401 | + def test_create_meta(self, mock_the_open, mock_wrap_exe): |
402 | + meta.create(self.config_data, ['amd64']) |
403 | + |
404 | + self.mock_makedirs.assert_has_calls([ |
405 | + call(self.meta_dir, exist_ok=True), |
406 | + call(self.hooks_dir), |
407 | + ]) |
408 | + |
409 | + mock_the_open.assert_has_calls(self.expected_open_calls) |
410 | + mock_wrap_exe.assert_called_once_with( |
411 | + 'bin/config', |
412 | + os.path.join(os.path.abspath(os.curdir), 'snap', 'meta', 'hooks', 'config'), |
413 | + args=[], |
414 | + cwd='$SNAP_APP_PATH', |
415 | + ) |
416 | + |
417 | + @patch('snapcraft.meta._write_wrap_exe') |
418 | + @patch('snapcraft.meta.open', create=True) |
419 | + def test_create_meta_with_vararg_config(self, mock_the_open, mock_wrap_exe): |
420 | + self.config_data['config'] = 'python3 my.py --config' |
421 | + |
422 | + meta.create(self.config_data, ['amd64']) |
423 | + |
424 | + self.mock_makedirs.assert_has_calls([ |
425 | + call(self.meta_dir, exist_ok=True), |
426 | + call(self.hooks_dir), |
427 | + ]) |
428 | + |
429 | + mock_the_open.assert_has_calls(self.expected_open_calls) |
430 | + mock_wrap_exe.assert_called_once_with( |
431 | + 'python3', |
432 | + os.path.join(os.path.abspath(os.curdir), 'snap', 'meta', 'hooks', 'config'), |
433 | + args=['my.py', '--config'], |
434 | + cwd='$SNAP_APP_PATH', |
435 | + ) |
436 | + |
437 | + @patch('snapcraft.meta.open', create=True) |
438 | + def test_create_meta_without_config(self, mock_the_open): |
439 | + del self.config_data['config'] |
440 | + |
441 | + meta.create(self.config_data, ['amd64']) |
442 | + |
443 | + self.mock_makedirs.assert_called_once_with(self.meta_dir, exist_ok=True) |
444 | + mock_the_open.assert_has_calls(self.expected_open_calls) |
445 | |
446 | |
447 | # TODO this needs more tests. |
448 | @@ -262,7 +309,7 @@ |
449 | wrapper_path = os.path.join(snapdir, relative_wrapper_path) |
450 | |
451 | expected = ('#!/bin/sh\n' |
452 | - '\n' |
453 | + '\n\n' |
454 | 'exec "$SNAP_APP_PATH/test_relexepath" $*\n') |
455 | with open(wrapper_path) as wrapper_file: |
456 | wrapper_contents = wrapper_file.read() |
hmm, my config.py is missing :/