Merge lp:~jdstrand/click-reviewers-tools/crt.cleanup into lp:click-reviewers-tools
- crt.cleanup
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Jamie Strandboge |
Proposed branch: | lp:~jdstrand/click-reviewers-tools/crt.cleanup |
Merge into: | lp:click-reviewers-tools |
Diff against target: |
9862 lines (+5148/-1356) 56 files modified
README (+7/-1) bin/click-check-bin-path (+2/-2) bin/click-check-content-hub (+2/-2) bin/click-check-desktop (+2/-2) bin/click-check-framework (+2/-2) bin/click-check-functional (+2/-2) bin/click-check-lint (+2/-2) bin/click-check-online-accounts (+2/-2) bin/click-check-push-helper (+2/-2) bin/click-check-scope (+2/-2) bin/click-check-security (+2/-2) bin/click-check-skeleton (+2/-2) bin/click-check-systemd (+2/-2) bin/click-check-url-dispatcher (+2/-2) bin/click-review (+4/-4) bin/click-run-checks (+6/-6) bin/click-show-files (+25/-18) bin/detect-package (+15/-0) bin/download-click (+12/-12) bin/repack-click (+12/-12) bin/snap-check-lint (+24/-0) bin/snap-check-skeleton (+24/-0) bin/unpack-package (+3/-3) check-names.list (+58/-3) clickreviews/common.py (+514/-0) clickreviews/cr_bin_path.py (+14/-8) clickreviews/cr_common.py (+33/-466) clickreviews/cr_content_hub.py (+8/-0) clickreviews/cr_desktop.py (+61/-0) clickreviews/cr_framework.py (+13/-7) clickreviews/cr_functional.py (+11/-0) clickreviews/cr_lint.py (+62/-99) clickreviews/cr_online_accounts.py (+20/-0) clickreviews/cr_push_helper.py (+12/-0) clickreviews/cr_scope.py (+6/-0) clickreviews/cr_security.py (+81/-12) clickreviews/cr_skeleton.py (+12/-0) clickreviews/cr_systemd.py (+21/-18) clickreviews/cr_tests.py (+27/-57) clickreviews/cr_url_dispatcher.py (+12/-0) clickreviews/modules.py (+11/-8) clickreviews/sr_common.py (+124/-0) clickreviews/sr_lint.py (+1150/-0) clickreviews/sr_skeleton.py (+63/-0) clickreviews/sr_tests.py (+241/-0) clickreviews/tests/test_bbb_example_sr_skeleton.py (+75/-0) clickreviews/tests/test_cr_bin_path.py (+4/-1) clickreviews/tests/test_cr_desktop.py (+0/-14) clickreviews/tests/test_cr_framework.py (+4/-1) clickreviews/tests/test_cr_lint.py (+74/-383) clickreviews/tests/test_cr_security.py (+74/-187) clickreviews/tests/test_cr_systemd.py (+3/-0) clickreviews/tests/test_modules.py (+7/-7) clickreviews/tests/test_sr_lint.py (+2189/-0) clickreviews/tests/utils.py (+4/-2) setup.py (+2/-1) |
To merge this branch: | bzr merge lp:~jdstrand/click-reviewers-tools/crt.cleanup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Holbach (community) | Approve | ||
Review via email: mp+284684@code.launchpad.net |
Commit message
Description of the change
Start cleanup which includes:
- factor common code into common.py, which cr_common.py and sr_common.py can both use
- add sr_test.py and sr_common.py
- add skeleton scripts and tests
- add simple sr_lint.py
- add detect_package()
This is first pass. The idea is, break out the snap v2 (16.04) tests into their own sr_*.py checks and tests. This will allow us to have clean code for snap v2. Next step is to adjust cr_common.py to use common.py, then to add all the snap.yaml tests (and removing the old squashfs snap tests that use package.yaml).
This could be merged as is, but is not complete (the snap.yaml checks aren't implemented and the squashfs package.yaml tests are still there). I am requesting an MP so people can see the direction I am going and to comment if I should change direction, etc.
- 578. By Jamie Strandboge
-
* bin/click-
show-files:
- exit early if snap v2 (only need to show the snap.yaml)
* clickreviews/common. py, clickreviews/ cr_common. py, clickreviews/ sr_common. py:
- move various functions out of *r_common.py into common.py
- add is_click, is_snap1 and is_snap2 variables
* clickreviews/cr_bin_ path.py, clickreviews/ cr_framework. py,
clickreviews/cr_systemd. py: only run with v1 snaps
* clickreviews/cr_content_ hub.py, clickreviews/ cr_desktop. py,
clickreviews/cr_functional. py, clickreviews/ cr_lint. py,
clickreviews/cr_online_ accounts. py, clickreviews/ cr_push_ helper. py,
clickreviews/cr_scope. py, clickreviews/ cr_security. py,
clickreviews/cr_url_ dispatcher. py: only run if click or snap v1
* clickreviews/cr_tests. py:
- remove snap.yaml support (moved to sr_tests.py in previous commits)
- updates for cr_common.py -> common.py refactoring
* clickreviews/sr_lint. py, clickreviews/ sr_skeleton. py:
- fix up imports for refactoring
- only run if snap v2
* clickreviews/tests/* :
- remove 16.04 snap tests from cr_*
- use self.set_test_pkgfmt( ) everywhere now that we are checking click, snap
v1 and snap v2 in the tests
Jamie Strandboge (jdstrand) wrote : | # |
Jamie Strandboge (jdstrand) wrote : | # |
r579 does more refactoring and adds a bunch of snap.yaml tests (but not done yet).
- 579. By Jamie Strandboge
-
clickreviews/
common. py:
- rename _check_path_exists( ) as _check_ package_ exists( )
- move _check_innerpath_ executable( ) here
- _verify_pkgversion( ) should isinstance() clickreviews/
cr_lint. py, clickreviews/ cr_tests. py: adjust for common.py changes clickreviews/
sr_common. py: add additional optional fields based on meta.md clickreviews/
sr_lint. py, clickreviews/ sr_tests. py: implement a bunch of tests - 580. By Jamie Strandboge
-
add check_description for snap.yaml
- 581. By Jamie Strandboge
-
clickreviews/
sr_lint. py:
- add tests for summary, license-agreement and license-version
- check_description should error if empty - 582. By Jamie Strandboge
-
add some apps tests to sr_lint.py
- 583. By Jamie Strandboge
-
clickreviews/
sr_lint. py:
- make too description and summary 'too short' checks info
- implement app 'command' tests - 584. By Jamie Strandboge
-
pep8 for last commit
- 585. By Jamie Strandboge
-
implement check_apps_daemon()
- 586. By Jamie Strandboge
-
add tests for stop and poststop
- 587. By Jamie Strandboge
-
implement restart-condition tests
- 588. By Jamie Strandboge
-
add bus-name checks
- 589. By Jamie Strandboge
-
add ports checks
- 590. By Jamie Strandboge
-
add daemon options tests
- 591. By Jamie Strandboge
-
add stop-timeout tests
- 592. By Jamie Strandboge
-
add socket tests
- 593. By Jamie Strandboge
-
move TODO items to the end
- 594. By Jamie Strandboge
-
clickreviews/
tests/test_ sr_lint. py: remove unneeded set name - 595. By Jamie Strandboge
-
add toplevel uses tests
- 596. By Jamie Strandboge
-
add type checking for skills
- 597. By Jamie Strandboge
-
update comment from last commit
Jamie Strandboge (jdstrand) wrote : | # |
Almost all of snap.yaml is being verified now, with the exception of 'uses' within an app. Once that is done, all that remains is implementing the TODO checks at the end of sr_lint.py and implementing sr_security.py. All of these are a matter of porting from cr_lint.py and cr_security.py to sr_*.
I think it would be worthwhile for people to look at what is implemented and give feedback.
Daniel Holbach (dholbach) wrote : | # |
./clickreviews/
=======
ERROR: test_check_
Test check_snappy_
-------
Traceback (most recent call last):
File "/home/
c.check_
File "/home/
if is_squashfs(
NameError: name 'is_squashfs' is not defined
Jamie Strandboge (jdstrand) wrote : | # |
@Daniel, do you have a merge conflict or something? The testsuite is passing here:
$ make check
./run-tests
...
test_check_
Test check_snappy_
test_check_
Test check_snappy_
test_check_
Test check_snappy_
...
Looking at cr_lint.py, is_squashfs is no longer used in check_snappy_
Daniel Holbach (dholbach) wrote : | # |
No conflicts. Have you pushed all changes?
=======
ERROR: test_check_
Test check_snappy_
-------
Traceback (most recent call last):
File "/home/
c.check_
File "/home/
if is_squashfs(
NameError: name 'is_squashfs' is not defined
=======
ERROR: test_check_
Test check_snappy_
-------
Traceback (most recent call last):
File "/home/
c.check_
File "/home/
if is_squashfs(
NameError: name 'is_squashfs' is not defined
=======
ERROR: test_check_
Test check_snappy_
-------
Traceback (most recent call last):
File "/home/
c.check_
File "/home/
if is_squashfs(
NameError: name 'is_squashfs' is not defined
=======
ERROR: test_check_
Test check_snappy_
-------
Traceback (most recent call last):
File "/home/
with patch("
File "/usr/lib/
original, local = self.get_original()
File "/usr/lib/
"%s does not have the attribute %r" % (target, name)
AttributeError: <module 'clickreviews.
-------
Ran 817 tests in 7.082s
FAILED (errors=4)
added:
bin/detect-
bin/snap-
bin/snap-
clickreviews/
clickreviews/
clickreviews/
cl...
Jamie Strandboge (jdstrand) wrote : | # |
You seem to be missing clickreviews/
Jamie Strandboge (jdstrand) wrote : | # |
Oh no, you have that. Not sure what is going on here. I'll investigate.
Jamie Strandboge (jdstrand) wrote : | # |
I just did:
$ bzr branch lp:~jdstrand/click-reviewers-tools/crt.cleanup click-reviewers
$ cd click-reviewers
$ ./run-tests
...
Ran 816 tests in 6.037s
OK
- 598. By Jamie Strandboge
-
merge from trunk even though this code was refactored. This will help bzr to
better understand what is happening with this branch[ James Tait ]
cr_lint.py: Don't check for the presence of readme.md if the package is a
squashfs filesystem. Snappy 2.0 uses squashfs as its file format, and
doesn't require readme.md.
Daniel Holbach (dholbach) wrote : | # |
Looks good AFAICS. :-)
- 599. By Jamie Strandboge
-
use 'invalid ...' consistently instead of 'not valid'
add 'uses' tests for 'apps'
Jamie Strandboge (jdstrand) wrote : | # |
I was asked to break this up, so I am going to have just the refactor (plus a couple teeny things), then branches that add the sr_ tests.
Jamie Strandboge (jdstrand) wrote : | # |
FYI, other branches are:
1. refactor: https:/
2. initial snap v2 https:/
3. https:/
Unmerged revisions
- 601. By Jamie Strandboge
-
merge from refactor branch
- 600. By Jamie Strandboge
-
merge from refactor branch
- 599. By Jamie Strandboge
-
use 'invalid ...' consistently instead of 'not valid'
add 'uses' tests for 'apps' - 598. By Jamie Strandboge
-
merge from trunk even though this code was refactored. This will help bzr to
better understand what is happening with this branch[ James Tait ]
cr_lint.py: Don't check for the presence of readme.md if the package is a
squashfs filesystem. Snappy 2.0 uses squashfs as its file format, and
doesn't require readme.md. - 597. By Jamie Strandboge
-
update comment from last commit
- 596. By Jamie Strandboge
-
add type checking for skills
- 595. By Jamie Strandboge
-
add toplevel uses tests
- 594. By Jamie Strandboge
-
clickreviews/
tests/test_ sr_lint. py: remove unneeded set name - 593. By Jamie Strandboge
-
move TODO items to the end
- 592. By Jamie Strandboge
-
add socket tests
Preview Diff
1 | === modified file 'README' |
2 | --- README 2015-06-10 21:56:24 +0000 |
3 | +++ README 2016-02-09 21:28:10 +0000 |
4 | @@ -1,4 +1,4 @@ |
5 | -Runnable tests: |
6 | +Runnable click and snap v1 tests: |
7 | - bin/click-check-bin-path: snappy bin-path tests |
8 | - bin/click-check-content-hub: content-hub hook tests |
9 | - bin/click-check-desktop: desktop hook tests |
10 | @@ -13,6 +13,10 @@ |
11 | - bin/click-check-url-dispatcher: url-dispatcher hook tests |
12 | - bin/click-run-checks: all tests |
13 | |
14 | +Runnable snap v2 tests: |
15 | +- bin/snap-check-lint: lint tests |
16 | +- bin/snap-run-checks: all tests |
17 | + |
18 | This gives an alternate view on bin/click-run-checks: |
19 | - bin/click-review |
20 | |
21 | @@ -43,6 +47,8 @@ |
22 | * add tests to cr_<something>.py. If you name the tests 'check_<sometest>' |
23 | ClickReview.do_checks() will enumerate and run them automatically |
24 | |
25 | +(substitute 'sr_' for 'cr_' for snap v2 checks) |
26 | + |
27 | To run tests, just execute: |
28 | $ ./run-tests # all tests |
29 | $ ./run-tests test_cr_security.py # only security tests |
30 | |
31 | === modified file 'bin/click-check-bin-path' |
32 | --- bin/click-check-bin-path 2015-03-18 19:57:47 +0000 |
33 | +++ bin/click-check-bin-path 2016-02-09 21:28:10 +0000 |
34 | @@ -17,8 +17,8 @@ |
35 | |
36 | from __future__ import print_function |
37 | |
38 | -import clickreviews.cr_common as cr_common |
39 | +import clickreviews.common as common |
40 | import clickreviews.cr_bin_path as cr_bin_path |
41 | |
42 | if __name__ == "__main__": |
43 | - cr_common.run_click_check(cr_bin_path.ClickReviewBinPath) |
44 | + common.run_check(cr_bin_path.ClickReviewBinPath) |
45 | |
46 | === modified file 'bin/click-check-content-hub' |
47 | --- bin/click-check-content-hub 2015-03-18 19:57:47 +0000 |
48 | +++ bin/click-check-content-hub 2016-02-09 21:28:10 +0000 |
49 | @@ -17,8 +17,8 @@ |
50 | |
51 | from __future__ import print_function |
52 | |
53 | -import clickreviews.cr_common as cr_common |
54 | +import clickreviews.common as common |
55 | import clickreviews.cr_content_hub as cr_content_hub |
56 | |
57 | if __name__ == "__main__": |
58 | - cr_common.run_click_check(cr_content_hub.ClickReviewContentHub) |
59 | + common.run_check(cr_content_hub.ClickReviewContentHub) |
60 | |
61 | === modified file 'bin/click-check-desktop' |
62 | --- bin/click-check-desktop 2015-03-18 19:57:47 +0000 |
63 | +++ bin/click-check-desktop 2016-02-09 21:28:10 +0000 |
64 | @@ -17,8 +17,8 @@ |
65 | |
66 | from __future__ import print_function |
67 | |
68 | -from clickreviews import cr_common |
69 | +from clickreviews import common |
70 | from clickreviews import cr_desktop |
71 | |
72 | if __name__ == "__main__": |
73 | - cr_common.run_click_check(cr_desktop.ClickReviewDesktop) |
74 | + common.run_check(cr_desktop.ClickReviewDesktop) |
75 | |
76 | === modified file 'bin/click-check-framework' |
77 | --- bin/click-check-framework 2015-03-18 19:57:47 +0000 |
78 | +++ bin/click-check-framework 2016-02-09 21:28:10 +0000 |
79 | @@ -17,8 +17,8 @@ |
80 | |
81 | from __future__ import print_function |
82 | |
83 | -import clickreviews.cr_common as cr_common |
84 | +import clickreviews.common as common |
85 | import clickreviews.cr_framework as cr_framework |
86 | |
87 | if __name__ == "__main__": |
88 | - cr_common.run_click_check(cr_framework.ClickReviewFramework) |
89 | + common.run_check(cr_framework.ClickReviewFramework) |
90 | |
91 | === modified file 'bin/click-check-functional' |
92 | --- bin/click-check-functional 2015-03-18 19:57:47 +0000 |
93 | +++ bin/click-check-functional 2016-02-09 21:28:10 +0000 |
94 | @@ -17,8 +17,8 @@ |
95 | |
96 | from __future__ import print_function |
97 | |
98 | -import clickreviews.cr_common as cr_common |
99 | +import clickreviews.common as common |
100 | import clickreviews.cr_functional as cr_functional |
101 | |
102 | if __name__ == "__main__": |
103 | - cr_common.run_click_check(cr_functional.ClickReviewFunctional) |
104 | + common.run_check(cr_functional.ClickReviewFunctional) |
105 | |
106 | === modified file 'bin/click-check-lint' |
107 | --- bin/click-check-lint 2015-03-18 19:57:47 +0000 |
108 | +++ bin/click-check-lint 2016-02-09 21:28:10 +0000 |
109 | @@ -17,8 +17,8 @@ |
110 | |
111 | from __future__ import print_function |
112 | |
113 | -import clickreviews.cr_common as cr_common |
114 | +import clickreviews.common as common |
115 | import clickreviews.cr_lint as cr_lint |
116 | |
117 | if __name__ == "__main__": |
118 | - cr_common.run_click_check(cr_lint.ClickReviewLint) |
119 | + common.run_check(cr_lint.ClickReviewLint) |
120 | |
121 | === modified file 'bin/click-check-online-accounts' |
122 | --- bin/click-check-online-accounts 2015-03-18 19:57:47 +0000 |
123 | +++ bin/click-check-online-accounts 2016-02-09 21:28:10 +0000 |
124 | @@ -17,8 +17,8 @@ |
125 | |
126 | from __future__ import print_function |
127 | |
128 | -import clickreviews.cr_common as cr_common |
129 | +import clickreviews.common as common |
130 | import clickreviews.cr_online_accounts as cr_online_accounts |
131 | |
132 | if __name__ == "__main__": |
133 | - cr_common.run_click_check(cr_online_accounts.ClickReviewAccounts) |
134 | + common.run_check(cr_online_accounts.ClickReviewAccounts) |
135 | |
136 | === modified file 'bin/click-check-push-helper' |
137 | --- bin/click-check-push-helper 2015-03-18 19:57:47 +0000 |
138 | +++ bin/click-check-push-helper 2016-02-09 21:28:10 +0000 |
139 | @@ -17,8 +17,8 @@ |
140 | |
141 | from __future__ import print_function |
142 | |
143 | -import clickreviews.cr_common as cr_common |
144 | +import clickreviews.common as common |
145 | import clickreviews.cr_push_helper as cr_push_helper |
146 | |
147 | if __name__ == "__main__": |
148 | - cr_common.run_click_check(cr_push_helper.ClickReviewPushHelper) |
149 | + common.run_check(cr_push_helper.ClickReviewPushHelper) |
150 | |
151 | === modified file 'bin/click-check-scope' |
152 | --- bin/click-check-scope 2015-03-18 19:57:47 +0000 |
153 | +++ bin/click-check-scope 2016-02-09 21:28:10 +0000 |
154 | @@ -17,8 +17,8 @@ |
155 | |
156 | from __future__ import print_function |
157 | |
158 | -import clickreviews.cr_common as cr_common |
159 | +import clickreviews.common as common |
160 | import clickreviews.cr_scope as cr_scope |
161 | |
162 | if __name__ == "__main__": |
163 | - cr_common.run_click_check(cr_scope.ClickReviewScope) |
164 | + common.run_check(cr_scope.ClickReviewScope) |
165 | |
166 | === modified file 'bin/click-check-security' |
167 | --- bin/click-check-security 2015-03-18 19:57:47 +0000 |
168 | +++ bin/click-check-security 2016-02-09 21:28:10 +0000 |
169 | @@ -17,8 +17,8 @@ |
170 | |
171 | from __future__ import print_function |
172 | |
173 | -import clickreviews.cr_common as cr_common |
174 | +import clickreviews.common as common |
175 | import clickreviews.cr_security as cr_security |
176 | |
177 | if __name__ == "__main__": |
178 | - cr_common.run_click_check(cr_security.ClickReviewSecurity) |
179 | + common.run_check(cr_security.ClickReviewSecurity) |
180 | |
181 | === modified file 'bin/click-check-skeleton' |
182 | --- bin/click-check-skeleton 2015-03-18 19:57:47 +0000 |
183 | +++ bin/click-check-skeleton 2016-02-09 21:28:10 +0000 |
184 | @@ -17,8 +17,8 @@ |
185 | |
186 | from __future__ import print_function |
187 | |
188 | -import clickreviews.cr_common as cr_common |
189 | +import clickreviews.common as common |
190 | import clickreviews.cr_skeleton as cr_skeleton |
191 | |
192 | if __name__ == "__main__": |
193 | - cr_common.run_click_check(cr_skeleton.ClickReviewSkeleton) |
194 | + common.run_check(cr_skeleton.ClickReviewSkeleton) |
195 | |
196 | === modified file 'bin/click-check-systemd' |
197 | --- bin/click-check-systemd 2015-03-18 20:01:56 +0000 |
198 | +++ bin/click-check-systemd 2016-02-09 21:28:10 +0000 |
199 | @@ -17,8 +17,8 @@ |
200 | |
201 | from __future__ import print_function |
202 | |
203 | -import clickreviews.cr_common as cr_common |
204 | +import clickreviews.common as common |
205 | import clickreviews.cr_systemd as cr_systemd |
206 | |
207 | if __name__ == "__main__": |
208 | - cr_common.run_click_check(cr_systemd.ClickReviewSystemd) |
209 | + common.run_check(cr_systemd.ClickReviewSystemd) |
210 | |
211 | === modified file 'bin/click-check-url-dispatcher' |
212 | --- bin/click-check-url-dispatcher 2015-03-18 19:57:47 +0000 |
213 | +++ bin/click-check-url-dispatcher 2016-02-09 21:28:10 +0000 |
214 | @@ -17,8 +17,8 @@ |
215 | |
216 | from __future__ import print_function |
217 | |
218 | -import clickreviews.cr_common as cr_common |
219 | +import clickreviews.common as common |
220 | import clickreviews.cr_url_dispatcher as cr_url_dispatcher |
221 | |
222 | if __name__ == "__main__": |
223 | - cr_common.run_click_check(cr_url_dispatcher.ClickReviewUrlDispatcher) |
224 | + common.run_check(cr_url_dispatcher.ClickReviewUrlDispatcher) |
225 | |
226 | === modified file 'bin/click-review' |
227 | --- bin/click-review 2014-11-22 04:33:00 +0000 |
228 | +++ bin/click-review 2016-02-09 21:28:10 +0000 |
229 | @@ -33,7 +33,7 @@ |
230 | def __init__(self, args): |
231 | self.args = args |
232 | self.click_fn = self.args.filename |
233 | - self.cr_modules = modules.get_modules() |
234 | + self.modules = modules.get_modules() |
235 | |
236 | def _sumarise_results(self): |
237 | for module in self.results: |
238 | @@ -96,12 +96,12 @@ |
239 | |
240 | def run_all_checks(self): |
241 | if self.args.sdk: |
242 | - for module in self.cr_modules: |
243 | + for module in self.modules: |
244 | section = self._run_module_checks(module) |
245 | if section: |
246 | self._report_module(section) |
247 | else: |
248 | - for module in self.cr_modules: |
249 | + for module in self.modules: |
250 | self._run_module_checks(module) |
251 | self._complete_report() |
252 | |
253 | @@ -125,7 +125,7 @@ |
254 | sys.exit(1) |
255 | |
256 | results = Results(args) |
257 | - if not results.cr_modules: |
258 | + if not results.modules: |
259 | print("No 'clickreviews' modules found.") |
260 | sys.exit(1) |
261 | |
262 | |
263 | === modified file 'bin/click-run-checks' |
264 | --- bin/click-run-checks 2015-03-18 20:49:03 +0000 |
265 | +++ bin/click-run-checks 2016-02-09 21:28:10 +0000 |
266 | @@ -11,10 +11,10 @@ |
267 | fi |
268 | |
269 | # prefer local script for testing, otherwise use system location |
270 | -CLICK_CHECKS_BIN_PATH="$(dirname $(readlink -f "$0"))" |
271 | +CHECKS_BIN_PATH="$(dirname $(readlink -f "$0"))" |
272 | |
273 | prefer_local() { |
274 | - path="${CLICK_CHECKS_BIN_PATH}/$1" |
275 | + path="${CHECKS_BIN_PATH}/$1" |
276 | if [ -x $path ]; then |
277 | "$path" "$2" || set_rc "$?" |
278 | return |
279 | @@ -37,10 +37,10 @@ |
280 | |
281 | prefer_local click-show-files "$c" |
282 | |
283 | -CLICK_CHECKS=$(ls ${CLICK_CHECKS_BIN_PATH}/click-check-* | grep -v click-check-skeleton | grep -v click-check-lint) |
284 | -CLICK_CHECKS="click-check-lint ${CLICK_CHECKS}" |
285 | -for check_path in ${CLICK_CHECKS}; do |
286 | - check=$(basename ${check_path} ${CLICK_CHECKS_BIN_PATH}) |
287 | +CHECKS=$(ls ${CHECKS_BIN_PATH}/click-check-* ${CHECKS_BIN_PATH}/snap-check-* | egrep -v '(click-check-skeleton|click-check-lint|snap-check-skeleton)') |
288 | +CHECKS="click-check-lint snap-check-lint ${CHECKS}" |
289 | +for check_path in ${CHECKS}; do |
290 | + check=$(basename ${check_path} ${CHECKS_BIN_PATH}) |
291 | echo "" |
292 | echo "=" $check "=" |
293 | prefer_local $check "$c" |
294 | |
295 | === modified file 'bin/click-show-files' |
296 | --- bin/click-show-files 2015-04-01 23:31:56 +0000 |
297 | +++ bin/click-show-files 2016-02-09 21:28:10 +0000 |
298 | @@ -19,7 +19,7 @@ |
299 | import os |
300 | import sys |
301 | |
302 | -from clickreviews import cr_common |
303 | +from clickreviews import common |
304 | from clickreviews import cr_desktop |
305 | from clickreviews import cr_lint |
306 | from clickreviews import cr_security |
307 | @@ -36,12 +36,22 @@ |
308 | |
309 | if __name__ == "__main__": |
310 | if len(sys.argv) < 2: |
311 | - cr_common.error("Must give path to click package") |
312 | + common.error("Must give path to package") |
313 | |
314 | review = cr_lint.ClickReviewLint(sys.argv[1]) |
315 | |
316 | + fn = os.path.join(review.unpack_dir, "meta", "snap.yaml") |
317 | + if os.path.exists(fn): # just show snap.yaml for snap v2+ snaps |
318 | + print("= %s =" % os.path.basename(fn)) |
319 | + fh = common.open_file_read(fn) |
320 | + for line in fh.readlines(): |
321 | + print(line, end="") |
322 | + fh.close() |
323 | + print("") |
324 | + sys.exit(0) |
325 | + |
326 | for i in sorted(review.control_files): |
327 | - fh = cr_common.open_file_read(review.control_files[i]) |
328 | + fh = common.open_file_read(review.control_files[i]) |
329 | print("= %s =" % os.path.basename(i)) |
330 | for line in fh.readlines(): |
331 | print(line, end="") |
332 | @@ -51,7 +61,7 @@ |
333 | fn = os.path.join(review.unpack_dir, "meta", "package.yaml") |
334 | if os.path.exists(fn): |
335 | print("= %s =" % os.path.basename(fn)) |
336 | - fh = cr_common.open_file_read(fn) |
337 | + fh = common.open_file_read(fn) |
338 | for line in fh.readlines(): |
339 | print(line, end="") |
340 | fh.close() |
341 | @@ -62,7 +72,7 @@ |
342 | review_content_hub = cr_content_hub.ClickReviewContentHub(sys.argv[1]) |
343 | for app in sorted(review_content_hub.content_hub_files): |
344 | f = review_content_hub.content_hub_files[app] |
345 | - fh = cr_common.open_file_read(os.path.join( |
346 | + fh = common.open_file_read(os.path.join( |
347 | review_content_hub.unpack_dir, f)) |
348 | print("== content_hub: %s ==" % os.path.basename(f)) |
349 | for line in fh.readlines(): |
350 | @@ -73,8 +83,7 @@ |
351 | review_desktop = cr_desktop.ClickReviewDesktop(sys.argv[1]) |
352 | for app in sorted(review_desktop.desktop_files): |
353 | f = review_desktop.desktop_files[app] |
354 | - fh = cr_common.open_file_read(os.path.join(review_desktop.unpack_dir, |
355 | - f)) |
356 | + fh = common.open_file_read(os.path.join(review_desktop.unpack_dir, f)) |
357 | print("== desktop: %s ==" % os.path.basename(f)) |
358 | for line in fh.readlines(): |
359 | print(line, end="") |
360 | @@ -87,7 +96,7 @@ |
361 | if account_type not in review_accounts.accounts_files[app]: |
362 | continue |
363 | f = review_accounts.accounts_files[app][account_type] |
364 | - fh = cr_common.open_file_read(os.path.join( |
365 | + fh = common.open_file_read(os.path.join( |
366 | review_accounts.unpack_dir, f)) |
367 | print("== online %s: %s ==" % (account_type, os.path.basename(f))) |
368 | for line in fh.readlines(): |
369 | @@ -98,7 +107,7 @@ |
370 | review_push_helper = cr_push_helper.ClickReviewPushHelper(sys.argv[1]) |
371 | for app in sorted(review_push_helper.push_helper_files): |
372 | f = review_push_helper.push_helper_files[app] |
373 | - fh = cr_common.open_file_read(os.path.join( |
374 | + fh = common.open_file_read(os.path.join( |
375 | review_push_helper.unpack_dir, f)) |
376 | print("== push_helper: %s ==" % os.path.basename(f)) |
377 | for line in fh.readlines(): |
378 | @@ -109,7 +118,7 @@ |
379 | review_scope = cr_scope.ClickReviewScope(sys.argv[1]) |
380 | for app in sorted(review_scope.scopes): |
381 | f = review_scope.scopes[app]["ini_file"] |
382 | - fh = cr_common.open_file_read(os.path.join(review_scope.unpack_dir, f)) |
383 | + fh = common.open_file_read(os.path.join(review_scope.unpack_dir, f)) |
384 | print("== scope .INI: %s ==" % os.path.basename(f)) |
385 | for line in fh.readlines(): |
386 | print(line, end="") |
387 | @@ -120,7 +129,7 @@ |
388 | for app in sorted(review_framework.frameworks_file): |
389 | f = os.path.join(review_framework.unpack_dir, |
390 | review_framework.frameworks_file[app]) |
391 | - fh = cr_common.open_file_read(os.path.join(review_framework.unpack_dir, f)) |
392 | + fh = common.open_file_read(os.path.join(review_framework.unpack_dir, f)) |
393 | print("== click .framework: %s ==" % os.path.basename(f)) |
394 | for line in fh.readlines(): |
395 | print(line, end="") |
396 | @@ -135,8 +144,7 @@ |
397 | |
398 | review_apparmor = cr_security.ClickReviewSecurity(sys.argv[1]) |
399 | for f in sorted(review_apparmor.security_manifests): |
400 | - fh = cr_common.open_file_read(os.path.join(review_apparmor.unpack_dir, |
401 | - f)) |
402 | + fh = common.open_file_read(os.path.join(review_apparmor.unpack_dir, f)) |
403 | print("== security: %s ==" % os.path.basename(f)) |
404 | for line in fh.readlines(): |
405 | print(line, end="") |
406 | @@ -146,8 +154,7 @@ |
407 | review_systemd = cr_systemd.ClickReviewSystemd(sys.argv[1]) |
408 | for app in sorted(review_systemd.systemd_files): |
409 | f = review_systemd.systemd_files[app] |
410 | - fh = cr_common.open_file_read(os.path.join(review_systemd.unpack_dir, |
411 | - f)) |
412 | + fh = common.open_file_read(os.path.join(review_systemd.unpack_dir, f)) |
413 | print("== systemd: %s ==" % os.path.basename(f)) |
414 | for line in fh.readlines(): |
415 | print(line, end="") |
416 | @@ -157,8 +164,8 @@ |
417 | review_url_dispatcher = cr_url_dispatcher.ClickReviewUrlDispatcher(sys.argv[1]) |
418 | for app in sorted(review_url_dispatcher.url_dispatcher_files): |
419 | f = review_url_dispatcher.url_dispatcher_files[app] |
420 | - fh = cr_common.open_file_read(os.path.join(review_url_dispatcher.unpack_dir, |
421 | - f)) |
422 | + fh = common.open_file_read(os.path.join(review_url_dispatcher.unpack_dir, |
423 | + f)) |
424 | print("== url_dispatcher: %s ==" % os.path.basename(f)) |
425 | for line in fh.readlines(): |
426 | print(line, end="") |
427 | @@ -166,4 +173,4 @@ |
428 | print("") |
429 | |
430 | # Cleanup our unpack directory |
431 | - cr_common.cleanup_unpack() |
432 | + common.cleanup_unpack() |
433 | |
434 | === added file 'bin/detect-package' |
435 | --- bin/detect-package 1970-01-01 00:00:00 +0000 |
436 | +++ bin/detect-package 2016-02-09 21:28:10 +0000 |
437 | @@ -0,0 +1,15 @@ |
438 | +#!/usr/bin/python3 |
439 | + |
440 | +import os |
441 | +import sys |
442 | + |
443 | +from clickreviews import common |
444 | + |
445 | +if __name__ == '__main__': |
446 | + if len(sys.argv) != 2: |
447 | + common.error("%s <pkg>" % os.path.basename(sys.argv[0])) |
448 | + |
449 | + pkg = sys.argv[1] |
450 | + |
451 | + (t, v) = common.detect_package(pkg) |
452 | + print ("%s\t%d" % (t, v)) |
453 | |
454 | === modified file 'bin/download-click' |
455 | --- bin/download-click 2013-10-02 14:02:34 +0000 |
456 | +++ bin/download-click 2016-02-09 21:28:10 +0000 |
457 | @@ -23,7 +23,7 @@ |
458 | import sys |
459 | import time |
460 | |
461 | -from clickreviews import cr_common |
462 | +from clickreviews import common |
463 | |
464 | top_url = "https://search.apps.ubuntu.com" |
465 | |
466 | @@ -40,12 +40,12 @@ |
467 | def download(entry, download_dir=None): |
468 | '''Download a click app''' |
469 | if 'resource_url' not in entry: |
470 | - cr_common.error("Could not find 'resource_url' in:\n%s" % entry) |
471 | + common.error("Could not find 'resource_url' in:\n%s" % entry) |
472 | resource_url = top_url + entry['resource_url'] |
473 | |
474 | res = get_store_json(resource_url) |
475 | if 'download_url' not in res: |
476 | - cr_common.error("Could not find 'download_url' in:\n%s" % res) |
477 | + common.error("Could not find 'download_url' in:\n%s" % res) |
478 | download_url = res['download_url'] |
479 | |
480 | if download_dir is None: |
481 | @@ -55,12 +55,12 @@ |
482 | |
483 | fn = os.path.join(download_dir, download_url.split('/')[-1]) |
484 | if os.path.exists(fn): |
485 | - cr_common.warn("'%s' already exists, skipping" % os.path.basename(fn)) |
486 | + common.warn("'%s' already exists, skipping" % os.path.basename(fn)) |
487 | return True |
488 | - cr_common.msg("Downloading %s" % os.path.basename(fn)) |
489 | + common.msg("Downloading %s" % os.path.basename(fn)) |
490 | # FIXME: on 2013-10-15 this will break |
491 | url = download_url + "?noauth=1" |
492 | - cr_common.msg("-- Downloading %s" % url) |
493 | + common.msg("-- Downloading %s" % url) |
494 | |
495 | # attempt to deal with intermittent failures |
496 | count = 0 |
497 | @@ -72,14 +72,14 @@ |
498 | (tmp, headers) = urllib.request.urlretrieve(url) |
499 | shutil.move(tmp, fn) |
500 | result = True |
501 | - cr_common.msg("-- Success!") |
502 | + common.msg("-- Success!") |
503 | except urllib.error.HTTPError as error: |
504 | err_str = "-- urlretrieve() failed: %d: %s" % (error.code, |
505 | error.reason) |
506 | count += 1 |
507 | time.sleep(5) |
508 | if not result: |
509 | - cr_common.warn("%s (tried %d times)" % (err_str, max_tries)) |
510 | + common.warn("%s (tried %d times)" % (err_str, max_tries)) |
511 | return result |
512 | |
513 | if __name__ == "__main__": |
514 | @@ -96,12 +96,12 @@ |
515 | (opt, args) = parser.parse_args() |
516 | |
517 | if not opt.all and len(args) < 1: |
518 | - cr_common.error("%s --all|<pkgname>" % os.path.basename(sys.argv[0])) |
519 | + common.error("%s --all|<pkgname>" % os.path.basename(sys.argv[0])) |
520 | |
521 | url = top_url + "/api/v1/search?q=" |
522 | items = get_store_json(url) |
523 | if not isinstance(items, list): |
524 | - cr_common.error("Didn't get valid result from: %s" % url) |
525 | + common.error("Didn't get valid result from: %s" % url) |
526 | |
527 | errors = False |
528 | if opt.all: |
529 | @@ -117,10 +117,10 @@ |
530 | break |
531 | |
532 | if not entry: |
533 | - cr_common.warn("Could not find '%s', skipping" % pkgname) |
534 | + common.warn("Could not find '%s', skipping" % pkgname) |
535 | continue |
536 | |
537 | if not download(entry, opt.download_dir): |
538 | errors = True |
539 | if errors: |
540 | - cr_common.error("problem downloading") |
541 | + common.error("problem downloading") |
542 | |
543 | === modified file 'bin/repack-click' |
544 | --- bin/repack-click 2014-06-27 18:58:30 +0000 |
545 | +++ bin/repack-click 2016-02-09 21:28:10 +0000 |
546 | @@ -6,7 +6,7 @@ |
547 | import sys |
548 | import os |
549 | |
550 | -from clickreviews import cr_common |
551 | +from clickreviews import common |
552 | from debian.deb822 import Deb822 |
553 | import glob |
554 | import shutil |
555 | @@ -16,45 +16,45 @@ |
556 | def repack_click(unpack_dir, click_package): |
557 | '''Repack the click package''' |
558 | if not os.path.isdir(unpack_dir): |
559 | - cr_common.error("'%s' does not exist" % unpack_dir) |
560 | + common.error("'%s' does not exist" % unpack_dir) |
561 | if os.path.exists(click_package): |
562 | - cr_common.error("'%s' exists" % click_package) |
563 | + common.error("'%s' exists" % click_package) |
564 | |
565 | control_fn = os.path.join(unpack_dir, "DEBIAN/control") |
566 | if not os.path.exists(control_fn): |
567 | - cr_common.error("Could not find '%s'" % control_fn) |
568 | - fh = cr_common.open_file_read(control_fn) |
569 | + common.error("Could not find '%s'" % control_fn) |
570 | + fh = common.open_file_read(control_fn) |
571 | tmp = list(Deb822.iter_paragraphs(fh.readlines())) |
572 | fh.close() |
573 | if len(tmp) != 1: |
574 | - cr_common.error("malformed control file: too many paragraphs") |
575 | + common.error("malformed control file: too many paragraphs") |
576 | control = tmp[0] |
577 | |
578 | click_fn = "%s_%s_%s.click" % (control['Package'], |
579 | control['Version'], |
580 | control['Architecture']) |
581 | if os.path.basename(click_package) != click_fn: |
582 | - cr_common.warn("'%s' should be '%s'" % (click_package, click_fn)) |
583 | + common.warn("'%s' should be '%s'" % (click_package, click_fn)) |
584 | |
585 | tmpdir = tempfile.mkdtemp(prefix='clickreview-') |
586 | curdir = os.getcwd() |
587 | os.chdir(tmpdir) |
588 | - (rc, out) = cr_common.cmd(['dpkg-deb', '-b', '--nocheck', '-Zgzip', |
589 | + (rc, out) = common.cmd(['dpkg-deb', '-b', '--nocheck', '-Zgzip', |
590 | os.path.abspath(unpack_dir), |
591 | os.path.join(tmpdir, click_fn)]) |
592 | os.chdir(curdir) |
593 | if rc != 0: |
594 | - cr_common.recursive_rm(tmpdir) |
595 | - cr_common.error("dpkg-deb -b failed with '%d':\n%s" % (rc, out)) |
596 | + common.recursive_rm(tmpdir) |
597 | + common.error("dpkg-deb -b failed with '%d':\n%s" % (rc, out)) |
598 | |
599 | debfile = glob.glob("%s/*.click" % tmpdir)[0] |
600 | shutil.move(debfile, os.path.abspath(click_package)) |
601 | - cr_common.recursive_rm(tmpdir) |
602 | + common.recursive_rm(tmpdir) |
603 | |
604 | |
605 | if __name__ == '__main__': |
606 | if len(sys.argv) != 3: |
607 | - cr_common.error("%s <unpacked dir> <clickpkg>" % |
608 | + common.error("%s <unpacked dir> <clickpkg>" % |
609 | os.path.basename(sys.argv[0])) |
610 | |
611 | dir = sys.argv[1] |
612 | |
613 | === added file 'bin/snap-check-lint' |
614 | --- bin/snap-check-lint 1970-01-01 00:00:00 +0000 |
615 | +++ bin/snap-check-lint 2016-02-09 21:28:10 +0000 |
616 | @@ -0,0 +1,24 @@ |
617 | +#!/usr/bin/python3 |
618 | +'''snap-check-lint: perform snap lint checks''' |
619 | +# |
620 | +# Copyright (C) 2013-2016 Canonical Ltd. |
621 | +# |
622 | +# This program is free software: you can redistribute it and/or modify |
623 | +# it under the terms of the GNU General Public License as published by |
624 | +# the Free Software Foundation; version 3 of the License. |
625 | +# |
626 | +# This program is distributed in the hope that it will be useful, |
627 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
628 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
629 | +# GNU General Public License for more details. |
630 | +# |
631 | +# You should have received a copy of the GNU General Public License |
632 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
633 | + |
634 | +from __future__ import print_function |
635 | + |
636 | +import clickreviews.common as common |
637 | +import clickreviews.sr_lint as sr_lint |
638 | + |
639 | +if __name__ == "__main__": |
640 | + common.run_check(sr_lint.SnapReviewLint) |
641 | |
642 | === added file 'bin/snap-check-skeleton' |
643 | --- bin/snap-check-skeleton 1970-01-01 00:00:00 +0000 |
644 | +++ bin/snap-check-skeleton 2016-02-09 21:28:10 +0000 |
645 | @@ -0,0 +1,24 @@ |
646 | +#!/usr/bin/python3 |
647 | +'''snap-check-skeleton: perform snap skeleton checks''' |
648 | +# |
649 | +# Copyright (C) 2013-2015 Canonical Ltd. |
650 | +# |
651 | +# This program is free software: you can redistribute it and/or modify |
652 | +# it under the terms of the GNU General Public License as published by |
653 | +# the Free Software Foundation; version 3 of the License. |
654 | +# |
655 | +# This program is distributed in the hope that it will be useful, |
656 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
657 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
658 | +# GNU General Public License for more details. |
659 | +# |
660 | +# You should have received a copy of the GNU General Public License |
661 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
662 | + |
663 | +from __future__ import print_function |
664 | + |
665 | +import clickreviews.common as common |
666 | +import clickreviews.sr_skeleton as sr_skeleton |
667 | + |
668 | +if __name__ == "__main__": |
669 | + common.run_check(sr_skeleton.SnapReviewSkeleton) |
670 | |
671 | === renamed file 'bin/unpack-click' => 'bin/unpack-package' |
672 | --- bin/unpack-click 2013-09-26 11:47:37 +0000 |
673 | +++ bin/unpack-package 2016-02-09 21:28:10 +0000 |
674 | @@ -3,14 +3,14 @@ |
675 | import os |
676 | import sys |
677 | |
678 | -from clickreviews import cr_common |
679 | +from clickreviews import common |
680 | |
681 | if __name__ == '__main__': |
682 | if len(sys.argv) != 3: |
683 | - cr_common.error("%s <clickpkg> <dir>" % os.path.basename(sys.argv[0])) |
684 | + common.error("%s <clickpkg> <dir>" % os.path.basename(sys.argv[0])) |
685 | |
686 | pkg = sys.argv[1] |
687 | dir = sys.argv[2] |
688 | |
689 | - cr_common.unpack_click(pkg, dir) |
690 | + common.unpack_pkg(pkg, dir) |
691 | print("Successfully unpacked to '%s'" % dir) |
692 | |
693 | === modified file 'check-names.list' |
694 | --- check-names.list 2016-02-01 19:12:30 +0000 |
695 | +++ check-names.list 2016-02-09 21:28:10 +0000 |
696 | @@ -45,7 +45,6 @@ |
697 | framework:policy_unknown| |
698 | framework:policy_valid_name| |
699 | lint:architecture_specified_needed| |
700 | -lint:check_squashfs_uses_snap_yaml| |
701 | lint:click_local_extensions| |
702 | lint:control_architecture_match| |
703 | lint:control_architecture_valid| |
704 | @@ -71,7 +70,6 @@ |
705 | lint:hooks_multiple_apps| |
706 | lint:hooks_redflag| |
707 | lint:hooks_valid| |
708 | -lint:is_squashfs| |
709 | lint:maintainer_format|http://askubuntu.com/questions/417351/what-does-lint-maintainer-format-mean/417352 |
710 | lint:maintainer_present| |
711 | lint:manifest_architecture_valid| |
712 | @@ -96,6 +94,64 @@ |
713 | lint:snappy_type_valid| |
714 | lint:snappy_unknown| |
715 | lint:snappy_version_valid| |
716 | +lint-snap-v2:apps| |
717 | +lint-snap-v2:apps_entry| |
718 | +lint-snap-v2:apps_present| |
719 | +lint-snap-v2:apps_required| |
720 | +lint-snap-v2:apps_unknown| |
721 | +lint-snap-v2:architecture_valid| |
722 | +lint-snap-v2:attributes| |
723 | +lint-snap-v2:bus-name| |
724 | +lint-snap-v2:bus-name_framework| |
725 | +lint-snap-v2:bus-name|http://dbus.freedesktop.org/doc/dbus-specification.html |
726 | +lint-snap-v2:bus-name_matches_name| |
727 | +lint-snap-v2:command| |
728 | +lint-snap-v2:config_hook_executable| |
729 | +lint-snap-v2:daemon| |
730 | +lint-snap-v2:daemon_required| |
731 | +lint-snap-v2:description| |
732 | +lint-snap-v2:description_present| |
733 | +lint-snap-v2:frameworks|http://askubuntu.com/questions/460512/what-framework-should-i-use-in-my-manifest-file |
734 | +lint-snap-v2:icon_absolute_path| |
735 | +lint-snap-v2:icon_empty| |
736 | +lint-snap-v2:icon_exists| |
737 | +lint-snap-v2:icon_present| |
738 | +lint-snap-v2:license-agreement| |
739 | +lint-snap-v2:license-agreement_present| |
740 | +lint-snap-v2:license-version| |
741 | +lint-snap-v2:license-version_present| |
742 | +lint-snap-v2:listen-stream| |
743 | +lint-snap-v2:listen-stream_matches_name| |
744 | +lint-snap-v2:migration-skill| |
745 | +lint-snap-v2:name_valid| |
746 | +lint-snap-v2:nonexistent| |
747 | +lint-snap-v2:ports| |
748 | +lint-snap-v2:ports_ext1_format| |
749 | +lint-snap-v2:ports_ext2_format| |
750 | +lint-snap-v2:ports_int1_format| |
751 | +lint-snap-v2:ports_unknown_key| |
752 | +lint-snap-v2:ports_unknown_subkey| |
753 | +lint-snap-v2:poststop| |
754 | +lint-snap-v2:restart-condition| |
755 | +lint-snap-v2:snap_type_redflag|https://developer.ubuntu.com/en/snappy/guides/frameworks/ |
756 | +lint-snap-v2:snap_type_valid| |
757 | +lint-snap-v2:socket| |
758 | +lint-snap-v2:socket-group| |
759 | +lint-snap-v2:socket-group_matches_name| |
760 | +lint-snap-v2:socket-group_reserved| |
761 | +lint-snap-v2:socket-user| |
762 | +lint-snap-v2:socket-user_matches_name| |
763 | +lint-snap-v2:socket-user_reserved| |
764 | +lint-snap-v2:stop| |
765 | +lint-snap-v2:stop-timeout| |
766 | +lint-snap-v2:stop-timeout_range| |
767 | +lint-snap-v2:summary| |
768 | +lint-snap-v2:summary_present| |
769 | +lint-snap-v2:type| |
770 | +lint-snap-v2:unknown_field| |
771 | +lint-snap-v2:uses| |
772 | +lint-snap-v2:uses_slot_reference| |
773 | +lint-snap-v2:version_valid| |
774 | online_accounts:account-application_hook| |
775 | online_accounts:account-application_id| |
776 | online_accounts:account-application_root| |
777 | @@ -159,7 +215,6 @@ |
778 | security:yaml_combinations| |
779 | security:yaml_override_click| |
780 | security:yaml_override_format| |
781 | -security:yaml_override_present|https://developer.ubuntu.com/en/snappy/guides/security-policy/ |
782 | security:yaml_policy_format| |
783 | security:yaml_policy_present|https://developer.ubuntu.com/en/snappy/guides/security-policy/ |
784 | security:yaml_security-template| |
785 | |
786 | === added file 'clickreviews/common.py' |
787 | --- clickreviews/common.py 1970-01-01 00:00:00 +0000 |
788 | +++ clickreviews/common.py 2016-02-09 21:28:10 +0000 |
789 | @@ -0,0 +1,514 @@ |
790 | +'''common.py: common classes and functions''' |
791 | +# |
792 | +# Copyright (C) 2013-2016 Canonical Ltd. |
793 | +# |
794 | +# This program is free software: you can redistribute it and/or modify |
795 | +# it under the terms of the GNU General Public License as published by |
796 | +# the Free Software Foundation; version 3 of the License. |
797 | +# |
798 | +# This program is distributed in the hope that it will be useful, |
799 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
800 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
801 | +# GNU General Public License for more details. |
802 | +# |
803 | +# You should have received a copy of the GNU General Public License |
804 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
805 | + |
806 | +from __future__ import print_function |
807 | +import atexit |
808 | +import codecs |
809 | +import inspect |
810 | +import json |
811 | +import logging |
812 | +import magic |
813 | +import os |
814 | +import re |
815 | +import shutil |
816 | +import subprocess |
817 | +import sys |
818 | +import tempfile |
819 | +import types |
820 | + |
821 | + |
822 | +DEBUGGING = False |
823 | +UNPACK_DIR = None |
824 | +RAW_UNPACK_DIR = None |
825 | + |
826 | + |
827 | +def cleanup_unpack(): |
828 | + global UNPACK_DIR |
829 | + if UNPACK_DIR is not None and os.path.isdir(UNPACK_DIR): |
830 | + recursive_rm(UNPACK_DIR) |
831 | + UNPACK_DIR = None |
832 | + global RAW_UNPACK_DIR |
833 | + if RAW_UNPACK_DIR is not None and os.path.isdir(RAW_UNPACK_DIR): |
834 | + recursive_rm(RAW_UNPACK_DIR) |
835 | + RAW_UNPACK_DIR = None |
836 | +atexit.register(cleanup_unpack) |
837 | + |
838 | + |
839 | +# |
840 | +# Utility classes |
841 | +# |
842 | +class ReviewException(Exception): |
843 | + '''This class represents Review exceptions''' |
844 | + def __init__(self, value): |
845 | + self.value = value |
846 | + |
847 | + def __str__(self): |
848 | + return repr(self.value) |
849 | + |
850 | + |
851 | +class Review(object): |
852 | + '''Common review class''' |
853 | + magic_binary_file_descriptions = [ |
854 | + 'application/x-executable; charset=binary', |
855 | + 'application/x-sharedlib; charset=binary', |
856 | + 'application/x-object; charset=binary', |
857 | + 'application/octet-stream; charset=binary' |
858 | + ] |
859 | + |
860 | + def __init__(self, fn, review_type, overrides=None): |
861 | + self.pkg_filename = fn |
862 | + self._check_package_exists() |
863 | + |
864 | + self.review_type = review_type |
865 | + # TODO: rename as pkg_report |
866 | + self.click_report = dict() |
867 | + |
868 | + self.result_types = ['info', 'warn', 'error'] |
869 | + for r in self.result_types: |
870 | + self.click_report[r] = dict() |
871 | + |
872 | + self.click_report_output = "json" |
873 | + |
874 | + global UNPACK_DIR |
875 | + if UNPACK_DIR is None: |
876 | + UNPACK_DIR = unpack_pkg(fn) |
877 | + self.unpack_dir = UNPACK_DIR |
878 | + |
879 | + global RAW_UNPACK_DIR |
880 | + if RAW_UNPACK_DIR is None: |
881 | + RAW_UNPACK_DIR = raw_unpack_pkg(fn) |
882 | + self.raw_unpack_dir = RAW_UNPACK_DIR |
883 | + |
884 | + self.is_click = False |
885 | + self.is_snap1 = False |
886 | + self.is_snap2 = False |
887 | + self.pkgfmt = {"type": "", "version": ""} |
888 | + |
889 | + (self.pkgfmt["type"], pkgver) = detect_package(fn, self.unpack_dir) |
890 | + |
891 | + if self._pkgfmt_type() == "snap": |
892 | + if pkgver < 2: |
893 | + self.is_snap1 = True |
894 | + self.pkgfmt["version"] = "15.04" |
895 | + else: |
896 | + self.is_snap2 = True |
897 | + self.pkgfmt["version"] = "16.04" |
898 | + elif self._pkgfmt_type() == "click": |
899 | + self.pkgfmt["version"] = "0.4" |
900 | + self.is_click = True |
901 | + else: |
902 | + error("Unknown package type: '%s'" % self._pkgfmt_type()) |
903 | + |
904 | + # Get a list of all unpacked files |
905 | + self.pkg_files = [] |
906 | + self._list_all_files() |
907 | + |
908 | + # Setup what is needed to get a list of all unpacked compiled binaries |
909 | + self.mime = magic.open(magic.MAGIC_MIME) |
910 | + self.mime.load() |
911 | + self.pkg_bin_files = [] |
912 | + # Don't run this here since only cr_lint.py and cr_functional.py need |
913 | + # it now |
914 | + # self._list_all_compiled_binaries() |
915 | + |
916 | + self.overrides = overrides if overrides is not None else {} |
917 | + |
918 | + def _check_innerpath_executable(self, fn): |
919 | + '''Check that the provided path exists and is executable''' |
920 | + return os.access(fn, os.X_OK) |
921 | + |
922 | + def _extract_statinfo(self, fn): |
923 | + '''Extract statinfo from file''' |
924 | + try: |
925 | + st = os.stat(fn) |
926 | + except Exception: |
927 | + return None |
928 | + return st |
929 | + |
930 | + def _path_join(self, dirname, rest): |
931 | + return os.path.join(dirname, rest) |
932 | + |
933 | + def _get_sha512sum(self, fn): |
934 | + '''Get sha512sum of file''' |
935 | + (rc, out) = cmd(['sha512sum', fn]) |
936 | + if rc != 0: |
937 | + return None |
938 | + return out.split()[0] |
939 | + |
940 | + def _pkgfmt_type(self): |
941 | + '''Return the package format type''' |
942 | + if "type" not in self.pkgfmt: |
943 | + return "" |
944 | + return self.pkgfmt["type"] |
945 | + |
946 | + def _pkgfmt_version(self): |
947 | + '''Return the package format version''' |
948 | + if "version" not in self.pkgfmt: |
949 | + return "" |
950 | + return self.pkgfmt["version"] |
951 | + |
952 | + def _check_package_exists(self): |
953 | + '''Check that the provided package exists''' |
954 | + if not os.path.exists(self.pkg_filename): |
955 | + error("Could not find '%s'" % self.pkg_filename) |
956 | + |
957 | + def _list_all_files(self): |
958 | + '''List all files included in this click package.''' |
959 | + for root, dirnames, filenames in os.walk(self.unpack_dir): |
960 | + for f in filenames: |
961 | + self.pkg_files.append(os.path.join(root, f)) |
962 | + |
963 | + def _check_if_message_catalog(self, fn): |
964 | + '''Check if file is a message catalog (.mo file).''' |
965 | + if fn.endswith('.mo'): |
966 | + return True |
967 | + return False |
968 | + |
969 | + def _list_all_compiled_binaries(self): |
970 | + '''List all compiled binaries in this click package.''' |
971 | + for i in self.pkg_files: |
972 | + res = self.mime.file(i) |
973 | + if res in self.magic_binary_file_descriptions and \ |
974 | + not self._check_if_message_catalog(i): |
975 | + self.pkg_bin_files.append(i) |
976 | + |
977 | + def _get_check_name(self, name, app='', extra=''): |
978 | + name = ':'.join([self.review_type, name]) |
979 | + if app: |
980 | + name += ':' + app |
981 | + if extra: |
982 | + name += ':' + extra |
983 | + return name |
984 | + |
985 | + def _verify_pkgversion(self, v): |
986 | + '''Verify package name''' |
987 | + if not isinstance(v, (str, int, float)): |
988 | + return False |
989 | + re_valid_version = re.compile(r'^((\d+):)?' # epoch |
990 | + '([A-Za-z0-9.+:~-]+?)' # upstream |
991 | + '(-([A-Za-z0-9+.~]+))?$') # debian |
992 | + if re_valid_version.match(str(v)): |
993 | + return True |
994 | + return False |
995 | + |
996 | + # click_report[<result_type>][<review_name>] = <result> |
997 | + # result_type: info, warn, error |
998 | + # review_name: name of the check (prefixed with self.review_type) |
999 | + # result: contents of the review |
1000 | + def _add_result(self, result_type, review_name, result, link=None, |
1001 | + manual_review=False): |
1002 | + '''Add result to report''' |
1003 | + if result_type not in self.result_types: |
1004 | + error("Invalid result type '%s'" % result_type) |
1005 | + |
1006 | + if review_name not in self.click_report[result_type]: |
1007 | + # log info about check so it can be collected into the |
1008 | + # check-names.list file |
1009 | + # format should be |
1010 | + # CHECK|<review_type:check_name>|<link> |
1011 | + msg = 'CHECK|{}|{}' |
1012 | + name = ':'.join(review_name.split(':')[:2]) |
1013 | + link_text = link if link is not None else "" |
1014 | + logging.debug(msg.format(name, link_text)) |
1015 | + self.click_report[result_type][review_name] = dict() |
1016 | + |
1017 | + self.click_report[result_type][review_name].update({ |
1018 | + 'text': result, |
1019 | + 'manual_review': manual_review, |
1020 | + }) |
1021 | + if link is not None: |
1022 | + self.click_report[result_type][review_name]["link"] = link |
1023 | + |
1024 | + def do_report(self): |
1025 | + '''Print report''' |
1026 | + if self.click_report_output == "console": |
1027 | + # TODO: format better |
1028 | + import pprint |
1029 | + pprint.pprint(self.click_report) |
1030 | + elif self.click_report_output == "json": |
1031 | + import json |
1032 | + msg(json.dumps(self.click_report, |
1033 | + sort_keys=True, |
1034 | + indent=2, |
1035 | + separators=(',', ': '))) |
1036 | + |
1037 | + rc = 0 |
1038 | + if len(self.click_report['error']): |
1039 | + rc = 2 |
1040 | + elif len(self.click_report['warn']): |
1041 | + rc = 1 |
1042 | + return rc |
1043 | + |
1044 | + def do_checks(self): |
1045 | + '''Run all methods that start with check_''' |
1046 | + methodList = [name for name, member in |
1047 | + inspect.getmembers(self, inspect.ismethod) |
1048 | + if isinstance(member, types.MethodType)] |
1049 | + for methodname in methodList: |
1050 | + if not methodname.startswith("check_"): |
1051 | + continue |
1052 | + func = getattr(self, methodname) |
1053 | + func() |
1054 | + |
1055 | + def set_review_type(self, name): |
1056 | + '''Set review name''' |
1057 | + self.review_type = name |
1058 | + |
1059 | + |
1060 | +# |
1061 | +# Utility functions |
1062 | +# |
1063 | + |
1064 | +def error(out, exit_code=1, do_exit=True): |
1065 | + '''Print error message and exit''' |
1066 | + try: |
1067 | + print("ERROR: %s" % (out), file=sys.stderr) |
1068 | + except IOError: |
1069 | + pass |
1070 | + |
1071 | + if do_exit: |
1072 | + sys.exit(exit_code) |
1073 | + |
1074 | + |
1075 | +def warn(out): |
1076 | + '''Print warning message''' |
1077 | + try: |
1078 | + print("WARN: %s" % (out), file=sys.stderr) |
1079 | + except IOError: |
1080 | + pass |
1081 | + |
1082 | + |
1083 | +def msg(out, output=sys.stdout): |
1084 | + '''Print message''' |
1085 | + try: |
1086 | + print("%s" % (out), file=output) |
1087 | + except IOError: |
1088 | + pass |
1089 | + |
1090 | + |
1091 | +def debug(out): |
1092 | + '''Print debug message''' |
1093 | + global DEBUGGING |
1094 | + if DEBUGGING: |
1095 | + try: |
1096 | + print("DEBUG: %s" % (out), file=sys.stderr) |
1097 | + except IOError: |
1098 | + pass |
1099 | + |
1100 | + |
1101 | +def cmd(command): |
1102 | + '''Try to execute the given command.''' |
1103 | + debug(command) |
1104 | + try: |
1105 | + sp = subprocess.Popen(command, stdout=subprocess.PIPE, |
1106 | + stderr=subprocess.STDOUT) |
1107 | + except OSError as ex: |
1108 | + return [127, str(ex)] |
1109 | + |
1110 | + if sys.version_info[0] >= 3: |
1111 | + out = sp.communicate()[0].decode('ascii', 'ignore') |
1112 | + else: |
1113 | + out = sp.communicate()[0] |
1114 | + |
1115 | + return [sp.returncode, out] |
1116 | + |
1117 | + |
1118 | +def cmd_pipe(command1, command2): |
1119 | + '''Try to pipe command1 into command2.''' |
1120 | + try: |
1121 | + sp1 = subprocess.Popen(command1, stdout=subprocess.PIPE) |
1122 | + sp2 = subprocess.Popen(command2, stdin=sp1.stdout) |
1123 | + except OSError as ex: |
1124 | + return [127, str(ex)] |
1125 | + |
1126 | + if sys.version_info[0] >= 3: |
1127 | + out = sp2.communicate()[0].decode('ascii', 'ignore') |
1128 | + else: |
1129 | + out = sp2.communicate()[0] |
1130 | + |
1131 | + return [sp2.returncode, out] |
1132 | + |
1133 | + |
1134 | +def _unpack_cmd(cmd_args, d, dest): |
1135 | + '''Low level unpack helper''' |
1136 | + curdir = os.getcwd() |
1137 | + os.chdir(d) |
1138 | + |
1139 | + (rc, out) = cmd(cmd_args) |
1140 | + os.chdir(curdir) |
1141 | + |
1142 | + if rc != 0: |
1143 | + if os.path.isdir(d): |
1144 | + recursive_rm(d) |
1145 | + error("unpacking failed with '%d':\n%s" % (rc, out)) |
1146 | + |
1147 | + if dest is None: |
1148 | + dest = d |
1149 | + else: |
1150 | + shutil.move(d, dest) |
1151 | + |
1152 | + return dest |
1153 | + |
1154 | + |
1155 | +def _unpack_snap_squashfs(snap_pkg, dest): |
1156 | + '''Unpack a squashfs based snap package to dest''' |
1157 | + d = tempfile.mkdtemp(prefix='clickreview-') |
1158 | + return _unpack_cmd(['unsquashfs', '-f', '-d', d, |
1159 | + os.path.abspath(snap_pkg)], d, dest) |
1160 | + |
1161 | + |
1162 | +def _unpack_click_deb(pkg, dest): |
1163 | + d = tempfile.mkdtemp(prefix='clickreview-') |
1164 | + return _unpack_cmd(['dpkg-deb', '-R', |
1165 | + os.path.abspath(pkg), d], d, dest) |
1166 | + |
1167 | + |
1168 | +def unpack_pkg(fn, dest=None): |
1169 | + '''Unpack package''' |
1170 | + if not os.path.isfile(fn): |
1171 | + error("Could not find '%s'" % fn) |
1172 | + pkg = fn |
1173 | + if not pkg.startswith('/'): |
1174 | + pkg = os.path.abspath(pkg) |
1175 | + |
1176 | + if dest is not None and os.path.exists(dest): |
1177 | + error("'%s' exists. Aborting." % dest) |
1178 | + |
1179 | + # check if its a squashfs based snap |
1180 | + if is_squashfs(pkg): |
1181 | + return _unpack_snap_squashfs(fn, dest) |
1182 | + |
1183 | + return _unpack_click_deb(fn, dest) |
1184 | + |
1185 | + |
1186 | +def is_squashfs(filename): |
1187 | + '''Return true if the given filename as a squashfs header''' |
1188 | + with open(filename, 'rb') as f: |
1189 | + header = f.read(10) |
1190 | + return header.startswith(b"hsqs") |
1191 | + |
1192 | + |
1193 | +def raw_unpack_pkg(fn, dest=None): |
1194 | + '''Unpack raw package''' |
1195 | + if not os.path.isfile(fn): |
1196 | + error("Could not find '%s'" % fn) |
1197 | + pkg = fn |
1198 | + if not pkg.startswith('/'): |
1199 | + pkg = os.path.abspath(pkg) |
1200 | + # nothing to do for squashfs images |
1201 | + if is_squashfs(pkg): |
1202 | + return "" |
1203 | + |
1204 | + if dest is not None and os.path.exists(dest): |
1205 | + error("'%s' exists. Aborting." % dest) |
1206 | + |
1207 | + d = tempfile.mkdtemp(prefix='review-') |
1208 | + |
1209 | + curdir = os.getcwd() |
1210 | + os.chdir(d) |
1211 | + (rc, out) = cmd(['ar', 'x', pkg]) |
1212 | + os.chdir(curdir) |
1213 | + |
1214 | + if rc != 0: |
1215 | + if os.path.isdir(d): |
1216 | + recursive_rm(d) |
1217 | + error("'ar x' failed with '%d':\n%s" % (rc, out)) |
1218 | + |
1219 | + if dest is None: |
1220 | + dest = d |
1221 | + else: |
1222 | + shutil.move(d, dest) |
1223 | + |
1224 | + return dest |
1225 | + |
1226 | + |
1227 | +def open_file_read(path): |
1228 | + '''Open specified file read-only''' |
1229 | + try: |
1230 | + orig = codecs.open(path, 'r', "UTF-8") |
1231 | + except Exception: |
1232 | + raise |
1233 | + |
1234 | + return orig |
1235 | + |
1236 | + |
1237 | +def recursive_rm(dirPath, contents_only=False): |
1238 | + '''recursively remove directory''' |
1239 | + names = os.listdir(dirPath) |
1240 | + for name in names: |
1241 | + path = os.path.join(dirPath, name) |
1242 | + if os.path.islink(path) or not os.path.isdir(path): |
1243 | + os.unlink(path) |
1244 | + else: |
1245 | + recursive_rm(path) |
1246 | + if contents_only is False: |
1247 | + os.rmdir(dirPath) |
1248 | + |
1249 | + |
1250 | +def run_check(cls): |
1251 | + if len(sys.argv) < 2: |
1252 | + error("Must give path to package") |
1253 | + |
1254 | + # extract args |
1255 | + fn = sys.argv[1] |
1256 | + if len(sys.argv) > 2: |
1257 | + overrides = json.loads(sys.argv[2]) |
1258 | + else: |
1259 | + overrides = None |
1260 | + |
1261 | + review = cls(fn, overrides=overrides) |
1262 | + review.do_checks() |
1263 | + rc = review.do_report() |
1264 | + sys.exit(rc) |
1265 | + |
1266 | + |
1267 | +def detect_package(fn, dir=None): |
1268 | + '''Detect what type of package this is''' |
1269 | + pkgtype = None |
1270 | + pkgver = None |
1271 | + |
1272 | + if not os.path.isfile(fn): |
1273 | + error("Could not find '%s'" % fn) |
1274 | + |
1275 | + if dir is None: |
1276 | + unpack_dir = unpack_pkg(fn) |
1277 | + else: |
1278 | + unpack_dir = dir |
1279 | + |
1280 | + if not os.path.isdir(unpack_dir): |
1281 | + error("Could not find '%s'" % unpack_dir) |
1282 | + |
1283 | + pkg = fn |
1284 | + if not pkg.startswith('/'): |
1285 | + pkg = os.path.abspath(pkg) |
1286 | + |
1287 | + # check if its a squashfs based snap |
1288 | + if is_squashfs(pkg): |
1289 | + # 16.04+ squashfs snaps |
1290 | + pkgtype = "snap" |
1291 | + pkgver = 2 |
1292 | + elif os.path.exists(os.path.join(unpack_dir, "meta/package.yaml")): |
1293 | + # 15.04 ar-based snaps |
1294 | + pkgtype = "snap" |
1295 | + pkgver = 1 |
1296 | + else: |
1297 | + pkgtype = "click" |
1298 | + pkgver = 1 |
1299 | + |
1300 | + if dir is None and os.path.isdir(unpack_dir): |
1301 | + recursive_rm(unpack_dir) |
1302 | + |
1303 | + return (pkgtype, pkgver) |
1304 | |
1305 | === modified file 'clickreviews/cr_bin_path.py' |
1306 | --- clickreviews/cr_bin_path.py 2015-08-18 16:04:05 +0000 |
1307 | +++ clickreviews/cr_bin_path.py 2016-02-09 21:28:10 +0000 |
1308 | @@ -26,16 +26,19 @@ |
1309 | # bin-path is ignored by snappy install so don't bother with peerhooks |
1310 | ClickReview.__init__(self, fn, "bin-path", overrides=overrides) |
1311 | |
1312 | + self.bin_paths_files = dict() |
1313 | + self.bin_paths = dict() |
1314 | + |
1315 | + if not self.is_snap1: |
1316 | + return |
1317 | + |
1318 | # snappy yaml currently only allows specifying: |
1319 | # - exec (optional) |
1320 | # - description (optional) |
1321 | self.required_keys = [] |
1322 | self.optional_keys = ['description', 'exec'] + self.snappy_exe_security |
1323 | |
1324 | - self.bin_paths_files = dict() |
1325 | - self.bin_paths = dict() |
1326 | - |
1327 | - if self.is_snap and 'binaries' in self.pkg_yaml: |
1328 | + if self.is_snap1 and 'binaries' in self.pkg_yaml: |
1329 | if len(self.pkg_yaml['binaries']) == 0: |
1330 | error("package.yaml malformed: 'binaries' is empty") |
1331 | for binary in self.pkg_yaml['binaries']: |
1332 | @@ -91,7 +94,7 @@ |
1333 | |
1334 | def check_snappy_required(self): |
1335 | '''Check for package.yaml required fields''' |
1336 | - if not self.is_snap or 'binaries' not in self.pkg_yaml: |
1337 | + if not self.is_snap1 or 'binaries' not in self.pkg_yaml: |
1338 | return |
1339 | self._verify_required(self._create_dict(self.pkg_yaml['binaries']), |
1340 | 'package_yaml') |
1341 | @@ -121,7 +124,7 @@ |
1342 | |
1343 | def check_snappy_optional(self): |
1344 | '''Check snappy packate.yaml optional fields''' |
1345 | - if not self.is_snap or 'binaries' not in self.pkg_yaml: |
1346 | + if not self.is_snap1 or 'binaries' not in self.pkg_yaml: |
1347 | return |
1348 | self._verify_optional(self._create_dict(self.pkg_yaml['binaries']), |
1349 | 'package_yaml') |
1350 | @@ -149,13 +152,16 @@ |
1351 | |
1352 | def check_snappy_unknown(self): |
1353 | '''Check snappy package.yaml unknown fields''' |
1354 | - if not self.is_snap or 'binaries' not in self.pkg_yaml: |
1355 | + if not self.is_snap1 or 'binaries' not in self.pkg_yaml: |
1356 | return |
1357 | self._verify_unknown(self._create_dict(self.pkg_yaml['binaries']), |
1358 | 'package_yaml') |
1359 | |
1360 | def check_path(self): |
1361 | '''Check path exists''' |
1362 | + if not self.is_snap1: |
1363 | + return |
1364 | + |
1365 | t = 'info' |
1366 | n = self._get_check_name('path_exists') |
1367 | s = "OK" |
1368 | @@ -172,7 +178,7 @@ |
1369 | |
1370 | def check_binary_description(self): |
1371 | '''Check package.yaml binary description''' |
1372 | - if not self.is_snap or 'binaries' not in self.pkg_yaml: |
1373 | + if not self.is_snap1 or 'binaries' not in self.pkg_yaml: |
1374 | return |
1375 | |
1376 | my_dict = self._create_dict(self.pkg_yaml['binaries']) |
1377 | |
1378 | === modified file 'clickreviews/cr_common.py' |
1379 | --- clickreviews/cr_common.py 2016-02-01 17:29:56 +0000 |
1380 | +++ clickreviews/cr_common.py 2016-02-09 21:28:10 +0000 |
1381 | @@ -1,6 +1,6 @@ |
1382 | -'''common.py: common classes and functions''' |
1383 | +'''cr_common.py: common classes and functions''' |
1384 | # |
1385 | -# Copyright (C) 2013-2015 Canonical Ltd. |
1386 | +# Copyright (C) 2013-2016 Canonical Ltd. |
1387 | # |
1388 | # This program is free software: you can redistribute it and/or modify |
1389 | # it under the terms of the GNU General Public License as published by |
1390 | @@ -15,54 +15,31 @@ |
1391 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1392 | |
1393 | from __future__ import print_function |
1394 | -import atexit |
1395 | -import codecs |
1396 | from debian.deb822 import Deb822 |
1397 | import glob |
1398 | -import inspect |
1399 | import json |
1400 | -import logging |
1401 | -import magic |
1402 | import os |
1403 | import pprint |
1404 | import re |
1405 | -import shutil |
1406 | -import subprocess |
1407 | -import sys |
1408 | -import tempfile |
1409 | -import types |
1410 | import yaml |
1411 | |
1412 | -DEBUGGING = False |
1413 | -UNPACK_DIR = None |
1414 | -RAW_UNPACK_DIR = None |
1415 | - |
1416 | - |
1417 | -def cleanup_unpack(): |
1418 | - global UNPACK_DIR |
1419 | - if UNPACK_DIR is not None and os.path.isdir(UNPACK_DIR): |
1420 | - recursive_rm(UNPACK_DIR) |
1421 | - UNPACK_DIR = None |
1422 | - global RAW_UNPACK_DIR |
1423 | - if RAW_UNPACK_DIR is not None and os.path.isdir(RAW_UNPACK_DIR): |
1424 | - recursive_rm(RAW_UNPACK_DIR) |
1425 | - RAW_UNPACK_DIR = None |
1426 | -atexit.register(cleanup_unpack) |
1427 | + |
1428 | +from clickreviews.common import( |
1429 | + Review, |
1430 | + ReviewException, |
1431 | + error, |
1432 | + open_file_read, |
1433 | +) |
1434 | |
1435 | |
1436 | # |
1437 | # Utility classes |
1438 | # |
1439 | -class ClickReviewException(Exception): |
1440 | +class ClickReviewException(ReviewException): |
1441 | '''This class represents ClickReview exceptions''' |
1442 | - def __init__(self, value): |
1443 | - self.value = value |
1444 | - |
1445 | - def __str__(self): |
1446 | - return repr(self.value) |
1447 | - |
1448 | - |
1449 | -class ClickReview(object): |
1450 | + |
1451 | + |
1452 | +class ClickReview(Review): |
1453 | '''This class represents click reviews''' |
1454 | # Convenience to break out common types of clicks (eg, app, scope, |
1455 | # click service) |
1456 | @@ -124,8 +101,13 @@ |
1457 | |
1458 | def __init__(self, fn, review_type, peer_hooks=None, overrides=None, |
1459 | peer_hooks_link=None): |
1460 | - self.pkg_filename = fn |
1461 | - self._check_path_exists() |
1462 | + Review.__init__(self, fn, review_type, overrides=overrides) |
1463 | + |
1464 | + # The cr_* scripts only support 15.04 snaps (v1). Use sr_* scripts for |
1465 | + # 16.04 (v2) or higher |
1466 | + if not self.is_click and not self.is_snap1: |
1467 | + return |
1468 | + |
1469 | if not self.pkg_filename.endswith(".click") and \ |
1470 | not self.pkg_filename.endswith(".snap"): |
1471 | if self.pkg_filename.endswith(".deb"): |
1472 | @@ -134,67 +116,24 @@ |
1473 | "how click packages are different.") |
1474 | error("filename does not end with '.click'") |
1475 | |
1476 | - self.review_type = review_type |
1477 | - self.click_report = dict() |
1478 | - |
1479 | - self.result_types = ['info', 'warn', 'error'] |
1480 | - for r in self.result_types: |
1481 | - self.click_report[r] = dict() |
1482 | - |
1483 | - self.click_report_output = "json" |
1484 | - |
1485 | - global UNPACK_DIR |
1486 | - if UNPACK_DIR is None: |
1487 | - UNPACK_DIR = unpack_click(fn) |
1488 | - self.unpack_dir = UNPACK_DIR |
1489 | - |
1490 | - global RAW_UNPACK_DIR |
1491 | - if RAW_UNPACK_DIR is None: |
1492 | - RAW_UNPACK_DIR = raw_unpack_pkg(fn) |
1493 | - self.raw_unpack_dir = RAW_UNPACK_DIR |
1494 | - |
1495 | - self.pkgfmt = {"type": "", "version": ""} |
1496 | self.manifest = None |
1497 | self.click_pkgname = None |
1498 | self.click_version = None |
1499 | self.pkg_arch = [] |
1500 | - |
1501 | - # Parse and store the package.yaml, if it exists |
1502 | - pkg_yaml = self._extract_package_yaml() |
1503 | - snap_yaml = self._extract_snap_yaml() |
1504 | - self.is_snap = False |
1505 | - if pkg_yaml is None and snap_yaml is None: |
1506 | - self.pkgfmt["type"] = "click" |
1507 | - else: |
1508 | - self.pkgfmt["type"] = "snap" |
1509 | - # Some day we will be able to introspect the version, but not |
1510 | - # today.... For now, decide on if it is a squashfs and if so, |
1511 | - # assume it is 16.04 |
1512 | - if is_squashfs(fn): |
1513 | - self.pkgfmt["version"] = "16.04" |
1514 | - self.peer_hooks = None |
1515 | - self.peer_hooks_link = None |
1516 | - else: |
1517 | - self.pkgfmt["version"] = "15.04" |
1518 | - |
1519 | - if snap_yaml: |
1520 | - try: |
1521 | - self.snap_yaml = yaml.safe_load(snap_yaml) |
1522 | - except Exception: |
1523 | - error("Could not load snap.yaml. Is it properly formatted?") |
1524 | - # convert enough of snap_yaml too pkg.yaml to make |
1525 | - # the basics of the review tools |
1526 | - if self.snap_yaml: |
1527 | - self.pkg_yaml = self.snap_yaml |
1528 | - self.pkg_yaml["version"] = str(self.snap_yaml["version"]) |
1529 | - |
1530 | + self.is_snap_oem = False |
1531 | + self.peer_hooks = peer_hooks |
1532 | + self.peer_hooks_link = peer_hooks_link |
1533 | + |
1534 | + if self.is_snap1: |
1535 | + pkg_yaml = self._extract_package_yaml() |
1536 | if pkg_yaml: |
1537 | try: |
1538 | self.pkg_yaml = yaml.safe_load(pkg_yaml) |
1539 | except Exception: |
1540 | error("Could not load package.yaml. Is it properly formatted?") |
1541 | self._verify_package_yaml_structure() |
1542 | - self.is_snap = True |
1543 | + else: |
1544 | + error("Could not load package.yaml.") |
1545 | |
1546 | # default to 'app' |
1547 | if 'type' not in self.pkg_yaml: |
1548 | @@ -212,6 +151,9 @@ |
1549 | else: |
1550 | self.pkg_arch = ['all'] |
1551 | |
1552 | + if 'type' in self.pkg_yaml and self.pkg_yaml['type'] == 'oem': |
1553 | + self.is_snap_oem = True |
1554 | + |
1555 | if self._pkgfmt_type() == "click" or self._pkgfmt_version() == "15.04": |
1556 | # Get some basic information from the control file |
1557 | control_file = self._extract_control_file() |
1558 | @@ -236,33 +178,6 @@ |
1559 | |
1560 | self.valid_frameworks = self._extract_click_frameworks() |
1561 | |
1562 | - self.peer_hooks = peer_hooks |
1563 | - self.peer_hooks_link = peer_hooks_link |
1564 | - |
1565 | - self.is_snap_oem = False |
1566 | - if self.is_snap and 'type' in self.pkg_yaml and \ |
1567 | - self.pkg_yaml['type'] == 'oem': |
1568 | - self.is_snap_oem = True |
1569 | - |
1570 | - self.is_snap_gadget = False |
1571 | - if self.is_snap and 'type' in self.pkg_yaml and \ |
1572 | - self.pkg_yaml['type'] == 'gadget': |
1573 | - self.is_snap_gadget = True |
1574 | - |
1575 | - # Get a list of all unpacked files, except DEBIAN/ |
1576 | - self.pkg_files = [] |
1577 | - self._list_all_files() |
1578 | - |
1579 | - # Setup what is needed to get a list of all unpacked compiled binaries |
1580 | - self.mime = magic.open(magic.MAGIC_MIME) |
1581 | - self.mime.load() |
1582 | - self.pkg_bin_files = [] |
1583 | - # Don't run this here since only cr_lint.py and cr_functional.py need |
1584 | - # it now |
1585 | - # self._list_all_compiled_binaries() |
1586 | - |
1587 | - self.overrides = overrides if overrides is not None else {} |
1588 | - |
1589 | def _extract_click_frameworks(self): |
1590 | '''Extract installed click frameworks''' |
1591 | # TODO: update to use libclick API when available |
1592 | @@ -291,78 +206,16 @@ |
1593 | return None # snappy packaging is still optional |
1594 | return open_file_read(y) |
1595 | |
1596 | - def _extract_snap_yaml(self): |
1597 | - '''Extract and read the snappy 16.04 snap.yaml''' |
1598 | - y = os.path.join(self.unpack_dir, "meta/snap.yaml") |
1599 | - if not os.path.isfile(y): |
1600 | - return None # snappy packaging is still optional |
1601 | - return open_file_read(y) |
1602 | - |
1603 | def _extract_hashes_yaml(self): |
1604 | '''Extract and read the snappy hashes.yaml''' |
1605 | y = os.path.join(self.unpack_dir, "DEBIAN/hashes.yaml") |
1606 | return open_file_read(y) |
1607 | |
1608 | - def _extract_statinfo(self, fn): |
1609 | - '''Extract statinfo from file''' |
1610 | - try: |
1611 | - st = os.stat(fn) |
1612 | - except Exception: |
1613 | - return None |
1614 | - return st |
1615 | - |
1616 | - def _path_join(self, dirname, rest): |
1617 | - return os.path.join(dirname, rest) |
1618 | - |
1619 | - def _get_sha512sum(self, fn): |
1620 | - '''Get sha512sum of file''' |
1621 | - (rc, out) = cmd(['sha512sum', fn]) |
1622 | - if rc != 0: |
1623 | - return None |
1624 | - return out.split()[0] |
1625 | - |
1626 | - def _pkgfmt_type(self): |
1627 | - '''Return the package format type''' |
1628 | - if "type" not in self.pkgfmt: |
1629 | - return "" |
1630 | - return self.pkgfmt["type"] |
1631 | - |
1632 | - def _pkgfmt_version(self): |
1633 | - '''Return the package format version''' |
1634 | - if "version" not in self.pkgfmt: |
1635 | - return "" |
1636 | - return self.pkgfmt["version"] |
1637 | - |
1638 | - def _check_path_exists(self): |
1639 | - '''Check that the provided path exists''' |
1640 | - if not os.path.exists(self.pkg_filename): |
1641 | - error("Could not find '%s'" % self.pkg_filename) |
1642 | - |
1643 | def _extract_control_file(self): |
1644 | '''Extract ''' |
1645 | fh = open_file_read(os.path.join(self.unpack_dir, "DEBIAN/control")) |
1646 | return fh.readlines() |
1647 | |
1648 | - def _list_all_files(self): |
1649 | - '''List all files included in this click package.''' |
1650 | - for root, dirnames, filenames in os.walk(self.unpack_dir): |
1651 | - for f in filenames: |
1652 | - self.pkg_files.append(os.path.join(root, f)) |
1653 | - |
1654 | - def _check_if_message_catalog(self, fn): |
1655 | - '''Check if file is a message catalog (.mo file).''' |
1656 | - if fn.endswith('.mo'): |
1657 | - return True |
1658 | - return False |
1659 | - |
1660 | - def _list_all_compiled_binaries(self): |
1661 | - '''List all compiled binaries in this click package.''' |
1662 | - for i in self.pkg_files: |
1663 | - res = self.mime.file(i) |
1664 | - if res in self.magic_binary_file_descriptions and \ |
1665 | - not self._check_if_message_catalog(i): |
1666 | - self.pkg_bin_files.append(i) |
1667 | - |
1668 | def _verify_manifest_structure(self): |
1669 | '''Verify manifest has the expected structure''' |
1670 | # lp:click doc/file-format.rst |
1671 | @@ -506,7 +359,7 @@ |
1672 | |
1673 | def _verify_pkgname(self, n): |
1674 | '''Verify package name''' |
1675 | - if self.is_snap: |
1676 | + if self.is_snap1: |
1677 | # snaps can't have '.' in the name |
1678 | pat = re.compile(r'^[a-z0-9][a-z0-9+-]+$') |
1679 | else: |
1680 | @@ -515,15 +368,6 @@ |
1681 | return True |
1682 | return False |
1683 | |
1684 | - def _verify_pkgversion(self, v): |
1685 | - '''Verify package name''' |
1686 | - re_valid_version = re.compile(r'^((\d+):)?' # epoch |
1687 | - '([A-Za-z0-9.+:~-]+?)' # upstream |
1688 | - '(-([A-Za-z0-9+.~]+))?$') # debian |
1689 | - if re_valid_version.match(v): |
1690 | - return True |
1691 | - return False |
1692 | - |
1693 | def _verify_maintainer(self, m): |
1694 | '''Verify maintainer email''' |
1695 | # Simple regex as used by python3-debian. If we wanted to be more |
1696 | @@ -550,7 +394,7 @@ |
1697 | def check_peer_hooks(self, hooks_sublist=[]): |
1698 | '''Check if peer hooks are valid''' |
1699 | # Nothing to verify |
1700 | - if self.peer_hooks is None: |
1701 | + if not hasattr(self, 'peer_hooks') or self.peer_hooks is None: |
1702 | return |
1703 | |
1704 | for hook in self.peer_hooks: |
1705 | @@ -584,280 +428,3 @@ |
1706 | link=self.peer_hooks_link) |
1707 | else: |
1708 | self._add_result(t, n, s) |
1709 | - |
1710 | - def set_review_type(self, name): |
1711 | - '''Set review name''' |
1712 | - self.review_type = name |
1713 | - |
1714 | - def _get_check_name(self, name, app='', extra=''): |
1715 | - name = ':'.join([self.review_type, name]) |
1716 | - if app: |
1717 | - name += ':' + app |
1718 | - if extra: |
1719 | - name += ':' + extra |
1720 | - return name |
1721 | - |
1722 | - # click_report[<result_type>][<review_name>] = <result> |
1723 | - # result_type: info, warn, error |
1724 | - # review_name: name of the check (prefixed with self.review_type) |
1725 | - # result: contents of the review |
1726 | - def _add_result(self, result_type, review_name, result, link=None, |
1727 | - manual_review=False): |
1728 | - '''Add result to report''' |
1729 | - if result_type not in self.result_types: |
1730 | - error("Invalid result type '%s'" % result_type) |
1731 | - |
1732 | - if review_name not in self.click_report[result_type]: |
1733 | - # log info about check so it can be collected into the |
1734 | - # check-names.list file |
1735 | - # format should be |
1736 | - # CHECK|<review_type:check_name>|<link> |
1737 | - msg = 'CHECK|{}|{}' |
1738 | - name = ':'.join(review_name.split(':')[:2]) |
1739 | - link_text = link if link is not None else "" |
1740 | - logging.debug(msg.format(name, link_text)) |
1741 | - self.click_report[result_type][review_name] = dict() |
1742 | - |
1743 | - self.click_report[result_type][review_name].update({ |
1744 | - 'text': result, |
1745 | - 'manual_review': manual_review, |
1746 | - }) |
1747 | - if link is not None: |
1748 | - self.click_report[result_type][review_name]["link"] = link |
1749 | - |
1750 | - def do_report(self): |
1751 | - '''Print report''' |
1752 | - if self.click_report_output == "console": |
1753 | - # TODO: format better |
1754 | - import pprint |
1755 | - pprint.pprint(self.click_report) |
1756 | - elif self.click_report_output == "json": |
1757 | - import json |
1758 | - msg(json.dumps(self.click_report, |
1759 | - sort_keys=True, |
1760 | - indent=2, |
1761 | - separators=(',', ': '))) |
1762 | - |
1763 | - rc = 0 |
1764 | - if len(self.click_report['error']): |
1765 | - rc = 2 |
1766 | - elif len(self.click_report['warn']): |
1767 | - rc = 1 |
1768 | - return rc |
1769 | - |
1770 | - def do_checks(self): |
1771 | - '''Run all methods that start with check_''' |
1772 | - methodList = [name for name, member in |
1773 | - inspect.getmembers(self, inspect.ismethod) |
1774 | - if isinstance(member, types.MethodType)] |
1775 | - for methodname in methodList: |
1776 | - if not methodname.startswith("check_"): |
1777 | - continue |
1778 | - func = getattr(self, methodname) |
1779 | - func() |
1780 | - |
1781 | - |
1782 | -# |
1783 | -# Utility functions |
1784 | -# |
1785 | -def error(out, exit_code=1, do_exit=True): |
1786 | - '''Print error message and exit''' |
1787 | - try: |
1788 | - print("ERROR: %s" % (out), file=sys.stderr) |
1789 | - except IOError: |
1790 | - pass |
1791 | - |
1792 | - if do_exit: |
1793 | - sys.exit(exit_code) |
1794 | - |
1795 | - |
1796 | -def warn(out): |
1797 | - '''Print warning message''' |
1798 | - try: |
1799 | - print("WARN: %s" % (out), file=sys.stderr) |
1800 | - except IOError: |
1801 | - pass |
1802 | - |
1803 | - |
1804 | -def msg(out, output=sys.stdout): |
1805 | - '''Print message''' |
1806 | - try: |
1807 | - print("%s" % (out), file=output) |
1808 | - except IOError: |
1809 | - pass |
1810 | - |
1811 | - |
1812 | -def debug(out): |
1813 | - '''Print debug message''' |
1814 | - global DEBUGGING |
1815 | - if DEBUGGING: |
1816 | - try: |
1817 | - print("DEBUG: %s" % (out), file=sys.stderr) |
1818 | - except IOError: |
1819 | - pass |
1820 | - |
1821 | - |
1822 | -def cmd(command): |
1823 | - '''Try to execute the given command.''' |
1824 | - debug(command) |
1825 | - try: |
1826 | - sp = subprocess.Popen(command, stdout=subprocess.PIPE, |
1827 | - stderr=subprocess.STDOUT) |
1828 | - except OSError as ex: |
1829 | - return [127, str(ex)] |
1830 | - |
1831 | - if sys.version_info[0] >= 3: |
1832 | - out = sp.communicate()[0].decode('ascii', 'ignore') |
1833 | - else: |
1834 | - out = sp.communicate()[0] |
1835 | - |
1836 | - return [sp.returncode, out] |
1837 | - |
1838 | - |
1839 | -def cmd_pipe(command1, command2): |
1840 | - '''Try to pipe command1 into command2.''' |
1841 | - try: |
1842 | - sp1 = subprocess.Popen(command1, stdout=subprocess.PIPE) |
1843 | - sp2 = subprocess.Popen(command2, stdin=sp1.stdout) |
1844 | - except OSError as ex: |
1845 | - return [127, str(ex)] |
1846 | - |
1847 | - if sys.version_info[0] >= 3: |
1848 | - out = sp2.communicate()[0].decode('ascii', 'ignore') |
1849 | - else: |
1850 | - out = sp2.communicate()[0] |
1851 | - |
1852 | - return [sp2.returncode, out] |
1853 | - |
1854 | - |
1855 | -def _unpack_cmd(cmd_args, d, dest): |
1856 | - '''Low level unpack helper''' |
1857 | - curdir = os.getcwd() |
1858 | - os.chdir(d) |
1859 | - |
1860 | - (rc, out) = cmd(cmd_args) |
1861 | - os.chdir(curdir) |
1862 | - |
1863 | - if rc != 0: |
1864 | - if os.path.isdir(d): |
1865 | - recursive_rm(d) |
1866 | - error("unpacking failed with '%d':\n%s" % (rc, out)) |
1867 | - |
1868 | - if dest is None: |
1869 | - dest = d |
1870 | - else: |
1871 | - shutil.move(d, dest) |
1872 | - |
1873 | - return dest |
1874 | - |
1875 | - |
1876 | -def _unpack_snap_squashfs(snap_pkg, dest): |
1877 | - '''Unpack a squashfs based snap package to dest''' |
1878 | - d = tempfile.mkdtemp(prefix='clickreview-') |
1879 | - return _unpack_cmd(['unsquashfs', '-f', '-d', d, |
1880 | - os.path.abspath(snap_pkg)], d, dest) |
1881 | - |
1882 | - |
1883 | -def _unpack_click_deb(click_pkg, dest): |
1884 | - d = tempfile.mkdtemp(prefix='clickreview-') |
1885 | - return _unpack_cmd(['dpkg-deb', '-R', |
1886 | - os.path.abspath(click_pkg), d], d, dest) |
1887 | - |
1888 | - |
1889 | -def unpack_click(fn, dest=None): |
1890 | - '''Unpack click package''' |
1891 | - if not os.path.isfile(fn): |
1892 | - error("Could not find '%s'" % fn) |
1893 | - click_pkg = fn |
1894 | - if not click_pkg.startswith('/'): |
1895 | - click_pkg = os.path.abspath(click_pkg) |
1896 | - |
1897 | - if dest is not None and os.path.exists(dest): |
1898 | - error("'%s' exists. Aborting." % dest) |
1899 | - |
1900 | - # check if its a squashfs based snap |
1901 | - if is_squashfs(click_pkg): |
1902 | - return _unpack_snap_squashfs(fn, dest) |
1903 | - |
1904 | - return _unpack_click_deb(fn, dest) |
1905 | - |
1906 | - |
1907 | -def is_squashfs(filename): |
1908 | - '''Return true if the given filename as a squashfs header''' |
1909 | - with open(filename, 'rb') as f: |
1910 | - header = f.read(10) |
1911 | - return header.startswith(b"hsqs") |
1912 | - |
1913 | - |
1914 | -def raw_unpack_pkg(fn, dest=None): |
1915 | - '''Unpack raw package''' |
1916 | - if not os.path.isfile(fn): |
1917 | - error("Could not find '%s'" % fn) |
1918 | - pkg = fn |
1919 | - if not pkg.startswith('/'): |
1920 | - pkg = os.path.abspath(pkg) |
1921 | - # nothing to do for squashfs images |
1922 | - if is_squashfs(pkg): |
1923 | - return "" |
1924 | - |
1925 | - if dest is not None and os.path.exists(dest): |
1926 | - error("'%s' exists. Aborting." % dest) |
1927 | - |
1928 | - d = tempfile.mkdtemp(prefix='review-') |
1929 | - |
1930 | - curdir = os.getcwd() |
1931 | - os.chdir(d) |
1932 | - (rc, out) = cmd(['ar', 'x', pkg]) |
1933 | - os.chdir(curdir) |
1934 | - |
1935 | - if rc != 0: |
1936 | - if os.path.isdir(d): |
1937 | - recursive_rm(d) |
1938 | - error("'ar x' failed with '%d':\n%s" % (rc, out)) |
1939 | - |
1940 | - if dest is None: |
1941 | - dest = d |
1942 | - else: |
1943 | - shutil.move(d, dest) |
1944 | - |
1945 | - return dest |
1946 | - |
1947 | - |
1948 | -def open_file_read(path): |
1949 | - '''Open specified file read-only''' |
1950 | - try: |
1951 | - orig = codecs.open(path, 'r', "UTF-8") |
1952 | - except Exception: |
1953 | - raise |
1954 | - |
1955 | - return orig |
1956 | - |
1957 | - |
1958 | -def recursive_rm(dirPath, contents_only=False): |
1959 | - '''recursively remove directory''' |
1960 | - names = os.listdir(dirPath) |
1961 | - for name in names: |
1962 | - path = os.path.join(dirPath, name) |
1963 | - if os.path.islink(path) or not os.path.isdir(path): |
1964 | - os.unlink(path) |
1965 | - else: |
1966 | - recursive_rm(path) |
1967 | - if contents_only is False: |
1968 | - os.rmdir(dirPath) |
1969 | - |
1970 | - |
1971 | -def run_click_check(cls): |
1972 | - if len(sys.argv) < 2: |
1973 | - error("Must give path to click package") |
1974 | - |
1975 | - # extract args |
1976 | - fn = sys.argv[1] |
1977 | - if len(sys.argv) > 2: |
1978 | - overrides = json.loads(sys.argv[2]) |
1979 | - else: |
1980 | - overrides = None |
1981 | - |
1982 | - review = cls(fn, overrides=overrides) |
1983 | - review.do_checks() |
1984 | - rc = review.do_report() |
1985 | - sys.exit(rc) |
1986 | |
1987 | === modified file 'clickreviews/cr_content_hub.py' |
1988 | --- clickreviews/cr_content_hub.py 2015-11-13 23:21:27 +0000 |
1989 | +++ clickreviews/cr_content_hub.py 2016-02-09 21:28:10 +0000 |
1990 | @@ -32,6 +32,8 @@ |
1991 | |
1992 | ClickReview.__init__(self, fn, "content_hub", peer_hooks=peer_hooks, |
1993 | overrides=overrides) |
1994 | + if not self.is_click and not self.is_snap1: |
1995 | + return |
1996 | |
1997 | self.valid_keys = ['destination', 'share', 'source'] |
1998 | |
1999 | @@ -79,6 +81,9 @@ |
2000 | |
2001 | def check_valid(self): |
2002 | '''Check validity of content-hub entries''' |
2003 | + if not self.is_click and not self.is_snap1: |
2004 | + return |
2005 | + |
2006 | for app in sorted(self.content_hub): |
2007 | for k in self.content_hub[app].keys(): |
2008 | t = "info" |
2009 | @@ -109,6 +114,9 @@ |
2010 | |
2011 | def check_unknown_keys(self): |
2012 | '''Check unknown''' |
2013 | + if not self.is_click and not self.is_snap1: |
2014 | + return |
2015 | + |
2016 | for app in sorted(self.content_hub): |
2017 | unknown = [] |
2018 | t = "info" |
2019 | |
2020 | === modified file 'clickreviews/cr_desktop.py' |
2021 | --- clickreviews/cr_desktop.py 2015-11-14 00:08:32 +0000 |
2022 | +++ clickreviews/cr_desktop.py 2016-02-09 21:28:10 +0000 |
2023 | @@ -37,6 +37,8 @@ |
2024 | |
2025 | ClickReview.__init__(self, fn, "desktop", peer_hooks=peer_hooks, |
2026 | overrides=overrides) |
2027 | + if not self.is_click and not self.is_snap1: |
2028 | + return |
2029 | |
2030 | self.desktop_files = dict() # click-show-files and a couple tests |
2031 | self.desktop_entries = dict() |
2032 | @@ -113,6 +115,9 @@ |
2033 | |
2034 | def check_desktop_file(self): |
2035 | '''Check desktop file''' |
2036 | + if not self.is_click and not self.is_snap1: |
2037 | + return |
2038 | + |
2039 | t = 'info' |
2040 | n = self._get_check_name('files_usable') |
2041 | s = 'OK' |
2042 | @@ -125,6 +130,9 @@ |
2043 | |
2044 | def check_desktop_file_valid(self): |
2045 | '''Check desktop file validates''' |
2046 | + if not self.is_click and not self.is_snap1: |
2047 | + return |
2048 | + |
2049 | for app in sorted(self.desktop_entries): |
2050 | de = self._get_desktop_entry(app) |
2051 | t = 'info' |
2052 | @@ -141,6 +149,9 @@ |
2053 | |
2054 | def check_desktop_required_keys(self): |
2055 | '''Check for required keys''' |
2056 | + if not self.is_click and not self.is_snap1: |
2057 | + return |
2058 | + |
2059 | for app in sorted(self.desktop_entries): |
2060 | de = self._get_desktop_entry(app) |
2061 | t = 'info' |
2062 | @@ -169,6 +180,9 @@ |
2063 | |
2064 | def check_desktop_blacklisted_keys(self): |
2065 | '''Check for blacklisted keys''' |
2066 | + if not self.is_click and not self.is_snap1: |
2067 | + return |
2068 | + |
2069 | for app in sorted(self.desktop_entries): |
2070 | de = self._get_desktop_entry(app) |
2071 | t = 'info' |
2072 | @@ -185,6 +199,9 @@ |
2073 | |
2074 | def check_desktop_exec(self): |
2075 | '''Check Exec entry''' |
2076 | + if not self.is_click and not self.is_snap1: |
2077 | + return |
2078 | + |
2079 | for app in sorted(self.desktop_entries): |
2080 | de = self._get_desktop_entry(app) |
2081 | t = 'info' |
2082 | @@ -218,6 +235,9 @@ |
2083 | |
2084 | def check_desktop_exec_webapp_container(self): |
2085 | '''Check Exec=webapp-container entry''' |
2086 | + if not self.is_click and not self.is_snap1: |
2087 | + return |
2088 | + |
2089 | if self.manifest is None: |
2090 | return |
2091 | |
2092 | @@ -272,6 +292,9 @@ |
2093 | |
2094 | def check_desktop_exec_webbrowser(self): |
2095 | '''Check Exec=webbrowser-app entry''' |
2096 | + if not self.is_click and not self.is_snap1: |
2097 | + return |
2098 | + |
2099 | for app in sorted(self.desktop_entries): |
2100 | de = self._get_desktop_entry(app) |
2101 | t = 'info' |
2102 | @@ -307,6 +330,9 @@ |
2103 | |
2104 | def check_desktop_exec_webapp_args(self): |
2105 | '''Check Exec=web* args''' |
2106 | + if not self.is_click and not self.is_snap1: |
2107 | + return |
2108 | + |
2109 | for app in sorted(self.desktop_entries): |
2110 | de = self._get_desktop_entry(app) |
2111 | t = 'info' |
2112 | @@ -475,6 +501,9 @@ |
2113 | |
2114 | def check_desktop_exec_webbrowser_urlpatterns(self): |
2115 | '''Check Exec=webbrowser-app entry has valid --webappUrlPatterns''' |
2116 | + if not self.is_click and not self.is_snap1: |
2117 | + return |
2118 | + |
2119 | for app in sorted(self.desktop_entries): |
2120 | de = self._get_desktop_entry(app) |
2121 | execline = de.getExec().split() |
2122 | @@ -532,6 +561,9 @@ |
2123 | |
2124 | def check_desktop_exec_webbrowser_modelsearchpath(self): |
2125 | '''Check Exec=webbrowser-app entry has valid --webappModelSearchPath''' |
2126 | + if not self.is_click and not self.is_snap1: |
2127 | + return |
2128 | + |
2129 | for app in sorted(self.desktop_entries): |
2130 | de = self._get_desktop_entry(app) |
2131 | execline = de.getExec().split() |
2132 | @@ -637,6 +669,9 @@ |
2133 | |
2134 | def check_desktop_groups(self): |
2135 | '''Check Desktop Entry entry''' |
2136 | + if not self.is_click and not self.is_snap1: |
2137 | + return |
2138 | + |
2139 | for app in sorted(self.desktop_entries): |
2140 | de = self._get_desktop_entry(app) |
2141 | t = 'info' |
2142 | @@ -652,6 +687,9 @@ |
2143 | |
2144 | def check_desktop_type(self): |
2145 | '''Check Type entry''' |
2146 | + if not self.is_click and not self.is_snap1: |
2147 | + return |
2148 | + |
2149 | for app in sorted(self.desktop_entries): |
2150 | de = self._get_desktop_entry(app) |
2151 | t = 'info' |
2152 | @@ -667,6 +705,9 @@ |
2153 | |
2154 | def check_desktop_x_ubuntu_touch(self): |
2155 | '''Check X-Ubuntu-Touch entry''' |
2156 | + if not self.is_click and not self.is_snap1: |
2157 | + return |
2158 | + |
2159 | for app in sorted(self.desktop_entries): |
2160 | de = self._get_desktop_entry(app) |
2161 | t = 'info' |
2162 | @@ -683,6 +724,9 @@ |
2163 | |
2164 | def check_desktop_x_ubuntu_stagehint(self): |
2165 | '''Check X-Ubuntu-StageHint entry''' |
2166 | + if not self.is_click and not self.is_snap1: |
2167 | + return |
2168 | + |
2169 | for app in sorted(self.desktop_entries): |
2170 | de = self._get_desktop_entry(app) |
2171 | t = 'info' |
2172 | @@ -700,6 +744,9 @@ |
2173 | |
2174 | def check_desktop_x_ubuntu_gettext_domain(self): |
2175 | '''Check X-Ubuntu-Gettext-Domain entry''' |
2176 | + if not self.is_click and not self.is_snap1: |
2177 | + return |
2178 | + |
2179 | for app in sorted(self.desktop_entries): |
2180 | de = self._get_desktop_entry(app) |
2181 | t = 'info' |
2182 | @@ -722,6 +769,9 @@ |
2183 | |
2184 | def check_desktop_terminal(self): |
2185 | '''Check Terminal entry''' |
2186 | + if not self.is_click and not self.is_snap1: |
2187 | + return |
2188 | + |
2189 | for app in sorted(self.desktop_entries): |
2190 | de = self._get_desktop_entry(app) |
2191 | t = 'info' |
2192 | @@ -736,6 +786,9 @@ |
2193 | |
2194 | def check_desktop_version(self): |
2195 | '''Check Version entry''' |
2196 | + if not self.is_click and not self.is_snap1: |
2197 | + return |
2198 | + |
2199 | for app in sorted(self.desktop_entries): |
2200 | de = self._get_desktop_entry(app) |
2201 | t = 'info' |
2202 | @@ -754,6 +807,9 @@ |
2203 | |
2204 | def check_desktop_comment(self): |
2205 | '''Check Comment entry''' |
2206 | + if not self.is_click and not self.is_snap1: |
2207 | + return |
2208 | + |
2209 | for app in sorted(self.desktop_entries): |
2210 | de = self._get_desktop_entry(app) |
2211 | t = 'info' |
2212 | @@ -769,6 +825,8 @@ |
2213 | |
2214 | def check_desktop_icon(self): |
2215 | '''Check Icon entry''' |
2216 | + if not self.is_click and not self.is_snap1: |
2217 | + return |
2218 | |
2219 | ICON_SUFFIXES = ['.svg', |
2220 | '.png', |
2221 | @@ -805,6 +863,9 @@ |
2222 | |
2223 | def check_desktop_duplicate_entries(self): |
2224 | '''Check desktop for duplicate entries''' |
2225 | + if not self.is_click and not self.is_snap1: |
2226 | + return |
2227 | + |
2228 | for app in sorted(self.desktop_entries): |
2229 | found = [] |
2230 | dupes = [] |
2231 | |
2232 | === modified file 'clickreviews/cr_framework.py' |
2233 | --- clickreviews/cr_framework.py 2015-11-13 23:21:27 +0000 |
2234 | +++ clickreviews/cr_framework.py 2016-02-09 21:28:10 +0000 |
2235 | @@ -30,6 +30,9 @@ |
2236 | self.frameworks_file = dict() |
2237 | self.frameworks = dict() |
2238 | |
2239 | + if not self.is_snap1: |
2240 | + return |
2241 | + |
2242 | if self.manifest is not None: |
2243 | for app in self.manifest['hooks']: |
2244 | if 'framework' not in self.manifest['hooks'][app]: |
2245 | @@ -98,7 +101,7 @@ |
2246 | |
2247 | def _has_framework_in_metadir(self): |
2248 | '''Check if snap has meta/<name>.framework''' |
2249 | - if not self.is_snap: |
2250 | + if not self.is_snap1: |
2251 | return False |
2252 | |
2253 | return os.path.exists(os.path.join(self.unpack_dir, 'meta', |
2254 | @@ -107,6 +110,9 @@ |
2255 | |
2256 | def check_framework_hook_obsolete(self): |
2257 | '''Check manifest doesn't specify 'framework' hook''' |
2258 | + if not self.is_snap1: |
2259 | + return |
2260 | + |
2261 | t = 'info' |
2262 | n = self._get_check_name("obsolete_declaration") |
2263 | s = "OK" |
2264 | @@ -118,7 +124,7 @@ |
2265 | |
2266 | def check_snappy_framework_file_obsolete(self): |
2267 | '''Check snap doesn't ship .framework file''' |
2268 | - if not self.is_snap or self.pkg_yaml['type'] != 'framework': |
2269 | + if not self.is_snap1 or self.pkg_yaml['type'] != 'framework': |
2270 | return |
2271 | t = 'info' |
2272 | n = self._get_check_name("obsolete_framework_file") |
2273 | @@ -130,7 +136,7 @@ |
2274 | |
2275 | def check_snappy_framework_depends(self): |
2276 | '''Check framework doesn't depend on other frameworks''' |
2277 | - if not self.is_snap or self.pkg_yaml['type'] != 'framework': |
2278 | + if not self.is_snap1 or self.pkg_yaml['type'] != 'framework': |
2279 | return |
2280 | t = 'info' |
2281 | n = self._get_check_name("dependency") |
2282 | @@ -142,7 +148,7 @@ |
2283 | |
2284 | def check_snappy_framework_policy(self): |
2285 | '''Check framework ships at least some policy''' |
2286 | - if not self.is_snap or self.pkg_yaml['type'] != 'framework': |
2287 | + if not self.is_snap1 or self.pkg_yaml['type'] != 'framework': |
2288 | return |
2289 | |
2290 | t = 'info' |
2291 | @@ -173,7 +179,7 @@ |
2292 | |
2293 | def check_snappy_framework_policy_metadata(self): |
2294 | '''Check framework policy has expected meta data''' |
2295 | - if not self.is_snap or self.pkg_yaml['type'] != 'framework': |
2296 | + if not self.is_snap1 or self.pkg_yaml['type'] != 'framework': |
2297 | return |
2298 | |
2299 | t = 'info' |
2300 | @@ -201,7 +207,7 @@ |
2301 | |
2302 | def check_snappy_framework_policy_matching(self): |
2303 | '''Check framework policy ships apparmor and seccomp for each''' |
2304 | - if not self.is_snap or self.pkg_yaml['type'] != 'framework': |
2305 | + if not self.is_snap1 or self.pkg_yaml['type'] != 'framework': |
2306 | return |
2307 | |
2308 | t = 'info' |
2309 | @@ -231,7 +237,7 @@ |
2310 | |
2311 | def check_snappy_framework_policy_filenames(self): |
2312 | '''Check framework policy file names''' |
2313 | - if not self.is_snap or self.pkg_yaml['type'] != 'framework': |
2314 | + if not self.is_snap1 or self.pkg_yaml['type'] != 'framework': |
2315 | return |
2316 | |
2317 | for i in self.framework_policy: |
2318 | |
2319 | === modified file 'clickreviews/cr_functional.py' |
2320 | --- clickreviews/cr_functional.py 2015-11-20 17:31:36 +0000 |
2321 | +++ clickreviews/cr_functional.py 2016-02-09 21:28:10 +0000 |
2322 | @@ -29,6 +29,8 @@ |
2323 | '''This class represents click lint reviews''' |
2324 | def __init__(self, fn, overrides=None): |
2325 | ClickReview.__init__(self, fn, "functional", overrides=overrides) |
2326 | + if not self.is_click and not self.is_snap1: |
2327 | + return |
2328 | |
2329 | self.qml_files = [] |
2330 | for i in self.pkg_files: |
2331 | @@ -39,6 +41,9 @@ |
2332 | |
2333 | def check_applicationName(self): |
2334 | '''Check applicationName matches click manifest''' |
2335 | + if not self.is_click and not self.is_snap1: |
2336 | + return |
2337 | + |
2338 | if self.manifest is None: |
2339 | return |
2340 | |
2341 | @@ -120,6 +125,9 @@ |
2342 | |
2343 | def check_qtwebkit(self): |
2344 | '''Check that QML applications don't use QtWebKit''' |
2345 | + if not self.is_click and not self.is_snap1: |
2346 | + return |
2347 | + |
2348 | t = 'info' |
2349 | n = self._get_check_name('qml_application_uses_QtWebKit') |
2350 | s = "OK" |
2351 | @@ -169,6 +177,9 @@ |
2352 | |
2353 | def check_friends(self): |
2354 | '''Check that QML applications don't use deprecated Friends API''' |
2355 | + if not self.is_click and not self.is_snap1: |
2356 | + return |
2357 | + |
2358 | t = 'info' |
2359 | n = self._get_check_name('qml_application_uses_friends') |
2360 | s = "OK" |
2361 | |
2362 | === modified file 'clickreviews/cr_lint.py' |
2363 | --- clickreviews/cr_lint.py 2016-02-01 19:12:30 +0000 |
2364 | +++ clickreviews/cr_lint.py 2016-02-09 21:28:10 +0000 |
2365 | @@ -26,12 +26,15 @@ |
2366 | from clickreviews.frameworks import Frameworks |
2367 | from clickreviews.cr_common import ( |
2368 | ClickReview, |
2369 | +) |
2370 | + |
2371 | +from clickreviews.common import ( |
2372 | open_file_read, |
2373 | cmd, |
2374 | error, |
2375 | - is_squashfs, |
2376 | ) |
2377 | |
2378 | + |
2379 | CONTROL_FILE_NAMES = ["control", "manifest", "preinst"] |
2380 | MINIMUM_CLICK_FRAMEWORK_VERSION = "0.4" |
2381 | |
2382 | @@ -42,9 +45,12 @@ |
2383 | def __init__(self, fn, overrides=None): |
2384 | '''Set up the class.''' |
2385 | ClickReview.__init__(self, fn, "lint", overrides=overrides) |
2386 | - if not self.is_snap and "md5sums" not in CONTROL_FILE_NAMES: |
2387 | + if not self.is_click and not self.is_snap1: |
2388 | + return |
2389 | + |
2390 | + if not self.is_snap1 and "md5sums" not in CONTROL_FILE_NAMES: |
2391 | CONTROL_FILE_NAMES.append("md5sums") |
2392 | - elif self.is_snap: |
2393 | + elif self.is_snap1: |
2394 | CONTROL_FILE_NAMES.append("hashes.yaml") |
2395 | self.control_files = dict() |
2396 | self._list_control_files() |
2397 | @@ -152,8 +158,7 @@ |
2398 | |
2399 | def check_control_files(self): |
2400 | '''Check DEBIAN/* files''' |
2401 | - if self._pkgfmt_type() == "snap" and \ |
2402 | - float(self._pkgfmt_version()) > 15.04: |
2403 | + if not self.is_click and not self.is_snap1: |
2404 | return |
2405 | |
2406 | for f in self.control_files: |
2407 | @@ -162,7 +167,7 @@ |
2408 | 'DEBIAN_has_files', extra=os.path.basename(f)) |
2409 | s = "OK" |
2410 | if not os.path.isfile(self.control_files[os.path.basename(f)]): |
2411 | - if self.is_snap and os.path.basename(f) == 'md5sums': |
2412 | + if self.is_snap1 and os.path.basename(f) == 'md5sums': |
2413 | s = "OK (skip md5sums with snap)" |
2414 | else: |
2415 | t = 'error' |
2416 | @@ -183,8 +188,7 @@ |
2417 | |
2418 | def check_control(self): |
2419 | '''Check control()''' |
2420 | - if self._pkgfmt_type() == "snap" and \ |
2421 | - float(self._pkgfmt_version()) > 15.04: |
2422 | + if not self.is_click and not self.is_snap1: |
2423 | return |
2424 | |
2425 | fh = self._extract_control_file() |
2426 | @@ -213,7 +217,7 @@ |
2427 | n = self._get_check_name('control_has_field', extra=f) |
2428 | s = 'OK' |
2429 | if f not in control: |
2430 | - if f == 'Maintainer' and self.is_snap: |
2431 | + if f == 'Maintainer' and self.is_snap1: |
2432 | s = 'OK (maintainer not required for snappy)' |
2433 | else: |
2434 | t = 'error' |
2435 | @@ -269,7 +273,7 @@ |
2436 | s = 'If arch=multi, manifest architecture needs to ' + \ |
2437 | 'comprise of only compiled architectures.' |
2438 | elif control['Architecture'] != self.manifest['architecture'] and \ |
2439 | - not self.is_snap: # snappy doesn't use this field; ignore |
2440 | + not self.is_snap1: # snappy doesn't use this field; ignore |
2441 | t = 'error' |
2442 | s = "Architecture=%s " % control['Architecture'] + \ |
2443 | "does not match manifest architecture=%s" % \ |
2444 | @@ -333,7 +337,7 @@ |
2445 | |
2446 | def check_md5sums(self): |
2447 | '''Check md5sums()''' |
2448 | - if self.is_snap: |
2449 | + if self.is_snap1 or not self.is_snap1: |
2450 | return |
2451 | curdir = os.getcwd() |
2452 | fh = open_file_read(self.control_files["md5sums"]) |
2453 | @@ -358,8 +362,7 @@ |
2454 | |
2455 | def check_preinst(self): |
2456 | '''Check preinst()''' |
2457 | - if self._pkgfmt_type() == "snap" and \ |
2458 | - float(self._pkgfmt_version()) > 15.04: |
2459 | + if not self.is_click and not self.is_snap1: |
2460 | return |
2461 | |
2462 | expected = '''#! /bin/sh |
2463 | @@ -383,17 +386,13 @@ |
2464 | |
2465 | def check_hooks(self): |
2466 | '''Check click manifest hooks''' |
2467 | - if self._pkgfmt_type() == "snap" and \ |
2468 | - float(self._pkgfmt_version()) > 15.04: |
2469 | + if not self.is_click and not self.is_snap1: |
2470 | return |
2471 | |
2472 | # oem snaps don't have a hooks entry |
2473 | if self.is_snap_oem: |
2474 | return |
2475 | |
2476 | - if self.is_snap_gadget: |
2477 | - return |
2478 | - |
2479 | # Some checks are already handled in |
2480 | # cr_common.py:_verify_manifest_structure() |
2481 | |
2482 | @@ -486,17 +485,13 @@ |
2483 | |
2484 | def check_hooks_unknown(self): |
2485 | '''Check if have any unknown hooks''' |
2486 | - if self._pkgfmt_type() == "snap" and \ |
2487 | - float(self._pkgfmt_version()) > 15.04: |
2488 | + if not self.is_click and not self.is_snap1: |
2489 | return |
2490 | |
2491 | # oem snaps don't have a hooks entry |
2492 | if self.is_snap_oem: |
2493 | return |
2494 | |
2495 | - if self.is_snap_gadget: |
2496 | - return |
2497 | - |
2498 | t = 'info' |
2499 | n = self._get_check_name('unknown_hooks') |
2500 | s = 'OK' |
2501 | @@ -514,8 +509,7 @@ |
2502 | |
2503 | def check_hooks_redflagged(self): |
2504 | '''Check if have any redflagged hooks''' |
2505 | - if self._pkgfmt_type() == "snap" and \ |
2506 | - float(self._pkgfmt_version()) > 15.04: |
2507 | + if not self.is_click and not self.is_snap1: |
2508 | return |
2509 | |
2510 | t = 'info' |
2511 | @@ -531,7 +525,7 @@ |
2512 | for hook in self.manifest['hooks'][app]: |
2513 | if hook in self.redflagged_hooks: |
2514 | # This check is handled elsewhere |
2515 | - if self.is_snap and hook == "apparmor-profile": |
2516 | + if self.is_snap1 and hook == "apparmor-profile": |
2517 | continue |
2518 | found.append(hook) |
2519 | if len(found) > 0: |
2520 | @@ -542,7 +536,9 @@ |
2521 | |
2522 | def check_external_symlinks(self): |
2523 | '''Check if symlinks in the click package go out to the system.''' |
2524 | - if self.is_snap and self.pkg_yaml['type'] not in ['app', 'framework']: |
2525 | + if not self.is_click and not self.is_snap1: |
2526 | + return |
2527 | + if self.is_snap1 and self.pkg_yaml['type'] not in ['app', 'framework']: |
2528 | return |
2529 | |
2530 | t = 'info' |
2531 | @@ -601,7 +597,7 @@ |
2532 | |
2533 | def check_pkgname(self): |
2534 | '''Check click package name valid''' |
2535 | - if self.is_snap: |
2536 | + if not self.is_click and not self.is_snap1: |
2537 | return |
2538 | p = self.manifest['name'] |
2539 | # http://www.debian.org/doc/debian-policy/ch-controlfields.html |
2540 | @@ -615,8 +611,7 @@ |
2541 | |
2542 | def check_version(self): |
2543 | '''Check click package version is valid''' |
2544 | - if self._pkgfmt_type() == "snap" and \ |
2545 | - float(self._pkgfmt_version()) > 15.04: |
2546 | + if not self.is_click and not self.is_snap1: |
2547 | return |
2548 | |
2549 | # deb-version(5) |
2550 | @@ -634,8 +629,7 @@ |
2551 | |
2552 | def check_architecture(self): |
2553 | '''Check click package architecture in DEBIAN/control is valid''' |
2554 | - if self._pkgfmt_type() == "snap" and \ |
2555 | - float(self._pkgfmt_version()) > 15.04: |
2556 | + if not self.is_click and not self.is_snap1: |
2557 | return |
2558 | |
2559 | t = 'info' |
2560 | @@ -648,6 +642,9 @@ |
2561 | |
2562 | def check_architecture_all(self): |
2563 | '''Check if actually architecture all''' |
2564 | + if not self.is_click and not self.is_snap1: |
2565 | + return |
2566 | + |
2567 | t = 'info' |
2568 | n = self._get_check_name('control_architecture_valid_contents') |
2569 | s = 'OK' |
2570 | @@ -667,6 +664,9 @@ |
2571 | |
2572 | def check_architecture_specified_needed(self): |
2573 | '''Check if the specified architecture is actually needed''' |
2574 | + if not self.is_click and not self.is_snap1: |
2575 | + return |
2576 | + |
2577 | for arch in self.pkg_arch: |
2578 | t = 'info' |
2579 | n = self._get_check_name('architecture_specified_needed') |
2580 | @@ -684,15 +684,14 @@ |
2581 | |
2582 | def check_maintainer(self): |
2583 | '''Check manifest maintainer()''' |
2584 | - if self._pkgfmt_type() == "snap" and \ |
2585 | - float(self._pkgfmt_version()) > 15.04: |
2586 | + if not self.is_click and not self.is_snap1: |
2587 | return |
2588 | |
2589 | t = 'info' |
2590 | n = self._get_check_name('maintainer_present') |
2591 | s = 'OK' |
2592 | if 'maintainer' not in self.manifest: |
2593 | - if self.is_snap: |
2594 | + if self.is_snap1: |
2595 | s = 'Skipped optional maintainer field not specified in ' + \ |
2596 | 'manifest' |
2597 | else: |
2598 | @@ -704,7 +703,7 @@ |
2599 | |
2600 | # Don't perform maintainer checks for snaps. They aren't used by |
2601 | # anything. |
2602 | - if self.is_snap: |
2603 | + if self.is_snap1: |
2604 | return |
2605 | |
2606 | # Simple regex as used by python3-debian. If we wanted to be more |
2607 | @@ -728,8 +727,7 @@ |
2608 | |
2609 | def check_title(self): |
2610 | '''Check manifest title()''' |
2611 | - if self._pkgfmt_type() == "snap" and \ |
2612 | - float(self._pkgfmt_version()) > 15.04: |
2613 | + if not self.is_click and not self.is_snap1: |
2614 | return |
2615 | |
2616 | t = 'info' |
2617 | @@ -752,8 +750,7 @@ |
2618 | |
2619 | def check_description(self): |
2620 | '''Check manifest description()''' |
2621 | - if self._pkgfmt_type() == "snap" and \ |
2622 | - float(self._pkgfmt_version()) > 15.04: |
2623 | + if not self.is_click and not self.is_snap1: |
2624 | return |
2625 | |
2626 | t = 'info' |
2627 | @@ -771,8 +768,8 @@ |
2628 | pkgname_base = self.click_pkgname.split('.')[-1] |
2629 | if len(self.manifest['description']) < len(pkgname_base): |
2630 | t = 'warn' |
2631 | - if self.is_snap and (self.manifest['description'] == '\n' or |
2632 | - self.manifest['description'] == ''): |
2633 | + if self.is_snap1 and (self.manifest['description'] == '\n' or |
2634 | + self.manifest['description'] == ''): |
2635 | s = "manifest description is empty. Is meta/readme.md " + \ |
2636 | "formatted correctly?" |
2637 | else: |
2638 | @@ -782,8 +779,7 @@ |
2639 | |
2640 | def check_framework(self): |
2641 | '''Check manifest framework()''' |
2642 | - if self._pkgfmt_type() == "snap" and \ |
2643 | - float(self._pkgfmt_version()) > 15.04: |
2644 | + if not self.is_click and not self.is_snap1: |
2645 | return |
2646 | |
2647 | n = self._get_check_name('framework') |
2648 | @@ -791,7 +787,7 @@ |
2649 | framework_overrides = self.overrides.get('framework', {}) |
2650 | frameworks = Frameworks(overrides=framework_overrides) |
2651 | |
2652 | - if not self.is_snap and ',' in self.manifest['framework']: |
2653 | + if not self.is_snap1 and ',' in self.manifest['framework']: |
2654 | # click doesn't support multiple frameworks yet |
2655 | t = 'error' |
2656 | s = 'ERROR: multiple frameworks found in click manifest' |
2657 | @@ -827,8 +823,7 @@ |
2658 | |
2659 | def check_click_local_extensions(self): |
2660 | '''Report any click local extensions''' |
2661 | - if self._pkgfmt_type() == "snap" and \ |
2662 | - float(self._pkgfmt_version()) > 15.04: |
2663 | + if not self.is_click and not self.is_snap1: |
2664 | return |
2665 | |
2666 | t = 'info' |
2667 | @@ -855,6 +850,9 @@ |
2668 | |
2669 | def check_vcs(self): |
2670 | '''Check for VCS files in the package''' |
2671 | + if not self.is_click and not self.is_snap1: |
2672 | + return |
2673 | + |
2674 | t = 'info' |
2675 | n = self._get_check_name('vcs_files') |
2676 | s = 'OK' |
2677 | @@ -889,8 +887,7 @@ |
2678 | |
2679 | def check_dot_click(self): |
2680 | '''Check for .click directory in the toplevel click package''' |
2681 | - if self._pkgfmt_type() == "snap" and \ |
2682 | - float(self._pkgfmt_version()) > 15.04: |
2683 | + if not self.is_click and not self.is_snap1: |
2684 | return |
2685 | |
2686 | t = 'info' |
2687 | @@ -938,14 +935,14 @@ |
2688 | return |
2689 | |
2690 | key = 'architecture' |
2691 | - if self.is_snap and 'architectures' in my_dict: |
2692 | + if self.is_snap1 and 'architectures' in my_dict: |
2693 | # new yaml allows for 'architecture' and 'architectures' |
2694 | key = 'architectures' |
2695 | |
2696 | archs_list = list(self.valid_control_architectures) |
2697 | archs_list.remove("multi") |
2698 | |
2699 | - if self.is_snap and key == 'architectures' and \ |
2700 | + if self.is_snap1 and key == 'architectures' and \ |
2701 | isinstance(my_dict[key], str): |
2702 | # new yaml uses 'architectures' that must be a list |
2703 | t = 'error' |
2704 | @@ -954,7 +951,7 @@ |
2705 | t = 'error' |
2706 | s = "not a valid architecture: %s" % my_dict[key] |
2707 | elif isinstance(my_dict[key], list): |
2708 | - if not self.is_snap: |
2709 | + if not self.is_snap1: |
2710 | archs_list.remove("all") |
2711 | bad_archs = [] |
2712 | for a in my_dict[key]: |
2713 | @@ -968,8 +965,7 @@ |
2714 | |
2715 | def check_manifest_architecture(self): |
2716 | '''Check package architecture in manifest is valid''' |
2717 | - if self._pkgfmt_type() == "snap" and \ |
2718 | - float(self._pkgfmt_version()) > 15.04: |
2719 | + if not self.is_click and not self.is_snap1: |
2720 | return |
2721 | |
2722 | self._verify_architecture(self.manifest, "manifest") |
2723 | @@ -1004,15 +1000,14 @@ |
2724 | |
2725 | def check_icon(self): |
2726 | '''Check icon()''' |
2727 | - if self._pkgfmt_type() == "snap" and \ |
2728 | - float(self._pkgfmt_version()) > 15.04: |
2729 | + if not self.is_click and not self.is_snap1: |
2730 | return |
2731 | |
2732 | self._verify_icon(self.manifest, "manifest") |
2733 | |
2734 | def check_snappy_name(self): |
2735 | '''Check package name''' |
2736 | - if not self.is_snap: |
2737 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2738 | return |
2739 | |
2740 | t = 'info' |
2741 | @@ -1028,7 +1023,7 @@ |
2742 | |
2743 | def check_snappy_version(self): |
2744 | '''Check package version''' |
2745 | - if not self.is_snap: |
2746 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2747 | return |
2748 | |
2749 | t = 'info' |
2750 | @@ -1044,7 +1039,7 @@ |
2751 | |
2752 | def check_snappy_type(self): |
2753 | '''Check type''' |
2754 | - if not self.is_snap: |
2755 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2756 | return |
2757 | |
2758 | t = 'info' |
2759 | @@ -1059,7 +1054,7 @@ |
2760 | |
2761 | def check_snappy_type_redflagged(self): |
2762 | '''Check if snappy type is redflagged''' |
2763 | - if not self.is_snap: |
2764 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2765 | return |
2766 | |
2767 | t = 'info' |
2768 | @@ -1079,14 +1074,14 @@ |
2769 | |
2770 | def check_snappy_icon(self): |
2771 | '''Check icon()''' |
2772 | - if not self.is_snap: |
2773 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2774 | return |
2775 | |
2776 | self._verify_icon(self.pkg_yaml, "package_yaml") |
2777 | |
2778 | def check_snappy_architecture(self): |
2779 | '''Check package architecture in package.yaml is valid''' |
2780 | - if not self.is_snap: |
2781 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2782 | return |
2783 | |
2784 | self._verify_architecture(self.pkg_yaml, "package yaml") |
2785 | @@ -1104,7 +1099,7 @@ |
2786 | |
2787 | def check_snappy_unknown_entries(self): |
2788 | '''Check for any unknown fields''' |
2789 | - if not self.is_snap: |
2790 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2791 | return |
2792 | |
2793 | t = 'info' |
2794 | @@ -1136,10 +1131,7 @@ |
2795 | |
2796 | def check_snappy_readme_md(self): |
2797 | '''Check snappy readme.md''' |
2798 | - if not self.is_snap: |
2799 | - return |
2800 | - |
2801 | - if is_squashfs(self.pkg_filename): |
2802 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2803 | return |
2804 | |
2805 | contents = self._extract_readme_md() |
2806 | @@ -1163,13 +1155,9 @@ |
2807 | s = "meta/readme.md is too short" |
2808 | self._add_result(t, n, s) |
2809 | |
2810 | - def _check_innerpath_executable(self, fn): |
2811 | - '''Check that the provided path exists and is executable''' |
2812 | - return os.access(fn, os.X_OK) |
2813 | - |
2814 | def check_snappy_config(self): |
2815 | '''Check snappy config''' |
2816 | - if not self.is_snap: |
2817 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2818 | return |
2819 | |
2820 | fn = os.path.join(self.unpack_dir, 'meta/hooks/config') |
2821 | @@ -1186,7 +1174,7 @@ |
2822 | |
2823 | def check_snappy_services_and_binaries(self): |
2824 | '''Services and binaries should not overlap''' |
2825 | - if not self.is_snap: |
2826 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2827 | return |
2828 | for exe_t in ['binaries', 'services']: |
2829 | if exe_t not in self.pkg_yaml: |
2830 | @@ -1217,34 +1205,9 @@ |
2831 | break |
2832 | self._add_result(t, n, s) |
2833 | |
2834 | - def check_is_squashfs(self): |
2835 | - '''Check snapfs''' |
2836 | - if is_squashfs(self.pkg_filename): |
2837 | - t = 'error' |
2838 | - n = self._get_check_name('is_squashfs') |
2839 | - s = "(NEEDS REVIEW) squashfs pkg" |
2840 | - manual_review = True |
2841 | - self._add_result(t, n, s, manual_review=manual_review) |
2842 | - |
2843 | - def check_squashfs_uses_snap_yaml(self): |
2844 | - '''Ensure that squashfs uses 16.04''' |
2845 | - if is_squashfs(self.pkg_filename) and not getattr(self, "snap_yaml"): |
2846 | - t = 'error' |
2847 | - n = self._get_check_name('check_squashfs_uses_snap_yaml') |
2848 | - s = "squashfs snaps must have a meta/snap.yaml" |
2849 | - manual_review = False |
2850 | - self._add_result(t, n, s, manual_review=manual_review) |
2851 | - |
2852 | def check_snappy_hashes(self): |
2853 | '''Check snappy hashes.yaml''' |
2854 | - if self._pkgfmt_type() == "snap" and \ |
2855 | - float(self._pkgfmt_version()) > 15.04: |
2856 | - return |
2857 | - |
2858 | - if not self.is_snap: |
2859 | - return |
2860 | - # no hashes.yaml for squashfs images |
2861 | - if is_squashfs(self.pkg_filename): |
2862 | + if self._pkgfmt_type() == "click" or not self.is_snap1: |
2863 | return |
2864 | |
2865 | def _check_allowed_perms(mode, allowed): |
2866 | |
2867 | === modified file 'clickreviews/cr_online_accounts.py' |
2868 | --- clickreviews/cr_online_accounts.py 2015-12-14 22:00:37 +0000 |
2869 | +++ clickreviews/cr_online_accounts.py 2016-02-09 21:28:10 +0000 |
2870 | @@ -69,6 +69,8 @@ |
2871 | peer_hooks=peer_hooks, |
2872 | overrides=overrides, |
2873 | peer_hooks_link="https://wiki.ubuntu.com/SecurityTeam/Specifications/OnlineAccountsConfinement") |
2874 | + if not self.is_click and not self.is_snap1: |
2875 | + return |
2876 | |
2877 | self.accounts_files = dict() |
2878 | self.accounts = dict() |
2879 | @@ -164,6 +166,9 @@ |
2880 | |
2881 | def check_hooks_versions(self): |
2882 | '''Check hooks versions''' |
2883 | + if not self.is_click and not self.is_snap1: |
2884 | + return |
2885 | + |
2886 | if self.manifest is None: |
2887 | return |
2888 | |
2889 | @@ -247,6 +252,9 @@ |
2890 | |
2891 | def check_manifest(self): |
2892 | '''Check manifest''' |
2893 | + if not self.is_click and not self.is_snap1: |
2894 | + return |
2895 | + |
2896 | for app in sorted(self.accounts.keys()): |
2897 | account_type = "accounts" |
2898 | |
2899 | @@ -278,6 +286,9 @@ |
2900 | |
2901 | def check_application(self): |
2902 | '''Check application''' |
2903 | + if not self.is_click and not self.is_snap1: |
2904 | + return |
2905 | + |
2906 | for app in sorted(self.accounts.keys()): |
2907 | account_type = "account-application" |
2908 | |
2909 | @@ -325,6 +336,9 @@ |
2910 | |
2911 | def check_service(self): |
2912 | '''Check service''' |
2913 | + if not self.is_click and not self.is_snap1: |
2914 | + return |
2915 | + |
2916 | for app in sorted(self.accounts.keys()): |
2917 | account_type = "account-service" |
2918 | |
2919 | @@ -366,6 +380,9 @@ |
2920 | |
2921 | def check_provider(self): |
2922 | '''Check provider''' |
2923 | + if not self.is_click and not self.is_snap1: |
2924 | + return |
2925 | + |
2926 | for app in sorted(self.accounts.keys()): |
2927 | account_type = "account-provider" |
2928 | |
2929 | @@ -398,6 +415,9 @@ |
2930 | |
2931 | def check_qml_plugin(self): |
2932 | '''Check qml-plugin''' |
2933 | + if not self.is_click and not self.is_snap1: |
2934 | + return |
2935 | + |
2936 | for app in sorted(self.accounts.keys()): |
2937 | account_type = "account-qml-plugin" |
2938 | |
2939 | |
2940 | === modified file 'clickreviews/cr_push_helper.py' |
2941 | --- clickreviews/cr_push_helper.py 2015-11-13 23:21:27 +0000 |
2942 | +++ clickreviews/cr_push_helper.py 2016-02-09 21:28:10 +0000 |
2943 | @@ -33,6 +33,9 @@ |
2944 | ClickReview.__init__(self, fn, "push_helper", peer_hooks=peer_hooks, |
2945 | overrides=overrides) |
2946 | |
2947 | + if not self.is_click and not self.is_snap1: |
2948 | + return |
2949 | + |
2950 | self.required_keys = ['exec'] |
2951 | self.optional_keys = ['app_id'] |
2952 | |
2953 | @@ -81,6 +84,9 @@ |
2954 | |
2955 | def check_valid(self): |
2956 | '''Check validity of push-helper entries''' |
2957 | + if not self.is_click and not self.is_snap1: |
2958 | + return |
2959 | + |
2960 | for app in sorted(self.push_helper): |
2961 | for k in self.push_helper[app].keys(): |
2962 | t = "info" |
2963 | @@ -106,6 +112,9 @@ |
2964 | |
2965 | def check_unknown_keys(self): |
2966 | '''Check unknown''' |
2967 | + if not self.is_click and not self.is_snap1: |
2968 | + return |
2969 | + |
2970 | for app in sorted(self.push_helper): |
2971 | unknown = [] |
2972 | t = "info" |
2973 | @@ -125,6 +134,9 @@ |
2974 | |
2975 | def check_hooks(self): |
2976 | '''Verify combinations of click hooks with the push-helper hook''' |
2977 | + if not self.is_click and not self.is_snap1: |
2978 | + return |
2979 | + |
2980 | if self.manifest is None: |
2981 | return |
2982 | |
2983 | |
2984 | === modified file 'clickreviews/cr_scope.py' |
2985 | --- clickreviews/cr_scope.py 2015-12-01 14:45:21 +0000 |
2986 | +++ clickreviews/cr_scope.py 2016-02-09 21:28:10 +0000 |
2987 | @@ -40,6 +40,9 @@ |
2988 | ClickReview.__init__(self, fn, "scope", peer_hooks=peer_hooks, |
2989 | overrides=overrides) |
2990 | |
2991 | + if not self.is_click and not self.is_snap1: |
2992 | + return |
2993 | + |
2994 | self.scopes = dict() |
2995 | |
2996 | if self.manifest is None: |
2997 | @@ -85,6 +88,9 @@ |
2998 | |
2999 | def check_scope_ini(self): |
3000 | '''Check scope .ini file''' |
3001 | + if not self.is_click and not self.is_snap1: |
3002 | + return |
3003 | + |
3004 | for app in sorted(self.scopes.keys()): |
3005 | t = 'info' |
3006 | n = self._get_check_name('ini_scope_section', app=app) |
3007 | |
3008 | === modified file 'clickreviews/cr_security.py' |
3009 | --- clickreviews/cr_security.py 2015-12-14 22:05:53 +0000 |
3010 | +++ clickreviews/cr_security.py 2016-02-09 21:28:10 +0000 |
3011 | @@ -47,6 +47,9 @@ |
3012 | ClickReview.__init__(self, fn, "security", peer_hooks=peer_hooks, |
3013 | overrides=overrides) |
3014 | |
3015 | + if not self.is_click and not self.is_snap1: |
3016 | + return |
3017 | + |
3018 | # If local_copy is None, then this will check the server to see if |
3019 | # we are up to date. However, if we are working within the development |
3020 | # tree, use it unconditionally. |
3021 | @@ -141,7 +144,7 @@ |
3022 | self.security_profiles = dict() |
3023 | self.security_apps_profiles = [] |
3024 | |
3025 | - if self.manifest is None and self.is_snap: |
3026 | + if self.manifest is None and self.is_snap1: |
3027 | for exe_t in ['services', 'binaries']: |
3028 | if exe_t not in self.pkg_yaml: |
3029 | continue |
3030 | @@ -394,6 +397,9 @@ |
3031 | |
3032 | def check_policy_vendor(self): |
3033 | '''Check policy_vendor''' |
3034 | + if not self.is_click and not self.is_snap1: |
3035 | + return |
3036 | + |
3037 | for app in sorted(self.security_apps): |
3038 | (f, m) = self._get_security_manifest(app) |
3039 | t = 'info' |
3040 | @@ -418,7 +424,7 @@ |
3041 | framework = self.manifest['framework'] |
3042 | # snappy compat manifest supports comma-separated list |
3043 | # for framework |
3044 | - if self.is_snap and ',' in framework: |
3045 | + if self.is_snap1 and ',' in framework: |
3046 | # For now, we know the release framework is appended. |
3047 | # TODO: fix for multiple frameworks |
3048 | framework = framework.split(',')[-1] |
3049 | @@ -440,6 +446,9 @@ |
3050 | |
3051 | def check_policy_version(self): |
3052 | '''Check policy version''' |
3053 | + if not self.is_click and not self.is_snap1: |
3054 | + return |
3055 | + |
3056 | for app in sorted(self.security_apps): |
3057 | (f, m) = self._get_security_manifest(app) |
3058 | |
3059 | @@ -486,7 +495,7 @@ |
3060 | framework = self.manifest['framework'] |
3061 | # snappy compat manifest supports comma-separated list |
3062 | # for framework |
3063 | - if self.is_snap and ',' in framework: |
3064 | + if self.is_snap1 and ',' in framework: |
3065 | # For now, we know the release framework is appended. |
3066 | # TODO: fix for multiple frameworks |
3067 | framework = framework.split(',')[-1] |
3068 | @@ -506,6 +515,9 @@ |
3069 | |
3070 | def check_template(self): |
3071 | '''Check template''' |
3072 | + if not self.is_click and not self.is_snap1: |
3073 | + return |
3074 | + |
3075 | for app in sorted(self.security_apps): |
3076 | (f, m) = self._get_security_manifest(app) |
3077 | |
3078 | @@ -556,7 +568,7 @@ |
3079 | found = False |
3080 | if m['template'] in self._get_templates(vendor, version): |
3081 | found = True |
3082 | - elif self.is_snap: |
3083 | + elif self.is_snap1: |
3084 | frameworks = [] |
3085 | if 'framework' in self.pkg_yaml: |
3086 | frameworks = [x.strip() for x in |
3087 | @@ -581,6 +593,9 @@ |
3088 | |
3089 | def check_policy_groups_webapps(self): |
3090 | '''Check policy_groups for webapps''' |
3091 | + if not self.is_click and not self.is_snap1: |
3092 | + return |
3093 | + |
3094 | for app in sorted(self.security_apps): |
3095 | (f, m) = self._get_security_manifest(app) |
3096 | t = 'info' |
3097 | @@ -617,6 +632,9 @@ |
3098 | |
3099 | def check_policy_groups_push_helpers(self): |
3100 | '''Check policy groups for push-helpers''' |
3101 | + if not self.is_click and not self.is_snap1: |
3102 | + return |
3103 | + |
3104 | for app in sorted(self.security_apps): |
3105 | (f, m) = self._get_security_manifest(app) |
3106 | t = 'info' |
3107 | @@ -647,6 +665,9 @@ |
3108 | |
3109 | def check_policy_groups_scopes(self): |
3110 | '''Check policy_groups for scopes''' |
3111 | + if not self.is_click and not self.is_snap1: |
3112 | + return |
3113 | + |
3114 | for app in sorted(self.security_apps): |
3115 | (f, m) = self._get_security_manifest(app) |
3116 | t = 'info' |
3117 | @@ -680,6 +701,9 @@ |
3118 | |
3119 | def check_policy_groups_ubuntu_account_plugin(self): |
3120 | '''Check policy_groups for ubuntu-account-plugin template''' |
3121 | + if not self.is_click and not self.is_snap1: |
3122 | + return |
3123 | + |
3124 | for app in sorted(self.security_apps): |
3125 | (f, m) = self._get_security_manifest(app) |
3126 | |
3127 | @@ -719,6 +743,9 @@ |
3128 | |
3129 | def check_policy_groups(self): |
3130 | '''Check policy_groups''' |
3131 | + if not self.is_click and not self.is_snap1: |
3132 | + return |
3133 | + |
3134 | for app in sorted(self.security_apps): |
3135 | (f, m) = self._get_security_manifest(app) |
3136 | |
3137 | @@ -762,7 +789,7 @@ |
3138 | self._add_result(t, n, s) |
3139 | |
3140 | frameworks = [] |
3141 | - if self.is_snap: |
3142 | + if self.is_snap1: |
3143 | if 'framework' in self.pkg_yaml: |
3144 | frameworks = [x.strip() for x in |
3145 | self.pkg_yaml['framework'].split(',')] |
3146 | @@ -841,6 +868,9 @@ |
3147 | |
3148 | def check_ignored(self): |
3149 | '''Check ignored fields''' |
3150 | + if not self.is_click and not self.is_snap1: |
3151 | + return |
3152 | + |
3153 | for app in sorted(self.security_apps): |
3154 | (f, m) = self._get_security_manifest(app) |
3155 | |
3156 | @@ -859,6 +889,9 @@ |
3157 | |
3158 | def check_redflag(self): |
3159 | '''Check redflag fields''' |
3160 | + if not self.is_click and not self.is_snap1: |
3161 | + return |
3162 | + |
3163 | for app in sorted(self.security_apps): |
3164 | (f, m) = self._get_security_manifest(app) |
3165 | |
3166 | @@ -881,6 +914,9 @@ |
3167 | |
3168 | def check_required(self): |
3169 | '''Check required fields''' |
3170 | + if not self.is_click and not self.is_snap1: |
3171 | + return |
3172 | + |
3173 | for app in sorted(self.security_apps): |
3174 | (f, m) = self._get_security_manifest(app) |
3175 | |
3176 | @@ -899,6 +935,9 @@ |
3177 | |
3178 | def check_apparmor_profile(self): |
3179 | '''Check apparmor-profile''' |
3180 | + if not self.is_click and not self.is_snap1: |
3181 | + return |
3182 | + |
3183 | for app in sorted(self.security_apps_profiles): |
3184 | (f, p) = self._get_security_profile(app) |
3185 | |
3186 | @@ -1145,7 +1184,10 @@ |
3187 | '''Verify click and security yaml are in sync (not including |
3188 | override) |
3189 | ''' |
3190 | - if not self.is_snap or \ |
3191 | + if not self.is_click and not self.is_snap1: |
3192 | + return |
3193 | + |
3194 | + if not self.is_snap1 or \ |
3195 | self.pkg_yaml['type'] in self.sec_skipped_types or \ |
3196 | float(self._pkgfmt_version()) > 15.04: |
3197 | return |
3198 | @@ -1187,7 +1229,10 @@ |
3199 | |
3200 | def check_security_yaml_override_and_click(self): |
3201 | '''Verify click and security yaml override are in sync''' |
3202 | - if not self.is_snap or \ |
3203 | + if not self.is_click and not self.is_snap1: |
3204 | + return |
3205 | + |
3206 | + if not self.is_snap1 or \ |
3207 | self.pkg_yaml['type'] in self.sec_skipped_types or \ |
3208 | float(self._pkgfmt_version()) > 15.04: |
3209 | return |
3210 | @@ -1226,7 +1271,10 @@ |
3211 | |
3212 | def check_security_yaml_override(self): |
3213 | '''Verify security yaml override''' |
3214 | - if not self.is_snap: |
3215 | + if not self.is_click and not self.is_snap1: |
3216 | + return |
3217 | + |
3218 | + if not self.is_snap1: |
3219 | return |
3220 | |
3221 | for exe_t in ['services', 'binaries']: |
3222 | @@ -1288,7 +1336,10 @@ |
3223 | |
3224 | def check_security_yaml_policy(self): |
3225 | '''Verify security yaml policy''' |
3226 | - if not self.is_snap: |
3227 | + if not self.is_click and not self.is_snap1: |
3228 | + return |
3229 | + |
3230 | + if not self.is_snap1: |
3231 | return |
3232 | |
3233 | for exe_t in ['services', 'binaries']: |
3234 | @@ -1330,7 +1381,10 @@ |
3235 | |
3236 | def check_security_yaml_combinations(self): |
3237 | '''Verify security yaml uses valid combinations''' |
3238 | - if not self.is_snap or self.pkg_yaml['type'] in self.sec_skipped_types: |
3239 | + if not self.is_click and not self.is_snap1: |
3240 | + return |
3241 | + |
3242 | + if not self.is_snap1 or self.pkg_yaml['type'] in self.sec_skipped_types: |
3243 | return |
3244 | |
3245 | for exe_t in ['services', 'binaries']: |
3246 | @@ -1367,7 +1421,10 @@ |
3247 | |
3248 | def check_security_template(self): |
3249 | '''Check snap security-template''' |
3250 | - if not self.is_snap or self.pkg_yaml['type'] in self.sec_skipped_types: |
3251 | + if not self.is_click and not self.is_snap1: |
3252 | + return |
3253 | + |
3254 | + if not self.is_snap1 or self.pkg_yaml['type'] in self.sec_skipped_types: |
3255 | return |
3256 | |
3257 | for exe_t in ['services', 'binaries']: |
3258 | @@ -1421,7 +1478,10 @@ |
3259 | |
3260 | def check_security_caps(self): |
3261 | '''Check snap caps''' |
3262 | - if not self.is_snap or self.pkg_yaml['type'] in self.sec_skipped_types: |
3263 | + if not self.is_click and not self.is_snap1: |
3264 | + return |
3265 | + |
3266 | + if not self.is_snap1 or self.pkg_yaml['type'] in self.sec_skipped_types: |
3267 | return |
3268 | |
3269 | for exe_t in ['services', 'binaries']: |
3270 | @@ -1475,6 +1535,9 @@ |
3271 | |
3272 | def check_template_online_accounts_provider(self): |
3273 | '''Check template for online accounts account-provider''' |
3274 | + if not self.is_click and not self.is_snap1: |
3275 | + return |
3276 | + |
3277 | if self._pkgfmt_type() == "snap" and \ |
3278 | float(self._pkgfmt_version()) > 15.04: |
3279 | return |
3280 | @@ -1493,6 +1556,9 @@ |
3281 | |
3282 | def check_template_online_accounts_qml_plugin(self): |
3283 | '''Check template for online accounts account-qml-plugin''' |
3284 | + if not self.is_click and not self.is_snap1: |
3285 | + return |
3286 | + |
3287 | if self._pkgfmt_type() == "snap" and \ |
3288 | float(self._pkgfmt_version()) > 15.04: |
3289 | return |
3290 | @@ -1511,6 +1577,9 @@ |
3291 | |
3292 | def check_apparmor_profile_name_length(self): |
3293 | '''Check AppArmor profile name length''' |
3294 | + if not self.is_click and not self.is_snap1: |
3295 | + return |
3296 | + |
3297 | # There are quite a few kernel interfaces that can cause problems with |
3298 | # long profile names. These are outlined in |
3299 | # https://launchpad.net/bugs/1499544. The big issue is that the audit |
3300 | |
3301 | === modified file 'clickreviews/cr_skeleton.py' |
3302 | --- clickreviews/cr_skeleton.py 2015-08-18 16:05:06 +0000 |
3303 | +++ clickreviews/cr_skeleton.py 2016-02-09 21:28:10 +0000 |
3304 | @@ -34,11 +34,17 @@ |
3305 | ClickReview.__init__(self, fn, "skeleton", peer_hooks=peer_hooks, |
3306 | overrides=overrides) |
3307 | |
3308 | + if not self.is_click and not self.is_snap1: |
3309 | + return |
3310 | + |
3311 | # If not a hooks test, skip the above and omit peer_hooks like so: |
3312 | # ClickReview.__init__(self, fn, "skeleton") |
3313 | |
3314 | def check_foo(self): |
3315 | '''Check foo''' |
3316 | + if not self.is_click and not self.is_snap1: |
3317 | + return |
3318 | + |
3319 | t = 'info' |
3320 | n = self._get_check_name('foo') |
3321 | s = "OK" |
3322 | @@ -49,6 +55,9 @@ |
3323 | |
3324 | def check_bar(self): |
3325 | '''Check bar''' |
3326 | + if not self.is_click and not self.is_snap1: |
3327 | + return |
3328 | + |
3329 | t = 'info' |
3330 | n = self._get_check_name('bar') |
3331 | s = "OK" |
3332 | @@ -59,6 +68,9 @@ |
3333 | |
3334 | def check_baz(self): |
3335 | '''Check baz''' |
3336 | + if not self.is_click and not self.is_snap1: |
3337 | + return |
3338 | + |
3339 | n = self._get_check_name('baz') |
3340 | self._add_result('warn', n, 'TODO', link="http://example.com") |
3341 | |
3342 | |
3343 | === modified file 'clickreviews/cr_systemd.py' |
3344 | --- clickreviews/cr_systemd.py 2015-11-11 16:05:23 +0000 |
3345 | +++ clickreviews/cr_systemd.py 2016-02-09 21:28:10 +0000 |
3346 | @@ -27,6 +27,12 @@ |
3347 | # systemd isn't implemented as a hook any more so don't setup peerhooks |
3348 | ClickReview.__init__(self, fn, "snappy-systemd", overrides=overrides) |
3349 | |
3350 | + self.systemd_files = dict() # click-show-files and tests |
3351 | + self.systemd = dict() |
3352 | + |
3353 | + if not self.is_snap1: |
3354 | + return |
3355 | + |
3356 | # snappy-systemd currently only allows specifying: |
3357 | # - start (required) |
3358 | # - description (required) |
3359 | @@ -49,10 +55,7 @@ |
3360 | 'ports' |
3361 | ] + self.snappy_exe_security |
3362 | |
3363 | - self.systemd_files = dict() # click-show-files and tests |
3364 | - self.systemd = dict() |
3365 | - |
3366 | - if self.is_snap and 'services' in self.pkg_yaml: |
3367 | + if self.is_snap1 and 'services' in self.pkg_yaml: |
3368 | if len(self.pkg_yaml['services']) == 0: |
3369 | error("package.yaml malformed: 'services' is empty") |
3370 | for service in self.pkg_yaml['services']: |
3371 | @@ -91,7 +94,7 @@ |
3372 | |
3373 | def check_snappy_required(self): |
3374 | '''Check for package.yaml required fields''' |
3375 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3376 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3377 | return |
3378 | self._verify_required(self._create_dict(self.pkg_yaml['services']), |
3379 | 'package_yaml') |
3380 | @@ -141,7 +144,7 @@ |
3381 | |
3382 | def check_snappy_optional(self): |
3383 | '''Check snappy packate.yaml optional fields''' |
3384 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3385 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3386 | return |
3387 | self._verify_optional(self._create_dict(self.pkg_yaml['services']), |
3388 | 'package_yaml') |
3389 | @@ -168,7 +171,7 @@ |
3390 | |
3391 | def check_snappy_unknown(self): |
3392 | '''Check snappy package.yaml unknown fields''' |
3393 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3394 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3395 | return |
3396 | self._verify_unknown(self._create_dict(self.pkg_yaml['services']), |
3397 | 'package_yaml') |
3398 | @@ -195,7 +198,7 @@ |
3399 | |
3400 | def check_snappy_service_description(self): |
3401 | '''Check snappy package.yaml description''' |
3402 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3403 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3404 | return |
3405 | self._verify_service_description(self._create_dict( |
3406 | self.pkg_yaml['services']), |
3407 | @@ -226,21 +229,21 @@ |
3408 | |
3409 | def check_snappy_service_start(self): |
3410 | '''Check snappy package.yaml start''' |
3411 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3412 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3413 | return |
3414 | self._verify_entry(self._create_dict(self.pkg_yaml['services']), |
3415 | 'start', 'package_yaml') |
3416 | |
3417 | def check_snappy_service_stop(self): |
3418 | '''Check snappy package.yaml stop''' |
3419 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3420 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3421 | return |
3422 | self._verify_entry(self._create_dict(self.pkg_yaml['services']), |
3423 | 'stop', 'package_yaml') |
3424 | |
3425 | def check_snappy_service_poststop(self): |
3426 | '''Check snappy package.yaml poststop''' |
3427 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3428 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3429 | return |
3430 | self._verify_entry(self._create_dict(self.pkg_yaml['services']), |
3431 | 'poststop', 'package_yaml') |
3432 | @@ -282,7 +285,7 @@ |
3433 | |
3434 | def check_snappy_service_stop_timeout(self): |
3435 | '''Check snappy package.yaml stop-timeout''' |
3436 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3437 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3438 | return |
3439 | self._verify_service_stop_timeout(self._create_dict( |
3440 | self.pkg_yaml['services']), |
3441 | @@ -343,7 +346,7 @@ |
3442 | |
3443 | def check_snappy_service_bus_name(self): |
3444 | '''Check snappy package.yaml bus-name''' |
3445 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3446 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3447 | return |
3448 | is_framework = False |
3449 | if 'type' in self.pkg_yaml and self.pkg_yaml['type'] == 'framework': |
3450 | @@ -455,7 +458,7 @@ |
3451 | |
3452 | def check_snappy_service_ports(self): |
3453 | '''Check snappy package.yaml ports''' |
3454 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3455 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3456 | return |
3457 | self._verify_service_ports(self.pkg_yaml['name'], |
3458 | self._create_dict( |
3459 | @@ -512,7 +515,7 @@ |
3460 | |
3461 | def check_snappy_service_listen_stream(self): |
3462 | '''Check snappy package.yaml listen-stream''' |
3463 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3464 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3465 | return |
3466 | |
3467 | self._verify_service_listen_stream(self.pkg_yaml['name'], |
3468 | @@ -522,7 +525,7 @@ |
3469 | |
3470 | def check_snappy_service_socket_user(self): |
3471 | '''Check snappy package.yaml socket-user''' |
3472 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3473 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3474 | return |
3475 | |
3476 | my_dict = self._create_dict(self.pkg_yaml['services']) |
3477 | @@ -557,7 +560,7 @@ |
3478 | |
3479 | def check_snappy_service_socket_group(self): |
3480 | '''Check snappy package.yaml socket-group''' |
3481 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3482 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3483 | return |
3484 | |
3485 | my_dict = self._create_dict(self.pkg_yaml['services']) |
3486 | @@ -592,7 +595,7 @@ |
3487 | |
3488 | def check_snappy_service_socket(self): |
3489 | '''Check snappy package.yaml socket''' |
3490 | - if not self.is_snap or 'services' not in self.pkg_yaml: |
3491 | + if not self.is_snap1 or 'services' not in self.pkg_yaml: |
3492 | return |
3493 | |
3494 | my_dict = self._create_dict(self.pkg_yaml['services']) |
3495 | |
3496 | === modified file 'clickreviews/cr_tests.py' |
3497 | --- clickreviews/cr_tests.py 2016-02-01 17:29:56 +0000 |
3498 | +++ clickreviews/cr_tests.py 2016-02-09 21:28:10 +0000 |
3499 | @@ -25,13 +25,12 @@ |
3500 | from unittest import TestCase |
3501 | |
3502 | from clickreviews.cr_lint import MINIMUM_CLICK_FRAMEWORK_VERSION |
3503 | -import clickreviews.cr_common as cr_common |
3504 | +import clickreviews.common as common |
3505 | |
3506 | # These should be set in the test cases |
3507 | TEST_CONTROL = "" |
3508 | TEST_MANIFEST = "" |
3509 | TEST_PKG_YAML = "" |
3510 | -TEST_SNAP_YAML = "" |
3511 | TEST_HASHES_YAML = "" |
3512 | TEST_README_MD = "" |
3513 | TEST_SECURITY = dict() |
3514 | @@ -78,11 +77,6 @@ |
3515 | return io.StringIO(TEST_PKG_YAML) |
3516 | |
3517 | |
3518 | -def _extract_snap_yaml(self): |
3519 | - '''Pretend we read the package.yaml file''' |
3520 | - return io.StringIO(TEST_SNAP_YAML) |
3521 | - |
3522 | - |
3523 | def _extract_hashes_yaml(self): |
3524 | '''Pretend we read the hashes.yaml file''' |
3525 | return io.StringIO(TEST_HASHES_YAML) |
3526 | @@ -95,7 +89,7 @@ |
3527 | |
3528 | def _get_sha512sum(self, fn): |
3529 | '''Pretend we found performed a sha512''' |
3530 | - (rc, out) = cr_common.cmd(['sha512sum', os.path.realpath(__file__)]) |
3531 | + (rc, out) = common.cmd(['sha512sum', os.path.realpath(__file__)]) |
3532 | if rc != 0: |
3533 | return None |
3534 | return out.split()[0] |
3535 | @@ -270,12 +264,20 @@ |
3536 | return (TEST_PKGFMT_TYPE == "snap" and float(TEST_PKGFMT_VERSION) > 15.04) |
3537 | |
3538 | |
3539 | +def _detect_package(self, fn): |
3540 | + '''Pretend we detected the package''' |
3541 | + ver = 1 |
3542 | + if TEST_PKGFMT_TYPE == "snap" and TEST_PKGFMT_VERSION != "15.04": |
3543 | + ver = 2 |
3544 | + return (TEST_PKGFMT_TYPE, ver) |
3545 | + |
3546 | + |
3547 | def create_patches(): |
3548 | # http://docs.python.org/3.4/library/unittest.mock-examples.html |
3549 | # Mock patching. Don't use decorators but instead patch in setUp() of the |
3550 | # child. |
3551 | patches = [] |
3552 | - patches.append(patch('clickreviews.cr_common.ClickReview._check_path_exists', |
3553 | + patches.append(patch('clickreviews.common.Review._check_package_exists', |
3554 | _mock_func)) |
3555 | patches.append(patch( |
3556 | 'clickreviews.cr_common.ClickReview._extract_control_file', |
3557 | @@ -287,45 +289,34 @@ |
3558 | 'clickreviews.cr_common.ClickReview._extract_package_yaml', |
3559 | _extract_package_yaml)) |
3560 | patches.append(patch( |
3561 | - 'clickreviews.cr_common.ClickReview._extract_snap_yaml', |
3562 | - _extract_snap_yaml)) |
3563 | - patches.append(patch( |
3564 | 'clickreviews.cr_common.ClickReview._extract_hashes_yaml', |
3565 | _extract_hashes_yaml)) |
3566 | - patches.append(patch( |
3567 | - 'clickreviews.cr_common.ClickReview._path_join', |
3568 | - _path_join)) |
3569 | - patches.append(patch( |
3570 | - 'clickreviews.cr_common.ClickReview._get_sha512sum', |
3571 | - _get_sha512sum)) |
3572 | - patches.append(patch( |
3573 | - 'clickreviews.cr_common.ClickReview._extract_statinfo', |
3574 | - _extract_statinfo)) |
3575 | + patches.append(patch('clickreviews.common.Review._path_join', _path_join)) |
3576 | + patches.append(patch( |
3577 | + 'clickreviews.common.Review._get_sha512sum', _get_sha512sum)) |
3578 | + patches.append(patch( |
3579 | + 'clickreviews.common.Review._extract_statinfo', _extract_statinfo)) |
3580 | patches.append(patch( |
3581 | 'clickreviews.cr_common.ClickReview._extract_click_frameworks', |
3582 | _extract_click_frameworks)) |
3583 | - patches.append(patch('clickreviews.cr_common.unpack_click', _mock_func)) |
3584 | - patches.append(patch('clickreviews.cr_common.raw_unpack_pkg', _mock_func)) |
3585 | - patches.append(patch('clickreviews.cr_common.ClickReview._list_all_files', |
3586 | + patches.append(patch('clickreviews.common.unpack_pkg', _mock_func)) |
3587 | + patches.append(patch('clickreviews.common.raw_unpack_pkg', _mock_func)) |
3588 | + patches.append(patch('clickreviews.common.detect_package', |
3589 | + _detect_package)) |
3590 | + patches.append(patch('clickreviews.common.Review._list_all_files', |
3591 | _mock_func)) |
3592 | patches.append(patch( |
3593 | - 'clickreviews.cr_common.ClickReview._list_all_compiled_binaries', |
3594 | - _mock_func)) |
3595 | + 'clickreviews.common.Review._list_all_compiled_binaries', _mock_func)) |
3596 | |
3597 | # lint overrides |
3598 | patches.append(patch( |
3599 | 'clickreviews.cr_lint.ClickReviewLint._list_control_files', |
3600 | _mock_func)) |
3601 | - patches.append(patch('clickreviews.cr_lint.ClickReviewLint._list_all_files', |
3602 | - _mock_func)) |
3603 | - patches.append(patch( |
3604 | - 'clickreviews.cr_lint.ClickReview._list_all_compiled_binaries', |
3605 | - _mock_func)) |
3606 | patches.append(patch( |
3607 | 'clickreviews.cr_lint.ClickReviewLint._extract_readme_md', |
3608 | _extract_readme_md)) |
3609 | patches.append(patch( |
3610 | - 'clickreviews.cr_lint.ClickReviewLint._check_innerpath_executable', |
3611 | + 'clickreviews.common.Review._check_innerpath_executable', |
3612 | _check_innerpath_executable)) |
3613 | |
3614 | # security overrides |
3615 | @@ -398,12 +389,11 @@ |
3616 | _has_framework_in_metadir)) |
3617 | |
3618 | # pkgfmt |
3619 | - patches.append(patch("clickreviews.cr_common.ClickReview._pkgfmt_type", |
3620 | + patches.append(patch("clickreviews.common.Review._pkgfmt_type", |
3621 | _pkgfmt_type)) |
3622 | - patches.append(patch("clickreviews.cr_common.ClickReview._pkgfmt_version", |
3623 | + patches.append(patch("clickreviews.common.Review._pkgfmt_version", |
3624 | _pkgfmt_version)) |
3625 | - patches.append(patch("clickreviews.cr_common.is_squashfs", _is_squashfs)) |
3626 | - patches.append(patch("clickreviews.cr_lint.is_squashfs", _is_squashfs)) |
3627 | + patches.append(patch("clickreviews.common.is_squashfs", _is_squashfs)) |
3628 | |
3629 | return patches |
3630 | |
3631 | @@ -457,8 +447,6 @@ |
3632 | [self.test_control['Architecture']]) |
3633 | self._update_test_pkg_yaml() |
3634 | |
3635 | - self.test_snap_yaml = dict() |
3636 | - |
3637 | self.test_hashes_yaml = dict() |
3638 | self._update_test_hashes_yaml() |
3639 | |
3640 | @@ -577,12 +565,6 @@ |
3641 | default_flow_style=False, |
3642 | indent=4) |
3643 | |
3644 | - def _update_test_snap_yaml(self): |
3645 | - global TEST_SNAP_YAML |
3646 | - TEST_SNAP_YAML = yaml.dump(self.test_snap_yaml, |
3647 | - default_flow_style=False, |
3648 | - indent=4) |
3649 | - |
3650 | def _update_test_hashes_yaml(self): |
3651 | global TEST_HASHES_YAML |
3652 | TEST_HASHES_YAML = yaml.dump(self.test_hashes_yaml, |
3653 | @@ -811,16 +793,6 @@ |
3654 | self.test_pkg_yaml[key] = value |
3655 | self._update_test_pkg_yaml() |
3656 | |
3657 | - def set_test_snap_yaml(self, key, value): |
3658 | - '''Set key in meta/snap.yaml to value. If value is None, remove |
3659 | - key''' |
3660 | - if value is None: |
3661 | - if key in self.test_snap_yaml: |
3662 | - self.test_snap_yaml.pop(key, None) |
3663 | - else: |
3664 | - self.test_snap_yaml[key] = value |
3665 | - self._update_test_snap_yaml() |
3666 | - |
3667 | def set_test_hashes_yaml(self, yaml): |
3668 | '''Set hashes.yaml to yaml''' |
3669 | self.test_hashes_yaml = yaml |
3670 | @@ -1164,8 +1136,6 @@ |
3671 | TEST_MANIFEST = "" |
3672 | global TEST_PKG_YAML |
3673 | TEST_PKG_YAML = "" |
3674 | - global TEST_SNAP_YAML |
3675 | - TEST_SNAP_YAML = "" |
3676 | global TEST_HASHES_YAML |
3677 | TEST_HASHES_YAML = "" |
3678 | global TEST_README_MD |
3679 | @@ -1208,4 +1178,4 @@ |
3680 | TEST_PKGFMT_VERSION = "0.4" |
3681 | |
3682 | self._reset_test_data() |
3683 | - cr_common.recursive_rm(self.desktop_tmpdir) |
3684 | + common.recursive_rm(self.desktop_tmpdir) |
3685 | |
3686 | === modified file 'clickreviews/cr_url_dispatcher.py' |
3687 | --- clickreviews/cr_url_dispatcher.py 2015-12-04 14:17:55 +0000 |
3688 | +++ clickreviews/cr_url_dispatcher.py 2016-02-09 21:28:10 +0000 |
3689 | @@ -35,6 +35,9 @@ |
3690 | ClickReview.__init__(self, fn, "url_dispatcher", peer_hooks=peer_hooks, |
3691 | overrides=overrides) |
3692 | |
3693 | + if not self.is_click and not self.is_snap1: |
3694 | + return |
3695 | + |
3696 | self.required_keys = ['protocol'] |
3697 | self.optional_keys = ['domain-suffix'] |
3698 | |
3699 | @@ -86,6 +89,9 @@ |
3700 | |
3701 | def check_required(self): |
3702 | '''Check url-dispatcher required fields''' |
3703 | + if not self.is_click and not self.is_snap1: |
3704 | + return |
3705 | + |
3706 | for app in sorted(self.url_dispatcher): |
3707 | for r in self.required_keys: |
3708 | found = False |
3709 | @@ -114,6 +120,9 @@ |
3710 | |
3711 | def check_optional(self): |
3712 | '''Check url-dispatcher optional fields''' |
3713 | + if not self.is_click and not self.is_snap1: |
3714 | + return |
3715 | + |
3716 | for app in sorted(self.url_dispatcher): |
3717 | for o in self.optional_keys: |
3718 | found = False |
3719 | @@ -141,6 +150,9 @@ |
3720 | |
3721 | def check_unknown(self): |
3722 | '''Check url-dispatcher unknown fields''' |
3723 | + if not self.is_click and not self.is_snap1: |
3724 | + return |
3725 | + |
3726 | for app in sorted(self.url_dispatcher): |
3727 | unknown = [] |
3728 | for entry in self.url_dispatcher[app]: |
3729 | |
3730 | === modified file 'clickreviews/modules.py' |
3731 | --- clickreviews/modules.py 2015-08-14 08:21:49 +0000 |
3732 | +++ clickreviews/modules.py 2016-02-09 21:28:10 +0000 |
3733 | @@ -4,7 +4,9 @@ |
3734 | import os |
3735 | import pkgutil |
3736 | |
3737 | -IRRELEVANT_MODULES = ['cr_common', 'cr_tests', 'cr_skeleton'] |
3738 | +IRRELEVANT_MODULES = ['cr_common', 'cr_tests', 'cr_skeleton', |
3739 | + 'sr_common', 'sr_tests', 'sr_skeleton', |
3740 | + 'common'] |
3741 | |
3742 | |
3743 | def narrow_down_modules(modules): |
3744 | @@ -16,7 +18,8 @@ |
3745 | for module in modules: |
3746 | module_name = os.path.basename(module).replace('.py', '') |
3747 | if module_name not in IRRELEVANT_MODULES and \ |
3748 | - module_name.startswith('cr_'): |
3749 | + (module_name.startswith('cr_') or |
3750 | + module_name.startswith('sr_')): |
3751 | relevant_modules += [module] |
3752 | return relevant_modules |
3753 | |
3754 | @@ -28,7 +31,7 @@ |
3755 | are not relevant. |
3756 | |
3757 | Basically we look at all the ones which are |
3758 | - derived from cr_common, where we can later on |
3759 | + derived from [cs]r_common, where we can later on |
3760 | instantiate a *Review* object and run the |
3761 | necessary checks. |
3762 | ''' |
3763 | @@ -49,14 +52,14 @@ |
3764 | |
3765 | classes = inspect.getmembers(module, inspect.isclass) |
3766 | |
3767 | - def find_cr_class(a): |
3768 | - return a[0].startswith('Click') and \ |
3769 | + def find_test_class(a): |
3770 | + return (a[0].startswith('Click') or a[0].startswith('Snap')) and \ |
3771 | not a[0].endswith('Exception') and \ |
3772 | a[1].__module__ == module_name |
3773 | - cr_class = list(filter(find_cr_class, classes)) |
3774 | - if not cr_class: |
3775 | + test_class = list(filter(find_test_class, classes)) |
3776 | + if not test_class: |
3777 | return None |
3778 | - init_object = getattr(module, cr_class[0][0]) |
3779 | + init_object = getattr(module, test_class[0][0]) |
3780 | return init_object |
3781 | |
3782 | |
3783 | |
3784 | === added file 'clickreviews/sr_common.py' |
3785 | --- clickreviews/sr_common.py 1970-01-01 00:00:00 +0000 |
3786 | +++ clickreviews/sr_common.py 2016-02-09 21:28:10 +0000 |
3787 | @@ -0,0 +1,124 @@ |
3788 | +'''sr_common.py: common classes and functions''' |
3789 | +# |
3790 | +# Copyright (C) 2013-2016 Canonical Ltd. |
3791 | +# |
3792 | +# This program is free software: you can redistribute it and/or modify |
3793 | +# it under the terms of the GNU General Public License as published by |
3794 | +# the Free Software Foundation; version 3 of the License. |
3795 | +# |
3796 | +# This program is distributed in the hope that it will be useful, |
3797 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3798 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3799 | +# GNU General Public License for more details. |
3800 | +# |
3801 | +# You should have received a copy of the GNU General Public License |
3802 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3803 | + |
3804 | +from __future__ import print_function |
3805 | +import os |
3806 | +import re |
3807 | +import yaml |
3808 | + |
3809 | + |
3810 | +from clickreviews.common import( |
3811 | + Review, |
3812 | + ReviewException, |
3813 | + error, |
3814 | + open_file_read, |
3815 | +) |
3816 | + |
3817 | + |
3818 | +# |
3819 | +# Utility classes |
3820 | +# |
3821 | +class SnapReviewException(ReviewException): |
3822 | + '''This class represents SnapReview exceptions''' |
3823 | + |
3824 | + |
3825 | +class SnapReview(Review): |
3826 | + '''This class represents snap reviews''' |
3827 | + snappy_required = ["name", |
3828 | + "version", |
3829 | + ] |
3830 | + # optional snappy fields here (may be required by appstore) |
3831 | + snappy_optional = ["apps", |
3832 | + "architectures", |
3833 | + "description", |
3834 | + "frameworks", |
3835 | + "license-agreement", |
3836 | + "license-version", |
3837 | + "summary", |
3838 | + "type", |
3839 | + "uses", |
3840 | + ] |
3841 | + |
3842 | + apps_required = ['command'] |
3843 | + apps_optional = ['daemon', |
3844 | + 'stop', |
3845 | + 'stop-timeout', |
3846 | + 'restart-condition', |
3847 | + 'poststop', |
3848 | + 'uses', |
3849 | + 'ports', |
3850 | + 'bus-name', |
3851 | + 'socket', |
3852 | + 'listen-stream', |
3853 | + 'socket-user', |
3854 | + 'socket-group', |
3855 | + ] |
3856 | + |
3857 | + # https://docs.google.com/document/d/14kTzvPL8WchnzDpKbuxSKlHklzofRGl2g_2iNfIStJU/edit#heading=h.smqdkiy9hs81 |
3858 | + # 'uses': |
3859 | + # 'type': name |
3860 | + # 'attrib-name': <type> |
3861 | + # skill_types lists types and the valid attribute names for the type with |
3862 | + # the valid python type for the attribute (eg, [], '', {}, etc). |
3863 | + # These skill_types are likely going to change based on release, but for |
3864 | + # now, this is fine. |
3865 | + skill_types = {'migration-skill': {'caps': [], |
3866 | + 'security-override': {}, |
3867 | + 'security-policy': {}, |
3868 | + 'security-template': "", |
3869 | + } |
3870 | + } |
3871 | + |
3872 | + def __init__(self, fn, review_type, overrides=None): |
3873 | + Review.__init__(self, fn, review_type, overrides=overrides) |
3874 | + |
3875 | + if not self.is_snap2: |
3876 | + return |
3877 | + |
3878 | + snap_yaml = self._extract_snap_yaml() |
3879 | + if snap_yaml: |
3880 | + try: |
3881 | + self.snap_yaml = yaml.safe_load(snap_yaml) |
3882 | + except Exception: |
3883 | + error("Could not load snap.yaml. Is it properly formatted?") |
3884 | + |
3885 | + # default to 'app' |
3886 | + if 'type' not in self.snap_yaml: |
3887 | + self.snap_yaml['type'] = 'app' |
3888 | + |
3889 | + if 'architectures' in self.snap_yaml: |
3890 | + self.pkg_arch = self.snap_yaml['architectures'] |
3891 | + else: |
3892 | + self.pkg_arch = ['all'] |
3893 | + |
3894 | + self.is_snap_gadget = False |
3895 | + if 'type' in self.snap_yaml and self.snap_yaml['type'] == 'gadget': |
3896 | + self.is_snap_gadget = True |
3897 | + |
3898 | + def _extract_snap_yaml(self): |
3899 | + '''Extract and read the snappy 16.04 snap.yaml''' |
3900 | + y = os.path.join(self.unpack_dir, "meta/snap.yaml") |
3901 | + if not os.path.isfile(y): |
3902 | + return None # snappy packaging is still optional |
3903 | + return open_file_read(y) |
3904 | + |
3905 | + def _verify_pkgname(self, n): |
3906 | + '''Verify package name''' |
3907 | + pat = re.compile(r'^[a-z0-9][a-z0-9+-]+$') |
3908 | + |
3909 | + if pat.search(n): |
3910 | + return True |
3911 | + return False |
3912 | |
3913 | === added file 'clickreviews/sr_lint.py' |
3914 | --- clickreviews/sr_lint.py 1970-01-01 00:00:00 +0000 |
3915 | +++ clickreviews/sr_lint.py 2016-02-09 21:28:10 +0000 |
3916 | @@ -0,0 +1,1150 @@ |
3917 | +'''sr_lint.py: lint checks''' |
3918 | +# |
3919 | +# Copyright (C) 2013-2016 Canonical Ltd. |
3920 | +# |
3921 | +# This program is free software: you can redistribute it and/or modify |
3922 | +# it under the terms of the GNU General Public License as published by |
3923 | +# the Free Software Foundation; version 3 of the License. |
3924 | +# |
3925 | +# This program is distributed in the hope that it will be useful, |
3926 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3927 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3928 | +# GNU General Public License for more details. |
3929 | +# |
3930 | +# You should have received a copy of the GNU General Public License |
3931 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3932 | + |
3933 | +from __future__ import print_function |
3934 | +from clickreviews.frameworks import Frameworks |
3935 | +from clickreviews.sr_common import ( |
3936 | + SnapReview, |
3937 | +) |
3938 | +from clickreviews.common import ( |
3939 | + is_squashfs, |
3940 | +) |
3941 | +import os |
3942 | +import re |
3943 | + |
3944 | + |
3945 | +class SnapReviewLint(SnapReview): |
3946 | + '''This class represents snap lint reviews''' |
3947 | + |
3948 | + def __init__(self, fn, overrides=None): |
3949 | + '''Set up the class.''' |
3950 | + SnapReview.__init__(self, fn, "lint-snap-v2", overrides=overrides) |
3951 | + if not self.is_snap2: |
3952 | + return |
3953 | + |
3954 | + self.valid_compiled_architectures = ['armhf', |
3955 | + 'i386', |
3956 | + 'amd64', |
3957 | + 'arm64', |
3958 | + ] |
3959 | + self.valid_architectures = ['all'] + self.valid_compiled_architectures |
3960 | + self.vcs_files = ['.bzr*', |
3961 | + # '.excludes', # autogenerated by SDK |
3962 | + '.git*', |
3963 | + '.idea', |
3964 | + '.svn*', |
3965 | + '.hg', |
3966 | + '.project', |
3967 | + 'CVS*', |
3968 | + 'RCS*' |
3969 | + ] |
3970 | + |
3971 | + self._list_all_compiled_binaries() |
3972 | + |
3973 | + # Valid values for 'type' in packaging yaml |
3974 | + # - app |
3975 | + # - framework |
3976 | + # - kernel |
3977 | + # - gadget |
3978 | + # - os |
3979 | + self.valid_snap_types = ['app', |
3980 | + 'framework', |
3981 | + 'kernel', |
3982 | + 'gadget', |
3983 | + 'os', |
3984 | + ] |
3985 | + self.redflagged_snap_types = ['framework', |
3986 | + 'kernel', |
3987 | + 'gadget', |
3988 | + 'os', |
3989 | + ] |
3990 | + |
3991 | + def check_architectures(self): |
3992 | + '''Check architectures in snap.yaml is valid''' |
3993 | + if not self.is_snap2: |
3994 | + return |
3995 | + |
3996 | + t = 'info' |
3997 | + n = self._get_check_name('architecture_valid') |
3998 | + s = 'OK' |
3999 | + |
4000 | + key = 'architectures' |
4001 | + if key not in self.snap_yaml: |
4002 | + s = 'OK (%s not specified)' % key |
4003 | + self._add_result(t, n, s) |
4004 | + return |
4005 | + |
4006 | + if not isinstance(self.snap_yaml[key], list): |
4007 | + t = 'error' |
4008 | + s = "invalid %s entry: %s (not a list)" % (key, |
4009 | + self.snap_yaml[key]) |
4010 | + else: |
4011 | + bad_archs = [] |
4012 | + for arch in self.snap_yaml[key]: |
4013 | + if arch not in self.valid_architectures: |
4014 | + bad_archs.append(arch) |
4015 | + if len(bad_archs) > 0: |
4016 | + t = 'error' |
4017 | + s = "invalid multi architecture: %s" % ",".join(bad_archs) |
4018 | + self._add_result(t, n, s) |
4019 | + |
4020 | + def check_description(self): |
4021 | + '''Check description''' |
4022 | + if not self.is_snap2: |
4023 | + return |
4024 | + |
4025 | + key = 'description' |
4026 | + |
4027 | + t = 'info' |
4028 | + n = self._get_check_name('%s_present' % key) |
4029 | + s = 'OK' |
4030 | + if key not in self.snap_yaml: |
4031 | + s = 'OK (optional %s field not specified)' % key |
4032 | + self._add_result(t, n, s) |
4033 | + return |
4034 | + self._add_result(t, n, s) |
4035 | + |
4036 | + t = 'info' |
4037 | + n = self._get_check_name(key) |
4038 | + s = 'OK' |
4039 | + if not isinstance(self.snap_yaml[key], str): |
4040 | + t = 'error' |
4041 | + s = "invalid %s entry: %s (not a str)" % (key, self.snap_yaml[key]) |
4042 | + self._add_result(t, n, s) |
4043 | + return |
4044 | + elif len(self.snap_yaml[key]) < 1: |
4045 | + t = 'error' |
4046 | + s = "invalid %s entry (empty)" % (key) |
4047 | + elif len(self.snap_yaml[key]) < len(self.snap_yaml['name']): |
4048 | + t = 'info' |
4049 | + s = "%s is too short: '%s'" % (key, self.snap_yaml[key]) |
4050 | + self._add_result(t, n, s) |
4051 | + |
4052 | + def check_frameworks(self): |
4053 | + '''Check framework''' |
4054 | + if not self.is_snap2: |
4055 | + return |
4056 | + |
4057 | + key = 'frameworks' |
4058 | + |
4059 | + t = 'info' |
4060 | + n = self._get_check_name(key) |
4061 | + l = "http://askubuntu.com/questions/460512/what-framework-should-i-use-in-my-manifest-file" |
4062 | + |
4063 | + if key not in self.snap_yaml: |
4064 | + s = 'OK (%s not specified)' % key |
4065 | + self._add_result(t, n, s) |
4066 | + return |
4067 | + |
4068 | + if not isinstance(self.snap_yaml[key], list): |
4069 | + t = 'error' |
4070 | + s = "invalid %s entry: %s (not a list)" % (key, |
4071 | + self.snap_yaml[key]) |
4072 | + self._add_result(t, n, s) |
4073 | + return |
4074 | + elif len(self.snap_yaml[key]) < 1: |
4075 | + t = 'error' |
4076 | + s = "invalid %s entry (empty)" % (key) |
4077 | + self._add_result(t, n, s) |
4078 | + return |
4079 | + |
4080 | + framework_overrides = self.overrides.get('framework', {}) |
4081 | + frameworks = Frameworks(overrides=framework_overrides) |
4082 | + |
4083 | + for framework in self.snap_yaml[key]: |
4084 | + if framework in frameworks.AVAILABLE_FRAMEWORKS: |
4085 | + t = 'info' |
4086 | + s = 'OK' |
4087 | + self._add_result(t, n, s) |
4088 | + # If it's an available framework, we're done checking |
4089 | + return |
4090 | + elif framework in frameworks.DEPRECATED_FRAMEWORKS: |
4091 | + t = 'warn' |
4092 | + s = "'%s' is deprecated. Please use a newer framework" % \ |
4093 | + framework |
4094 | + self._add_result(t, n, s, l) |
4095 | + return |
4096 | + elif framework in frameworks.OBSOLETE_FRAMEWORKS: |
4097 | + t = 'error' |
4098 | + s = "'%s' is obsolete. Please use a newer framework" % \ |
4099 | + framework |
4100 | + self._add_result(t, n, s, l) |
4101 | + return |
4102 | + else: |
4103 | + # None of the above checks triggered, this is an unknown |
4104 | + # framework |
4105 | + t = 'error' |
4106 | + s = "'%s' is not a supported framework" % \ |
4107 | + framework |
4108 | + self._add_result(t, n, s, l) |
4109 | + |
4110 | + # TODO: verify this is a field |
4111 | + def check_license_agreement(self): |
4112 | + '''Check license-agreement''' |
4113 | + if not self.is_snap2: |
4114 | + return |
4115 | + |
4116 | + key = 'license-agreement' |
4117 | + |
4118 | + t = 'info' |
4119 | + n = self._get_check_name('%s_present' % key) |
4120 | + s = 'OK' |
4121 | + if key not in self.snap_yaml: |
4122 | + s = 'OK (optional %s field not specified)' % key |
4123 | + self._add_result(t, n, s) |
4124 | + return |
4125 | + self._add_result(t, n, s) |
4126 | + |
4127 | + t = 'info' |
4128 | + n = self._get_check_name(key) |
4129 | + s = 'OK' |
4130 | + if not isinstance(self.snap_yaml[key], str): |
4131 | + t = 'error' |
4132 | + s = "invalid %s entry: %s (not a str)" % (key, self.snap_yaml[key]) |
4133 | + self._add_result(t, n, s) |
4134 | + return |
4135 | + elif len(self.snap_yaml[key]) < 1: |
4136 | + t = 'error' |
4137 | + s = "invalid %s entry (empty)" % (key) |
4138 | + self._add_result(t, n, s) |
4139 | + return |
4140 | + self._add_result(t, n, s) |
4141 | + |
4142 | + def check_license_version(self): |
4143 | + '''license-version''' |
4144 | + if not self.is_snap2: |
4145 | + return |
4146 | + |
4147 | + key = 'license-version' |
4148 | + t = 'info' |
4149 | + n = self._get_check_name('%s_present' % key) |
4150 | + s = 'OK' |
4151 | + if key not in self.snap_yaml: |
4152 | + s = 'OK (optional %s field not specified)' % key |
4153 | + self._add_result(t, n, s) |
4154 | + return |
4155 | + self._add_result(t, n, s) |
4156 | + |
4157 | + t = 'info' |
4158 | + n = self._get_check_name(key) |
4159 | + s = 'OK' |
4160 | + if not isinstance(self.snap_yaml[key], str): |
4161 | + t = 'error' |
4162 | + s = "invalid %s entry: %s (not a str)" % (key, self.snap_yaml[key]) |
4163 | + self._add_result(t, n, s) |
4164 | + return |
4165 | + elif len(self.snap_yaml[key]) < 1: |
4166 | + t = 'error' |
4167 | + s = "invalid %s entry (empty)" % (key) |
4168 | + self._add_result(t, n, s) |
4169 | + return |
4170 | + self._add_result(t, n, s) |
4171 | + |
4172 | + def check_name(self): |
4173 | + '''Check package name''' |
4174 | + if not self.is_snap2: |
4175 | + return |
4176 | + |
4177 | + t = 'info' |
4178 | + n = self._get_check_name('name_valid') |
4179 | + s = 'OK' |
4180 | + if 'name' not in self.snap_yaml: |
4181 | + t = 'error' |
4182 | + s = "could not find 'name' in yaml" |
4183 | + elif not isinstance(self.snap_yaml['name'], str): |
4184 | + t = 'error' |
4185 | + s = "malformed 'name': %s (not a str)" % (self.snap_yaml['name']) |
4186 | + elif not self._verify_pkgname(self.snap_yaml['name']): |
4187 | + t = 'error' |
4188 | + s = "malformed 'name': '%s'" % self.snap_yaml['name'] |
4189 | + self._add_result(t, n, s) |
4190 | + |
4191 | + def check_summary(self): |
4192 | + '''Check summary''' |
4193 | + if not self.is_snap2: |
4194 | + return |
4195 | + |
4196 | + key = 'summary' |
4197 | + |
4198 | + t = 'info' |
4199 | + n = self._get_check_name('%s_present' % key) |
4200 | + s = 'OK' |
4201 | + if key not in self.snap_yaml: |
4202 | + s = 'OK (optional %s field not specified)' % key |
4203 | + self._add_result(t, n, s) |
4204 | + return |
4205 | + self._add_result(t, n, s) |
4206 | + |
4207 | + t = 'info' |
4208 | + n = self._get_check_name(key) |
4209 | + s = 'OK' |
4210 | + if not isinstance(self.snap_yaml[key], str): |
4211 | + t = 'error' |
4212 | + s = "invalid %s entry: %s (not a str)" % (key, self.snap_yaml[key]) |
4213 | + self._add_result(t, n, s) |
4214 | + return |
4215 | + elif len(self.snap_yaml[key]) < 1: |
4216 | + t = 'error' |
4217 | + s = "invalid %s entry (empty)" % (key) |
4218 | + elif len(self.snap_yaml[key]) < len(self.snap_yaml['name']): |
4219 | + t = 'info' |
4220 | + s = "%s is too short: '%s'" % (key, self.snap_yaml[key]) |
4221 | + self._add_result(t, n, s) |
4222 | + |
4223 | + def check_type(self): |
4224 | + '''Check type''' |
4225 | + if not self.is_snap2: |
4226 | + return |
4227 | + |
4228 | + t = 'info' |
4229 | + n = self._get_check_name('snap_type_valid') |
4230 | + s = 'OK' |
4231 | + if 'type' not in self.snap_yaml: |
4232 | + s = 'OK (skip missing)' |
4233 | + elif self.snap_yaml['type'] not in self.valid_snap_types: |
4234 | + t = 'error' |
4235 | + s = "unknown 'type': '%s'" % self.snap_yaml['type'] |
4236 | + self._add_result(t, n, s) |
4237 | + |
4238 | + def check_type_redflagged(self): |
4239 | + '''Check if type is redflagged''' |
4240 | + if not self.is_snap2: |
4241 | + return |
4242 | + |
4243 | + t = 'info' |
4244 | + n = self._get_check_name('snap_type_redflag') |
4245 | + s = "OK" |
4246 | + l = None |
4247 | + manual_review = False |
4248 | + if 'type' not in self.snap_yaml: |
4249 | + s = 'OK (skip missing)' |
4250 | + elif self.snap_yaml['type'] in self.redflagged_snap_types: |
4251 | + t = 'error' |
4252 | + s = "(NEEDS REVIEW) type '%s' not allowed" % self.snap_yaml['type'] |
4253 | + manual_review = True |
4254 | + if self.snap_yaml['type'] == "framework": |
4255 | + l = "https://developer.ubuntu.com/en/snappy/guides/frameworks/" |
4256 | + self._add_result(t, n, s, link=l, manual_review=manual_review) |
4257 | + |
4258 | + def check_version(self): |
4259 | + '''Check package version''' |
4260 | + if not self.is_snap2: |
4261 | + return |
4262 | + |
4263 | + t = 'info' |
4264 | + n = self._get_check_name('version_valid') |
4265 | + s = 'OK' |
4266 | + if 'version' not in self.snap_yaml: |
4267 | + t = 'error' |
4268 | + s = "could not find 'version' in yaml" |
4269 | + elif not self._verify_pkgversion(self.snap_yaml['version']): |
4270 | + t = 'error' |
4271 | + s = "malformed 'version': '%s'" % self.snap_yaml['version'] |
4272 | + self._add_result(t, n, s) |
4273 | + |
4274 | + def check_config(self): |
4275 | + '''Check config''' |
4276 | + if not self.is_snap2: |
4277 | + return |
4278 | + |
4279 | + fn = os.path.join(self.unpack_dir, 'meta/hooks/config') |
4280 | + if fn not in self.pkg_files: |
4281 | + return |
4282 | + |
4283 | + t = 'info' |
4284 | + n = self._get_check_name('config_hook_executable') |
4285 | + s = 'OK' |
4286 | + if not self._check_innerpath_executable(fn): |
4287 | + t = 'error' |
4288 | + s = 'meta/hooks/config is not executable' |
4289 | + self._add_result(t, n, s) |
4290 | + |
4291 | + def check_icon(self): |
4292 | + '''Check icon''' |
4293 | + # see docs/meta.md and docs/gadget.md |
4294 | + if not self.is_snap2: |
4295 | + return |
4296 | + |
4297 | + t = 'info' |
4298 | + n = self._get_check_name('icon_present') |
4299 | + s = 'OK' |
4300 | + if 'icon' not in self.snap_yaml: |
4301 | + s = 'Skipped, optional icon not present' |
4302 | + self._add_result(t, n, s) |
4303 | + return |
4304 | + elif 'type' in self.snap_yaml and self.snap_yaml['type'] != "gadget": |
4305 | + t = 'warn' |
4306 | + s = 'icon only used with gadget snaps' |
4307 | + self._add_result(t, n, s) |
4308 | + return |
4309 | + self._add_result(t, n, s) |
4310 | + |
4311 | + t = 'info' |
4312 | + n = self._get_check_name('icon_empty') |
4313 | + s = 'OK' |
4314 | + if len(self.snap_yaml['icon']) == 0: |
4315 | + t = 'error' |
4316 | + s = "icon entry is empty" |
4317 | + return |
4318 | + self._add_result(t, n, s) |
4319 | + |
4320 | + t = 'info' |
4321 | + n = self._get_check_name('icon_absolute_path') |
4322 | + s = 'OK' |
4323 | + if self.snap_yaml['icon'].startswith('/'): |
4324 | + t = 'error' |
4325 | + s = "icon entry '%s' should not specify absolute path" % \ |
4326 | + self.snap_yaml['icon'] |
4327 | + self._add_result(t, n, s) |
4328 | + |
4329 | + t = 'info' |
4330 | + n = self._get_check_name('icon_exists') |
4331 | + s = 'OK' |
4332 | + fn = self._path_join(self.unpack_dir, self.snap_yaml['icon']) |
4333 | + if fn not in self.pkg_files: |
4334 | + t = 'error' |
4335 | + s = "icon entry '%s' does not exist" % self.snap_yaml['icon'] |
4336 | + self._add_result(t, n, s) |
4337 | + |
4338 | + def check_unknown_entries(self): |
4339 | + '''Check for any unknown fields''' |
4340 | + if not self.is_snap2: |
4341 | + return |
4342 | + |
4343 | + t = 'info' |
4344 | + n = self._get_check_name('unknown_field') |
4345 | + s = 'OK' |
4346 | + unknown = [] |
4347 | + for f in self.snap_yaml: |
4348 | + if f not in self.snappy_required + self.snappy_optional: |
4349 | + unknown.append(f) |
4350 | + if len(unknown) > 0: |
4351 | + t = 'warn' |
4352 | + s = "unknown entries in snap.yaml: '%s'" % \ |
4353 | + (",".join(sorted(unknown))) |
4354 | + self._add_result(t, n, s) |
4355 | + |
4356 | + def check_is_squashfs(self): |
4357 | + '''Check snapfs''' |
4358 | + if is_squashfs(self.pkg_filename): |
4359 | + t = 'error' |
4360 | + n = self._get_check_name('is_squashfs') |
4361 | + s = "(NEEDS REVIEW) squashfs pkg" |
4362 | + manual_review = True |
4363 | + self._add_result(t, n, s, manual_review=manual_review) |
4364 | + |
4365 | + def check_squashfs_uses_snap_yaml(self): |
4366 | + '''Ensure that squashfs uses 16.04''' |
4367 | + if is_squashfs(self.pkg_filename) and not getattr(self, "snap_yaml"): |
4368 | + t = 'error' |
4369 | + n = self._get_check_name('check_squashfs_uses_snap_yaml') |
4370 | + s = "squashfs snaps must have a meta/snap.yaml" |
4371 | + manual_review = False |
4372 | + self._add_result(t, n, s, manual_review=manual_review) |
4373 | + |
4374 | + def check_apps(self): |
4375 | + '''Check apps''' |
4376 | + if not self.is_snap2: |
4377 | + return |
4378 | + |
4379 | + key = 'apps' |
4380 | + |
4381 | + t = 'info' |
4382 | + n = self._get_check_name('%s_present' % key) |
4383 | + s = 'OK' |
4384 | + if key not in self.snap_yaml: |
4385 | + s = 'OK (optional %s field not specified)' % key |
4386 | + self._add_result(t, n, s) |
4387 | + return |
4388 | + self._add_result(t, n, s) |
4389 | + |
4390 | + t = 'info' |
4391 | + n = self._get_check_name(key) |
4392 | + s = 'OK' |
4393 | + if not isinstance(self.snap_yaml[key], dict): |
4394 | + t = 'error' |
4395 | + s = "invalid %s entry: %s (not a dict)" % (key, |
4396 | + self.snap_yaml[key]) |
4397 | + self._add_result(t, n, s) |
4398 | + return |
4399 | + elif len(self.snap_yaml[key].keys()) < 1: |
4400 | + t = 'error' |
4401 | + s = "invalid %s entry (empty)" % (key) |
4402 | + self._add_result(t, n, s) |
4403 | + return |
4404 | + self._add_result(t, n, s) |
4405 | + |
4406 | + for app in self.snap_yaml[key]: |
4407 | + t = 'info' |
4408 | + n = self._get_check_name('%s_entry' % key, app=app) |
4409 | + s = 'OK' |
4410 | + |
4411 | + if not isinstance(self.snap_yaml[key][app], dict): |
4412 | + t = 'error' |
4413 | + s = "invalid entry: %s (not a dict)" % ( |
4414 | + self.snap_yaml[key][app]) |
4415 | + self._add_result(t, n, s) |
4416 | + continue |
4417 | + elif len(self.snap_yaml[key][app].keys()) < 1: |
4418 | + t = 'error' |
4419 | + s = "invalid entry for '%s' (empty)" % (app) |
4420 | + self._add_result(t, n, s) |
4421 | + continue |
4422 | + self._add_result(t, n, s) |
4423 | + |
4424 | + for field in self.apps_required: |
4425 | + t = 'info' |
4426 | + n = self._get_check_name('%s_required' % key, app=app) |
4427 | + s = 'OK' |
4428 | + if field not in self.snap_yaml[key][app]: |
4429 | + t = 'error' |
4430 | + s = "required field '%s' not specified" % field |
4431 | + self._add_result(t, n, s) |
4432 | + |
4433 | + t = 'info' |
4434 | + n = self._get_check_name('%s_unknown' % key, app=app) |
4435 | + s = 'OK' |
4436 | + unknown = [] |
4437 | + for field in self.snap_yaml[key][app]: |
4438 | + if field not in self.apps_required + self.apps_optional: |
4439 | + unknown.append(field) |
4440 | + if len(unknown) > 0: |
4441 | + t = 'warn' |
4442 | + s = "unknown fields: '%s'" % (",".join(sorted(unknown))) |
4443 | + self._add_result(t, n, s) |
4444 | + |
4445 | + def _verify_value_is_file(self, app, key): |
4446 | + t = 'info' |
4447 | + n = self._get_check_name('%s' % key, app=app) |
4448 | + s = 'OK' |
4449 | + if not isinstance(self.snap_yaml['apps'][app][key], str): |
4450 | + t = 'error' |
4451 | + s = "%s '%s' (not a str)" % (key, |
4452 | + self.snap_yaml['apps'][app][key]) |
4453 | + self._add_result(t, n, s) |
4454 | + elif len(self.snap_yaml['apps'][app][key]) < 1: |
4455 | + t = 'error' |
4456 | + s = "invalid %s (empty)" % (key) |
4457 | + self._add_result(t, n, s) |
4458 | + else: |
4459 | + fn = self._path_join(self.unpack_dir, |
4460 | + self.snap_yaml['apps'][app][key]) |
4461 | + if fn not in self.pkg_files: |
4462 | + t = 'error' |
4463 | + s = "%s does not exist" % ( |
4464 | + self.snap_yaml['apps'][app][key]) |
4465 | + self._add_result(t, n, s) |
4466 | + |
4467 | + def check_apps_command(self): |
4468 | + '''Check apps - command''' |
4469 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4470 | + return |
4471 | + |
4472 | + for app in self.snap_yaml['apps']: |
4473 | + key = 'command' |
4474 | + if key not in self.snap_yaml['apps'][app]: |
4475 | + # We check for required elsewhere |
4476 | + continue |
4477 | + |
4478 | + self._verify_value_is_file(app, key) |
4479 | + |
4480 | + def check_apps_stop(self): |
4481 | + '''Check apps - stop''' |
4482 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4483 | + return |
4484 | + |
4485 | + for app in self.snap_yaml['apps']: |
4486 | + key = 'stop' |
4487 | + if key not in self.snap_yaml['apps'][app]: |
4488 | + # We check for required elsewhere |
4489 | + continue |
4490 | + |
4491 | + self._verify_value_is_file(app, key) |
4492 | + |
4493 | + def check_apps_poststop(self): |
4494 | + '''Check apps - poststop''' |
4495 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4496 | + return |
4497 | + |
4498 | + for app in self.snap_yaml['apps']: |
4499 | + key = 'poststop' |
4500 | + if key not in self.snap_yaml['apps'][app]: |
4501 | + # We check for required elsewhere |
4502 | + continue |
4503 | + |
4504 | + self._verify_value_is_file(app, key) |
4505 | + |
4506 | + def check_apps_stop_timeout(self): |
4507 | + '''Check apps - stop-timeout''' |
4508 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4509 | + return |
4510 | + |
4511 | + for app in self.snap_yaml['apps']: |
4512 | + key = 'stop-timeout' |
4513 | + if key not in self.snap_yaml['apps'][app]: |
4514 | + # We check for required elsewhere |
4515 | + continue |
4516 | + |
4517 | + t = 'info' |
4518 | + n = self._get_check_name('%s' % key, app=app) |
4519 | + s = "OK" |
4520 | + if not isinstance(self.snap_yaml['apps'][app][key], int) and \ |
4521 | + not isinstance(self.snap_yaml['apps'][app][key], str): |
4522 | + t = 'error' |
4523 | + s = "'%s' is not a string or integer" % key |
4524 | + elif not re.search(r'[0-9]+[ms]?$', |
4525 | + str(self.snap_yaml['apps'][app][key])): |
4526 | + t = 'error' |
4527 | + s = "'%s' is not of form NN[ms] (%s)" % \ |
4528 | + (self.snap_yaml['apps'][app][key], key) |
4529 | + self._add_result(t, n, s) |
4530 | + |
4531 | + if t == 'error': |
4532 | + continue |
4533 | + |
4534 | + t = 'info' |
4535 | + n = self._get_check_name('%s_range' % key, app=app) |
4536 | + s = "OK" |
4537 | + st = int(str(self.snap_yaml['apps'][app][key]).rstrip(r'[ms]')) |
4538 | + if st < 0 or st > 60: |
4539 | + t = 'error' |
4540 | + s = "stop-timeout '%d' out of range (0-60)" % \ |
4541 | + self.snap_yaml['apps'][app][key] |
4542 | + self._add_result(t, n, s) |
4543 | + |
4544 | + def _verify_valid_values(self, app, key, valid): |
4545 | + '''Verify valid values for key in app''' |
4546 | + t = 'info' |
4547 | + n = self._get_check_name('%s' % key, app=app) |
4548 | + s = 'OK' |
4549 | + if not isinstance(self.snap_yaml['apps'][app][key], str): |
4550 | + t = 'error' |
4551 | + s = "%s '%s' (not a str)" % (key, |
4552 | + self.snap_yaml['apps'][app][key]) |
4553 | + self._add_result(t, n, s) |
4554 | + elif len(self.snap_yaml['apps'][app][key]) < 1: |
4555 | + t = 'error' |
4556 | + s = "invalid %s (empty)" % (key) |
4557 | + self._add_result(t, n, s) |
4558 | + elif self.snap_yaml['apps'][app][key] not in valid: |
4559 | + t = 'error' |
4560 | + s = "invalid %s: '%s'" % (key, self.snap_yaml['apps'][app][key]) |
4561 | + self._add_result(t, n, s) |
4562 | + |
4563 | + def check_apps_daemon(self): |
4564 | + '''Check apps - daemon''' |
4565 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4566 | + return |
4567 | + |
4568 | + valid = ["simple", |
4569 | + "forking", |
4570 | + "oneshot", |
4571 | + "dbus", |
4572 | + ] |
4573 | + |
4574 | + for app in self.snap_yaml['apps']: |
4575 | + key = 'daemon' |
4576 | + if key not in self.snap_yaml['apps'][app]: |
4577 | + # We check for required elsewhere |
4578 | + continue |
4579 | + |
4580 | + self._verify_valid_values(app, key, valid) |
4581 | + |
4582 | + def check_apps_nondaemon(self): |
4583 | + '''Check apps - non-daemon''' |
4584 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4585 | + return |
4586 | + |
4587 | + # Certain options require 'daemon' so list the keys that are shared |
4588 | + # by services and binaries |
4589 | + ok_keys = ['command', 'uses'] |
4590 | + |
4591 | + for app in self.snap_yaml['apps']: |
4592 | + needs_daemon = [] |
4593 | + for key in self.snap_yaml['apps'][app]: |
4594 | + if key not in self.apps_optional or \ |
4595 | + key == 'daemon' or \ |
4596 | + key in ok_keys or \ |
4597 | + 'daemon' in self.snap_yaml['apps'][app]: |
4598 | + continue |
4599 | + needs_daemon.append(key) |
4600 | + |
4601 | + t = 'info' |
4602 | + n = self._get_check_name('daemon_required', app=app) |
4603 | + s = "OK" |
4604 | + if len(needs_daemon) > 0: |
4605 | + t = 'error' |
4606 | + s = "'%s' must be used with 'daemon'" % ",".join(needs_daemon) |
4607 | + self._add_result(t, n, s) |
4608 | + |
4609 | + def check_apps_restart_condition(self): |
4610 | + '''Check apps - restart-condition''' |
4611 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4612 | + return |
4613 | + |
4614 | + valid = ["always", |
4615 | + "never", |
4616 | + "on-abnormal", |
4617 | + "on-abort", |
4618 | + "on-failure", |
4619 | + "on-success", |
4620 | + ] |
4621 | + |
4622 | + for app in self.snap_yaml['apps']: |
4623 | + key = 'restart-condition' |
4624 | + if key not in self.snap_yaml['apps'][app]: |
4625 | + # We check for required elsewhere |
4626 | + continue |
4627 | + |
4628 | + self._verify_valid_values(app, key, valid) |
4629 | + |
4630 | + def check_apps_busname(self): |
4631 | + '''Check apps - bus-name''' |
4632 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4633 | + return |
4634 | + |
4635 | + for app in self.snap_yaml['apps']: |
4636 | + key = 'bus-name' |
4637 | + if key not in self.snap_yaml['apps'][app]: |
4638 | + # We check for required elsewhere |
4639 | + continue |
4640 | + |
4641 | + t = 'info' |
4642 | + n = self._get_check_name('%s_framework' % key, app=app) |
4643 | + s = 'OK' |
4644 | + if 'type' in self.snap_yaml and \ |
4645 | + self.snap_yaml['type'] != 'framework': |
4646 | + t = 'error' |
4647 | + s = "Use of bus-name requires package be of 'type: framework'" |
4648 | + self._add_result(t, n, s) |
4649 | + |
4650 | + t = 'info' |
4651 | + n = self._get_check_name('%s' % key, app=app) |
4652 | + s = 'OK' |
4653 | + l = None |
4654 | + if not isinstance(self.snap_yaml['apps'][app][key], str): |
4655 | + t = 'error' |
4656 | + s = "%s '%s' (not a str)" % (key, |
4657 | + self.snap_yaml['apps'][app][key]) |
4658 | + elif len(self.snap_yaml['apps'][app][key]) < 1: |
4659 | + t = 'error' |
4660 | + s = "invalid %s (empty)" % (key) |
4661 | + elif not re.search( |
4662 | + r'^[A-Za-z0-9][A-Za-z0-9_-]*(\.[A-Za-z0-9][A-Za-z0-9_-]*)+$', |
4663 | + self.snap_yaml['apps'][app][key]): |
4664 | + t = 'error' |
4665 | + l = 'http://dbus.freedesktop.org/doc/dbus-specification.html' |
4666 | + s = "'%s' is not of form '^[A-Za-z0-9][A-Za-z0-9_-]*(\\.[A-Za-z0-9][A-Za-z0-9_-]*)+$'" % \ |
4667 | + (self.snap_yaml['apps'][app][key]) |
4668 | + self._add_result(t, n, s, l) |
4669 | + if t == 'error': |
4670 | + continue |
4671 | + |
4672 | + t = 'info' |
4673 | + n = self._get_check_name('%s_matches_name' % key, app=app) |
4674 | + s = 'OK' |
4675 | + suggested = [self.snap_yaml['name'], |
4676 | + "%s.%s" % (self.snap_yaml['name'], app) |
4677 | + ] |
4678 | + found = False |
4679 | + for name in suggested: |
4680 | + if self.snap_yaml['apps'][app][key].endswith(name): |
4681 | + found = True |
4682 | + break |
4683 | + if not found: |
4684 | + t = 'error' |
4685 | + s = "'%s' doesn't end with one of: %s" % \ |
4686 | + (self.snap_yaml['apps'][app][key], ", ".join(suggested)) |
4687 | + self._add_result(t, n, s) |
4688 | + |
4689 | + def check_apps_ports(self): |
4690 | + '''Check apps - ports''' |
4691 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4692 | + return |
4693 | + |
4694 | + valid_keys = ['internal', 'external'] |
4695 | + valid_subkeys = ['port', 'negotiable'] |
4696 | + for app in self.snap_yaml['apps']: |
4697 | + if 'ports' not in self.snap_yaml['apps'][app]: |
4698 | + # We check for required elsewhere |
4699 | + continue |
4700 | + |
4701 | + t = 'info' |
4702 | + n = self._get_check_name('ports', app=app) |
4703 | + s = 'OK' |
4704 | + l = None |
4705 | + if not isinstance(self.snap_yaml['apps'][app]['ports'], dict): |
4706 | + t = 'error' |
4707 | + s = "ports '%s' (not a dict)" % ( |
4708 | + self.snap_yaml['apps'][app]['ports']) |
4709 | + elif len(self.snap_yaml['apps'][app]['ports'].keys()) < 1: |
4710 | + t = 'error' |
4711 | + s = "'ports' must contain 'internal' and/or 'external'" |
4712 | + self._add_result(t, n, s, l) |
4713 | + if t == 'error': |
4714 | + continue |
4715 | + |
4716 | + # unknown |
4717 | + unknown = [] |
4718 | + for key in self.snap_yaml['apps'][app]['ports']: |
4719 | + if key not in valid_keys: |
4720 | + unknown.append(key) |
4721 | + if len(unknown) > 0: |
4722 | + t = 'error' |
4723 | + n = self._get_check_name('ports_unknown_key', extra=key, |
4724 | + app=app) |
4725 | + s = "Unknown '%s' for ports" % (",".join(unknown)) |
4726 | + self._add_result(t, n, s) |
4727 | + |
4728 | + port_pat = re.compile(r'^[0-9]+/[a-z0-9\-]+$') |
4729 | + for key in valid_keys: |
4730 | + if key not in self.snap_yaml['apps'][app]['ports']: |
4731 | + continue |
4732 | + |
4733 | + if len(self.snap_yaml['apps'][app]['ports'][key].keys()) < 1: |
4734 | + t = 'error' |
4735 | + n = self._get_check_name('ports', extra=key, app=app) |
4736 | + s = 'Could not find any %s ports' % key |
4737 | + self._add_result(t, n, s) |
4738 | + continue |
4739 | + |
4740 | + for tagname in self.snap_yaml['apps'][app]['ports'][key]: |
4741 | + entry = self.snap_yaml['apps'][app]['ports'][key][tagname] |
4742 | + if len(entry.keys()) < 1 or ('negotiable' not in entry and |
4743 | + 'port' not in entry): |
4744 | + t = 'error' |
4745 | + n = self._get_check_name('ports', extra=key, app=app) |
4746 | + s = "Could not find 'port' or 'negotiable' in '%s'" % \ |
4747 | + tagname |
4748 | + self._add_result(t, n, s) |
4749 | + continue |
4750 | + |
4751 | + # unknown |
4752 | + unknown = [] |
4753 | + for subkey in entry: |
4754 | + if subkey not in valid_subkeys: |
4755 | + unknown.append(subkey) |
4756 | + if len(unknown) > 0: |
4757 | + t = 'error' |
4758 | + n = self._get_check_name('ports_unknown_subkey', |
4759 | + extra=key, app=app) |
4760 | + s = "Unknown '%s' for %s" % (",".join(unknown), |
4761 | + tagname) |
4762 | + self._add_result(t, n, s) |
4763 | + |
4764 | + # port |
4765 | + subkey = 'port' |
4766 | + t = 'info' |
4767 | + n = self._get_check_name('ports_%s_format' % tagname, |
4768 | + extra=subkey) |
4769 | + s = 'OK' |
4770 | + if subkey not in entry: |
4771 | + s = 'OK (skipped, not found)' |
4772 | + elif not isinstance(entry[subkey], str): |
4773 | + t = 'error' |
4774 | + s = "invalid entry: %s (not a str)" % (entry[subkey]) |
4775 | + else: |
4776 | + tmp = entry[subkey].split('/') |
4777 | + if not port_pat.search(entry[subkey]) or \ |
4778 | + int(tmp[0]) < 1 or int(tmp[0]) > 65535: |
4779 | + t = 'error' |
4780 | + s = "'%s' should be of form " % entry[subkey] + \ |
4781 | + "'port/protocol' where port is an integer " + \ |
4782 | + "(1-65535) and protocol is found in " + \ |
4783 | + "/etc/protocols" |
4784 | + self._add_result(t, n, s) |
4785 | + |
4786 | + # negotiable |
4787 | + subkey = 'negotiable' |
4788 | + t = 'info' |
4789 | + n = self._get_check_name('ports_%s_format' % tagname, |
4790 | + extra=subkey) |
4791 | + s = 'OK' |
4792 | + if subkey not in entry: |
4793 | + s = 'OK (skipped, not found)' |
4794 | + elif not isinstance(entry[subkey], bool): |
4795 | + t = 'error' |
4796 | + s = "'%s: %s' should be either 'yes' or 'no'" % \ |
4797 | + (subkey, entry[subkey]) |
4798 | + self._add_result(t, n, s) |
4799 | + |
4800 | + def check_apps_socket(self): |
4801 | + '''Check apps - socket''' |
4802 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4803 | + return |
4804 | + |
4805 | + for app in self.snap_yaml['apps']: |
4806 | + key = 'socket' |
4807 | + if key not in self.snap_yaml['apps'][app]: |
4808 | + # We check for required elsewhere |
4809 | + continue |
4810 | + |
4811 | + t = 'info' |
4812 | + n = self._get_check_name(key, app=app) |
4813 | + s = 'OK' |
4814 | + if not isinstance(self.snap_yaml['apps'][app][key], bool): |
4815 | + t = 'error' |
4816 | + s = "'%s: %s' should be either 'yes' or 'no'" % ( |
4817 | + key, self.snap_yaml['apps'][app][key]) |
4818 | + elif 'listen-stream' not in self.snap_yaml['apps'][app]: |
4819 | + t = 'error' |
4820 | + s = "'socket' specified without 'listen-stream'" |
4821 | + self._add_result(t, n, s) |
4822 | + |
4823 | + def check_apps_listen_stream(self): |
4824 | + '''Check apps - listen-stream''' |
4825 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4826 | + return |
4827 | + |
4828 | + for app in self.snap_yaml['apps']: |
4829 | + key = 'listen-stream' |
4830 | + if key not in self.snap_yaml['apps'][app]: |
4831 | + # We check for required elsewhere |
4832 | + continue |
4833 | + |
4834 | + t = 'info' |
4835 | + n = self._get_check_name(key, app=app) |
4836 | + s = 'OK' |
4837 | + if not isinstance(self.snap_yaml['apps'][app][key], str): |
4838 | + t = 'error' |
4839 | + s = "invalid entry: %s (not a str)" % ( |
4840 | + self.snap_yaml['apps'][app][key]) |
4841 | + elif len(self.snap_yaml['apps'][app][key]) == 0: |
4842 | + t = 'error' |
4843 | + s = "'%s' is empty" % key |
4844 | + self._add_result(t, n, s) |
4845 | + if t == 'error': |
4846 | + continue |
4847 | + |
4848 | + t = 'info' |
4849 | + n = self._get_check_name('%s_matches_name' % key, app=app) |
4850 | + s = 'OK' |
4851 | + sock = self.snap_yaml['apps'][app][key] |
4852 | + pkgname = self.snap_yaml['name'] |
4853 | + if sock.startswith('@'): |
4854 | + if sock != '@%s' % pkgname and \ |
4855 | + not sock.startswith('@%s_' % pkgname): |
4856 | + t = 'error' |
4857 | + s = ("abstract socket '%s' is neither '%s' nor starts " |
4858 | + "with '%s'" % (sock, '@%s' % pkgname, |
4859 | + '@%s_' % pkgname)) |
4860 | + elif sock.startswith('/'): |
4861 | + found = False |
4862 | + for path in ["/tmp/", |
4863 | + "/var/lib/snaps/%s/" % pkgname, |
4864 | + "/var/lib/snaps/%s." % pkgname, |
4865 | + "/run/shm/snaps/%s/" % pkgname, |
4866 | + "/run/shm/snaps/%s." % pkgname]: |
4867 | + if sock.startswith(path): |
4868 | + found = True |
4869 | + break |
4870 | + if not found: |
4871 | + t = 'error' |
4872 | + s = ("named socket '%s' should be in a writable " |
4873 | + "app-specific area or /tmp" % sock) |
4874 | + else: |
4875 | + t = 'error' |
4876 | + s = ("'%s' does not specify an abstract socket (starts " |
4877 | + "with '@') or absolute filename" % (sock)) |
4878 | + self._add_result(t, n, s) |
4879 | + |
4880 | + def _verify_valid_socket(self, app, key): |
4881 | + '''Verify valid values for socket key''' |
4882 | + t = 'info' |
4883 | + n = self._get_check_name(key, app=app) |
4884 | + s = 'OK' |
4885 | + if not isinstance(self.snap_yaml['apps'][app][key], str): |
4886 | + t = 'error' |
4887 | + s = "invalid entry: %s (not a str)" % ( |
4888 | + self.snap_yaml['apps'][app][key]) |
4889 | + elif len(self.snap_yaml['apps'][app][key]) == 0: |
4890 | + t = 'error' |
4891 | + s = "'%s' is empty" % key |
4892 | + elif 'listen-stream' not in self.snap_yaml['apps'][app]: |
4893 | + t = 'error' |
4894 | + s = "'%s' specified without 'listen-stream'" % key |
4895 | + self._add_result(t, n, s) |
4896 | + if t == 'error': |
4897 | + return |
4898 | + |
4899 | + t = 'error' |
4900 | + n = self._get_check_name('%s_reserved' % key, app=app) |
4901 | + s = "'%s' should not be used until snappy supports per-app users" \ |
4902 | + % key |
4903 | + self._add_result(t, n, s) |
4904 | + |
4905 | + t = 'info' |
4906 | + n = self._get_check_name("%s_matches_name" % key, app=app) |
4907 | + s = 'OK' |
4908 | + if self.snap_yaml['apps'][app][key] != self.snap_yaml['name']: |
4909 | + t = 'error' |
4910 | + s = "'%s' != '%s'" % (self.snap_yaml['apps'][app][key], |
4911 | + self.snap_yaml['name']) |
4912 | + self._add_result(t, n, s) |
4913 | + |
4914 | + def check_apps_socket_user(self): |
4915 | + '''Check apps - socket-user''' |
4916 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4917 | + return |
4918 | + |
4919 | + for app in self.snap_yaml['apps']: |
4920 | + key = 'socket-user' |
4921 | + if key not in self.snap_yaml['apps'][app]: |
4922 | + # We check for required elsewhere |
4923 | + continue |
4924 | + |
4925 | + self._verify_valid_socket(app, key) |
4926 | + |
4927 | + def check_apps_socket_group(self): |
4928 | + '''Check apps - socket-group''' |
4929 | + if not self.is_snap2 or 'apps' not in self.snap_yaml: |
4930 | + return |
4931 | + |
4932 | + for app in self.snap_yaml['apps']: |
4933 | + key = 'socket-group' |
4934 | + if key not in self.snap_yaml['apps'][app]: |
4935 | + # We check for required elsewhere |
4936 | + continue |
4937 | + |
4938 | + self._verify_valid_socket(app, key) |
4939 | + |
4940 | + def check_uses(self): |
4941 | + '''Check uses''' |
4942 | + if not self.is_snap2 or 'uses' not in self.snap_yaml: |
4943 | + return |
4944 | + |
4945 | + for slot in self.snap_yaml['uses']: |
4946 | + # If the 'type' name is the same as the 'slot' name, then 'type' |
4947 | + # is optional since the type name and the slot name are the same |
4948 | + skill_type = slot |
4949 | + if 'type' in self.snap_yaml['uses'][slot]: |
4950 | + skill_type = self.snap_yaml['uses'][slot]['type'] |
4951 | + |
4952 | + key = 'type' |
4953 | + t = 'info' |
4954 | + n = self._get_check_name(key, extra=slot) |
4955 | + s = 'OK' |
4956 | + if not isinstance(self.snap_yaml['uses'][slot][key], str): |
4957 | + t = 'error' |
4958 | + s = "invalid %s: %s (not a str)" % \ |
4959 | + (key, self.snap_yaml['uses'][slot][key]) |
4960 | + elif len(self.snap_yaml['uses'][slot][key]) == 0: |
4961 | + t = 'error' |
4962 | + s = "'%s' is empty" % key |
4963 | + self._add_result(t, n, s) |
4964 | + if t == 'error': |
4965 | + continue |
4966 | + |
4967 | + t = 'info' |
4968 | + n = self._get_check_name(skill_type, extra=slot) |
4969 | + s = 'OK' |
4970 | + if skill_type not in self.skill_types: |
4971 | + t = 'error' |
4972 | + s = "unknown skill type '%s'" % skill_type |
4973 | + self._add_result(t, n, s) |
4974 | + if t == 'error': |
4975 | + continue |
4976 | + |
4977 | + min = 1 |
4978 | + if 'type' in self.snap_yaml['uses'][slot]: |
4979 | + min = 2 |
4980 | + t = 'info' |
4981 | + n = self._get_check_name('attributes') |
4982 | + s = 'OK' |
4983 | + if len(self.snap_yaml['uses'][slot]) < min: |
4984 | + t = 'error' |
4985 | + s = "'%s' has no attributes" % slot |
4986 | + self._add_result(t, n, s) |
4987 | + if t == 'error': |
4988 | + continue |
4989 | + |
4990 | + for attrib in self.snap_yaml['uses'][slot]: |
4991 | + if attrib == 'type': |
4992 | + continue |
4993 | + t = 'info' |
4994 | + n = self._get_check_name('attributes', app=slot, extra=attrib) |
4995 | + s = "OK" |
4996 | + if attrib not in self.skill_types[skill_type]: |
4997 | + t = 'error' |
4998 | + s = "unknown attribute '%s' for type '%s'" % (attrib, |
4999 | + skill_type) |
5000 | + elif not isinstance(self.snap_yaml['uses'][slot][attrib], |
As of r578, cr_common.py now uses common.py and cr_* tests only run on click and snap v1 now. Next up, add back the relevant snap v2 tests that were removed from cr_* to sr_* and do meta/snap.yaml tests.