Merge lp:~jdstrand/click-reviewers-tools/crt.cleanup into lp:click-reviewers-tools

Proposed by Jamie Strandboge
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
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Approve
Review via email: mp+284684@code.launchpad.net

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.

To post a comment you must log in.
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

Revision history for this message
Jamie Strandboge (jdstrand) wrote :

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.

Revision history for this message
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

Revision history for this message
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.

Revision history for this message
Daniel Holbach (dholbach) wrote :

./clickreviews/cr_lint.py:1137: undefined name 'is_squashfs'

======================================================================
ERROR: test_check_snappy_readme_md (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md()
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/tests/test_cr_lint.py", line 1500, in test_check_snappy_readme_md
    c.check_snappy_readme_md()
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/cr_lint.py", line 1137, in check_snappy_readme_md
    if is_squashfs(self.pkg_filename):
NameError: name 'is_squashfs' is not defined

review: Needs Fixing
Revision history for this message
Jamie Strandboge (jdstrand) wrote :

@Daniel, do you have a merge conflict or something? The testsuite is passing here:

$ make check
./run-tests
...
test_check_snappy_readme_md (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md() ... ok
test_check_snappy_readme_md_bad (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md() - short ... ok
test_check_snappy_readme_md_bad2 (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md() - missing ... ok
...

Looking at cr_lint.py, is_squashfs is no longer used in check_snappy_readme_md().

Revision history for this message
Daniel Holbach (dholbach) wrote :
Download full text (6.5 KiB)

No conflicts. Have you pushed all changes?

======================================================================
ERROR: test_check_snappy_readme_md (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md()
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/tests/test_cr_lint.py", line 1500, in test_check_snappy_readme_md
    c.check_snappy_readme_md()
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/cr_lint.py", line 1137, in check_snappy_readme_md
    if is_squashfs(self.pkg_filename):
NameError: name 'is_squashfs' is not defined

======================================================================
ERROR: test_check_snappy_readme_md_bad (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md() - short
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/tests/test_cr_lint.py", line 1526, in test_check_snappy_readme_md_bad
    c.check_snappy_readme_md()
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/cr_lint.py", line 1137, in check_snappy_readme_md
    if is_squashfs(self.pkg_filename):
NameError: name 'is_squashfs' is not defined

======================================================================
ERROR: test_check_snappy_readme_md_bad2 (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md() - missing
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/tests/test_cr_lint.py", line 1537, in test_check_snappy_readme_md_bad2
    c.check_snappy_readme_md()
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/cr_lint.py", line 1137, in check_snappy_readme_md
    if is_squashfs(self.pkg_filename):
NameError: name 'is_squashfs' is not defined

======================================================================
ERROR: test_check_snappy_readme_md_squashfs (test_cr_lint.TestClickReviewLint)
Test check_snappy_readme_md() - squashfs file
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/daniel/dev/click/click-reviewers-tools/clickreviews/tests/test_cr_lint.py", line 1507, in test_check_snappy_readme_md_squashfs
    with patch("clickreviews.cr_lint.is_squashfs") as mock_is_squashfs:
  File "/usr/lib/python3.5/unittest/mock.py", line 1221, in __enter__
    original, local = self.get_original()
  File "/usr/lib/python3.5/unittest/mock.py", line 1195, in get_original
    "%s does not have the attribute %r" % (target, name)
AttributeError: <module 'clickreviews.cr_lint' from '/home/daniel/dev/click/click-reviewers-tools/clickreviews/cr_lint.py'> does not have the attribute 'is_squashfs'

----------------------------------------------------------------------
Ran 817 tests in 7.082s

FAILED (errors=4)
added:
  bin/detect-package
  bin/snap-check-lint
  bin/snap-check-skeleton
  clickreviews/common.py
  clickreviews/sr_common.py
  clickreviews/sr_lint.py
  cl...

Read more...

Revision history for this message
Jamie Strandboge (jdstrand) wrote :

You seem to be missing clickreviews/common.py...

Revision history for this message
Jamie Strandboge (jdstrand) wrote :

Oh no, you have that. Not sure what is going on here. I'll investigate.

Revision history for this message
Jamie Strandboge (jdstrand) wrote :

I just did:
$ bzr branch lp:~jdstrand/click-reviewers-tools/crt.cleanup click-reviewers-tools.crt.cleanup
$ cd click-reviewers-tools.crt.cleanup
$ ./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.

Revision history for this message
Daniel Holbach (dholbach) wrote :

Looks good AFAICS. :-)

review: Approve
599. By Jamie Strandboge

use 'invalid ...' consistently instead of 'not valid'
add 'uses' tests for 'apps'

Revision history for this message
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.

Revision history for this message
Jamie Strandboge (jdstrand) wrote :

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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],
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches