blob: 30e7479f9e48d4fd1c43c57d2123dfa7a690d93e [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
Milosz Wasilewskiab7645a2016-11-07 10:45:34 +000032def pep8_check(filepath, ignore_options=[]):
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +000033 _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
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +000061
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +000062def metadata_check(filepath):
63 if filepath.lower().endswith("yaml"):
64 with open(filepath, "r") as f:
65 result_message_list = []
66 y = yaml.load(f.read())
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +000067 if 'metadata' not in y.keys():
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +000068 result_message_list.append("* METADATA [FAILED]: " + filepath)
69 result_message_list.append("\tmetadata section missing")
70 publish_result(result_message_list)
71 exit(1)
72 metadata_dict = y['metadata']
73 mandatory_keys = set([
74 'name',
75 'format',
76 'description',
77 'maintainer',
78 'os',
79 'devices'])
80 if not mandatory_keys.issubset(set(metadata_dict.keys())):
81 result_message_list.append("* METADATA [FAILED]: " + filepath)
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +000082 result_message_list.append("\tmandatory keys missing: %s" %
83 mandatory_keys.difference(set(metadata_dict.keys())))
84 result_message_list.append("\tactual keys present: %s" %
85 metadata_dict.keys())
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +000086 publish_result(result_message_list)
87 return 1
88 for key in mandatory_keys:
89 if len(metadata_dict[key]) == 0:
90 result_message_list.append("* METADATA [FAILED]: " + filepath)
91 result_message_list.append("\t%s has no content" % key)
92 publish_result(result_message_list)
93 return 1
94 result_message_list.append("* METADATA [PASSED]: " + filepath)
95 publish_result(result_message_list)
96 return 0
97
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +000098
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +000099def validate_yaml(filename):
100 with open(filename, "r") as f:
101 try:
102 y = yaml.load(f.read())
103 message = "* YAMLVALID: [PASSED]: " + filename
104 print_stderr(message)
105 except:
106 message = "* YAMLVALID: [FAILED]: " + filename
107 result_message_list = []
108 result_message_list.append(message)
109 result_message_list.append("\n\n")
110 exc_type, exc_value, exc_traceback = sys.exc_info()
111 for line in traceback.format_exception_only(exc_type, exc_value):
112 result_message_list.append(' ' + line)
113 publish_result(result_message_list)
114 return 1
115 return 0
116
117
118def validate_shell(filename, ignore_options):
119 ignore_string = ""
120 if ignore_options is not None:
Milosz Wasilewskiab7645a2016-11-07 10:45:34 +0000121 ignore_string = "-e %s" % " ".join(ignore_options)
122 if len(ignore_string) < 4: # contains only "-e "
123 ignore_string = ""
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000124 cmd = 'shellcheck %s' % ignore_string
125 return validate_external(cmd, filename, "SHELLCHECK")
126
127
128def validate_php(filename):
129 cmd = 'php -l'
130 return validate_external(cmd, filename, "PHPLINT")
131
132
133def validate_external(cmd, filename, prefix):
134 final_cmd = "%s %s 2>&1" % (cmd, filename)
135 status, output = subprocess.getstatusoutput(final_cmd)
136 if status == 0:
137 message = '* %s: [PASSED]: %s' % (prefix, filename)
138 print_stderr(message)
139 else:
140 result_message_list = []
141 result_message_list.append('* %s: [FAILED]: %s' % (prefix, filename))
142 result_message_list.append('* %s: [OUTPUT]:' % prefix)
143 for line in output.splitlines():
144 result_message_list.append(' ' + line)
145 publish_result(result_message_list)
146 return 1
147 return 0
148
149
150def validate_file(args, path):
151 exitcode = 0
152 if path.endswith(".yaml"):
153 exitcode = exitcode + validate_yaml(path)
154 exitcode = exitcode + metadata_check(path)
155 elif run_pep8 and path.endswith(".py"):
156 exitcode = pep8_check(path, args.pep8_ignore)
157 elif path.endswith(".php"):
158 exitcode = validate_php(path)
159 else:
160 # try shellcheck by default
161 exitcode = validate_shell(path, args.shellcheck_ignore)
162 return exitcode
163
164
165def run_unit_tests(args, filelist=None):
166 exitcode = 0
167 if filelist is not None:
168 for filename in filelist:
169 exitcode = validate_file(args, filename)
170 else:
171 for root, dirs, files in os.walk('.'):
172 if not root.startswith("./.git"):
173 for name in files:
174 exitcode = validate_file(
175 args,
176 root + "/" + name)
177 return exitcode
178
179
180def main(args):
181 exitcode = 0
182 if args.git_latest:
183 # check if git exists
184 git_status, git_result = subprocess.getstatusoutput(
185 "git show --name-only --format=''")
186 if git_status == 0:
187 filelist = git_result.split()
188 exitcode = run_unit_tests(args, filelist)
189 elif len(args.file_path) > 0:
190 exitcode = run_unit_tests(args, [args.file_path])
191 else:
192 exitcode = run_unit_tests(args)
193 exit(exitcode)
194
195
196if __name__ == '__main__':
197 parser = argparse.ArgumentParser()
198 parser.add_argument("-p",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000199 "--pep8-ignore",
200 nargs="*",
Milosz Wasilewskiab7645a2016-11-07 10:45:34 +0000201 default=["E501"],
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000202 help="Space separated list of pep8 exclusions",
203 dest="pep8_ignore")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000204 parser.add_argument("-s",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000205 "--shellcheck-ignore",
206 nargs="*",
207 help="Space separated list of shellcheck exclusions",
208 dest="shellcheck_ignore")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000209 parser.add_argument("-g",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000210 "--git-latest",
211 action="store_true",
212 default=False,
213 help="If set, the script will try to evaluate files in last git \
214 commit instead of the whole repository",
215 dest="git_latest")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000216 parser.add_argument("-f",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000217 "--file-path",
218 default="",
219 help="Path to the file that should be checked",
220 dest="file_path")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000221
222 args = parser.parse_args()
223 main(args)