|
import os |
|
import re |
|
import sys |
|
import subprocess |
|
import logging |
|
import collections |
|
|
|
ExecutionResult = collections.namedtuple( |
|
"ExecutionResult", |
|
"status, stdout, stderr" |
|
) |
|
|
|
|
|
def execute(cmd): |
|
""" Executes specified command """ |
|
process = subprocess.Popen( |
|
cmd, |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.PIPE |
|
) |
|
stdout, stderr = process.communicate() |
|
status = process.poll() |
|
return ExecutionResult(status, stdout, stderr) |
|
|
|
|
|
def _current_commit(): |
|
""" Returns the current commit. """ |
|
if execute("git rev-parse --verify HEAD".split()).status: |
|
return "4b825dc642cb6eb9a060e54bf8d69288fbee4904" # Empty tree hash |
|
else: |
|
return "HEAD" |
|
|
|
|
|
def echo_status(title, error_levels): |
|
"""Used for reporting and logging errors""" |
|
if error_levels: |
|
logging.warning("\n{}".format(title)) |
|
for p in error_levels: |
|
logging.warning("{}".format(p)) |
|
logging.warning("\n") |
|
return False |
|
return True |
|
|
|
|
|
def get_list_of_committed_files(): |
|
""" Returns a list of files about to be commited. """ |
|
files = [] |
|
|
|
diff_index_cmd = "git diff-index --cached %s" % _current_commit() |
|
output = subprocess.check_output( |
|
diff_index_cmd.split() |
|
) |
|
for result in output.decode('utf-8').split("\n"): |
|
if result != "": |
|
result = result.split() |
|
if result[4] in ["A", "M"]: |
|
files.append(result[5]) |
|
|
|
return files |
|
|
|
|
|
def luacheck_command(files): |
|
"""The used luacheck command""" |
|
command = ["luacheck"] |
|
command.extend(files) |
|
command.extend( |
|
["--config", os.path.join(os.path.dirname(os.path.dirname(__file__)), "luainspect", "defold_std.luacheckrc")]) |
|
command.extend(["--ignore", "61."]) |
|
command.append("--no-unused-args") |
|
command.append("--no-color") |
|
command.append("--no-max-line-length") |
|
return command |
|
|
|
|
|
def is_lua_file(f): |
|
"""There are more than one kind of lua file""" |
|
if f.endswith(".lua") or f.endswith(".gui_script") or f.endswith(".script"): |
|
return True |
|
|
|
|
|
def clean_output(output): |
|
"""The output we get from the luacheck command isn't in a format I like, so we clean it up""" |
|
output = output.decode("utf-8") |
|
clean = list() |
|
lines = output.split("\n") |
|
old = "" |
|
for line in lines: |
|
fix_line = line.replace("", "") # Remove odd whitespace character |
|
if old.startswith("Checking") and not old.endswith("OK"): # Remove the extra new line after Checking line |
|
old = "pass" |
|
continue |
|
if not line.startswith("Checking"): |
|
a = re.search("(/.*?\.\w+)", line) |
|
path = a.group(1) if a else None |
|
fix_line = fix_line.replace(path, os.path.basename(path)) if path else fix_line |
|
|
|
if fix_line and not (fix_line.endswith("OK") or fix_line.startswith("Total:")): |
|
clean.append(fix_line) |
|
|
|
old = line |
|
return clean |
|
|
|
|
|
def check_file(lua_files): |
|
"""Check a single lua file""" |
|
if lua_files: |
|
cmd = luacheck_command(lua_files) |
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
|
out, err = process.communicate() |
|
return clean_output(out) |
|
return [] |
|
|
|
|
|
def run_test(root, files): |
|
error_levels = list() |
|
lua_files = list() |
|
for f in files: |
|
file_path = os.path.join(root, f) |
|
if is_lua_file(file_path): |
|
lua_files.append(file_path) |
|
error_levels.extend(check_file(lua_files)) |
|
echo_status("STATIC CODE ANALYSIS", error_levels) |
|
|
|
return False if error_levels else True |
|
|
|
|
|
def run_on_commit(root): |
|
return run_test(root, get_list_of_committed_files()) |
|
|
|
|
|
if __name__ == '__main__': |
|
if len(sys.argv) == 2: |
|
run_on_commit(sys.argv[1]) |
|
else: |
|
run_on_commit(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))) |
Just wanted to say that this was super helpful, thanks!
I found this flag also very helpful:
--allow-defined
. Otherwise it seems I have to define all my functions in the luacheck config.