diff --git a/Dockerfile.minimal b/Dockerfile.minimal index bbaac67..d74ef07 100644 --- a/Dockerfile.minimal +++ b/Dockerfile.minimal @@ -1,6 +1,6 @@ # Minimal Dockerfile - No compilation required # Works on all architectures including ARM/Raspberry Pi -FROM python:3.13-slim +FROM python:3.11-slim # Set environment variables ENV PYTHONDONTWRITEBYTECODE=1 \ diff --git a/Dockerfile.pizero b/Dockerfile.pizero index 92bece9..a9726f0 100644 --- a/Dockerfile.pizero +++ b/Dockerfile.pizero @@ -1,5 +1,6 @@ # Optimized Dockerfile for Raspberry Pi Zero (ARMv6) # Minimal memory footprint and no compilation required +# Using Python 3.11 for compatibility with Pydantic v1 FROM python:3.11-slim # Set environment variables @@ -19,17 +20,20 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # Set working directory WORKDIR /app +# Copy requirements file first for better layer caching +COPY requirements-pizero.txt . + # Install Python dependencies one by one for better memory management on Pi Zero -# Using older stable versions that are known to work on ARMv6 -RUN pip install --no-cache-dir fastapi==0.95.2 -RUN pip install --no-cache-dir pydantic==1.10.9 -RUN pip install --no-cache-dir uvicorn==0.22.0 -RUN pip install --no-cache-dir httpx==0.24.1 -RUN pip install --no-cache-dir icalendar==5.0.7 -RUN pip install --no-cache-dir jinja2==3.1.2 -RUN pip install --no-cache-dir apscheduler==3.10.1 -RUN pip install --no-cache-dir pytz==2023.3 -RUN pip install --no-cache-dir python-multipart==0.0.6 +# Using versions that are compatible with Python 3.11 and don't require compilation +RUN pip install --no-cache-dir --no-compile fastapi==0.95.2 +RUN pip install --no-cache-dir --no-compile pydantic==1.10.9 +RUN pip install --no-cache-dir --no-compile uvicorn==0.22.0 +RUN pip install --no-cache-dir --no-compile httpx==0.24.1 +RUN pip install --no-cache-dir --no-compile icalendar==5.0.7 +RUN pip install --no-cache-dir --no-compile jinja2==3.1.2 +RUN pip install --no-cache-dir --no-compile apscheduler==3.10.1 +RUN pip install --no-cache-dir --no-compile pytz==2023.3 +RUN pip install --no-cache-dir --no-compile python-multipart==0.0.6 # Copy application files COPY main.py . @@ -43,4 +47,5 @@ RUN mkdir -p static && \ EXPOSE 8000 # Run with limited workers and basic asyncio loop for Pi Zero -CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", "--loop", "asyncio"] \ No newline at end of file +# Using explicit python command to ensure correct Python version +CMD ["python3.11", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", "--loop", "asyncio"] \ No newline at end of file diff --git a/Dockerfile.pizero-py312 b/Dockerfile.pizero-py312 new file mode 100644 index 0000000..32985df --- /dev/null +++ b/Dockerfile.pizero-py312 @@ -0,0 +1,46 @@ +# Optimized Dockerfile for Raspberry Pi Zero (ARMv6) +# Using Python 3.12 with Pydantic v2 for better compatibility +FROM python:3.12-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + TZ=Europe/Berlin \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +# Install only essential runtime dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + tzdata \ + && rm -rf /var/lib/apt/lists/* \ + && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \ + && echo $TZ > /etc/timezone + +# Set working directory +WORKDIR /app + +# Install Python dependencies one by one for better memory management on Pi Zero +# Using Pydantic v2 which is compatible with newer Python versions +RUN pip install --no-cache-dir --no-compile fastapi==0.109.0 +RUN pip install --no-cache-dir --no-compile pydantic==2.5.3 +RUN pip install --no-cache-dir --no-compile uvicorn==0.25.0 +RUN pip install --no-cache-dir --no-compile httpx==0.26.0 +RUN pip install --no-cache-dir --no-compile icalendar==5.0.11 +RUN pip install --no-cache-dir --no-compile jinja2==3.1.3 +RUN pip install --no-cache-dir --no-compile apscheduler==3.10.4 +RUN pip install --no-cache-dir --no-compile pytz==2023.3 +RUN pip install --no-cache-dir --no-compile python-multipart==0.0.6 + +# Copy application files +COPY main.py . +COPY Vektor-Logo.svg ./ + +# Create static directory and copy logo +RUN mkdir -p static && \ + cp Vektor-Logo.svg static/logo.svg + +# Expose port +EXPOSE 8000 + +# Run with limited workers and basic asyncio loop for Pi Zero +CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", "--loop", "asyncio"] \ No newline at end of file diff --git a/QR Code 2025-10-31 22-57-13.png b/QR Code 2025-10-31 22-57-13.png new file mode 100644 index 0000000..90fd94b Binary files /dev/null and b/QR Code 2025-10-31 22-57-13.png differ diff --git a/build-pi.sh b/build-pi.sh index ad62bef..1c88881 100755 --- a/build-pi.sh +++ b/build-pi.sh @@ -83,8 +83,19 @@ build_container() { # Choose the right Dockerfile if [ -f "Dockerfile.pizero" ] && [[ "$ARCH" == "armv6l" ]]; then - DOCKERFILE="Dockerfile.pizero" - print_info "Using Pi Zero optimized Dockerfile" + # Check if we need to use modern Python version + if [ "${USE_MODERN_PYTHON}" = "true" ] || [ -f ".use-modern-python" ]; then + if [ -f "Dockerfile.pizero-py312" ]; then + DOCKERFILE="Dockerfile.pizero-py312" + print_info "Using Pi Zero optimized Dockerfile with Python 3.12" + else + DOCKERFILE="Dockerfile.pizero" + print_info "Using Pi Zero optimized Dockerfile with Python 3.11" + fi + else + DOCKERFILE="Dockerfile.pizero" + print_info "Using Pi Zero optimized Dockerfile with Python 3.11" + fi elif [ -f "Dockerfile.minimal" ]; then DOCKERFILE="Dockerfile.minimal" print_info "Using minimal Dockerfile (no compilation)" @@ -97,7 +108,15 @@ build_container() { fi # Build without memory limits (they may not be supported on all systems) + # Force rebuild if requested + BUILD_ARGS="" + if [ "${FORCE_REBUILD}" = "true" ]; then + BUILD_ARGS="--no-cache" + print_info "Forcing rebuild without cache" + fi + ${RUNTIME} build \ + ${BUILD_ARGS} \ --file "${DOCKERFILE}" \ --tag "${IMAGE_NAME}" \ . || { @@ -185,6 +204,26 @@ show_logs() { ${RUNTIME} logs -f ${CONTAINER_NAME} } +# Clean up old images and containers +cleanup() { + print_info "Cleaning up old containers and images..." + + # Stop and remove container if it exists + if ${RUNTIME} ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + print_info "Stopping and removing old container..." + ${RUNTIME} stop ${CONTAINER_NAME} 2>/dev/null || true + ${RUNTIME} rm ${CONTAINER_NAME} 2>/dev/null || true + fi + + # Remove old image + if ${RUNTIME} images --format '{{.Repository}}' | grep -q "^${IMAGE_NAME}$"; then + print_info "Removing old image..." + ${RUNTIME} rmi ${IMAGE_NAME} 2>/dev/null || true + fi + + print_success "Cleanup complete!" +} + # Main script case "${1:-build}" in build) @@ -198,6 +237,18 @@ case "${1:-build}" in run_container check_status ;; + clean-build) + detect_system + check_runtime + cleanup + FORCE_REBUILD=true + build_container + ;; + cleanup|clean) + detect_system + check_runtime + cleanup + ;; start) detect_system check_runtime diff --git a/fix-python-compatibility.sh b/fix-python-compatibility.sh new file mode 100755 index 0000000..89d6907 --- /dev/null +++ b/fix-python-compatibility.sh @@ -0,0 +1,243 @@ +#!/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 \ No newline at end of file diff --git a/requirements-pizero-modern.txt b/requirements-pizero-modern.txt new file mode 100644 index 0000000..2e26eee --- /dev/null +++ b/requirements-pizero-modern.txt @@ -0,0 +1,45 @@ +# Requirements for Raspberry Pi Zero (ARMv6) +# Compatible with Python 3.12+ and Pydantic v2 +# All pure Python packages - NO compilation required + +# Core web framework - using versions compatible with Pydantic v2 +fastapi==0.109.0 +pydantic==2.5.3 +pydantic-core==2.14.6 +uvicorn[standard]==0.25.0 + +# HTTP client and utilities +httpx==0.26.0 +httpcore==1.0.2 +h11==0.14.0 + +# Calendar and timezone handling +icalendar==5.0.11 +pytz==2023.3 +python-dateutil==2.8.2 + +# Web framework dependencies +jinja2==3.1.3 +python-multipart==0.0.6 +starlette==0.35.1 + +# Task scheduling +apscheduler==3.10.4 + +# Type hints and utilities +typing-extensions==4.9.0 +annotated-types==0.6.0 + +# Standard utilities +click==8.1.7 +anyio==4.2.0 +sniffio==1.3.0 +idna==3.6 +certifi==2023.11.17 + +# Uvicorn extras for standard installation +websockets==12.0 +httptools==0.6.1 +python-dotenv==1.0.0 +pyyaml==6.0.1 +watchfiles==0.21.0 \ No newline at end of file