#!/bin/bash # Smart container build script for Turmli Bar Calendar # Automatically detects architecture and chooses the best build strategy 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 # Configuration IMAGE_NAME="${IMAGE_NAME:-turmli-calendar}" BUILD_TYPE="" RUNTIME="" # Print functions print_error() { echo -e "${RED}❌ $1${NC}" } print_success() { echo -e "${GREEN}✅ $1${NC}" } print_info() { echo -e "${BLUE}ℹ️ $1${NC}" } print_warning() { echo -e "${YELLOW}⚠️ $1${NC}" } # Detect container runtime detect_runtime() { if command -v podman &> /dev/null; then RUNTIME="podman" print_info "Using Podman" elif command -v docker &> /dev/null; then RUNTIME="docker" print_info "Using Docker" else print_error "No container runtime found. Please install Podman or Docker." exit 1 fi } # Detect architecture and choose build strategy detect_architecture() { ARCH=$(uname -m) OS=$(uname -s) print_info "System: ${OS} ${ARCH}" case "${ARCH}" in armv6l|armv7l) print_info "ARM 32-bit detected (Raspberry Pi)" BUILD_TYPE="arm" ;; aarch64|arm64) print_info "ARM 64-bit detected" BUILD_TYPE="arm" ;; x86_64|amd64) print_info "x86_64 detected" BUILD_TYPE="standard" ;; *) print_warning "Unknown architecture: ${ARCH}" BUILD_TYPE="standard" ;; esac } # Check if we have enough memory for build check_memory() { if [ -f /proc/meminfo ]; then TOTAL_MEM=$(grep MemTotal /proc/meminfo | awk '{print $2}') TOTAL_MEM_MB=$((TOTAL_MEM / 1024)) if [ $TOTAL_MEM_MB -lt 512 ]; then print_warning "Low memory detected: ${TOTAL_MEM_MB}MB" print_warning "Build may fail. Consider using pre-built images or increasing swap." # Check swap SWAP=$(grep SwapTotal /proc/meminfo | awk '{print $2}') SWAP_MB=$((SWAP / 1024)) if [ $SWAP_MB -lt 1024 ]; then print_warning "Swap is low: ${SWAP_MB}MB. Consider adding swap space:" echo " sudo dd if=/dev/zero of=/swapfile bs=1G count=2" echo " sudo mkswap /swapfile" echo " sudo swapon /swapfile" fi else print_info "Memory: ${TOTAL_MEM_MB}MB" fi fi } # Build for ARM architecture build_arm() { print_info "Building for ARM architecture..." # Check which Dockerfile to use if [ -f "Dockerfile.arm" ]; then print_info "Using ARM-optimized Dockerfile" DOCKERFILE="Dockerfile.arm" else print_warning "Dockerfile.arm not found, using standard Dockerfile" print_warning "This may take longer and require more memory" DOCKERFILE="Dockerfile" fi # Check if requirements-arm.txt exists if [ -f "requirements-arm.txt" ] && [ -f "Dockerfile.arm" ]; then print_info "Using simplified requirements for ARM" elif [ -f "requirements-arm.txt" ]; then print_warning "Found requirements-arm.txt but no Dockerfile.arm" print_info "Consider creating Dockerfile.arm for better ARM support" fi # Build with limited parallelism on ARM to save memory print_info "Building with limited parallelism to save memory..." export CARGO_BUILD_JOBS=1 export MAKEFLAGS="-j1" ${RUNTIME} build \ --file "${DOCKERFILE}" \ --tag "${IMAGE_NAME}" \ --memory-swap -1 \ . || { print_error "Build failed!" print_info "Troubleshooting tips:" echo " 1. Try increasing swap space" echo " 2. Use Dockerfile.arm with requirements-arm.txt" echo " 3. Build on a more powerful machine and transfer the image" exit 1 } } # Build for standard architecture build_standard() { print_info "Building for standard architecture..." # Choose Dockerfile if [ "$RUNTIME" = "podman" ] && [ -f "Containerfile" ]; then DOCKERFILE="Containerfile" elif [ -f "Dockerfile" ]; then DOCKERFILE="Dockerfile" else print_error "No Dockerfile found!" exit 1 fi print_info "Using ${DOCKERFILE}" ${RUNTIME} build \ --file "${DOCKERFILE}" \ --tag "${IMAGE_NAME}" \ . || { print_error "Build failed!" exit 1 } } # Build with cache mount (if supported) build_with_cache() { print_info "Attempting build with cache mount..." # Check if BuildKit is available (Docker) or if Podman supports cache mounts if [ "$RUNTIME" = "docker" ]; then export DOCKER_BUILDKIT=1 CACHE_OPT="--mount=type=cache,target=/root/.cache/pip" elif [ "$RUNTIME" = "podman" ]; then # Podman 4.1+ supports cache mounts CACHE_OPT="--mount=type=cache,target=/root/.cache/pip" else CACHE_OPT="" fi # Attempt build with cache if [ -n "$CACHE_OPT" ]; then print_info "Using build cache for faster rebuilds" fi if [ "$BUILD_TYPE" = "arm" ]; then build_arm else build_standard fi } # Clean build (no cache) clean_build() { print_info "Performing clean build (no cache)..." ${RUNTIME} build \ --no-cache \ --file "${DOCKERFILE:-Dockerfile}" \ --tag "${IMAGE_NAME}" \ . || { print_error "Build failed!" exit 1 } } # Show build information show_info() { echo -e "${BLUE}Build Configuration:${NC}" echo " Runtime: ${RUNTIME}" echo " Architecture: $(uname -m)" echo " Build Type: ${BUILD_TYPE}" echo " Image Name: ${IMAGE_NAME}" if [ "$BUILD_TYPE" = "arm" ]; then echo "" echo -e "${YELLOW}ARM Build Notes:${NC}" echo " - Uses simplified dependencies where possible" echo " - Avoids packages requiring Rust compilation" echo " - May have slightly reduced performance" echo " - Optimized for low memory usage" fi echo "" print_info "Checking available Dockerfiles..." for file in Dockerfile Containerfile Dockerfile.arm; do if [ -f "$file" ]; then print_success "$file found" fi done echo "" print_info "Checking requirements files..." for file in requirements.txt requirements-arm.txt; do if [ -f "$file" ]; then print_success "$file found" fi done } # Main function main() { case "${1:-}" in --clean) detect_runtime detect_architecture clean_build ;; --info) detect_runtime detect_architecture check_memory show_info ;; --help) echo "Smart Container Build Script for Turmli Bar Calendar" echo "" echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --clean Perform a clean build (no cache)" echo " --info Show build configuration and system info" echo " --help Show this help message" echo "" echo "Environment Variables:" echo " IMAGE_NAME Name for the built image (default: turmli-calendar)" echo "" echo "The script automatically detects:" echo " - Container runtime (Podman/Docker)" echo " - System architecture (ARM/x86_64)" echo " - Available memory" echo " - Best Dockerfile to use" ;; *) detect_runtime detect_architecture check_memory print_info "Starting build process..." if [ "$BUILD_TYPE" = "arm" ]; then build_arm else build_standard fi if [ $? -eq 0 ]; then print_success "Build completed successfully!" print_info "Image: ${IMAGE_NAME}" print_info "Run with: ${RUNTIME} run -d -p 8000:8000 ${IMAGE_NAME}" fi ;; esac } # Run main function main "$@"