300 lines
8.2 KiB
Bash
Executable File
300 lines
8.2 KiB
Bash
Executable File
#!/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 "$@" |