Merge lp:~jamestait/click-reviewers-tools/enable-store-metadata-report into lp:click-reviewers-tools

Proposed by James Tait
Status: Work in progress
Proposed branch: lp:~jamestait/click-reviewers-tools/enable-store-metadata-report
Merge into: lp:click-reviewers-tools
Diff against target: 345 lines (+271/-0)
5 files modified
bin/click-metadata (+27/-0)
clickreviews/common.py (+66/-0)
clickreviews/cr_common.py (+39/-0)
clickreviews/cr_metadata.py (+106/-0)
clickreviews/sr_common.py (+33/-0)
To merge this branch: bzr merge lp:~jamestait/click-reviewers-tools/enable-store-metadata-report
Reviewer Review Type Date Requested Status
Canonical Store Reviewers Pending
Review via email: mp+291092@code.launchpad.net

Commit message

Add support for extracting package metadata for the Store.

Description of the change

This branch adds a new script that the Store can use to extract metadata from packages and have it returned in a consistent format.

To post a comment you must log in.

Unmerged revisions

617. By James Tait

Add support for extracting package metadata for the Store.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'bin/click-metadata'
--- bin/click-metadata 1970-01-01 00:00:00 +0000
+++ bin/click-metadata 2016-04-06 09:31:46 +0000
@@ -0,0 +1,27 @@
1#!/usr/bin/python3
2
3import json
4import sys
5
6from clickreviews import (
7 common,
8 cr_common,
9 sr_common,
10)
11
12
13if __name__ == '__main__':
14 if len(sys.argv) < 2:
15 common.error("Must give path to package")
16
17 review = common.Review(sys.argv[1], '')
18
19 if review.is_click or review.is_snap1:
20 metadata = cr_common.ClickReview(sys.argv[1], 'metadata')
21 elif review.is_snap2:
22 metadata = sr_common.SnapReview(sys.argv[1], 'metadata')
23 else:
24 common.error('Unknown package type.')
25
26 print(json.dumps(metadata.get_metadata(), sort_keys=True,
27 indent=2, separators=(',', ': ')))
028
=== modified file 'clickreviews/common.py'
--- clickreviews/common.py 2016-02-23 21:09:07 +0000
+++ clickreviews/common.py 2016-04-06 09:31:46 +0000
@@ -373,6 +373,62 @@
373 '''Set review name'''373 '''Set review name'''
374 self.review_type = name374 self.review_type = name
375375
376 #
377 # Package Metadata extraction, primarily for the Store.
378 #
379
380 def get_metadata(self):
381 metadata = {
382 'name': self.get_name(),
383 'version': self.get_version(),
384 'title': self.get_title(),
385 'description': self.get_description(),
386 'architecture': self.pkg_arch,
387 }
388 framework = self.get_framework()
389 if framework is not None:
390 metadata['framework'] = framework
391 icon = self.get_icon()
392 if icon['path'] is not None:
393 metadata['icon'] = icon
394 installed_size = self.get_installed_size()
395 if installed_size is not None:
396 metadata['installed-size'] = installed_size
397 maintainer = self.get_maintainer()
398 if maintainer is not None:
399 metadata['maintainer'] = maintainer
400 return [
401 metadata, {
402 'is_snap': self.is_snap1 or self.is_snap2,
403 'is_squashfs': self.is_snap2
404 }]
405
406 def get_name(self):
407 return None
408
409 def get_version(self):
410 return None
411
412 def get_installed_size(self):
413 return None
414
415 def get_title(self):
416 return None
417
418 def get_description(self):
419 return None
420
421 def get_framework(self):
422 return None
423
424 def get_icon(self):
425 return None
426
427 def get_architecture(self):
428 return None
429
430 def get_maintainer(self):
431 return None
376432
377#433#
378# Utility functions434# Utility functions
@@ -541,6 +597,16 @@
541 return dest597 return dest
542598
543599
600def open_binary_file_read(path):
601 '''Open specified binary file read-only'''
602 try:
603 orig = codecs.open(path, 'rb')
604 except Exception:
605 raise
606
607 return orig
608
609
544def create_tempdir():610def create_tempdir():
545 '''Create/reuse a temporary directory that is automatically cleaned up'''611 '''Create/reuse a temporary directory that is automatically cleaned up'''
546 global TMP_DIR612 global TMP_DIR
547613
=== modified file 'clickreviews/cr_common.py'
--- clickreviews/cr_common.py 2016-02-09 22:49:57 +0000
+++ clickreviews/cr_common.py 2016-04-06 09:31:46 +0000
@@ -16,6 +16,7 @@
1616
17from __future__ import print_function17from __future__ import print_function
18from debian.deb822 import Deb82218from debian.deb822 import Deb822
19import base64
19import glob20import glob
20import json21import json
21import os22import os
@@ -28,6 +29,7 @@
28 Review,29 Review,
29 ReviewException,30 ReviewException,
30 error,31 error,
32 open_binary_file_read,
31 open_file_read,33 open_file_read,
32)34)
3335
@@ -391,6 +393,43 @@
391 d[name][key] = entry[key]393 d[name][key] = entry[key]
392 return d394 return d
393395
396 def get_name(self):
397 return self.click_pkgname
398
399 def get_version(self):
400 return self.click_version
401
402 def get_installed_size(self):
403 return self.manifest.get('installed-size')
404
405 def get_title(self):
406 return self.manifest.get('title')
407
408 def get_description(self):
409 return self.manifest.get('description')
410
411 def get_framework(self):
412 return self.manifest.get('framework')
413
414 def get_icon(self):
415 icon_path = self.manifest.get('icon')
416 icon_data = None
417 if icon_path:
418 full_path = os.path.join(self.unpack_dir, icon_path)
419 with open_binary_file_read(full_path) as f:
420 icon_data = base64.b64encode(f.read())
421 return {
422 'path': icon_path,
423 'data': str(icon_data) if icon_data else None,
424 'filename': os.path.basename(icon_path) if icon_path else None,
425 }
426
427 def get_architecture(self):
428 return self.manifest.get('architecture')
429
430 def get_maintainer(self):
431 return self.manifest.get('maintainer')
432
394 def check_peer_hooks(self, hooks_sublist=[]):433 def check_peer_hooks(self, hooks_sublist=[]):
395 '''Check if peer hooks are valid'''434 '''Check if peer hooks are valid'''
396 # Nothing to verify435 # Nothing to verify
397436
=== added file 'clickreviews/cr_metadata.py'
--- clickreviews/cr_metadata.py 1970-01-01 00:00:00 +0000
+++ clickreviews/cr_metadata.py 2016-04-06 09:31:46 +0000
@@ -0,0 +1,106 @@
1'''cr_content_hub.py: click content-hub checks'''
2#
3# Copyright (C) 2014-2015 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; version 3 of the License.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17from __future__ import print_function
18
19from clickreviews.cr_common import ClickReview, error, open_file_read
20import json
21import os
22
23
24class ClickReviewMetadata(ClickReview):
25 '''This class represents click lint reviews'''
26 def __init__(self, fn, overrides=None):
27 ClickReview.__init__(self, fn, "metadata", overrides=overrides)
28 if not self.is_click and not self.is_snap1:
29 return
30
31 self.valid_keys = ['destination', 'share', 'source']
32
33 self.content_hub_files = dict() # click-show-files and tests
34 self.content_hub = dict()
35
36 if self.manifest is None:
37 return
38
39 for app in self.manifest['hooks']:
40 if 'content-hub' not in self.manifest['hooks'][app]:
41 # msg("Skipped missing content-hub hook for '%s'" % app)
42 continue
43 if not isinstance(self.manifest['hooks'][app]['content-hub'], str):
44 error("manifest malformed: hooks/%s/urls is not str" % app)
45 (full_fn, jd) = self._extract_content_hub(app)
46 self.content_hub_files[app] = full_fn
47 self.content_hub[app] = jd
48
49 def _extract_content_hub(self, app):
50 '''Get content-hub hook content'''
51 c = self.manifest['hooks'][app]['content-hub']
52 fn = os.path.join(self.unpack_dir, c)
53
54 bn = os.path.basename(fn)
55 if not os.path.exists(fn):
56 error("Could not find '%s'" % bn)
57
58 fh = open_file_read(fn)
59 contents = ""
60 for line in fh.readlines():
61 contents += line
62 fh.close()
63
64 try:
65 jd = json.loads(contents)
66 except Exception as e:
67 error("content-hub json unparseable: %s (%s):\n%s" % (bn,
68 str(e), contents))
69
70 if not isinstance(jd, dict):
71 error("content-hub json is malformed: %s:\n%s" % (bn, contents))
72
73 return (fn, jd)
74
75 def check_valid(self):
76 '''Check validity of content-hub entries'''
77 if not self.is_click and not self.is_snap1:
78 return
79
80 for app in sorted(self.content_hub):
81 for k in self.content_hub[app].keys():
82 t = "info"
83 n = self._get_check_name('valid', app=app, extra=k)
84 s = "OK"
85
86 if not isinstance(self.content_hub[app][k], list):
87 t = "error"
88 s = "'%s' is not a list" % k
89 elif len(self.content_hub[app][k]) < 1:
90 t = "error"
91 s = "'%s' is empty" % k
92 self._add_result(t, n, s)
93 if t == "error":
94 continue
95
96 for v in self.content_hub[app][k]:
97 t = "info"
98 n = self._get_check_name('valid_value', app=app, extra=k)
99 s = "OK"
100 if not isinstance(v, str):
101 t = "error"
102 s = "'%s' is not a string" % k
103 elif v == "":
104 t = "error"
105 s = "'%s' is empty" % k
106 self._add_result(t, n, s)
0107
=== modified file 'clickreviews/sr_common.py'
--- clickreviews/sr_common.py 2016-03-14 19:20:55 +0000
+++ clickreviews/sr_common.py 2016-04-06 09:31:46 +0000
@@ -15,6 +15,7 @@
15# along with this program. If not, see <http://www.gnu.org/licenses/>.15# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
17from __future__ import print_function17from __future__ import print_function
18import base64
18import os19import os
19import re20import re
20import yaml21import yaml
@@ -24,6 +25,7 @@
24 Review,25 Review,
25 ReviewException,26 ReviewException,
26 error,27 error,
28 open_binary_file_read,
27 open_file_read,29 open_file_read,
28)30)
2931
@@ -129,3 +131,34 @@
129 if pat.search(n):131 if pat.search(n):
130 return True132 return True
131 return False133 return False
134
135 def get_name(self):
136 return self.snap_yaml.get('name')
137
138 def get_version(self):
139 return str(self.snap_yaml.get('version'))
140
141 def get_description(self):
142 return self.snap_yaml.get('description')
143
144 def get_icon(self):
145 icon_path = None
146 for ext in ('svg', 'png'):
147 path = 'meta/icon.' + ext
148 full_path = os.path.join(self.unpack_dir, path)
149 if os.path.isfile(full_path):
150 icon_path = path
151 break
152 icon_data = None
153 if icon_path:
154 full_path = os.path.join(self.unpack_dir, icon_path)
155 with open_binary_file_read(full_path) as f:
156 icon_data = base64.b64encode(f.read())
157 return {
158 'path': icon_path,
159 'data': str(icon_data) if icon_data else None,
160 'filename': os.path.basename(icon_path) if icon_path else None,
161 }
162
163 def get_architecture(self):
164 return self.pkg_arch

Subscribers

People subscribed via source and target branches