From 574ef5203a4cb2c5cfd476323089dd9e9217999a Mon Sep 17 00:00:00 2001 From: Grimdhex <176165533+Grimdhex@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:19:13 +0200 Subject: [PATCH] chore(Apps/CodeStyle): rewrite Ci-codestyle in python (#19714) * initial version * port most current checks * allow to specify for each checks the exclude files * Update _ci-codestyle.py * fix trailing whitespace check and the error_handler logic * optimize the script to parse all files only one time * restore the error handling after that all checks are passed * fix double blank lines at the end of the file * improve outputs and the codestyle * . * update github workflow to the new script *drop the old ci-codestyle *move and rename the python script * use getcwd instead abspath to define src_directory where the script is launch * test for github workflow * few cleanup * fix the logic with the result output * remove GetTypeId from the output result * restore GetTypeId() check * fix reviews * remove GetTypeId check for gameobjects *not currently fixed in the core * fix type hint for file parameters * remove the test file --- .github/workflows/codestyle.yml | 6 +- apps/ci/ci-codestyle.sh | 40 --------- apps/codestyle/codestyle.py | 148 ++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 41 deletions(-) delete mode 100644 apps/ci/ci-codestyle.sh create mode 100644 apps/codestyle/codestyle.py diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index ce72ed58f..f24a1d252 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -13,8 +13,12 @@ jobs: if: github.repository == 'azerothcore/azerothcore-wotlk' steps: - uses: actions/checkout@v4 + - name: Setup python + uses: actions/setup-python@v4 + with: + python-version: '3.10' - name: AzerothCore codestyle - run: source ./apps/ci/ci-codestyle.sh + run: python ./apps/codestyle/codestyle.py - name: C++ Advanced run: | sudo apt update -y diff --git a/apps/ci/ci-codestyle.sh b/apps/ci/ci-codestyle.sh deleted file mode 100644 index c96a31df0..000000000 --- a/apps/ci/ci-codestyle.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -set -e - -echo "Codestyle check script:" -echo - -declare -A singleLineRegexChecks=( - ["LOG_.+GetCounter"]="Use ObjectGuid::ToString().c_str() method instead of ObjectGuid::GetCounter() when logging. Check the lines above" - ["[[:blank:]]$"]="Remove whitespace at the end of the lines above" - ["\t"]="Replace tabs with 4 spaces in the lines above" -) - -for check in ${!singleLineRegexChecks[@]}; do - echo " Checking RegEx: '${check}'" - - if grep -P -r -I -n ${check} src; then - echo - echo "${singleLineRegexChecks[$check]}" - exit 1 - fi -done - -declare -A multiLineRegexChecks=( - ["LOG_[^;]+GetCounter"]="Use ObjectGuid::ToString().c_str() method instead of ObjectGuid::GetCounter() when logging. Check the lines above" - ["\n\n\n"]="Multiple blank lines detected, keep only one. Check the files above" -) - -for check in ${!multiLineRegexChecks[@]}; do - echo " Checking RegEx: '${check}'" - - if grep -Pzo -r -I ${check} src; then - echo - echo - echo "${multiLineRegexChecks[$check]}" - exit 1 - fi -done - -echo -echo "Everything looks good" diff --git a/apps/codestyle/codestyle.py b/apps/codestyle/codestyle.py new file mode 100644 index 000000000..8c595d704 --- /dev/null +++ b/apps/codestyle/codestyle.py @@ -0,0 +1,148 @@ +import io +import os +import sys + +# Get the src directory of the project +src_directory = os.path.join(os.getcwd(), 'src') + +# Global variables +error_handler = False +results = { + "Multiple blank lines check": "Passed", + "Trailing whitespace check": "Passed", + "GetCounter() check": "Passed", + "GetTypeId() check": "Passed", + "NpcFlagHelpers check": "Passed" +} + +# Main function to parse all the files of the project +def parsing_file(directory: str) -> None: + for root, _, files in os.walk(directory): + for file in files: + if not file.endswith('.ico'): # Skip .ico files that cannot be read + file_path = os.path.join(root, file) + file_name = file + try: + with open(file_path, 'r', encoding='utf-8') as file: + multiple_blank_lines_check(file, file_path) + trailing_whitespace_check(file, file_path) + get_counter_check(file, file_path) + if file_name != 'Object.h': + get_typeid_check(file, file_path) + if file_name != 'Unit.h': + npcflags_helpers_check(file, file_path) + except UnicodeDecodeError: + print(f"\nCould not decode file {file_path}") + sys.exit(1) + # Output the results + print("") + for check, result in results.items(): + print(f"{check} : {result}") + if error_handler: + print("\nPlease fix the codestyle issues above.") + sys.exit(1) + else: + print(f"\nEverything looks good") + +# Codestyle patterns checking for multiple blank lines +def multiple_blank_lines_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + consecutive_blank_lines = 0 + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if line.strip() == '': + consecutive_blank_lines += 1 + if consecutive_blank_lines > 1: + print(f"Multiple blank lines found in {file_path} at line {line_number - 1}") + check_failed = True + else: + consecutive_blank_lines = 0 + # Additional check for the end of the file + if consecutive_blank_lines >= 1: + print(f"Multiple blank lines found at the end of: {file_path}") + check_failed = True + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["Multiple blank lines check"] = "Failed" + +# Codestyle patterns checking for whitespace at the end of the lines +def trailing_whitespace_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if line.endswith(' \n'): + print(f"Trailing whitespace found: {file_path} at line {line_number}") + if not error_handler: + error_handler = True + results["Trailing whitespace check"] = "Failed" + +# Codestyle patterns checking for ObjectGuid::GetCounter() +def get_counter_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'ObjectGuid::GetCounter()' in line: + print(f"Please use ObjectGuid::ToString().c_str() instead ObjectGuid::GetCounter(): {file_path} at line {line_number}") + if not error_handler: + error_handler = True + results["GetCounter() check"] = "Failed" + +# Codestyle patterns checking for GetTypeId() +def get_typeid_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'GetTypeId() == TYPEID_PLAYER' in line: + print(f"Please use IsPlayer() instead GetTypeId(): {file_path} at line {line_number}") + check_failed = True + if 'GetTypeId() == TYPEID_ITEM' in line: + print(f"Please use IsItem() instead GetTypeId(): {file_path} at line {line_number}") + check_failed = True + if 'GetTypeId() == TYPEID_DYNOBJECT' in line: + print(f"Please use IsDynamicObject() instead GetTypeId(): {file_path} at line {line_number}") + check_failed = True + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["GetTypeId() check"] = "Failed" + +# Codestyle patterns checking for NpcFlag helpers +def npcflags_helpers_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'GetUInt32Value(UNIT_NPC_FLAGS)' in line: + print( + f"Please use GetNpcFlags() instead GetUInt32Value(UNIT_NPC_FLAGS): {file_path} at line {line_number}") + check_failed = True + if 'HasFlag(UNIT_NPC_FLAGS,' in line: + print( + f"Please use HasNpcFlag() instead HasFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + check_failed = True + if 'SetUInt32Value(UNIT_NPC_FLAGS,' in line: + print( + f"Please use ReplaceAllNpcFlags() instead SetUInt32Value(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + check_failed = True + if 'SetFlag(UNIT_NPC_FLAGS,' in line: + print( + f"Please use SetNpcFlag() instead SetFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + check_failed = True + if 'RemoveFlag(UNIT_NPC_FLAGS,' in line: + print( + f"Please use RemoveNpcFlag() instead RemoveFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["NpcFlagHelpers check"] = "Failed" + +# Main function +parsing_file(src_directory)