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.
This commit is contained in:
445
scripts/setup-ci.sh
Executable file
445
scripts/setup-ci.sh
Executable file
@@ -0,0 +1,445 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user