Skip to content

Commit 42c2ba5

Browse files
committed
feat(ci): Add local CI runner script
Created run_local_ci.py for faster development: Features: - Run all CI checks locally in seconds instead of minutes - No waiting for GitHub Actions queue - Immediate feedback on errors - Can run specific checks individually - Saves GitHub Actions quota Usage: - python run_local_ci.py # Run all checks - python run_local_ci.py --syntax # Run only syntax check - python run_local_ci.py --lint # Run only linting - python run_local_ci.py --import # Run only import check - python run_local_ci.py --type # Run only type check - python run_local_ci.py --config # Run only config check - python run_local_ci.py --build # Run only build test Benefits: - 10-100x faster than GitHub Actions - Interactive debugging - Unlimited runs - Free (uses own computer) - Same environment as development (Windows) This complements GitHub Actions CI for faster iteration.
1 parent 882498e commit 42c2ba5

3 files changed

Lines changed: 156 additions & 3 deletions

File tree

.github/scripts/config_validation.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
import sys
44
import os
55

6-
# Add current directory to Python path
7-
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
6+
# Get repository root (2 levels up from this script)
7+
repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
8+
print(f"Repository root: {repo_root}")
9+
10+
# Add repository root to Python path
11+
sys.path.insert(0, repo_root)
812

913
# Suppress Qt warnings for CI
1014
os.environ['QT_QPA_PLATFORM'] = 'offscreen'

.github/scripts/type_check.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ def check_file(filepath):
88
"""Check type hints in a Python file."""
99
try:
1010
with open(filepath, 'r', encoding='utf-8') as f:
11-
ast.parse(f, filepath)
11+
source = f.read()
12+
ast.parse(source, filepath)
1213
return True, None
1314
except SyntaxError as e:
1415
return False, str(e)

run_local_ci.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/env python3
2+
"""Run GitHub Actions CI jobs locally for faster development iteration."""
3+
import subprocess
4+
import sys
5+
import os
6+
from pathlib import Path
7+
8+
def run_command(name, command, description):
9+
"""Run a command and display result."""
10+
print(f"\n{'='*60}")
11+
print(f"Running: {name}")
12+
print(f"Description: {description}")
13+
print(f"{'='*60}\n")
14+
15+
result = subprocess.run(command, shell=True)
16+
17+
if result.returncode == 0:
18+
print(f"\n[SUCCESS] {name} passed!")
19+
return True
20+
else:
21+
print(f"\n[FAILED] {name} failed with exit code {result.returncode}")
22+
return False
23+
24+
def run_syntax_check():
25+
"""Run Python syntax check locally."""
26+
return run_command(
27+
"Python Syntax Check",
28+
"python .github/scripts/syntax_check.py",
29+
"Validate Python syntax for all files"
30+
)
31+
32+
def run_lint_check():
33+
"""Run linting check locally."""
34+
return run_command(
35+
"Code Linting",
36+
"pyflakes main.py core/*.py core/ui/*.py",
37+
"Check code quality with pyflakes"
38+
)
39+
40+
def run_import_check():
41+
"""Run import validation locally."""
42+
return run_command(
43+
"Import Validation",
44+
"python -c \"import main, core.overlay, core.input_mon, core.settings_manager, core.configuration, core.gui, core.ui.components, core.logging_config\"",
45+
"Test all module imports"
46+
)
47+
48+
def run_type_check():
49+
"""Run type validation locally."""
50+
return run_command(
51+
"Type Validation",
52+
"python .github/scripts/type_check.py",
53+
"Validate type hints in all files"
54+
)
55+
56+
def run_config_validation():
57+
"""Run configuration validation locally."""
58+
return run_command(
59+
"Configuration Validation",
60+
"python .github/scripts/config_validation.py",
61+
"Validate configuration schema and operations"
62+
)
63+
64+
def run_build_test():
65+
"""Run build test locally."""
66+
return run_command(
67+
"Windows Build Test",
68+
"python build.py",
69+
"Test PyInstaller build process"
70+
)
71+
72+
def run_all_checks():
73+
"""Run all CI checks sequentially."""
74+
checks = [
75+
("Syntax Check", run_syntax_check),
76+
("Linting Check", run_lint_check),
77+
("Import Check", run_import_check),
78+
("Type Check", run_type_check),
79+
("Config Check", run_config_validation),
80+
("Build Test", run_build_test),
81+
]
82+
83+
results = {}
84+
for name, check_func in checks:
85+
results[name] = check_func()
86+
87+
# Print summary
88+
print(f"\n{'='*60}")
89+
print("LOCAL CI SUMMARY")
90+
print(f"{'='*60}\n")
91+
92+
all_passed = True
93+
for name, passed in results.items():
94+
status = "[PASS]" if passed else "[FAIL]"
95+
print(f"{status} {name}")
96+
if not passed:
97+
all_passed = False
98+
99+
print(f"\n{'='*60}")
100+
if all_passed:
101+
print("ALL CHECKS PASSED!")
102+
else:
103+
print("SOME CHECKS FAILED!")
104+
print(f"{'='*60}\n")
105+
106+
return 0 if all_passed else 1
107+
108+
def main():
109+
"""Main entry point."""
110+
print("Local GitHub Actions CI Runner")
111+
print("="*60)
112+
print("\nThis script runs all CI checks locally for faster development.")
113+
print("\nUsage:")
114+
print(" python run_local_ci.py # Run all checks")
115+
print(" python run_local_ci.py --syntax # Run only syntax check")
116+
print(" python run_local_ci.py --lint # Run only linting")
117+
print(" python run_local_ci.py --import # Run only import check")
118+
print(" python run_local_ci.py --type # Run only type check")
119+
print(" python run_local_ci.py --config # Run only config check")
120+
print(" python run_local_ci.py --build # Run only build test")
121+
print("\n" + "="*60 + "\n")
122+
123+
# Parse arguments
124+
if len(sys.argv) > 1:
125+
arg = sys.argv[1].lower()
126+
127+
if arg == '--syntax':
128+
sys.exit(0 if run_syntax_check() else 1)
129+
elif arg == '--lint':
130+
sys.exit(0 if run_lint_check() else 1)
131+
elif arg == '--import':
132+
sys.exit(0 if run_import_check() else 1)
133+
elif arg == '--type':
134+
sys.exit(0 if run_type_check() else 1)
135+
elif arg == '--config':
136+
sys.exit(0 if run_config_validation() else 1)
137+
elif arg == '--build':
138+
sys.exit(0 if run_build_test() else 1)
139+
else:
140+
print(f"Unknown argument: {arg}")
141+
print("\nRun 'python run_local_ci.py' for usage.")
142+
sys.exit(1)
143+
else:
144+
# Run all checks
145+
sys.exit(run_all_checks())
146+
147+
if __name__ == "__main__":
148+
main()

0 commit comments

Comments
 (0)