mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-06 03:30:25 -06:00
feat: Implement comprehensive CI/CD pipeline with GitHub Actions
Implement a complete, production-ready CI/CD pipeline that runs 100% on GitHub Actions with zero external dependencies. This replaces and consolidates existing workflows with an optimized, streamlined pipeline. ## Major Changes - Add 3 new workflows (ci-comprehensive, cd-development, cd-release) - Remove 2 redundant workflows (backed up) - Add 130+ tests across 4 new test files - Add 8 documentation guides (60+ KB) - Add developer tools and scripts
This commit is contained in:
268
scripts/validate-setup.py
Normal file
268
scripts/validate-setup.py
Normal file
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TimeTracker CI/CD Setup Validation Script
|
||||
Validates that all CI/CD components are properly configured
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class Colors:
|
||||
"""ANSI color codes for terminal output"""
|
||||
GREEN = '\033[92m'
|
||||
RED = '\033[91m'
|
||||
YELLOW = '\033[93m'
|
||||
BLUE = '\033[94m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
|
||||
|
||||
def print_header(text):
|
||||
"""Print a formatted header"""
|
||||
print(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.ENDC}")
|
||||
print(f"{Colors.BOLD}{Colors.BLUE}{text:^60}{Colors.ENDC}")
|
||||
print(f"{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.ENDC}\n")
|
||||
|
||||
|
||||
def print_success(text):
|
||||
"""Print success message"""
|
||||
print(f"{Colors.GREEN}✓{Colors.ENDC} {text}")
|
||||
|
||||
|
||||
def print_error(text):
|
||||
"""Print error message"""
|
||||
print(f"{Colors.RED}✗{Colors.ENDC} {text}")
|
||||
|
||||
|
||||
def print_warning(text):
|
||||
"""Print warning message"""
|
||||
print(f"{Colors.YELLOW}⚠{Colors.ENDC} {text}")
|
||||
|
||||
|
||||
def print_info(text):
|
||||
"""Print info message"""
|
||||
print(f"{Colors.BLUE}ℹ{Colors.ENDC} {text}")
|
||||
|
||||
|
||||
def check_file_exists(filepath, required=True):
|
||||
"""Check if a file exists"""
|
||||
path = Path(filepath)
|
||||
if path.exists():
|
||||
print_success(f"Found: {filepath}")
|
||||
return True
|
||||
else:
|
||||
if required:
|
||||
print_error(f"Missing required file: {filepath}")
|
||||
else:
|
||||
print_warning(f"Optional file not found: {filepath}")
|
||||
return False
|
||||
|
||||
|
||||
def check_python_package(package_name):
|
||||
"""Check if a Python package is installed"""
|
||||
try:
|
||||
__import__(package_name)
|
||||
print_success(f"Python package '{package_name}' is installed")
|
||||
return True
|
||||
except ImportError:
|
||||
print_error(f"Python package '{package_name}' is NOT installed")
|
||||
return False
|
||||
|
||||
|
||||
def run_command(command, description):
|
||||
"""Run a command and check if it succeeds"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print_success(f"{description}: OK")
|
||||
return True
|
||||
else:
|
||||
print_error(f"{description}: FAILED")
|
||||
if result.stderr:
|
||||
print(f" Error: {result.stderr[:200]}")
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
print_error(f"{description}: TIMEOUT")
|
||||
return False
|
||||
except Exception as e:
|
||||
print_error(f"{description}: ERROR ({str(e)})")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Main validation function"""
|
||||
print_header("TimeTracker CI/CD Setup Validation")
|
||||
|
||||
# Track results
|
||||
checks = {
|
||||
'workflows': [],
|
||||
'tests': [],
|
||||
'config': [],
|
||||
'docs': [],
|
||||
'python': []
|
||||
}
|
||||
|
||||
# 1. Check GitHub Actions workflows
|
||||
print_header("1. GitHub Actions Workflows")
|
||||
workflows = [
|
||||
'.github/workflows/ci-comprehensive.yml',
|
||||
'.github/workflows/cd-development.yml',
|
||||
'.github/workflows/cd-release.yml',
|
||||
'.github/workflows/docker-publish.yml',
|
||||
'.github/workflows/migration-check.yml',
|
||||
]
|
||||
|
||||
for workflow in workflows:
|
||||
checks['workflows'].append(check_file_exists(workflow))
|
||||
|
||||
# 2. Check test files
|
||||
print_header("2. Test Files")
|
||||
test_files = [
|
||||
'tests/conftest.py',
|
||||
'tests/test_basic.py',
|
||||
'tests/test_routes.py',
|
||||
'tests/test_models_comprehensive.py',
|
||||
'tests/test_security.py',
|
||||
'tests/test_analytics.py',
|
||||
'tests/test_invoices.py',
|
||||
]
|
||||
|
||||
for test_file in test_files:
|
||||
checks['tests'].append(check_file_exists(test_file))
|
||||
|
||||
# 3. Check configuration files
|
||||
print_header("3. Configuration Files")
|
||||
config_files = [
|
||||
('pytest.ini', True),
|
||||
('requirements-test.txt', True),
|
||||
('.pre-commit-config.yaml', False),
|
||||
('Makefile', False),
|
||||
('.gitignore', True),
|
||||
]
|
||||
|
||||
for config_file, required in config_files:
|
||||
checks['config'].append(check_file_exists(config_file, required))
|
||||
|
||||
# 4. Check documentation
|
||||
print_header("4. Documentation")
|
||||
docs = [
|
||||
'CI_CD_DOCUMENTATION.md',
|
||||
'CI_CD_QUICK_START.md',
|
||||
'CI_CD_IMPLEMENTATION_SUMMARY.md',
|
||||
]
|
||||
|
||||
for doc in docs:
|
||||
checks['docs'].append(check_file_exists(doc))
|
||||
|
||||
# 5. Check Python dependencies
|
||||
print_header("5. Python Dependencies")
|
||||
packages = [
|
||||
'pytest',
|
||||
'flask',
|
||||
'sqlalchemy',
|
||||
]
|
||||
|
||||
for package in packages:
|
||||
checks['python'].append(check_python_package(package))
|
||||
|
||||
# 6. Check Python test dependencies
|
||||
print_header("6. Test Dependencies")
|
||||
test_packages = [
|
||||
'pytest',
|
||||
'pytest_cov',
|
||||
'pytest_flask',
|
||||
'black',
|
||||
'flake8',
|
||||
'bandit',
|
||||
]
|
||||
|
||||
test_deps_ok = True
|
||||
for package in test_packages:
|
||||
if not check_python_package(package.replace('_', '-')):
|
||||
test_deps_ok = False
|
||||
|
||||
if not test_deps_ok:
|
||||
print_info("Install test dependencies: pip install -r requirements-test.txt")
|
||||
|
||||
# 7. Run quick tests
|
||||
print_header("7. Quick Test Validation")
|
||||
|
||||
# Check if pytest can discover tests
|
||||
if run_command('pytest --collect-only -q', 'Test discovery'):
|
||||
print_info("Tests can be discovered successfully")
|
||||
|
||||
# Try to run smoke tests (if they exist)
|
||||
if run_command('pytest -m smoke --co -q', 'Smoke test discovery'):
|
||||
print_info("Smoke tests are properly marked")
|
||||
|
||||
# 8. Check Docker setup
|
||||
print_header("8. Docker Configuration")
|
||||
docker_files = [
|
||||
('Dockerfile', True),
|
||||
('docker-compose.yml', True),
|
||||
('.dockerignore', False),
|
||||
]
|
||||
|
||||
for docker_file, required in docker_files:
|
||||
check_file_exists(docker_file, required)
|
||||
|
||||
# 9. Check helper scripts
|
||||
print_header("9. Helper Scripts")
|
||||
scripts = [
|
||||
'scripts/run-tests.sh',
|
||||
'scripts/run-tests.bat',
|
||||
]
|
||||
|
||||
for script in scripts:
|
||||
check_file_exists(script, required=False)
|
||||
|
||||
# 10. Summary
|
||||
print_header("Validation Summary")
|
||||
|
||||
total_checks = sum(len(v) for v in checks.values())
|
||||
passed_checks = sum(sum(v) for v in checks.values())
|
||||
|
||||
print(f"\n{Colors.BOLD}Results:{Colors.ENDC}")
|
||||
print(f" Workflows: {sum(checks['workflows'])}/{len(checks['workflows'])}")
|
||||
print(f" Tests: {sum(checks['tests'])}/{len(checks['tests'])}")
|
||||
print(f" Configuration: {sum(checks['config'])}/{len(checks['config'])}")
|
||||
print(f" Documentation: {sum(checks['docs'])}/{len(checks['docs'])}")
|
||||
print(f" Python deps: {sum(checks['python'])}/{len(checks['python'])}")
|
||||
print(f"\n{Colors.BOLD}Total: {passed_checks}/{total_checks}{Colors.ENDC}")
|
||||
|
||||
if passed_checks == total_checks:
|
||||
print(f"\n{Colors.GREEN}{Colors.BOLD}✓ All checks passed! CI/CD setup is complete.{Colors.ENDC}")
|
||||
print(f"\n{Colors.BOLD}Next steps:{Colors.ENDC}")
|
||||
print(" 1. Run smoke tests: pytest -m smoke")
|
||||
print(" 2. Create a test PR to verify CI works")
|
||||
print(" 3. Review documentation: CI_CD_QUICK_START.md")
|
||||
return 0
|
||||
else:
|
||||
failed = total_checks - passed_checks
|
||||
print(f"\n{Colors.YELLOW}{Colors.BOLD}⚠ Setup incomplete: {failed} checks failed{Colors.ENDC}")
|
||||
print(f"\n{Colors.BOLD}Action required:{Colors.ENDC}")
|
||||
print(" 1. Review errors above")
|
||||
print(" 2. Install missing dependencies: pip install -r requirements-test.txt")
|
||||
print(" 3. Check documentation: CI_CD_DOCUMENTATION.md")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
sys.exit(main())
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n\n{Colors.YELLOW}Validation interrupted by user{Colors.ENDC}")
|
||||
sys.exit(130)
|
||||
except Exception as e:
|
||||
print(f"\n{Colors.RED}Validation failed with error: {e}{Colors.ENDC}")
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user