Add and update name convention
Some checks failed
Some checks failed
This commit is contained in:
@@ -11,6 +11,7 @@ Usage examples:
|
||||
python tools/run_development.py --ruff # Only Python format & lint
|
||||
python tools/run_development.py --validate # Only file validation
|
||||
python tools/run_development.py --validate --silent # Silent validation (only errors)
|
||||
python tools/run_development.py --naming # Only naming convention check
|
||||
python tools/run_development.py --test --yaml # Test results in YAML format
|
||||
python tools/run_development.py --steps lint ruff test # Custom workflow
|
||||
python tools/run_development.py --async-mode # Force async mode
|
||||
@@ -20,6 +21,7 @@ Features:
|
||||
- Python formatting & linting with Ruff (fast formatter + linter with auto-fix)
|
||||
- Test execution
|
||||
- YAML, TOML, and JSON file validation (respects .gitignore)
|
||||
- PascalCase naming convention checks for .tscn and .gd files
|
||||
- Colored output and comprehensive error reporting
|
||||
- Machine-readable YAML output for CI/CD integration
|
||||
- **Async processing for significantly faster execution with multiple files**
|
||||
@@ -622,6 +624,57 @@ def validate_json_file(file_path: Path) -> Tuple[bool, str]:
|
||||
return False, f"Error reading file: {e}"
|
||||
|
||||
|
||||
def check_naming_convention(file_path: Path) -> Tuple[bool, str]:
|
||||
"""Check if file follows PascalCase naming convention for .tscn and .gd files."""
|
||||
file_name = file_path.name
|
||||
|
||||
# Skip files that shouldn't follow PascalCase
|
||||
if file_path.suffix not in [".tscn", ".gd"]:
|
||||
return True, ""
|
||||
|
||||
# Skip certain directories and files
|
||||
skip_dirs = {"autoloads", "helpers"}
|
||||
if any(part in skip_dirs for part in file_path.parts):
|
||||
return True, ""
|
||||
|
||||
# Skip project-specific files that are exempt
|
||||
exempt_files = {
|
||||
"project.godot",
|
||||
"icon.svg",
|
||||
"export_presets.cfg",
|
||||
"default_bus_layout.tres",
|
||||
}
|
||||
if file_name in exempt_files:
|
||||
return True, ""
|
||||
|
||||
# Check PascalCase pattern
|
||||
name_without_ext = file_path.stem
|
||||
|
||||
# PascalCase: starts with capital letter, can have more capitals, no underscores or hyphens
|
||||
if not re.match(r"^[A-Z][a-zA-Z0-9]*$", name_without_ext):
|
||||
return (
|
||||
False,
|
||||
f"File name '{file_name}' should use PascalCase (e.g., 'MainMenu.gd', 'Match3Gameplay.tscn')",
|
||||
)
|
||||
|
||||
return True, ""
|
||||
|
||||
|
||||
def get_naming_files(project_root: Path) -> List[Path]:
|
||||
"""Get all .tscn and .gd files that should follow naming conventions."""
|
||||
files = []
|
||||
for pattern in ["**/*.tscn", "**/*.gd"]:
|
||||
files.extend(project_root.glob(pattern))
|
||||
|
||||
# Filter out files that should be ignored
|
||||
filtered_files = []
|
||||
for file_path in files:
|
||||
if not should_skip_file(file_path):
|
||||
filtered_files.append(file_path)
|
||||
|
||||
return filtered_files
|
||||
|
||||
|
||||
async def validate_json_file_async(file_path: Path) -> Tuple[bool, str]:
|
||||
"""Validate a JSON file asynchronously."""
|
||||
if aiofiles is None:
|
||||
@@ -1249,6 +1302,100 @@ async def run_validate_async(
|
||||
)
|
||||
|
||||
|
||||
def run_naming(
|
||||
project_root: Path, silent: bool = False, yaml_output: bool = False
|
||||
) -> Tuple[bool, Dict]:
|
||||
"""Check naming conventions for .tscn and .gd files."""
|
||||
if not silent and not yaml_output:
|
||||
print_header("📝 Naming Convention Check")
|
||||
|
||||
# Get all files that should follow naming conventions
|
||||
naming_files = get_naming_files(project_root)
|
||||
total_files = len(naming_files)
|
||||
|
||||
if total_files == 0:
|
||||
if not silent:
|
||||
msg = "No .tscn or .gd files found to check naming conventions."
|
||||
colored_msg = Colors.colorize(msg, Colors.YELLOW)
|
||||
print(colored_msg)
|
||||
return True, {"Total files": 0, "Valid files": 0, "Invalid files": 0}
|
||||
|
||||
if not silent and not yaml_output:
|
||||
count_msg = f"Checking naming conventions for {total_files} files..."
|
||||
colored_count = Colors.colorize(count_msg, Colors.BLUE)
|
||||
print(colored_count)
|
||||
print()
|
||||
|
||||
# Process files
|
||||
valid_files = 0
|
||||
invalid_files = 0
|
||||
all_output = []
|
||||
|
||||
for file_path in naming_files:
|
||||
is_valid, error_msg = check_naming_convention(file_path)
|
||||
relative_path = file_path.relative_to(project_root)
|
||||
|
||||
if is_valid:
|
||||
valid_files += 1
|
||||
if not silent and not yaml_output:
|
||||
file_msg = f"📄 Checking: {relative_path}"
|
||||
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
||||
success_msg = " ✅ Follows PascalCase convention"
|
||||
colored_success = Colors.colorize(success_msg, Colors.GREEN)
|
||||
all_output.extend([file_msg, success_msg])
|
||||
print(colored_file)
|
||||
print(colored_success)
|
||||
else:
|
||||
invalid_files += 1
|
||||
# Always show naming errors, even in silent mode
|
||||
if not yaml_output:
|
||||
file_msg = f"📄 Checking: {relative_path}"
|
||||
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
||||
error_msg_display = f" ❌ {error_msg}"
|
||||
colored_error = Colors.colorize(error_msg_display, Colors.RED)
|
||||
all_output.extend([file_msg, error_msg_display])
|
||||
print(colored_file)
|
||||
print(colored_error)
|
||||
|
||||
# Results summary
|
||||
overall_success = invalid_files == 0
|
||||
stats = {
|
||||
"Total files": total_files,
|
||||
"Valid files": valid_files,
|
||||
"Invalid files": invalid_files,
|
||||
}
|
||||
|
||||
if not silent and not yaml_output:
|
||||
print()
|
||||
if overall_success:
|
||||
success_msg = "✅ All files follow PascalCase naming convention!"
|
||||
colored_success = Colors.colorize(success_msg, Colors.GREEN)
|
||||
print(colored_success)
|
||||
else:
|
||||
error_msg = f"❌ {invalid_files} file(s) don't follow PascalCase convention"
|
||||
colored_error = Colors.colorize(error_msg, Colors.RED)
|
||||
print(colored_error)
|
||||
|
||||
if yaml_output:
|
||||
# Collect only failed files for YAML output (don't include all files)
|
||||
failed_paths = []
|
||||
|
||||
for file_path in naming_files:
|
||||
relative_path_str = str(file_path.relative_to(project_root))
|
||||
is_valid, error_msg = check_naming_convention(file_path)
|
||||
|
||||
if not is_valid:
|
||||
failed_paths.append(relative_path_str)
|
||||
|
||||
results = {
|
||||
**stats, # Include stats at top level for consistency
|
||||
"failed_paths": failed_paths, # For failed_items extraction
|
||||
}
|
||||
output_yaml_results("naming", results, overall_success)
|
||||
|
||||
return overall_success, stats
|
||||
|
||||
|
||||
def run_format(
|
||||
project_root: Path, silent: bool = False, yaml_output: bool = False
|
||||
) -> Tuple[bool, Dict]:
|
||||
@@ -1476,7 +1623,7 @@ def discover_test_files(project_root: Path) -> List[Tuple[Path, str]]:
|
||||
for test_dir, prefix in test_dirs:
|
||||
test_path = project_root / test_dir
|
||||
if test_path.exists():
|
||||
for test_file in test_path.glob("test_*.gd"):
|
||||
for test_file in test_path.glob("Test*.gd"):
|
||||
test_files.append((test_file, prefix))
|
||||
|
||||
return test_files
|
||||
@@ -1754,6 +1901,10 @@ def run_workflow(
|
||||
"📋 File format validation (yaml/toml/json)",
|
||||
lambda root: run_validate(root, silent, yaml_output),
|
||||
),
|
||||
"naming": (
|
||||
"📝 Naming convention check (PascalCase)",
|
||||
lambda root: run_naming(root, silent, yaml_output),
|
||||
),
|
||||
}
|
||||
|
||||
if not silent:
|
||||
@@ -1903,6 +2054,12 @@ async def run_workflow_async(
|
||||
"📋 File format validation (yaml/toml/json)",
|
||||
lambda root: run_validate_async(root, silent, yaml_output),
|
||||
),
|
||||
"naming": (
|
||||
"📝 Naming convention check (PascalCase)",
|
||||
lambda root: run_naming(
|
||||
root, silent, yaml_output
|
||||
), # Using sync version - lightweight
|
||||
),
|
||||
}
|
||||
|
||||
if not silent:
|
||||
@@ -2017,8 +2174,8 @@ async def main_async():
|
||||
parser.add_argument(
|
||||
"--steps",
|
||||
nargs="+",
|
||||
choices=["lint", "format", "test", "validate", "ruff"],
|
||||
default=["format", "lint", "ruff", "test", "validate"],
|
||||
choices=["lint", "format", "test", "validate", "ruff", "naming"],
|
||||
default=["format", "lint", "ruff", "test", "validate", "naming"],
|
||||
help="Workflow steps to run",
|
||||
)
|
||||
parser.add_argument("--lint", action="store_true", help="Run GDScript linting")
|
||||
@@ -2030,6 +2187,9 @@ async def main_async():
|
||||
parser.add_argument(
|
||||
"--ruff", action="store_true", help="Run Python formatting & linting with ruff"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--naming", action="store_true", help="Check PascalCase naming conventions"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--silent",
|
||||
"-s",
|
||||
@@ -2078,6 +2238,7 @@ async def main_async():
|
||||
root, args.silent, args.yaml
|
||||
),
|
||||
"ruff": lambda root: run_ruff_async(root, args.silent, args.yaml),
|
||||
"naming": lambda root: run_naming(root, args.silent, args.yaml),
|
||||
}
|
||||
success, _ = await step_funcs[steps[0]](project_root)
|
||||
else:
|
||||
@@ -2087,6 +2248,7 @@ async def main_async():
|
||||
"test": lambda root: run_tests(root, args.silent, args.yaml),
|
||||
"validate": lambda root: run_validate(root, args.silent, args.yaml),
|
||||
"ruff": lambda root: run_ruff(root, args.silent, args.yaml),
|
||||
"naming": lambda root: run_naming(root, args.silent, args.yaml),
|
||||
}
|
||||
success, _ = step_funcs[steps[0]](project_root)
|
||||
else:
|
||||
@@ -2114,8 +2276,8 @@ def main():
|
||||
parser.add_argument(
|
||||
"--steps",
|
||||
nargs="+",
|
||||
choices=["lint", "format", "test", "validate", "ruff"],
|
||||
default=["format", "lint", "ruff", "test", "validate"],
|
||||
choices=["lint", "format", "test", "validate", "ruff", "naming"],
|
||||
default=["format", "lint", "ruff", "test", "validate", "naming"],
|
||||
help="Workflow steps to run",
|
||||
)
|
||||
parser.add_argument("--lint", action="store_true", help="Run GDScript linting")
|
||||
@@ -2131,6 +2293,9 @@ def main():
|
||||
action="store_true",
|
||||
help="Run Python formatting & linting with ruff",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--naming", action="store_true", help="Check PascalCase naming conventions"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--silent",
|
||||
"-s",
|
||||
@@ -2173,6 +2338,7 @@ def main():
|
||||
"test": lambda root: run_tests(root, args.silent, args.yaml),
|
||||
"validate": lambda root: run_validate(root, args.silent, args.yaml),
|
||||
"ruff": lambda root: run_ruff(root, args.silent, args.yaml),
|
||||
"naming": lambda root: run_naming(root, args.silent, args.yaml),
|
||||
}
|
||||
success, _ = step_funcs[steps[0]](project_root)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user