diff --git a/.github/scripts/check_gd_nomenclature.py b/.github/scripts/check_gd_nomenclature.py new file mode 100644 index 00000000..83dc4120 --- /dev/null +++ b/.github/scripts/check_gd_nomenclature.py @@ -0,0 +1,63 @@ +import os +import re +import sys + +EXCLUDED_DIRS = {"addons", ".git", ".github"} + +PASCAL_CASE = re.compile(r"^[A-Z][A-Za-z0-9]*$") +SNAKE_CASE = re.compile(r"^_?[a-z][a-z0-9_]*$") +UPPER_SNAKE_CASE = re.compile(r"^[A-Z][A-Z0-9_]*$") + +issues = [] + +for root, dirs, files in os.walk('.', topdown=True): + rel_root = os.path.relpath(root, '.') + if any(rel_root == ex or rel_root.startswith(f"{ex}{os.sep}") for ex in EXCLUDED_DIRS): + dirs[:] = [] + continue + for fname in files: + if fname.endswith('.gd'): + path = os.path.join(root, fname) + try: + with open(path, 'r', encoding='utf-8') as f: + lines = f.readlines() + except Exception as e: + issues.append((path, 0, 'error', f'Could not read file: {e}')) + continue + for idx, line in enumerate(lines, 1): + stripped = line.strip() + if stripped.startswith('#') or not stripped: + continue + m = re.match(r"class_name\s+([A-Za-z0-9_]+)", stripped) + if m: + name = m.group(1) + if not PASCAL_CASE.match(name): + issues.append((path, idx, 'class', name)) + m = re.match(r"func\s+([A-Za-z0-9_]+)", stripped) + if m: + name = m.group(1).split('(')[0] + if not SNAKE_CASE.match(name): + issues.append((path, idx, 'function', name)) + m = re.match(r"(?:@export\s+)?var\s+([A-Za-z0-9_]+)", stripped) + if m: + name = m.group(1) + if not SNAKE_CASE.match(name): + issues.append((path, idx, 'variable', name)) + m = re.match(r"const\s+([A-Za-z0-9_]+)", stripped) + if m: + name = m.group(1) + if not UPPER_SNAKE_CASE.match(name): + issues.append((path, idx, 'constant', name)) + m = re.match(r"signal\s+([A-Za-z0-9_]+)", stripped) + if m: + name = m.group(1) + if not SNAKE_CASE.match(name): + issues.append((path, idx, 'signal', name)) + +if issues: + print('GDScript naming issues found:') + for path, idx, kind, name in issues: + print(f"{path}:{idx}: {kind} '{name}' does not follow convention") + sys.exit(1) +else: + print('All GDScript files follow the naming conventions.') diff --git a/.github/workflows/gdscript-naming.yml b/.github/workflows/gdscript-naming.yml new file mode 100644 index 00000000..d6dbe41a --- /dev/null +++ b/.github/workflows/gdscript-naming.yml @@ -0,0 +1,34 @@ +name: GDScript Naming Convention + +on: + pull_request: + types: [opened, synchronize] + +jobs: + gdscript_naming: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v3 + - name: Run GDScript naming convention check + id: naming + run: | + python3 .github/scripts/check_gd_nomenclature.py > gd_naming_output.txt + continue-on-error: true + - name: Comment on PR with naming convention output + if: steps.naming.outcome != 'success' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const body = fs.readFileSync('gd_naming_output.txt', 'utf8'); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + - name: Fail if naming convention check failed + if: steps.naming.outcome != 'success' + run: exit 1