blob: faae7444a7645ac0a3dd046acd9f2c21ad219f3f [file] [log] [blame]
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +00001#!/usr/bin/python3
2import argparse
3import os
4import sys
5import subprocess
6import traceback
7import yaml
8
9run_pep8 = False
10try:
11 import pep8
12 run_pep8 = True
13except:
14 print("PEP8 is not available")
15
16
17def print_stderr(message):
18 sys.stderr.write(message)
19 sys.stderr.write("\n")
20
21
22def publish_result(result_message_list):
23 result_message = '\n'.join(result_message_list)
24 f = open('build-error.txt', 'a')
25 f.write("\n\n")
26 f.write(result_message)
27 f.write("\n\n")
28 f.close()
29 print_stderr(result_message)
30
31
32def pep8_check(filepath, ignore_options=None):
33 _fmt = "%(row)d:%(col)d: %(code)s %(text)s"
34 options = {
35 'ignore': ignore_options,
36 "show_source": True}
37 pep8_checker = pep8.StyleGuide(options)
38 fchecker = pep8_checker.checker_class(
39 filepath,
40 options=pep8_checker.options)
41 fchecker.check_all()
42 if fchecker.report.file_errors > 0:
43 result_message_list = []
44 result_message_list.append("* PEP8: [FAILED]: " + filepath)
45 fchecker.report.print_statistics()
46 for line_number, offset, code, text, doc in fchecker.report._deferred_print:
47 result_message_list.append(
48 _fmt % {
49 'path': filepath,
50 'row': fchecker.report.line_offset + line_number,
51 'col': offset + 1,
52 'code': code, 'text': text,
53 })
54 publish_result(result_message_list)
55 return 1
56 else:
57 message = "* PEP8: [PASSED]: " + filepath
58 print_stderr(message)
59 return 0
60
61def metadata_check(filepath):
62 if filepath.lower().endswith("yaml"):
63 with open(filepath, "r") as f:
64 result_message_list = []
65 y = yaml.load(f.read())
66 if not 'metadata' in y.keys():
67 result_message_list.append("* METADATA [FAILED]: " + filepath)
68 result_message_list.append("\tmetadata section missing")
69 publish_result(result_message_list)
70 exit(1)
71 metadata_dict = y['metadata']
72 mandatory_keys = set([
73 'name',
74 'format',
75 'description',
76 'maintainer',
77 'os',
78 'devices'])
79 if not mandatory_keys.issubset(set(metadata_dict.keys())):
80 result_message_list.append("* METADATA [FAILED]: " + filepath)
81 result_message_list.append("\tmandatory keys missing: %s" % \
82 mandatory_keys.difference(set(metadata_dict.keys())))
83 result_message_list.append("\tactual keys present: %s" % \
84 metadata_dict.keys())
85 publish_result(result_message_list)
86 return 1
87 for key in mandatory_keys:
88 if len(metadata_dict[key]) == 0:
89 result_message_list.append("* METADATA [FAILED]: " + filepath)
90 result_message_list.append("\t%s has no content" % key)
91 publish_result(result_message_list)
92 return 1
93 result_message_list.append("* METADATA [PASSED]: " + filepath)
94 publish_result(result_message_list)
95 return 0
96
97def validate_yaml(filename):
98 with open(filename, "r") as f:
99 try:
100 y = yaml.load(f.read())
101 message = "* YAMLVALID: [PASSED]: " + filename
102 print_stderr(message)
103 except:
104 message = "* YAMLVALID: [FAILED]: " + filename
105 result_message_list = []
106 result_message_list.append(message)
107 result_message_list.append("\n\n")
108 exc_type, exc_value, exc_traceback = sys.exc_info()
109 for line in traceback.format_exception_only(exc_type, exc_value):
110 result_message_list.append(' ' + line)
111 publish_result(result_message_list)
112 return 1
113 return 0
114
115
116def validate_shell(filename, ignore_options):
117 ignore_string = ""
118 if ignore_options is not None:
119 ignore_string = "-e %s" % ignore_options
120 cmd = 'shellcheck %s' % ignore_string
121 return validate_external(cmd, filename, "SHELLCHECK")
122
123
124def validate_php(filename):
125 cmd = 'php -l'
126 return validate_external(cmd, filename, "PHPLINT")
127
128
129def validate_external(cmd, filename, prefix):
130 final_cmd = "%s %s 2>&1" % (cmd, filename)
131 status, output = subprocess.getstatusoutput(final_cmd)
132 if status == 0:
133 message = '* %s: [PASSED]: %s' % (prefix, filename)
134 print_stderr(message)
135 else:
136 result_message_list = []
137 result_message_list.append('* %s: [FAILED]: %s' % (prefix, filename))
138 result_message_list.append('* %s: [OUTPUT]:' % prefix)
139 for line in output.splitlines():
140 result_message_list.append(' ' + line)
141 publish_result(result_message_list)
142 return 1
143 return 0
144
145
146def validate_file(args, path):
147 exitcode = 0
148 if path.endswith(".yaml"):
149 exitcode = exitcode + validate_yaml(path)
150 exitcode = exitcode + metadata_check(path)
151 elif run_pep8 and path.endswith(".py"):
152 exitcode = pep8_check(path, args.pep8_ignore)
153 elif path.endswith(".php"):
154 exitcode = validate_php(path)
155 else:
156 # try shellcheck by default
157 exitcode = validate_shell(path, args.shellcheck_ignore)
158 return exitcode
159
160
161def run_unit_tests(args, filelist=None):
162 exitcode = 0
163 if filelist is not None:
164 for filename in filelist:
165 exitcode = validate_file(args, filename)
166 else:
167 for root, dirs, files in os.walk('.'):
168 if not root.startswith("./.git"):
169 for name in files:
170 exitcode = validate_file(
171 args,
172 root + "/" + name)
173 return exitcode
174
175
176def main(args):
177 exitcode = 0
178 if args.git_latest:
179 # check if git exists
180 git_status, git_result = subprocess.getstatusoutput(
181 "git show --name-only --format=''")
182 if git_status == 0:
183 filelist = git_result.split()
184 exitcode = run_unit_tests(args, filelist)
185 elif len(args.file_path) > 0:
186 exitcode = run_unit_tests(args, [args.file_path])
187 else:
188 exitcode = run_unit_tests(args)
189 exit(exitcode)
190
191
192if __name__ == '__main__':
193 parser = argparse.ArgumentParser()
194 parser.add_argument("-p",
195 "--pep8-ignore",
196 nargs="*",
197 default="E501",
198 help="Space separated list of pep8 exclusions",
199 dest="pep8_ignore")
200 parser.add_argument("-s",
201 "--shellcheck-ignore",
202 nargs="*",
203 help="Space separated list of shellcheck exclusions",
204 dest="shellcheck_ignore")
205 parser.add_argument("-g",
206 "--git-latest",
207 action="store_true",
208 default=False,
209 help="If set, the script will try to evaluate files in last git \
210 commit instead of the whole repository",
211 dest="git_latest")
212 parser.add_argument("-f",
213 "--file-path",
214 default="",
215 help="Path to the file that should be checked",
216 dest="file_path")
217
218 args = parser.parse_args()
219 main(args)