Files
unitforge/scripts/setup-ci.sh
William Valentin 25666a76cf feat: Add comprehensive Gitea CI/CD workflows for multi-arch container builds
- 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.
2025-09-15 02:04:07 -07:00

446 lines
11 KiB
Bash
Executable File

#!/bin/bash
# CI/CD Setup Script for UnitForge
# Sets up local environment for testing CI/CD workflows
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
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"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check Docker and Docker Buildx
check_docker() {
log_step "Checking Docker installation..."
if ! command_exists docker; then
log_error "Docker is not installed"
echo "Please install Docker from: https://docs.docker.com/get-docker/"
return 1
fi
log_info "Docker found: $(docker --version)"
# Check if Docker daemon is running
if ! docker info >/dev/null 2>&1; then
log_error "Docker daemon is not running"
echo "Please start Docker daemon"
return 1
fi
# Check Docker Buildx
if ! docker buildx version >/dev/null 2>&1; then
log_warn "Docker Buildx not found, installing..."
docker buildx install 2>/dev/null || true
fi
log_info "Docker Buildx found: $(docker buildx version)"
return 0
}
# Setup Docker Buildx for multi-arch builds
setup_buildx() {
log_step "Setting up Docker Buildx for multi-arch builds..."
# Create builder if it doesn't exist
if ! docker buildx ls | grep -q "unitforge-builder"; then
log_info "Creating unitforge-builder..."
docker buildx create --name unitforge-builder --use
else
log_info "Using existing unitforge-builder"
docker buildx use unitforge-builder
fi
# Bootstrap the builder
log_info "Bootstrapping builder..."
docker buildx inspect --bootstrap
log_info "Builder setup complete"
return 0
}
# Check container registry access
check_registry() {
log_step "Checking container registry configuration..."
if [ -f ".env" ]; then
log_info "Found .env file"
if grep -q "CONTAINER_REGISTRY_URL" .env; then
local registry_url
registry_url=$(grep '^CONTAINER_REGISTRY_URL=' .env | cut -d'=' -f2)
log_info "Registry URL: $registry_url"
else
log_warn "CONTAINER_REGISTRY_URL not found in .env"
fi
if grep -q "CONTAINER_TAG" .env; then
local container_tag
container_tag=$(grep '^CONTAINER_TAG=' .env | cut -d'=' -f2)
log_info "Container tag: $container_tag"
else
log_warn "CONTAINER_TAG not found in .env"
fi
else
log_warn ".env file not found"
log_info "Creating sample .env file..."
cat > .env << EOF
# Container Registry Configuration
CONTAINER_REGISTRY_URL=gitea-http.taildb3494.ts.net/will/unitforge
CONTAINER_TAG=latest
# Development Configuration
DEBUG=true
LOG_LEVEL=debug
EOF
log_info "Sample .env file created. Please update with your registry details."
fi
return 0
}
# Verify vendor assets
check_vendor_assets() {
log_step "Checking vendor assets..."
local assets=(
"frontend/static/vendor/bootstrap/css/bootstrap.min.css"
"frontend/static/vendor/bootstrap/js/bootstrap.bundle.min.js"
"frontend/static/vendor/fontawesome/css/all.min.css"
"frontend/static/vendor/fontawesome/webfonts/fa-solid-900.woff2"
"frontend/static/img/osi-logo.svg"
)
local missing_assets=0
for asset in "${assets[@]}"; do
if [ ! -f "$asset" ]; then
log_warn "Missing asset: $asset"
missing_assets=$((missing_assets + 1))
else
log_info "Found asset: $asset"
fi
done
if [ $missing_assets -gt 0 ]; then
log_error "$missing_assets vendor assets are missing"
log_info "Please ensure all vendor assets are downloaded and committed"
log_info "Run: make setup-dev to download missing assets"
return 1
fi
log_info "All vendor assets found"
return 0
}
# Test local build
test_local_build() {
log_step "Testing local Docker build..."
if docker build -t unitforge:test . >/dev/null 2>&1; then
log_info "Local Docker build successful"
# Test container startup
log_info "Testing container startup..."
if docker run -d --name unitforge-test -p 8080:8000 unitforge:test >/dev/null 2>&1; then
sleep 5
if curl -s -f http://localhost:8080/ >/dev/null 2>&1; then
log_info "Container startup test successful"
else
log_warn "Container started but not responding on port 8080"
fi
docker stop unitforge-test >/dev/null 2>&1
docker rm unitforge-test >/dev/null 2>&1
else
log_warn "Container startup test failed"
fi
# Clean up test image
docker rmi unitforge:test >/dev/null 2>&1 || true
return 0
else
log_error "Local Docker build failed"
return 1
fi
}
# Test multi-arch build
test_multiarch_build() {
log_step "Testing multi-architecture build..."
if docker buildx build --platform linux/amd64,linux/arm64 -t unitforge:multiarch-test . >/dev/null 2>&1; then
log_info "Multi-architecture build successful"
# Clean up
docker buildx rm --force >/dev/null 2>&1 || true
return 0
else
log_error "Multi-architecture build failed"
return 1
fi
}
# Check development environment
check_dev_environment() {
log_step "Checking development environment..."
# Check uv
if ! command_exists uv; then
log_error "uv is not installed"
echo "Install with: curl -LsSf https://astral.sh/uv/install.sh | sh"
return 1
fi
log_info "uv found: $(uv --version)"
# Check Python
if ! command_exists python3; then
log_error "Python 3 is not installed"
return 1
fi
log_info "Python found: $(python3 --version)"
# Check if virtual environment exists
if [ -d ".venv" ]; then
log_info "Virtual environment exists"
else
log_warn "Virtual environment not found"
log_info "Run: make setup-dev to create it"
fi
return 0
}
# Test CI/CD workflow syntax
check_workflow_syntax() {
log_step "Checking workflow syntax..."
local workflows_dir=".gitea/workflows"
if [ ! -d "$workflows_dir" ]; then
log_error "Workflows directory not found: $workflows_dir"
return 1
fi
local yaml_files=("$workflows_dir"/*.yml)
if [ ${#yaml_files[@]} -eq 0 ]; then
log_warn "No YAML workflow files found"
return 0
fi
local syntax_errors=0
for file in "${yaml_files[@]}"; do
if [ -f "$file" ]; then
log_info "Checking syntax: $(basename "$file")"
# Basic YAML syntax check (if python3 is available)
if command_exists python3; then
if python3 -c "import yaml; yaml.safe_load(open('$file'))" 2>/dev/null; then
log_info "$(basename "$file") syntax OK"
else
log_error "$(basename "$file") has syntax errors"
syntax_errors=$((syntax_errors + 1))
fi
else
log_warn "Python3 not available, skipping YAML syntax check"
fi
fi
done
if [ $syntax_errors -eq 0 ]; then
log_info "All workflow files have valid syntax"
return 0
else
log_error "$syntax_errors workflow files have syntax errors"
return 1
fi
}
# Generate CI/CD documentation
generate_docs() {
log_step "Generating CI/CD documentation..."
local docs_dir="docs/ci-cd"
mkdir -p "$docs_dir"
cat > "$docs_dir/local-testing.md" << 'EOF'
# Local CI/CD Testing
This guide helps you test CI/CD workflows locally before pushing to the repository.
## Prerequisites
- Docker with Buildx support
- uv package manager
- Python 3.8+
## Local Testing Commands
```bash
# Test local build
make docker-build
# Test multi-arch build
make docker-buildx-local
# Test full development workflow
make dev
# Run health checks
./scripts/health_check.sh
```
## Workflow Testing
Use `act` to test GitHub/Gitea workflows locally:
```bash
# Install act
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
# Test PR workflow
act pull_request -s CONTAINER_REGISTRY_USERNAME=test -s CONTAINER_REGISTRY_PASSWORD=test
# Test release workflow
act push -e tests/fixtures/release-event.json
```
## Troubleshooting
### Build Issues
- Ensure all vendor assets are committed
- Check Docker daemon is running
- Verify buildx is properly configured
### Registry Issues
- Check .env file configuration
- Verify registry credentials
- Test registry connectivity
### Performance Issues
- Use build cache: `--cache-from type=gha`
- Optimize Docker layers
- Use multi-stage builds
```
EOF
log_info "Local testing documentation generated: $docs_dir/local-testing.md"
return 0
}
# Main setup function
main() {
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE} UnitForge CI/CD Setup${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
local failed_checks=0
# Run all checks
check_docker || failed_checks=$((failed_checks + 1))
setup_buildx || failed_checks=$((failed_checks + 1))
check_registry || true # Don't fail on registry issues
check_vendor_assets || failed_checks=$((failed_checks + 1))
check_dev_environment || failed_checks=$((failed_checks + 1))
check_workflow_syntax || failed_checks=$((failed_checks + 1))
# Optional tests
if [ "$1" = "--test-build" ]; then
test_local_build || failed_checks=$((failed_checks + 1))
test_multiarch_build || failed_checks=$((failed_checks + 1))
fi
# Generate documentation
generate_docs || true
echo ""
echo -e "${BLUE}========================================${NC}"
if [ $failed_checks -eq 0 ]; then
log_info "✅ CI/CD setup completed successfully!"
echo ""
echo "Next steps:"
echo "1. Update .env with your registry details"
echo "2. Test local build: make docker-buildx-local"
echo "3. Run full test suite: make dev"
echo "4. Check workflow syntax: ./scripts/setup-ci.sh"
echo ""
echo "For testing builds:"
echo " ./scripts/setup-ci.sh --test-build"
else
log_error "❌ CI/CD setup completed with $failed_checks issues"
echo ""
echo "Please fix the issues above before proceeding."
exit 1
fi
echo -e "${BLUE}========================================${NC}"
}
# Usage information
usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --test-build Run local and multi-arch build tests"
echo " --help Show this help message"
echo ""
echo "This script sets up your local environment for CI/CD development."
echo "It checks Docker, Buildx, dependencies, and workflow syntax."
}
# Parse command line arguments
case "${1:-}" in
--help)
usage
exit 0
;;
--test-build)
main --test-build
;;
"")
main
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac