#!/bin/bash # Fix script for Python/Pydantic compatibility issues on Raspberry Pi # This script helps resolve the Python 3.13 / Pydantic v1 incompatibility 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 # 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 } # Main fix process main() { print_info "Python/Pydantic Compatibility Fix Script" print_info "=========================================" # Detect runtime detect_runtime # Check current situation print_info "Checking current container status..." if ${RUNTIME} ps -a --format '{{.Names}}' | grep -q "^turmli-calendar$"; then print_warning "Found existing turmli-calendar container" # Show current Python version if container is running if ${RUNTIME} ps --format '{{.Names}}' | grep -q "^turmli-calendar$"; then print_info "Checking Python version in running container..." PYTHON_VERSION=$(${RUNTIME} exec turmli-calendar python --version 2>&1 || echo "Unable to determine") print_info "Current Python version: ${PYTHON_VERSION}" fi fi print_info "" print_info "This script will fix the Python compatibility issue by:" print_info "1. Stopping and removing the existing container" print_info "2. Removing the old container image" print_info "3. Building a new image with Python 3.11 and compatible packages" print_info "" read -p "Do you want to proceed? (y/N) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then print_info "Operation cancelled" exit 0 fi # Step 1: Stop and remove existing container print_info "Step 1: Cleaning up existing container..." if ${RUNTIME} ps -a --format '{{.Names}}' | grep -q "^turmli-calendar$"; then ${RUNTIME} stop turmli-calendar 2>/dev/null || true ${RUNTIME} rm turmli-calendar 2>/dev/null || true print_success "Container removed" else print_info "No existing container found" fi # Step 2: Remove old image print_info "Step 2: Removing old container image..." if ${RUNTIME} images --format '{{.Repository}}' | grep -q "^turmli-calendar$"; then ${RUNTIME} rmi turmli-calendar 2>/dev/null || true print_success "Old image removed" else print_info "No existing image found" fi # Step 3: Detect architecture and select appropriate Dockerfile ARCH=$(uname -m) print_info "Step 3: Selecting appropriate Dockerfile for architecture: ${ARCH}" if [[ "$ARCH" == "armv6l" ]]; then # Raspberry Pi Zero if [ -f "Dockerfile.pizero" ]; then DOCKERFILE="Dockerfile.pizero" print_info "Using Dockerfile.pizero (Python 3.11 with Pydantic v1)" else print_error "Dockerfile.pizero not found!" exit 1 fi elif [[ "$ARCH" == "armv7l" ]] || [[ "$ARCH" == "aarch64" ]]; then # Newer Raspberry Pi models if [ -f "Dockerfile.pizero-py312" ]; then DOCKERFILE="Dockerfile.pizero-py312" print_info "Using Dockerfile.pizero-py312 (Python 3.12 with Pydantic v2)" elif [ -f "Dockerfile.pizero" ]; then DOCKERFILE="Dockerfile.pizero" print_info "Using Dockerfile.pizero (Python 3.11 with Pydantic v1)" else print_error "No suitable Dockerfile found!" exit 1 fi else # Other architectures if [ -f "Dockerfile.minimal" ]; then DOCKERFILE="Dockerfile.minimal" print_info "Using Dockerfile.minimal" elif [ -f "Dockerfile" ]; then DOCKERFILE="Dockerfile" print_info "Using standard Dockerfile" else print_error "No Dockerfile found!" exit 1 fi fi # Step 4: Build new image print_info "Step 4: Building new container image..." print_info "This may take a few minutes, especially on Pi Zero..." # Check available memory TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}') TOTAL_MEM_MB=$((TOTAL_MEM_KB / 1024)) if [ $TOTAL_MEM_MB -lt 512 ]; then print_warning "Low memory detected: ${TOTAL_MEM_MB}MB" print_info "Build may be slow. Consider adding swap if it fails." fi # Build with no cache to ensure fresh build if ${RUNTIME} build --no-cache --file "${DOCKERFILE}" --tag turmli-calendar . ; then print_success "Image built successfully!" else print_error "Build failed!" print_info "" print_info "Troubleshooting tips:" print_info "1. If you're on a Pi Zero with low memory, try adding swap:" print_info " sudo dd if=/dev/zero of=/swapfile bs=1M count=1024" print_info " sudo chmod 600 /swapfile" print_info " sudo mkswap /swapfile" print_info " sudo swapon /swapfile" print_info "" print_info "2. Try building with the minimal Dockerfile:" print_info " ${RUNTIME} build --no-cache --file Dockerfile.minimal --tag turmli-calendar ." print_info "" print_info "3. Use a pre-built image (if available):" print_info " ${RUNTIME} pull ghcr.io/yourusername/turmli-calendar:armv6" exit 1 fi # Step 5: Create cache file if needed if [ ! -f "calendar_cache.json" ]; then echo "{}" > calendar_cache.json print_info "Created cache file" fi # Step 6: Run the new container print_info "Step 5: Starting new container..." # Try to run with SELinux labels first (Fedora/RHEL) if ${RUNTIME} run -d \ --name turmli-calendar \ -p 8000:8000 \ -v $(pwd)/calendar_cache.json:/app/calendar_cache.json:Z \ -e TZ=Europe/Berlin \ --restart unless-stopped \ turmli-calendar 2>/dev/null; then print_success "Container started with SELinux labels!" else # Retry without SELinux labels (Debian/Ubuntu/Raspbian) if ${RUNTIME} run -d \ --name turmli-calendar \ -p 8000:8000 \ -v $(pwd)/calendar_cache.json:/app/calendar_cache.json \ -e TZ=Europe/Berlin \ --restart unless-stopped \ turmli-calendar; then print_success "Container started!" else print_error "Failed to start container!" print_info "Check logs with: ${RUNTIME} logs turmli-calendar" exit 1 fi fi # Step 7: Verify the fix print_info "Step 6: Verifying the fix..." sleep 3 # Check if container is running if ${RUNTIME} ps --format '{{.Names}}' | grep -q "^turmli-calendar$"; then print_success "Container is running!" # Check Python version PYTHON_VERSION=$(${RUNTIME} exec turmli-calendar python --version 2>&1) print_info "Python version in container: ${PYTHON_VERSION}" # Check if app is responding if curl -s -f http://localhost:8000/api/events > /dev/null 2>&1; then print_success "Application is responding!" print_info "" print_success "✨ Fix completed successfully! ✨" print_info "Access your calendar at:" print_info " http://$(hostname -I | awk '{print $1}'):8000" print_info " http://localhost:8000" else print_warning "Application not responding yet, checking logs..." ${RUNTIME} logs --tail 10 turmli-calendar print_info "" print_info "The application may still be starting up." print_info "Check full logs with: ${RUNTIME} logs -f turmli-calendar" fi else print_error "Container failed to start!" print_info "Checking logs..." ${RUNTIME} logs turmli-calendar exit 1 fi } # Run main function main