#!/bin/bash # Build script for Raspberry Pi Zero/ARM devices # Optimized for low memory and no compilation 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="turmli-calendar" CONTAINER_NAME="turmli-calendar" # 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 system detect_system() { ARCH=$(uname -m) OS=$(cat /etc/os-release | grep "^ID=" | cut -d= -f2 | tr -d '"') VERSION=$(cat /etc/os-release | grep "^VERSION_CODENAME=" | cut -d= -f2 | tr -d '"') print_info "System: ${OS} ${VERSION} on ${ARCH}" # Check if we're on a Raspberry Pi if [ -f /proc/device-tree/model ]; then MODEL=$(tr -d '\0' < /proc/device-tree/model) print_info "Raspberry Pi detected: ${MODEL}" fi # Check available memory if [ -f /proc/meminfo ]; then TOTAL_MEM=$(grep MemTotal /proc/meminfo | awk '{print $2}') TOTAL_MEM_MB=$((TOTAL_MEM / 1024)) print_info "Available RAM: ${TOTAL_MEM_MB}MB" if [ $TOTAL_MEM_MB -lt 512 ]; then print_warning "Low memory detected! Build may be slow." print_info "Tip: Consider adding swap space if build fails" fi fi } # Check for container runtime check_runtime() { if command -v podman &> /dev/null; then RUNTIME="podman" print_success "Using Podman" elif command -v docker &> /dev/null; then RUNTIME="docker" print_success "Using Docker" else print_error "No container runtime found!" print_info "Install Podman (recommended) or Docker:" echo " sudo apt-get update" echo " sudo apt-get install podman" exit 1 fi } # Build the container build_container() { print_info "Building container image..." # Choose the right Dockerfile if [ -f "Dockerfile.pizero" ] && [[ "$ARCH" == "armv6l" ]]; then DOCKERFILE="Dockerfile.pizero" print_info "Using Pi Zero optimized Dockerfile" elif [ -f "Dockerfile.minimal" ]; then DOCKERFILE="Dockerfile.minimal" print_info "Using minimal Dockerfile (no compilation)" elif [ -f "Dockerfile" ]; then DOCKERFILE="Dockerfile" print_warning "Using standard Dockerfile (may require compilation)" else print_error "No Dockerfile found!" exit 1 fi # Build without memory limits (they may not be supported on all systems) ${RUNTIME} build \ --file "${DOCKERFILE}" \ --tag "${IMAGE_NAME}" \ . || { print_error "Build failed!" if [ $TOTAL_MEM_MB -lt 512 ]; then print_info "Try these solutions for low memory:" echo " 1. Add swap space:" echo " sudo dd if=/dev/zero of=/swapfile bs=1M count=1024" echo " sudo mkswap /swapfile" echo " sudo swapon /swapfile" echo " 2. Use pre-built image:" echo " ${RUNTIME} pull docker.io/yourusername/turmli-calendar:armv6" fi exit 1 } print_success "Container image built successfully!" } # Run the container run_container() { print_info "Starting container..." # Check if container already exists if ${RUNTIME} ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then print_info "Container already exists, removing it..." ${RUNTIME} stop ${CONTAINER_NAME} 2>/dev/null || true ${RUNTIME} rm ${CONTAINER_NAME} 2>/dev/null || true fi # Create cache file if it doesn't exist if [ ! -f "calendar_cache.json" ]; then echo "{}" > calendar_cache.json print_info "Created cache file" fi # Run container (without memory limits as they may not be supported) print_info "Starting container..." ${RUNTIME} run -d \ --name ${CONTAINER_NAME} \ -p 8000:8000 \ -v $(pwd)/calendar_cache.json:/app/calendar_cache.json:Z \ -e TZ=Europe/Berlin \ --restart unless-stopped \ ${IMAGE_NAME} || { # If it fails with :Z flag (SELinux label), try without it print_warning "Retrying without SELinux labels..." ${RUNTIME} run -d \ --name ${CONTAINER_NAME} \ -p 8000:8000 \ -v $(pwd)/calendar_cache.json:/app/calendar_cache.json \ -e TZ=Europe/Berlin \ --restart unless-stopped \ ${IMAGE_NAME} } print_success "Container started!" print_info "Access the calendar at: http://$(hostname -I | awk '{print $1}'):8000" print_info "or http://localhost:8000" } # Check container status check_status() { print_info "Checking container status..." if ${RUNTIME} ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -q ${CONTAINER_NAME}; then ${RUNTIME} ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "NAMES|${CONTAINER_NAME}" # Test if the app is responding print_info "Testing application..." sleep 3 if curl -s -f http://localhost:8000/api/events > /dev/null 2>&1; then print_success "Application is running and healthy!" else print_warning "Application not responding yet, checking logs..." ${RUNTIME} logs --tail 20 ${CONTAINER_NAME} fi else print_warning "Container is not running" fi } # Show logs show_logs() { ${RUNTIME} logs -f ${CONTAINER_NAME} } # Main script case "${1:-build}" in build) detect_system check_runtime build_container ;; run) detect_system check_runtime run_container check_status ;; start) detect_system check_runtime build_container run_container check_status ;; status) check_runtime check_status ;; logs) check_runtime show_logs ;; stop) check_runtime print_info "Stopping container..." ${RUNTIME} stop ${CONTAINER_NAME} print_success "Container stopped" ;; help) echo "Raspberry Pi Build Script for Turmli Calendar" echo "" echo "Usage: $0 [COMMAND]" echo "" echo "Commands:" echo " build - Build the container image (default)" echo " run - Run the container" echo " start - Build and run" echo " status - Check container status" echo " logs - Show container logs" echo " stop - Stop the container" echo " help - Show this help" echo "" echo "This script is optimized for Raspberry Pi Zero and other" echo "low-resource ARM devices. It uses Pydantic v1 to avoid" echo "Rust compilation requirements." ;; *) echo "Unknown command: $1" echo "Run '$0 help' for usage" exit 1 ;; esac