Files
Paradise/tools/ci/identical_variables.py
Contrabang a57a8b8c40 Adds CI to error on unnecessary variable modification (#29861)
* begin the burndown? (y/n)

* okay update requirements?

* change the error name

* there we go

* tweak

* first bit of the burndown

* more

* burndown complete

* return the exit code!

* review

* Revert "first bit of the burndown"

This reverts commit 34155bd991.

* fix

* fixes

* last fix

* remove deprecated avulto stuff
2025-07-27 22:26:13 +00:00

66 lines
2.7 KiB
Python

from avulto import DME
from collections import namedtuple
import os
import sys
import time
Failure = namedtuple("Failure", ["filename", "lineno", "message"])
RED = "\033[0;31m"
GREEN = "\033[0;32m"
BLUE = "\033[0;34m"
NC = "\033[0m" # No Color
def print_error(message: str, filename: str, line_number: int):
if os.getenv("GITHUB_ACTIONS") == "true": # We're on github, output in a special format.
print(f"::error file={filename},line={line_number},title=Identical Variables::{filename}:{line_number}: {message}")
else:
print(f"{filename}:{line_number}: {message}")
def main():
print("identical_variables started")
exit_code = 0
start = time.time()
dme = DME.from_file("paradise.dme")
all_failures = []
for path in dme.subtypesof("/"):
typepath = dme.type_decl(path)
for variable_name in typepath.var_names(modified=True):
modded = typepath.var_decl(variable_name, False)
if(not modded):
all_failures.append(Failure(typepath.source_loc.file_path, typepath.source_loc.line, f"{RED}Avulto failed to read {path.rel}::{variable_name}.{NC} This is probably not your fault."))
continue
if path.parent.is_root:
continue
parent_typepath = dme.type_decl(path.parent)
original = parent_typepath.var_decl(variable_name, True)
if(modded.const_val == original.const_val):
if(modded.const_val == None): # Both proc calls (like sound() or icon()) and nulls are treated as "None", this sucks.
continue
# Make an exception for directional helpers, where you cant guarantee.
if(modded.name in ["dir", "pixel_x", "pixel_y"] and path.parent.stem in ["directional", "offset"]):
continue
# Make an exception for subsystems, as they are much less OOP dependent.
if(path.child_of("/datum/controller/subsystem")):
continue
# And make an exception for this fucked up edge case. wtf.
if(path.rel == "/obj" and variable_name == "layer"):
continue
all_failures.append(Failure(typepath.source_loc.file_path, typepath.source_loc.line, f"{RED}{path.rel}{NC} has a identical variable to its parents: {RED}{variable_name}{NC} = {modded.const_val}"))
if all_failures:
exit_code = 1
for failure in all_failures:
print_error(failure.message, failure.filename, failure.lineno)
end = time.time()
print(f"identical_variables tests completed in {end - start:.2f}s\n")
sys.exit(exit_code)
if __name__ == "__main__":
main()