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:
Dries Peeters
2025-10-09 13:02:39 +02:00
parent 77aec94b86
commit 0752332ed6
32 changed files with 8909 additions and 0 deletions

268
scripts/validate-setup.py Normal file
View 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)