add silent and machine readable formats to testing tool
Some checks failed
Some checks failed
This commit is contained in:
@@ -84,7 +84,16 @@ jobs:
|
|||||||
id: format
|
id: format
|
||||||
run: |
|
run: |
|
||||||
echo "🎨 Running GDScript formatting..."
|
echo "🎨 Running GDScript formatting..."
|
||||||
python tools/run_development.py --format
|
python tools/run_development.py --format --silent --yaml > format_results.yaml
|
||||||
|
|
||||||
|
- name: Upload formatting results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: format-results
|
||||||
|
path: |
|
||||||
|
format_results.yaml
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
- name: Check for formatting changes
|
- name: Check for formatting changes
|
||||||
id: check-changes
|
id: check-changes
|
||||||
@@ -156,15 +165,15 @@ jobs:
|
|||||||
id: lint
|
id: lint
|
||||||
run: |
|
run: |
|
||||||
echo "🔍 Running GDScript linting..."
|
echo "🔍 Running GDScript linting..."
|
||||||
python tools/run_development.py --lint
|
python tools/run_development.py --lint --silent --yaml > lint_results.yaml
|
||||||
|
|
||||||
- name: Upload linting results
|
- name: Upload linting results
|
||||||
if: failure()
|
if: always()
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: lint-results
|
name: lint-results
|
||||||
path: |
|
path: |
|
||||||
**/*.gd
|
lint_results.yaml
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@@ -201,15 +210,15 @@ jobs:
|
|||||||
id: test
|
id: test
|
||||||
run: |
|
run: |
|
||||||
echo "🧪 Running GDScript tests..."
|
echo "🧪 Running GDScript tests..."
|
||||||
python tools/run_development.py --test
|
python tools/run_development.py --test --silent --yaml > test_results.yaml
|
||||||
|
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
if: failure()
|
if: always()
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: test-results
|
||||||
path: |
|
path: |
|
||||||
tests/**/*.gd
|
test_results.yaml
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
summary:
|
summary:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ Usage examples:
|
|||||||
python tools/run_development.py # Run all steps
|
python tools/run_development.py # Run all steps
|
||||||
python tools/run_development.py --lint # Only linting
|
python tools/run_development.py --lint # Only linting
|
||||||
python tools/run_development.py --validate # Only file validation
|
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 --test --yaml # Test results in YAML format
|
||||||
python tools/run_development.py --steps lint test # Custom workflow
|
python tools/run_development.py --steps lint test # Custom workflow
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
@@ -16,6 +18,7 @@ Features:
|
|||||||
- Test execution
|
- Test execution
|
||||||
- YAML, TOML, and JSON file validation (respects .gitignore)
|
- YAML, TOML, and JSON file validation (respects .gitignore)
|
||||||
- Colored output and comprehensive error reporting
|
- Colored output and comprehensive error reporting
|
||||||
|
- Machine-readable YAML output for CI/CD integration
|
||||||
|
|
||||||
NOTE: Handles "successful but noisy" linter output such as
|
NOTE: Handles "successful but noisy" linter output such as
|
||||||
"Success: no problems found" - treats these as clean instead of warnings.
|
"Success: no problems found" - treats these as clean instead of warnings.
|
||||||
@@ -75,8 +78,9 @@ class Colors:
|
|||||||
return f"{color}{text}{Colors.RESET}"
|
return f"{color}{text}{Colors.RESET}"
|
||||||
|
|
||||||
|
|
||||||
def print_header(title: str) -> None:
|
def print_header(title: str, silent: bool = False) -> None:
|
||||||
"""Print a formatted header."""
|
"""Print a formatted header."""
|
||||||
|
if not silent:
|
||||||
separator = Colors.colorize("=" * 48, Colors.CYAN)
|
separator = Colors.colorize("=" * 48, Colors.CYAN)
|
||||||
colored_title = Colors.colorize(title, Colors.BOLD + Colors.WHITE)
|
colored_title = Colors.colorize(title, Colors.BOLD + Colors.WHITE)
|
||||||
print(separator)
|
print(separator)
|
||||||
@@ -85,8 +89,9 @@ def print_header(title: str) -> None:
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
def print_summary(title: str, stats: Dict[str, int]) -> None:
|
def print_summary(title: str, stats: Dict[str, int], silent: bool = False) -> None:
|
||||||
"""Print a formatted summary."""
|
"""Print a formatted summary."""
|
||||||
|
if not silent:
|
||||||
separator = Colors.colorize("=" * 48, Colors.CYAN)
|
separator = Colors.colorize("=" * 48, Colors.CYAN)
|
||||||
colored_title = Colors.colorize(title, Colors.BOLD + Colors.WHITE)
|
colored_title = Colors.colorize(title, Colors.BOLD + Colors.WHITE)
|
||||||
print(separator)
|
print(separator)
|
||||||
@@ -98,6 +103,37 @@ def print_summary(title: str, stats: Dict[str, int]) -> None:
|
|||||||
print(f"{colored_key}: {colored_value}")
|
print(f"{colored_key}: {colored_value}")
|
||||||
|
|
||||||
|
|
||||||
|
def output_yaml_results(step_name: str, results: Dict, success: bool) -> None:
|
||||||
|
"""Output results in YAML format."""
|
||||||
|
if yaml is None:
|
||||||
|
print("# YAML output requires PyYAML. Install with: pip install PyYAML")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Convert results to YAML-friendly format
|
||||||
|
yaml_data = {
|
||||||
|
"step": step_name,
|
||||||
|
"success": success,
|
||||||
|
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||||
|
"statistics": {},
|
||||||
|
"failed_items": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract statistics
|
||||||
|
for key, value in results.items():
|
||||||
|
if key != "failed_paths" and key != "results":
|
||||||
|
yaml_data["statistics"][key.lower().replace(" ", "_")] = value
|
||||||
|
|
||||||
|
# Extract failed items
|
||||||
|
if "failed_paths" in results:
|
||||||
|
yaml_data["failed_items"] = results["failed_paths"]
|
||||||
|
elif "results" in results:
|
||||||
|
# For test results, extract failed tests
|
||||||
|
failed_tests = [result[0] for result in results["results"] if not result[1]]
|
||||||
|
yaml_data["failed_items"] = failed_tests
|
||||||
|
|
||||||
|
print(yaml.dump(yaml_data, default_flow_style=False, sort_keys=False))
|
||||||
|
|
||||||
|
|
||||||
def run_command(cmd: List[str], cwd: Path, timeout: int = 30) -> subprocess.CompletedProcess:
|
def run_command(cmd: List[str], cwd: Path, timeout: int = 30) -> subprocess.CompletedProcess:
|
||||||
"""
|
"""
|
||||||
Execute a shell command with error handling and output filtering.
|
Execute a shell command with error handling and output filtering.
|
||||||
@@ -157,17 +193,18 @@ def print_skip_message(tool: str) -> None:
|
|||||||
print(f" {colored_message}")
|
print(f" {colored_message}")
|
||||||
|
|
||||||
|
|
||||||
def print_result(success: bool, output: str = "") -> None:
|
def print_result(success: bool, output: str = "", silent: bool = False) -> None:
|
||||||
"""Print command result."""
|
"""Print command result."""
|
||||||
if success:
|
if success:
|
||||||
if not output:
|
if not output:
|
||||||
|
if not silent:
|
||||||
message = "✅ Clean"
|
message = "✅ Clean"
|
||||||
colored_message = Colors.colorize(message, Colors.GREEN)
|
colored_message = Colors.colorize(message, Colors.GREEN)
|
||||||
|
print(f" {colored_message}")
|
||||||
else:
|
else:
|
||||||
message = "⚠️ WARNINGS found:"
|
message = "⚠️ WARNINGS found:"
|
||||||
colored_message = Colors.colorize(message, Colors.YELLOW)
|
colored_message = Colors.colorize(message, Colors.YELLOW)
|
||||||
print(f" {colored_message}")
|
print(f" {colored_message}")
|
||||||
if output:
|
|
||||||
# Indent and color the output
|
# Indent and color the output
|
||||||
for line in output.split('\n'):
|
for line in output.split('\n'):
|
||||||
if line.strip():
|
if line.strip():
|
||||||
@@ -283,11 +320,13 @@ def _is_successful_linter_output(output: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def run_lint(project_root: Path) -> Tuple[bool, Dict]:
|
def run_lint(project_root: Path, silent: bool = False, yaml_output: bool = False) -> Tuple[bool, Dict]:
|
||||||
"""Run gdlint on all GDScript files."""
|
"""Run gdlint on all GDScript files."""
|
||||||
print_header("🔍 GDScript Linter")
|
if not yaml_output:
|
||||||
|
print_header("🔍 GDScript Linter", silent)
|
||||||
|
|
||||||
gd_files = get_gd_files(project_root)
|
gd_files = get_gd_files(project_root)
|
||||||
|
if not silent and not yaml_output:
|
||||||
count_msg = f"Found {len(gd_files)} GDScript files to lint."
|
count_msg = f"Found {len(gd_files)} GDScript files to lint."
|
||||||
colored_count = Colors.colorize(count_msg, Colors.BLUE)
|
colored_count = Colors.colorize(count_msg, Colors.BLUE)
|
||||||
print(f"{colored_count}\n")
|
print(f"{colored_count}\n")
|
||||||
@@ -297,13 +336,16 @@ def run_lint(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
|
|
||||||
for gd_file in gd_files:
|
for gd_file in gd_files:
|
||||||
relative_path = gd_file.relative_to(project_root)
|
relative_path = gd_file.relative_to(project_root)
|
||||||
|
if not silent and not yaml_output:
|
||||||
file_msg = f"📄 Linting: {relative_path.name}"
|
file_msg = f"📄 Linting: {relative_path.name}"
|
||||||
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
||||||
print(colored_file)
|
print(colored_file)
|
||||||
|
|
||||||
if should_skip_file(gd_file):
|
if should_skip_file(gd_file):
|
||||||
|
if not silent and not yaml_output:
|
||||||
print_skip_message("gdlint")
|
print_skip_message("gdlint")
|
||||||
clean_files += 1
|
clean_files += 1
|
||||||
|
if not silent and not yaml_output:
|
||||||
print()
|
print()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -315,23 +357,29 @@ def run_lint(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
# If output is "no problems" (or similar), treat as clean.
|
# If output is "no problems" (or similar), treat as clean.
|
||||||
if _is_successful_linter_output(output):
|
if _is_successful_linter_output(output):
|
||||||
clean_files += 1
|
clean_files += 1
|
||||||
print_result(True, "")
|
if not yaml_output:
|
||||||
|
print_result(True, "", silent)
|
||||||
else:
|
else:
|
||||||
warning_files += 1
|
warning_files += 1
|
||||||
print_result(True, output)
|
if not yaml_output:
|
||||||
|
print_result(True, output, silent)
|
||||||
else:
|
else:
|
||||||
error_files += 1
|
error_files += 1
|
||||||
failed_paths.append(str(relative_path))
|
failed_paths.append(str(relative_path))
|
||||||
print_result(False, output)
|
if not yaml_output:
|
||||||
|
print_result(False, output, silent)
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
if not silent and not yaml_output:
|
||||||
print(" ❌ ERROR: gdlint not found")
|
print(" ❌ ERROR: gdlint not found")
|
||||||
return False, {}
|
return False, {}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if not silent and not yaml_output:
|
||||||
print(f" ❌ ERROR: {e}")
|
print(f" ❌ ERROR: {e}")
|
||||||
error_files += 1
|
error_files += 1
|
||||||
failed_paths.append(str(relative_path))
|
failed_paths.append(str(relative_path))
|
||||||
|
|
||||||
|
if not silent and not yaml_output:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
@@ -341,9 +389,14 @@ def run_lint(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
"Files with warnings": warning_files,
|
"Files with warnings": warning_files,
|
||||||
"Files with errors": error_files
|
"Files with errors": error_files
|
||||||
}
|
}
|
||||||
print_summary("Linting Summary", stats)
|
|
||||||
|
|
||||||
success = error_files == 0
|
success = error_files == 0
|
||||||
|
|
||||||
|
if yaml_output:
|
||||||
|
output_yaml_results("lint", {**stats, "failed_paths": failed_paths}, success)
|
||||||
|
else:
|
||||||
|
print_summary("Linting Summary", stats, silent)
|
||||||
|
if not silent:
|
||||||
print()
|
print()
|
||||||
if not success:
|
if not success:
|
||||||
msg = "❌ Linting FAILED - Please fix the errors above"
|
msg = "❌ Linting FAILED - Please fix the errors above"
|
||||||
@@ -357,6 +410,10 @@ def run_lint(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
msg = "✅ All GDScript files passed linting!"
|
msg = "✅ All GDScript files passed linting!"
|
||||||
colored_msg = Colors.colorize(msg, Colors.GREEN + Colors.BOLD)
|
colored_msg = Colors.colorize(msg, Colors.GREEN + Colors.BOLD)
|
||||||
print(colored_msg)
|
print(colored_msg)
|
||||||
|
elif not success:
|
||||||
|
# In silent mode, still show failed files
|
||||||
|
for failed_path in failed_paths:
|
||||||
|
print(f"❌ {failed_path}")
|
||||||
|
|
||||||
return success, {**stats, "failed_paths": failed_paths}
|
return success, {**stats, "failed_paths": failed_paths}
|
||||||
|
|
||||||
@@ -403,8 +460,9 @@ def validate_json_file(file_path: Path) -> Tuple[bool, str]:
|
|||||||
return False, f"Error reading file: {e}"
|
return False, f"Error reading file: {e}"
|
||||||
|
|
||||||
|
|
||||||
def run_validate(project_root: Path) -> Tuple[bool, Dict]:
|
def run_validate(project_root: Path, silent: bool = False, yaml_output: bool = False) -> Tuple[bool, Dict]:
|
||||||
"""Run validation on YAML, TOML, and JSON files."""
|
"""Run validation on YAML, TOML, and JSON files."""
|
||||||
|
if not silent and not yaml_output:
|
||||||
print_header("📋 File Format Validation")
|
print_header("📋 File Format Validation")
|
||||||
|
|
||||||
# Get all validation files
|
# Get all validation files
|
||||||
@@ -412,11 +470,13 @@ def run_validate(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
total_files = sum(len(files) for files in validation_files.values())
|
total_files = sum(len(files) for files in validation_files.values())
|
||||||
|
|
||||||
if total_files == 0:
|
if total_files == 0:
|
||||||
|
if not silent:
|
||||||
msg = "No YAML, TOML, or JSON files found to validate."
|
msg = "No YAML, TOML, or JSON files found to validate."
|
||||||
colored_msg = Colors.colorize(msg, Colors.YELLOW)
|
colored_msg = Colors.colorize(msg, Colors.YELLOW)
|
||||||
print(colored_msg)
|
print(colored_msg)
|
||||||
return True, {"Total files": 0, "Valid files": 0, "Invalid files": 0}
|
return True, {"Total files": 0, "Valid files": 0, "Invalid files": 0}
|
||||||
|
|
||||||
|
if not silent and not yaml_output:
|
||||||
count_msg = f"Found {total_files} files to validate:"
|
count_msg = f"Found {total_files} files to validate:"
|
||||||
colored_count = Colors.colorize(count_msg, Colors.BLUE)
|
colored_count = Colors.colorize(count_msg, Colors.BLUE)
|
||||||
print(colored_count)
|
print(colored_count)
|
||||||
@@ -445,12 +505,14 @@ def run_validate(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
validator = validators[file_type]
|
validator = validators[file_type]
|
||||||
|
if not silent and not yaml_output:
|
||||||
type_header = f"🔍 Validating {file_type.upper()} files"
|
type_header = f"🔍 Validating {file_type.upper()} files"
|
||||||
colored_header = Colors.colorize(type_header, Colors.MAGENTA + Colors.BOLD)
|
colored_header = Colors.colorize(type_header, Colors.MAGENTA + Colors.BOLD)
|
||||||
print(colored_header)
|
print(colored_header)
|
||||||
|
|
||||||
for file_path in files:
|
for file_path in files:
|
||||||
relative_path = file_path.relative_to(project_root)
|
relative_path = file_path.relative_to(project_root)
|
||||||
|
if not silent and not yaml_output:
|
||||||
file_msg = f"📄 Validating: {relative_path}"
|
file_msg = f"📄 Validating: {relative_path}"
|
||||||
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
||||||
print(colored_file)
|
print(colored_file)
|
||||||
@@ -459,12 +521,15 @@ def run_validate(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
|
|
||||||
if is_valid:
|
if is_valid:
|
||||||
valid_files += 1
|
valid_files += 1
|
||||||
print_result(True, "")
|
if not yaml_output:
|
||||||
|
print_result(True, "", silent)
|
||||||
else:
|
else:
|
||||||
invalid_files += 1
|
invalid_files += 1
|
||||||
failed_paths.append(str(relative_path))
|
failed_paths.append(str(relative_path))
|
||||||
print_result(False, error_msg)
|
if not yaml_output:
|
||||||
|
print_result(False, error_msg, silent)
|
||||||
|
|
||||||
|
if not silent and not yaml_output:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
@@ -473,9 +538,14 @@ def run_validate(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
"Valid files": valid_files,
|
"Valid files": valid_files,
|
||||||
"Invalid files": invalid_files
|
"Invalid files": invalid_files
|
||||||
}
|
}
|
||||||
print_summary("Validation Summary", stats)
|
|
||||||
|
|
||||||
success = invalid_files == 0
|
success = invalid_files == 0
|
||||||
|
|
||||||
|
if yaml_output:
|
||||||
|
output_yaml_results("validate", {**stats, "failed_paths": failed_paths}, success)
|
||||||
|
else:
|
||||||
|
if not silent:
|
||||||
|
print_summary("Validation Summary", stats)
|
||||||
print()
|
print()
|
||||||
if not success:
|
if not success:
|
||||||
msg = "❌ File validation FAILED - Please fix the syntax errors above"
|
msg = "❌ File validation FAILED - Please fix the syntax errors above"
|
||||||
@@ -485,14 +555,20 @@ def run_validate(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
msg = "✅ All files passed validation!"
|
msg = "✅ All files passed validation!"
|
||||||
colored_msg = Colors.colorize(msg, Colors.GREEN + Colors.BOLD)
|
colored_msg = Colors.colorize(msg, Colors.GREEN + Colors.BOLD)
|
||||||
print(colored_msg)
|
print(colored_msg)
|
||||||
|
elif not success:
|
||||||
|
# In silent mode, still show errors
|
||||||
|
for failed_path in failed_paths:
|
||||||
|
print(f"❌ {failed_path}")
|
||||||
|
|
||||||
return success, {**stats, "failed_paths": failed_paths}
|
return success, {**stats, "failed_paths": failed_paths}
|
||||||
|
|
||||||
def run_format(project_root: Path) -> Tuple[bool, Dict]:
|
def run_format(project_root: Path, silent: bool = False, yaml_output: bool = False) -> Tuple[bool, Dict]:
|
||||||
"""Run gdformat on all GDScript files."""
|
"""Run gdformat on all GDScript files."""
|
||||||
print_header("🎨 GDScript Formatter")
|
if not yaml_output:
|
||||||
|
print_header("🎨 GDScript Formatter", silent)
|
||||||
|
|
||||||
gd_files = get_gd_files(project_root)
|
gd_files = get_gd_files(project_root)
|
||||||
|
if not silent and not yaml_output:
|
||||||
count_msg = f"Found {len(gd_files)} GDScript files to format."
|
count_msg = f"Found {len(gd_files)} GDScript files to format."
|
||||||
colored_count = Colors.colorize(count_msg, Colors.BLUE)
|
colored_count = Colors.colorize(count_msg, Colors.BLUE)
|
||||||
print(f"{colored_count}\n")
|
print(f"{colored_count}\n")
|
||||||
@@ -502,13 +578,16 @@ def run_format(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
|
|
||||||
for gd_file in gd_files:
|
for gd_file in gd_files:
|
||||||
relative_path = gd_file.relative_to(project_root)
|
relative_path = gd_file.relative_to(project_root)
|
||||||
|
if not silent and not yaml_output:
|
||||||
file_msg = f"🎯 Formatting: {relative_path.name}"
|
file_msg = f"🎯 Formatting: {relative_path.name}"
|
||||||
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
colored_file = Colors.colorize(file_msg, Colors.CYAN)
|
||||||
print(colored_file)
|
print(colored_file)
|
||||||
|
|
||||||
if should_skip_file(gd_file):
|
if should_skip_file(gd_file):
|
||||||
|
if not silent and not yaml_output:
|
||||||
print_skip_message("gdformat")
|
print_skip_message("gdformat")
|
||||||
formatted_files += 1
|
formatted_files += 1
|
||||||
|
if not silent and not yaml_output:
|
||||||
print()
|
print()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -516,11 +595,13 @@ def run_format(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
result = run_command(["gdformat", str(gd_file)], project_root)
|
result = run_command(["gdformat", str(gd_file)], project_root)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
|
if not silent and not yaml_output:
|
||||||
success_msg = "✅ Success"
|
success_msg = "✅ Success"
|
||||||
colored_success = Colors.colorize(success_msg, Colors.GREEN)
|
colored_success = Colors.colorize(success_msg, Colors.GREEN)
|
||||||
print(f" {colored_success}")
|
print(f" {colored_success}")
|
||||||
formatted_files += 1
|
formatted_files += 1
|
||||||
else:
|
else:
|
||||||
|
if not silent and not yaml_output:
|
||||||
fail_msg = f"❌ FAILED: {relative_path}"
|
fail_msg = f"❌ FAILED: {relative_path}"
|
||||||
colored_fail = Colors.colorize(fail_msg, Colors.RED)
|
colored_fail = Colors.colorize(fail_msg, Colors.RED)
|
||||||
print(f" {colored_fail}")
|
print(f" {colored_fail}")
|
||||||
@@ -532,13 +613,16 @@ def run_format(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
failed_paths.append(str(relative_path))
|
failed_paths.append(str(relative_path))
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
if not silent and not yaml_output:
|
||||||
print(" ❌ ERROR: gdformat not found")
|
print(" ❌ ERROR: gdformat not found")
|
||||||
return False, {}
|
return False, {}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if not silent and not yaml_output:
|
||||||
print(f" ❌ ERROR: {e}")
|
print(f" ❌ ERROR: {e}")
|
||||||
failed_files += 1
|
failed_files += 1
|
||||||
failed_paths.append(str(relative_path))
|
failed_paths.append(str(relative_path))
|
||||||
|
|
||||||
|
if not silent and not yaml_output:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
@@ -547,9 +631,14 @@ def run_format(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
"Successfully formatted": formatted_files,
|
"Successfully formatted": formatted_files,
|
||||||
"Failed": failed_files
|
"Failed": failed_files
|
||||||
}
|
}
|
||||||
print_summary("Formatting Summary", stats)
|
|
||||||
|
|
||||||
success = failed_files == 0
|
success = failed_files == 0
|
||||||
|
|
||||||
|
if yaml_output:
|
||||||
|
output_yaml_results("format", {**stats, "failed_paths": failed_paths}, success)
|
||||||
|
else:
|
||||||
|
print_summary("Formatting Summary", stats, silent)
|
||||||
|
if not silent:
|
||||||
print()
|
print()
|
||||||
if not success:
|
if not success:
|
||||||
msg = "⚠️ WARNING: Some files failed to format"
|
msg = "⚠️ WARNING: Some files failed to format"
|
||||||
@@ -559,6 +648,10 @@ def run_format(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
msg = "✅ All GDScript files formatted successfully!"
|
msg = "✅ All GDScript files formatted successfully!"
|
||||||
colored_msg = Colors.colorize(msg, Colors.GREEN + Colors.BOLD)
|
colored_msg = Colors.colorize(msg, Colors.GREEN + Colors.BOLD)
|
||||||
print(colored_msg)
|
print(colored_msg)
|
||||||
|
elif not success:
|
||||||
|
# In silent mode, still show failed files
|
||||||
|
for failed_path in failed_paths:
|
||||||
|
print(f"❌ {failed_path}")
|
||||||
|
|
||||||
return success, {**stats, "failed_paths": failed_paths}
|
return success, {**stats, "failed_paths": failed_paths}
|
||||||
|
|
||||||
@@ -581,12 +674,14 @@ def discover_test_files(project_root: Path) -> List[Tuple[Path, str]]:
|
|||||||
return test_files
|
return test_files
|
||||||
|
|
||||||
|
|
||||||
def run_tests(project_root: Path) -> Tuple[bool, Dict]:
|
def run_tests(project_root: Path, silent: bool = False, yaml_output: bool = False) -> Tuple[bool, Dict]:
|
||||||
"""Run Godot tests."""
|
"""Run Godot tests."""
|
||||||
print_header("🧪 GDScript Test Runner")
|
if not yaml_output:
|
||||||
|
print_header("🧪 GDScript Test Runner", silent)
|
||||||
|
|
||||||
test_files = discover_test_files(project_root)
|
test_files = discover_test_files(project_root)
|
||||||
|
|
||||||
|
if not silent and not yaml_output:
|
||||||
scan_msg = "🔍 Scanning for test files in tests\\ directory..."
|
scan_msg = "🔍 Scanning for test files in tests\\ directory..."
|
||||||
colored_scan = Colors.colorize(scan_msg, Colors.BLUE)
|
colored_scan = Colors.colorize(scan_msg, Colors.BLUE)
|
||||||
print(colored_scan)
|
print(colored_scan)
|
||||||
@@ -612,6 +707,7 @@ def run_tests(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
test_name = format_test_name(test_file.stem)
|
test_name = format_test_name(test_file.stem)
|
||||||
full_test_name = f"{prefix}{test_name}"
|
full_test_name = f"{prefix}{test_name}"
|
||||||
|
|
||||||
|
if not silent and not yaml_output:
|
||||||
header_msg = f"=== {full_test_name} ==="
|
header_msg = f"=== {full_test_name} ==="
|
||||||
colored_header = Colors.colorize(header_msg, Colors.CYAN + Colors.BOLD)
|
colored_header = Colors.colorize(header_msg, Colors.CYAN + Colors.BOLD)
|
||||||
print(f"\n{colored_header}")
|
print(f"\n{colored_header}")
|
||||||
@@ -628,14 +724,18 @@ def run_tests(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
|
if not silent and not yaml_output:
|
||||||
pass_msg = f"✅ PASSED: {full_test_name}"
|
pass_msg = f"✅ PASSED: {full_test_name}"
|
||||||
colored_pass = Colors.colorize(pass_msg, Colors.GREEN + Colors.BOLD)
|
colored_pass = Colors.colorize(pass_msg, Colors.GREEN + Colors.BOLD)
|
||||||
print(colored_pass)
|
print(colored_pass)
|
||||||
test_results.append((full_test_name, True, ""))
|
test_results.append((full_test_name, True, ""))
|
||||||
else:
|
else:
|
||||||
|
if not silent and not yaml_output:
|
||||||
fail_msg = f"❌ FAILED: {full_test_name}"
|
fail_msg = f"❌ FAILED: {full_test_name}"
|
||||||
colored_fail = Colors.colorize(fail_msg, Colors.RED + Colors.BOLD)
|
colored_fail = Colors.colorize(fail_msg, Colors.RED + Colors.BOLD)
|
||||||
print(colored_fail)
|
print(colored_fail)
|
||||||
|
elif silent and not yaml_output:
|
||||||
|
print(f"❌ {full_test_name}")
|
||||||
failed_tests += 1
|
failed_tests += 1
|
||||||
error_msg = (result.stderr + result.stdout).strip() or "Unknown error"
|
error_msg = (result.stderr + result.stdout).strip() or "Unknown error"
|
||||||
test_results.append((full_test_name, False, error_msg))
|
test_results.append((full_test_name, False, error_msg))
|
||||||
@@ -643,25 +743,33 @@ def run_tests(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
total_tests += 1
|
total_tests += 1
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
|
if not silent and not yaml_output:
|
||||||
timeout_msg = f"⏰ FAILED: {full_test_name} (TIMEOUT)"
|
timeout_msg = f"⏰ FAILED: {full_test_name} (TIMEOUT)"
|
||||||
colored_timeout = Colors.colorize(timeout_msg, Colors.RED + Colors.BOLD)
|
colored_timeout = Colors.colorize(timeout_msg, Colors.RED + Colors.BOLD)
|
||||||
print(colored_timeout)
|
print(colored_timeout)
|
||||||
|
elif silent and not yaml_output:
|
||||||
|
print(f"⏰ {full_test_name} (TIMEOUT)")
|
||||||
failed_tests += 1
|
failed_tests += 1
|
||||||
test_results.append((full_test_name, False, "Test timed out"))
|
test_results.append((full_test_name, False, "Test timed out"))
|
||||||
total_tests += 1
|
total_tests += 1
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
if not yaml_output and not silent:
|
||||||
error_msg = "❌ ERROR: Godot not found"
|
error_msg = "❌ ERROR: Godot not found"
|
||||||
colored_error = Colors.colorize(error_msg, Colors.RED + Colors.BOLD)
|
colored_error = Colors.colorize(error_msg, Colors.RED + Colors.BOLD)
|
||||||
print(colored_error)
|
print(colored_error)
|
||||||
return False, {}
|
return False, {}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if not silent and not yaml_output:
|
||||||
exc_msg = f"💥 FAILED: {full_test_name} (ERROR: {e})"
|
exc_msg = f"💥 FAILED: {full_test_name} (ERROR: {e})"
|
||||||
colored_exc = Colors.colorize(exc_msg, Colors.RED + Colors.BOLD)
|
colored_exc = Colors.colorize(exc_msg, Colors.RED + Colors.BOLD)
|
||||||
print(colored_exc)
|
print(colored_exc)
|
||||||
|
elif silent and not yaml_output:
|
||||||
|
print(f"💥 {full_test_name} (ERROR: {e})")
|
||||||
failed_tests += 1
|
failed_tests += 1
|
||||||
test_results.append((full_test_name, False, str(e)))
|
test_results.append((full_test_name, False, str(e)))
|
||||||
total_tests += 1
|
total_tests += 1
|
||||||
|
|
||||||
|
if not silent and not yaml_output:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
@@ -671,9 +779,14 @@ def run_tests(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
"Tests Passed": passed_tests,
|
"Tests Passed": passed_tests,
|
||||||
"Tests Failed": failed_tests
|
"Tests Failed": failed_tests
|
||||||
}
|
}
|
||||||
print_summary("Test Execution Summary", stats)
|
|
||||||
|
|
||||||
success = failed_tests == 0
|
success = failed_tests == 0
|
||||||
|
|
||||||
|
if yaml_output:
|
||||||
|
output_yaml_results("test", {**stats, "results": test_results}, success)
|
||||||
|
else:
|
||||||
|
print_summary("Test Execution Summary", stats, silent)
|
||||||
|
if not silent:
|
||||||
print()
|
print()
|
||||||
if success:
|
if success:
|
||||||
msg = "🎉 ALL TESTS PASSED!"
|
msg = "🎉 ALL TESTS PASSED!"
|
||||||
@@ -687,7 +800,7 @@ def run_tests(project_root: Path) -> Tuple[bool, Dict]:
|
|||||||
return success, {**stats, "results": test_results}
|
return success, {**stats, "results": test_results}
|
||||||
|
|
||||||
|
|
||||||
def run_workflow(project_root: Path, steps: List[str]) -> bool:
|
def run_workflow(project_root: Path, steps: List[str], silent: bool = False, yaml_output: bool = False) -> bool:
|
||||||
"""
|
"""
|
||||||
Execute development workflow steps in sequence.
|
Execute development workflow steps in sequence.
|
||||||
|
|
||||||
@@ -700,15 +813,17 @@ def run_workflow(project_root: Path, steps: List[str]) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True if all steps completed successfully, False if any failed
|
bool: True if all steps completed successfully, False if any failed
|
||||||
"""
|
"""
|
||||||
|
if not silent:
|
||||||
print_header("🔄 Development Workflow Runner")
|
print_header("🔄 Development Workflow Runner")
|
||||||
|
|
||||||
workflow_steps = {
|
workflow_steps = {
|
||||||
"lint": ("🔍 Code linting (gdlint)", run_lint),
|
"lint": ("🔍 Code linting (gdlint)", lambda root: run_lint(root, silent, yaml_output)),
|
||||||
"format": ("🎨 Code formatting (gdformat)", run_format),
|
"format": ("🎨 Code formatting (gdformat)", lambda root: run_format(root, silent, yaml_output)),
|
||||||
"test": ("🧪 Test execution (godot tests)", run_tests),
|
"test": ("🧪 Test execution (godot tests)", lambda root: run_tests(root, silent, yaml_output)),
|
||||||
"validate": ("📋 File format validation (yaml/toml/json)", run_validate)
|
"validate": ("📋 File format validation (yaml/toml/json)", lambda root: run_validate(root, silent, yaml_output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if not silent:
|
||||||
intro_msg = "🚀 This script will run the development workflow:"
|
intro_msg = "🚀 This script will run the development workflow:"
|
||||||
colored_intro = Colors.colorize(intro_msg, Colors.BLUE + Colors.BOLD)
|
colored_intro = Colors.colorize(intro_msg, Colors.BLUE + Colors.BOLD)
|
||||||
print(colored_intro)
|
print(colored_intro)
|
||||||
@@ -725,6 +840,7 @@ def run_workflow(project_root: Path, steps: List[str]) -> bool:
|
|||||||
|
|
||||||
for step in steps:
|
for step in steps:
|
||||||
step_name, step_func = workflow_steps[step]
|
step_name, step_func = workflow_steps[step]
|
||||||
|
if not silent:
|
||||||
separator = Colors.colorize("-" * 48, Colors.MAGENTA)
|
separator = Colors.colorize("-" * 48, Colors.MAGENTA)
|
||||||
print(separator)
|
print(separator)
|
||||||
|
|
||||||
@@ -736,6 +852,7 @@ def run_workflow(project_root: Path, steps: List[str]) -> bool:
|
|||||||
success, step_results = step_func(project_root)
|
success, step_results = step_func(project_root)
|
||||||
results[step] = step_results
|
results[step] = step_results
|
||||||
|
|
||||||
|
if not silent:
|
||||||
if not success:
|
if not success:
|
||||||
fail_msg = f"❌ {step.upper()} FAILED - Continuing with remaining steps"
|
fail_msg = f"❌ {step.upper()} FAILED - Continuing with remaining steps"
|
||||||
colored_fail = Colors.colorize(fail_msg, Colors.RED + Colors.BOLD)
|
colored_fail = Colors.colorize(fail_msg, Colors.RED + Colors.BOLD)
|
||||||
@@ -752,12 +869,15 @@ def run_workflow(project_root: Path, steps: List[str]) -> bool:
|
|||||||
|
|
||||||
# Final summary
|
# Final summary
|
||||||
elapsed_time = time.time() - start_time
|
elapsed_time = time.time() - start_time
|
||||||
|
|
||||||
|
if not silent:
|
||||||
print_header("📊 Workflow Summary")
|
print_header("📊 Workflow Summary")
|
||||||
|
|
||||||
all_success = True
|
all_success = True
|
||||||
any_failures = False
|
any_failures = False
|
||||||
for step in steps:
|
for step in steps:
|
||||||
step_success = results[step].get("Tests Failed", results[step].get("Failed", 0)) == 0
|
step_success = results[step].get("Tests Failed", results[step].get("Failed", 0)) == 0
|
||||||
|
if not silent:
|
||||||
status_emoji = "✅" if step_success else "❌"
|
status_emoji = "✅" if step_success else "❌"
|
||||||
status_text = "PASSED" if step_success else "FAILED"
|
status_text = "PASSED" if step_success else "FAILED"
|
||||||
status_color = Colors.GREEN if step_success else Colors.RED
|
status_color = Colors.GREEN if step_success else Colors.RED
|
||||||
@@ -770,6 +890,7 @@ def run_workflow(project_root: Path, steps: List[str]) -> bool:
|
|||||||
any_failures = True
|
any_failures = True
|
||||||
all_success = False
|
all_success = False
|
||||||
|
|
||||||
|
if not silent:
|
||||||
print()
|
print()
|
||||||
if all_success:
|
if all_success:
|
||||||
success_msg = "🎉 ALL WORKFLOW STEPS COMPLETED SUCCESSFULLY!"
|
success_msg = "🎉 ALL WORKFLOW STEPS COMPLETED SUCCESSFULLY!"
|
||||||
@@ -791,6 +912,7 @@ def run_workflow(project_root: Path, steps: List[str]) -> bool:
|
|||||||
time_msg = f"⏱️ Elapsed time: {elapsed_time:.1f} seconds"
|
time_msg = f"⏱️ Elapsed time: {elapsed_time:.1f} seconds"
|
||||||
colored_time = Colors.colorize(time_msg, Colors.MAGENTA)
|
colored_time = Colors.colorize(time_msg, Colors.MAGENTA)
|
||||||
print(f"\n{colored_time}")
|
print(f"\n{colored_time}")
|
||||||
|
|
||||||
return all_success
|
return all_success
|
||||||
|
|
||||||
|
|
||||||
@@ -803,6 +925,8 @@ def main():
|
|||||||
parser.add_argument("--format", action="store_true", help="Run formatting")
|
parser.add_argument("--format", action="store_true", help="Run formatting")
|
||||||
parser.add_argument("--test", action="store_true", help="Run tests")
|
parser.add_argument("--test", action="store_true", help="Run tests")
|
||||||
parser.add_argument("--validate", action="store_true", help="Run file format validation")
|
parser.add_argument("--validate", action="store_true", help="Run file format validation")
|
||||||
|
parser.add_argument("--silent", "-s", action="store_true", help="Silent mode - hide success messages, only show errors")
|
||||||
|
parser.add_argument("--yaml", action="store_true", help="Output results in machine-readable YAML format")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
project_root = Path(__file__).parent.parent
|
project_root = Path(__file__).parent.parent
|
||||||
@@ -821,10 +945,15 @@ def main():
|
|||||||
|
|
||||||
# Run workflow or individual step
|
# Run workflow or individual step
|
||||||
if len(steps) == 1:
|
if len(steps) == 1:
|
||||||
step_funcs = {"lint": run_lint, "format": run_format, "test": run_tests, "validate": run_validate}
|
step_funcs = {
|
||||||
|
"lint": lambda root: run_lint(root, args.silent, args.yaml),
|
||||||
|
"format": lambda root: run_format(root, args.silent, args.yaml),
|
||||||
|
"test": lambda root: run_tests(root, args.silent, args.yaml),
|
||||||
|
"validate": lambda root: run_validate(root, args.silent, args.yaml)
|
||||||
|
}
|
||||||
success, _ = step_funcs[steps[0]](project_root)
|
success, _ = step_funcs[steps[0]](project_root)
|
||||||
else:
|
else:
|
||||||
success = run_workflow(project_root, steps)
|
success = run_workflow(project_root, steps, args.silent, args.yaml)
|
||||||
|
|
||||||
sys.exit(0 if success else 1)
|
sys.exit(0 if success else 1)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user