diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index f30e5337529bf0..1380bc6835fa9b 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -18,7 +18,7 @@ jobs: with: python-version: '3.10' - name: AzerothCore codestyle - run: python ./apps/codestyle/codestyle.py + run: python ./apps/codestyle/codestyle-cpp.py - name: C++ Advanced run: | sudo apt update -y diff --git a/.github/workflows/sql-codestyle.yml b/.github/workflows/sql-codestyle.yml index a0208871609ac6..fa2c64292d1eda 100644 --- a/.github/workflows/sql-codestyle.yml +++ b/.github/workflows/sql-codestyle.yml @@ -13,5 +13,9 @@ jobs: if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft steps: - uses: actions/checkout@v4 - - name: Check pending SQL - run: source ./apps/ci/ci-pending.sh + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: AzerothCore codestyle + run: python ./apps/codestyle/codestyle-sql.py diff --git a/apps/ci/ci-pending.sh b/apps/ci/ci-pending.sh deleted file mode 100644 index 87858b33c6fc5b..00000000000000 --- a/apps/ci/ci-pending.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -echo "Pending SQL check script:" -echo - -# We want to ensure the end of file has a semicolon and doesn't have extra -# newlines -find data/sql/updates/pending* -name "*.sql" -type f | while read -r file; do - # The first sed script collapses all strings into an empty string. The - # contents of strings aren't necessary for this check and its still valid - # sql. - # - # The second rule removes sql comments. - ERR_AT_EOF="$(sed -e "s/'.*'/''/g" -e 's/ --([^-])*$//' "$file" | tr -d '\n ' | tail -c 1)" - if [[ "$ERR_AT_EOF" != ";" ]]; then - echo "Missing Semicolon (;) or multiple newlines at the end of the file." - exit 1 - else - echo "> Semicolon check - OK" - fi -done - -find data/sql/updates/pending* -name "*.sql" -type f | while read -r file; do - if sed "s/'.*'\(.*\)/\1/g" "$file" | grep -q -i -E "broadcast_text"; then - echo "> broadcast_text check - Failed" - echo " - DON'T EDIT broadcast_text TABLE UNLESS YOU KNOW WHAT YOU ARE DOING!" - echo " - This error can safely be ignored if the changes are approved to be sniffed." - exit 1 - else - echo "> broadcast_text check - OK" - fi -done - -echo -echo "Everything looks good" diff --git a/apps/codestyle/codestyle.py b/apps/codestyle/codestyle-cpp.py similarity index 100% rename from apps/codestyle/codestyle.py rename to apps/codestyle/codestyle-cpp.py diff --git a/apps/codestyle/codestyle-sql.py b/apps/codestyle/codestyle-sql.py new file mode 100644 index 00000000000000..840be4904588d7 --- /dev/null +++ b/apps/codestyle/codestyle-sql.py @@ -0,0 +1,141 @@ +import io +import os +import sys +import re +import glob + +# Get the pending directory of the project +base_dir = os.getcwd() +pattern = os.path.join(base_dir, 'data/sql/updates/pending_db_*') +src_directory = glob.glob(pattern) + +# Global variables +error_handler = False +results = { + "Multiple blank lines check": "Passed", + "Trailing whitespace check": "Passed", + "SQL codestyle check": "Passed", +} + +# Collect all files in all directories +def collect_files_from_directories(directories: list) -> list: + all_files = [] + for directory in directories: + for root, _, files in os.walk(directory): + for file in files: + if not file.endswith('.sh'): # Skip .sh files + all_files.append(os.path.join(root, file)) + return all_files + +# Main function to parse all the files of the project +def parsing_file(files: list) -> None: + print("Starting AzerothCore SQL Codestyle check...") + print(" ") + print("Please read the SQL Standards for AzerothCore:") + print("https://www.azerothcore.org/wiki/sql-standards") + print(" ") + + # Iterate over all files + for file_path in files: + try: + with open(file_path, 'r', encoding='utf-8') as file: + multiple_blank_lines_check(file, file_path) + trailing_whitespace_check(file, file_path) + sql_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 + check_failed = False + # 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}") + check_failed = True + if check_failed: + error_handler = True + results["Trailing whitespace check"] = "Failed" + +# Codestyle patterns checking for various codestyle issues +def sql_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 [match for match in ['broadcast_text'] if match in line]: + print( + f"DON'T EDIT broadcast_text TABLE UNLESS YOU KNOW WHAT YOU ARE DOING!\nThis error can safely be ignored if the changes are approved to be sniffed: {file_path} at line {line_number}") + check_failed = True + if [match for match in [';;'] if match in line]: + print( + f"Double semicolon (;;) found in {file_path} at line {line_number}") + check_failed = True + if re.match(r"\t", line): + print( + f"Tab found! Replace it to 4 spaces: {file_path} at line {line_number}") + check_failed = True + + # Ignore comments (remove content after --) + line_without_comment = re.sub(r'--.*', '', line).strip() + # Check if the last non-empty line ends with a semicolon + if not line_without_comment.endswith(';'): + print( + f"The last non-empty line does not end with a semicolon: {file_path}") + check_failed = True + + last_line = line[-1].strip() + if last_line: + print( + f"The last line is not a newline. Please add a newline: {file_path}") + check_failed = True + + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["SQL codestyle check"] = "Failed" + +# Collect all files from matching directories +all_files = collect_files_from_directories(src_directory) + +# Main function +parsing_file(all_files)