blob: 94d394e35ac453803ef4900f4203fcb900596e66 [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
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:
121 ignore_string = "-e %s" % ignore_options
122 cmd = 'shellcheck %s' % ignore_string
123 return validate_external(cmd, filename, "SHELLCHECK")
124
125
126def validate_php(filename):
127 cmd = 'php -l'
128 return validate_external(cmd, filename, "PHPLINT")
129
130
131def validate_external(cmd, filename, prefix):
132 final_cmd = "%s %s 2>&1" % (cmd, filename)
133 status, output = subprocess.getstatusoutput(final_cmd)
134 if status == 0:
135 message = '* %s: [PASSED]: %s' % (prefix, filename)
136 print_stderr(message)
137 else:
138 result_message_list = []
139 result_message_list.append('* %s: [FAILED]: %s' % (prefix, filename))
140 result_message_list.append('* %s: [OUTPUT]:' % prefix)
141 for line in output.splitlines():
142 result_message_list.append(' ' + line)
143 publish_result(result_message_list)
144 return 1
145 return 0
146
147
148def validate_file(args, path):
149 exitcode = 0
150 if path.endswith(".yaml"):
151 exitcode = exitcode + validate_yaml(path)
152 exitcode = exitcode + metadata_check(path)
153 elif run_pep8 and path.endswith(".py"):
154 exitcode = pep8_check(path, args.pep8_ignore)
155 elif path.endswith(".php"):
156 exitcode = validate_php(path)
157 else:
158 # try shellcheck by default
159 exitcode = validate_shell(path, args.shellcheck_ignore)
160 return exitcode
161
162
163def run_unit_tests(args, filelist=None):
164 exitcode = 0
165 if filelist is not None:
166 for filename in filelist:
167 exitcode = validate_file(args, filename)
168 else:
169 for root, dirs, files in os.walk('.'):
170 if not root.startswith("./.git"):
171 for name in files:
172 exitcode = validate_file(
173 args,
174 root + "/" + name)
175 return exitcode
176
177
178def main(args):
179 exitcode = 0
180 if args.git_latest:
181 # check if git exists
182 git_status, git_result = subprocess.getstatusoutput(
183 "git show --name-only --format=''")
184 if git_status == 0:
185 filelist = git_result.split()
186 exitcode = run_unit_tests(args, filelist)
187 elif len(args.file_path) > 0:
188 exitcode = run_unit_tests(args, [args.file_path])
189 else:
190 exitcode = run_unit_tests(args)
191 exit(exitcode)
192
193
194if __name__ == '__main__':
195 parser = argparse.ArgumentParser()
196 parser.add_argument("-p",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000197 "--pep8-ignore",
198 nargs="*",
199 default="E501",
200 help="Space separated list of pep8 exclusions",
201 dest="pep8_ignore")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000202 parser.add_argument("-s",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000203 "--shellcheck-ignore",
204 nargs="*",
205 help="Space separated list of shellcheck exclusions",
206 dest="shellcheck_ignore")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000207 parser.add_argument("-g",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000208 "--git-latest",
209 action="store_true",
210 default=False,
211 help="If set, the script will try to evaluate files in last git \
212 commit instead of the whole repository",
213 dest="git_latest")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000214 parser.add_argument("-f",
Milosz Wasilewski7ae041c2016-11-07 11:10:06 +0000215 "--file-path",
216 default="",
217 help="Path to the file that should be checked",
218 dest="file_path")
Milosz Wasilewskidbf52aa2016-11-01 17:51:26 +0000219
220 args = parser.parse_args()
221 main(args)