- Add build-container.yml: Main build pipeline with multi-arch support - Add pr-check.yml: Pull request validation with comprehensive testing - Add release.yml: Automated release pipeline with security scanning - Add nightly.yml: Daily builds with performance testing - Add health_check.sh: Container health validation script - Add setup-ci.sh: Local CI/CD environment setup script - Add comprehensive CI/CD documentation Features: - Multi-architecture builds (linux/amd64, linux/arm64) - Security scanning with Trivy - Automated PyPI publishing for releases - Container registry integration - Performance testing and validation - Artifact management and cleanup - Build caching and optimization Supports full development workflow from PR to production deployment.
307 lines
8.4 KiB
Bash
Executable File
307 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# Health check script for UnitForge CI/CD workflows
|
|
# Tests basic functionality of the running application
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
HOST=${HOST:-localhost}
|
|
PORT=${PORT:-8000}
|
|
TIMEOUT=${TIMEOUT:-30}
|
|
MAX_RETRIES=${MAX_RETRIES:-5}
|
|
RETRY_DELAY=${RETRY_DELAY:-2}
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Helper functions
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Check if application is responding
|
|
check_health() {
|
|
local url="http://${HOST}:${PORT}/health"
|
|
local retry_count=0
|
|
|
|
log_info "Checking health endpoint: $url"
|
|
|
|
while [ $retry_count -lt "$MAX_RETRIES" ]; do
|
|
if curl -s -f --max-time "$TIMEOUT" "$url" > /dev/null 2>&1; then
|
|
log_info "Health check passed"
|
|
return 0
|
|
fi
|
|
|
|
retry_count=$((retry_count + 1))
|
|
log_warn "Health check failed (attempt $retry_count/$MAX_RETRIES)"
|
|
|
|
if [ $retry_count -lt "$MAX_RETRIES" ]; then
|
|
log_info "Retrying in ${RETRY_DELAY} seconds..."
|
|
sleep "$RETRY_DELAY"
|
|
fi
|
|
done
|
|
|
|
log_error "Health check failed after $MAX_RETRIES attempts"
|
|
return 1
|
|
}
|
|
|
|
# Check if main page loads
|
|
check_main_page() {
|
|
local url="http://${HOST}:${PORT}/"
|
|
log_info "Checking main page: $url"
|
|
|
|
local response
|
|
response=$(curl -s -w "%{http_code}" --max-time "$TIMEOUT" "$url")
|
|
local http_code="${response: -3}"
|
|
|
|
if [ "$http_code" = "200" ]; then
|
|
log_info "Main page check passed (HTTP $http_code)"
|
|
return 0
|
|
else
|
|
log_error "Main page check failed (HTTP $http_code)"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check API endpoints
|
|
check_api() {
|
|
local base_url="http://${HOST}:${PORT}/api"
|
|
log_info "Checking API endpoints"
|
|
|
|
# Check API health
|
|
local api_health_url="${base_url}/health"
|
|
if curl -s -f --max-time "$TIMEOUT" "$api_health_url" > /dev/null 2>&1; then
|
|
log_info "API health endpoint passed"
|
|
else
|
|
log_warn "API health endpoint failed or not available"
|
|
fi
|
|
|
|
# Check API version
|
|
local api_version_url="${base_url}/version"
|
|
if curl -s -f --max-time "$TIMEOUT" "$api_version_url" > /dev/null 2>&1; then
|
|
log_info "API version endpoint passed"
|
|
else
|
|
log_warn "API version endpoint failed or not available"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Check static assets
|
|
check_static_assets() {
|
|
log_info "Checking static assets"
|
|
|
|
local assets=(
|
|
"/static/css/style.css"
|
|
"/static/js/app.js"
|
|
"/static/vendor/bootstrap/css/bootstrap.min.css"
|
|
"/static/vendor/fontawesome/css/all.min.css"
|
|
)
|
|
|
|
local failed_assets=0
|
|
|
|
for asset in "${assets[@]}"; do
|
|
local url="http://${HOST}:${PORT}${asset}"
|
|
if curl -s -f --max-time "$TIMEOUT" "$url" > /dev/null 2>&1; then
|
|
log_info "Asset check passed: $asset"
|
|
else
|
|
log_warn "Asset check failed: $asset"
|
|
failed_assets=$((failed_assets + 1))
|
|
fi
|
|
done
|
|
|
|
if [ $failed_assets -eq 0 ]; then
|
|
log_info "All static assets available"
|
|
return 0
|
|
else
|
|
log_warn "$failed_assets static assets failed to load"
|
|
return 0 # Don't fail health check for missing assets
|
|
fi
|
|
}
|
|
|
|
# Performance test
|
|
check_performance() {
|
|
log_info "Running basic performance test"
|
|
|
|
local url="http://${HOST}:${PORT}/"
|
|
local response_time
|
|
|
|
# Test response time
|
|
local response_time
|
|
response_time=$(curl -s -w "%{time_total}" --max-time "$TIMEOUT" -o /dev/null "$url")
|
|
|
|
if curl -s -w "%{time_total}" --max-time "$TIMEOUT" -o /dev/null "$url" > /dev/null 2>&1; then
|
|
log_info "Response time: ${response_time}s"
|
|
|
|
# Check if response time is reasonable (< 5 seconds)
|
|
if (( $(echo "$response_time < 5.0" | bc -l) )); then
|
|
log_info "Performance check passed"
|
|
return 0
|
|
else
|
|
log_warn "Performance check warning: slow response time (${response_time}s)"
|
|
return 0 # Don't fail health check for slow response
|
|
fi
|
|
else
|
|
log_error "Performance check failed: no response"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Memory usage check (if running in container)
|
|
check_memory() {
|
|
if command -v docker > /dev/null 2>&1 && [ -n "$CONTAINER_NAME" ]; then
|
|
log_info "Checking container memory usage"
|
|
|
|
local memory_usage
|
|
memory_usage=$(docker stats "$CONTAINER_NAME" --no-stream --format "{{.MemUsage}}" | cut -d'/' -f1)
|
|
|
|
if [ -n "$memory_usage" ]; then
|
|
log_info "Memory usage: $memory_usage"
|
|
else
|
|
log_warn "Could not determine memory usage"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Wait for application to start
|
|
wait_for_startup() {
|
|
log_info "Waiting for application to start..."
|
|
local startup_timeout=60
|
|
local elapsed=0
|
|
|
|
while [ $elapsed -lt $startup_timeout ]; do
|
|
if curl -s --max-time 5 "http://${HOST}:${PORT}/" > /dev/null 2>&1; then
|
|
log_info "Application is responding"
|
|
return 0
|
|
fi
|
|
|
|
sleep 5
|
|
elapsed=$((elapsed + 5))
|
|
log_info "Waiting... (${elapsed}s/${startup_timeout}s)"
|
|
done
|
|
|
|
log_error "Application failed to start within ${startup_timeout} seconds"
|
|
return 1
|
|
}
|
|
|
|
# Main health check function
|
|
run_health_check() {
|
|
log_info "Starting UnitForge health check"
|
|
log_info "Target: http://${HOST}:${PORT}"
|
|
|
|
local failed_checks=0
|
|
|
|
# Wait for startup if needed
|
|
if ! curl -s --max-time 5 "http://${HOST}:${PORT}/" > /dev/null 2>&1; then
|
|
wait_for_startup || return 1
|
|
fi
|
|
|
|
# Run all checks
|
|
check_health || failed_checks=$((failed_checks + 1))
|
|
check_main_page || failed_checks=$((failed_checks + 1))
|
|
check_api || failed_checks=$((failed_checks + 1))
|
|
check_static_assets || true # Don't count static asset failures
|
|
check_performance || true # Don't count performance warnings
|
|
check_memory || true # Don't count memory check failures
|
|
|
|
# Summary
|
|
if [ $failed_checks -eq 0 ]; then
|
|
log_info "✅ All health checks passed"
|
|
return 0
|
|
else
|
|
log_error "❌ $failed_checks health checks failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Usage information
|
|
usage() {
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -h, --host HOST Target host (default: localhost)"
|
|
echo " -p, --port PORT Target port (default: 8000)"
|
|
echo " -t, --timeout TIMEOUT Request timeout in seconds (default: 30)"
|
|
echo " -r, --retries RETRIES Maximum retry attempts (default: 5)"
|
|
echo " -d, --delay DELAY Retry delay in seconds (default: 2)"
|
|
echo " -c, --container NAME Container name for memory checks"
|
|
echo " --help Show this help message"
|
|
echo ""
|
|
echo "Environment variables:"
|
|
echo " HOST Same as --host"
|
|
echo " PORT Same as --port"
|
|
echo " TIMEOUT Same as --timeout"
|
|
echo " MAX_RETRIES Same as --retries"
|
|
echo " RETRY_DELAY Same as --delay"
|
|
echo " CONTAINER_NAME Same as --container"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 # Check localhost:8000"
|
|
echo " $0 -h production.example.com -p 80 # Check production server"
|
|
echo " $0 -c unitforge-container # Include container memory check"
|
|
}
|
|
|
|
# Parse command line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--host)
|
|
HOST="$2"
|
|
shift 2
|
|
;;
|
|
-p|--port)
|
|
PORT="$2"
|
|
shift 2
|
|
;;
|
|
-t|--timeout)
|
|
TIMEOUT="$2"
|
|
shift 2
|
|
;;
|
|
-r|--retries)
|
|
MAX_RETRIES="$2"
|
|
shift 2
|
|
;;
|
|
-d|--delay)
|
|
RETRY_DELAY="$2"
|
|
shift 2
|
|
;;
|
|
-c|--container)
|
|
CONTAINER_NAME="$2"
|
|
shift 2
|
|
;;
|
|
--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Check dependencies
|
|
if ! command -v curl > /dev/null 2>&1; then
|
|
log_error "curl is required but not installed"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v bc > /dev/null 2>&1; then
|
|
log_warn "bc is not installed, performance timing may not work properly"
|
|
fi
|
|
|
|
# Run the health check
|
|
run_health_check
|
|
exit $?
|