mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> Co-authored-by: C.L. <killer65311@gmail.com>
157 lines
4.7 KiB
Python
157 lines
4.7 KiB
Python
import codecs
|
|
import fnmatch
|
|
import functools
|
|
import glob
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
# simple way to check if we're running on github actions, or on a local machine
|
|
on_github = os.getenv("GITHUB_ACTIONS") == "true"
|
|
|
|
def green(text):
|
|
return "\033[32m" + str(text) + "\033[0m"
|
|
|
|
def red(text):
|
|
return "\033[31m" + str(text) + "\033[0m"
|
|
|
|
def blue(text):
|
|
return "\033[34m" + str(text) + "\033[0m"
|
|
|
|
schema = json.load(sys.stdin)
|
|
file_reference = schema["file"]
|
|
file_reference_basename = os.path.basename(file_reference)
|
|
scannable_directory = schema["scannable_directory"]
|
|
subdirectories = schema["subdirectories"]
|
|
FORBIDDEN_INCLUDES = schema["forbidden_includes"]
|
|
excluded_files = schema["excluded_files"]
|
|
|
|
def post_error(string):
|
|
print(red(f"Ticked File Enforcement [{file_reference}]: " + string))
|
|
if on_github:
|
|
print(f"::error file={file_reference},line=1,title=Ticked File Enforcement::{string}")
|
|
|
|
for excluded_file in excluded_files:
|
|
full_file_path = scannable_directory + excluded_file
|
|
if not os.path.isfile(full_file_path):
|
|
post_error(f"Excluded file {full_file_path} does not exist, please remove it!")
|
|
sys.exit(1)
|
|
|
|
file_extensions = ("dm", "dmf")
|
|
|
|
reading = False
|
|
lines = []
|
|
total = 0
|
|
|
|
with open(file_reference, 'r') as file:
|
|
for line in file:
|
|
total += 1
|
|
line = line.strip()
|
|
|
|
if line == "// BEGIN_INCLUDE":
|
|
reading = True
|
|
continue
|
|
elif line == "// END_INCLUDE":
|
|
break
|
|
elif not reading:
|
|
continue
|
|
|
|
lines.append(line)
|
|
|
|
offset = total - len(lines)
|
|
print(blue(f"Ticked File Enforcement: {offset} lines were ignored in output for [{file_reference}]."))
|
|
fail_no_include = False
|
|
|
|
scannable_files = []
|
|
for file_extension in file_extensions:
|
|
compiled_directory = f"{scannable_directory}/**/*.{file_extension}"
|
|
scannable_files += glob.glob(compiled_directory, recursive=True)
|
|
|
|
if len(scannable_files) == 0:
|
|
post_error(f"No files were found in {scannable_directory}. Ticked File Enforcement has failed!")
|
|
sys.exit(1)
|
|
|
|
for code_file in scannable_files:
|
|
dm_path = ""
|
|
|
|
if subdirectories is True:
|
|
dm_path = code_file.replace('/', '\\')
|
|
else:
|
|
dm_path = os.path.basename(code_file)
|
|
|
|
included = f"#include \"{dm_path}\"" in lines
|
|
|
|
forbid_include = False
|
|
for forbidable in FORBIDDEN_INCLUDES:
|
|
if not fnmatch.fnmatch(code_file, forbidable):
|
|
continue
|
|
|
|
forbid_include = True
|
|
|
|
if included:
|
|
post_error(f"{dm_path} should NOT be included.")
|
|
fail_no_include = True
|
|
|
|
if forbid_include:
|
|
continue
|
|
|
|
if not included:
|
|
if(dm_path == file_reference_basename):
|
|
continue
|
|
|
|
if(dm_path in excluded_files):
|
|
continue
|
|
|
|
post_error(f"Missing include for {dm_path}.")
|
|
fail_no_include = True
|
|
|
|
if fail_no_include:
|
|
sys.exit(1)
|
|
|
|
def compare_lines(a, b):
|
|
# Remove initial include as well as the final quotation mark
|
|
a = a[len("#include \""):-1].lower()
|
|
b = b[len("#include \""):-1].lower()
|
|
|
|
split_by_period = a.split('.')
|
|
a_suffix = ""
|
|
if len(split_by_period) >= 2:
|
|
a_suffix = split_by_period[len(split_by_period) - 1]
|
|
split_by_period = b.split('.')
|
|
b_suffix = ""
|
|
if len(split_by_period) >= 2:
|
|
b_suffix = split_by_period[len(split_by_period) - 1]
|
|
|
|
a_segments = a.split('\\')
|
|
b_segments = b.split('\\')
|
|
|
|
for (a_segment, b_segment) in zip(a_segments, b_segments):
|
|
a_is_file = a_segment.endswith(file_extensions)
|
|
b_is_file = b_segment.endswith(file_extensions)
|
|
|
|
# code\something.dm will ALWAYS come before code\directory\something.dm
|
|
if a_is_file and not b_is_file:
|
|
return -1
|
|
|
|
if b_is_file and not a_is_file:
|
|
return 1
|
|
|
|
# interface\something.dm will ALWAYS come after code\something.dm
|
|
if a_segment != b_segment:
|
|
# if we're at the end of a compare, then this is about the file name
|
|
# files with longer suffixes come after ones with shorter ones
|
|
if a_suffix != b_suffix:
|
|
return (a_suffix > b_suffix) - (a_suffix < b_suffix)
|
|
return (a_segment > b_segment) - (a_segment < b_segment)
|
|
|
|
print(f"Two lines were exactly the same ({a} vs. {b})")
|
|
sys.exit(1)
|
|
|
|
sorted_lines = sorted(lines, key = functools.cmp_to_key(compare_lines))
|
|
for (index, line) in enumerate(lines):
|
|
if sorted_lines[index] != line:
|
|
post_error(f"The include at line {index + offset} is out of order ({line}, expected {sorted_lines[index]})")
|
|
sys.exit(1)
|
|
|
|
print(green(f"Ticked File Enforcement: [{file_reference}] All includes (for {len(scannable_files)} scanned files) are in order!"))
|