#!/bin/bash # # Smartbotic Production Build Script # # This script handles the Docker build workflow: # 1. Checks if the pre-built base image exists # 2. Creates it if needed # 3. Builds production packages using the base image # # Usage: # ./packaging/build.sh # Build with auto-detected version # ./packaging/build.sh 0.1.0 # Build with specific version # ./packaging/build.sh --rebuild-base # Force rebuild the base image # set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" cd "$PROJECT_DIR" # Configuration BASE_IMAGE_NAME="smartbotic-build-base:debian13" OUTPUT_DIR="${PROJECT_DIR}/dist" BUILD_JOBS="${BUILD_JOBS:-16}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[OK]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # Parse arguments REBUILD_BASE=false CLEAR_CACHE=false NO_CACHE=false VERSION="" while [[ $# -gt 0 ]]; do case $1 in --rebuild-base) REBUILD_BASE=true shift ;; --clear-cache) CLEAR_CACHE=true shift ;; --no-cache) NO_CACHE=true shift ;; --help|-h) echo "Smartbotic Production Build Script" echo "" echo "Usage: $0 [OPTIONS] [VERSION]" echo "" echo "Options:" echo " --rebuild-base Force rebuild the base image" echo " --clear-cache Clear BuildKit build caches (ccache, npm)" echo " --no-cache Build without using caches" echo " --help, -h Show this help message" echo "" echo "Arguments:" echo " VERSION Version string (default: auto-detect from CMakeLists.txt)" echo "" echo "Environment variables:" echo " BUILD_JOBS Number of parallel build jobs (default: 16)" echo "" echo "Examples:" echo " $0 # Build with auto-detected version" echo " $0 0.1.0 # Build version 0.1.0" echo " $0 --rebuild-base # Rebuild base image and build packages" echo " $0 --clear-cache # Clear caches and build fresh" exit 0 ;; *) VERSION="$1" shift ;; esac done # Auto-detect version if not specified if [ -z "$VERSION" ]; then VERSION=$(grep -oP 'project\(smartbotic-microbit VERSION \K[0-9.]+' CMakeLists.txt) if [ -z "$VERSION" ]; then log_error "Could not detect version from CMakeLists.txt" exit 1 fi fi log_info "Smartbotic Build Script" log_info "Version: $VERSION" log_info "Output directory: $OUTPUT_DIR" echo "" # Check if Docker is available if ! command -v docker &> /dev/null; then log_error "Docker is not installed or not in PATH" exit 1 fi # Check if base image exists base_image_exists() { docker image inspect "$BASE_IMAGE_NAME" &> /dev/null } # Build base image build_base_image() { log_info "Building base image: $BASE_IMAGE_NAME" log_info "This may take several minutes on first run..." echo "" docker buildx build \ -f packaging/Dockerfile.base \ -t "$BASE_IMAGE_NAME" \ . log_success "Base image built successfully" echo "" } # Build production packages build_packages() { log_info "Building production packages..." log_info "Using base image: $BASE_IMAGE_NAME" log_info "Build jobs: $BUILD_JOBS" if [ "$NO_CACHE" = true ]; then log_warn "Building without caches (--no-cache)" else log_info "Using ccache and npm cache for faster builds" fi echo "" # Get git info GIT_COMMIT=$(git rev-parse HEAD 2>/dev/null || echo "unknown") GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") log_info "Git commit: $GIT_COMMIT" log_info "Git branch: $GIT_BRANCH" echo "" # Clean output directory rm -rf "$OUTPUT_DIR" mkdir -p "$OUTPUT_DIR" # Build arguments BUILD_ARGS=( -f packaging/Dockerfile.build --build-arg BASE_IMAGE="$BASE_IMAGE_NAME" --build-arg BUILD_VERSION="$VERSION" --build-arg BUILD_GIT_COMMIT="$GIT_COMMIT" --build-arg BUILD_GIT_BRANCH="$GIT_BRANCH" --build-arg BUILD_JOBS="$BUILD_JOBS" --target packages --output "type=local,dest=$OUTPUT_DIR" ) # Add --no-cache if requested if [ "$NO_CACHE" = true ]; then BUILD_ARGS+=(--no-cache) fi # Build packages docker buildx build "${BUILD_ARGS[@]}" . log_success "Packages built successfully" echo "" } # Clear build caches clear_build_cache() { log_info "Clearing BuildKit build caches..." docker builder prune --filter type=exec.cachemount -f log_success "Build caches cleared" echo "" } # Main flow echo "==================================================" echo " Smartbotic Production Build" echo " Version: $VERSION" echo "==================================================" echo "" # Clear cache if requested if [ "$CLEAR_CACHE" = true ]; then clear_build_cache fi # Check/build base image if [ "$REBUILD_BASE" = true ]; then log_warn "Force rebuilding base image..." build_base_image elif base_image_exists; then log_success "Base image found: $BASE_IMAGE_NAME" else log_warn "Base image not found, building..." build_base_image fi # Build packages build_packages # List created packages echo "==================================================" echo " Build Complete" echo "==================================================" echo "" log_info "Created packages in $OUTPUT_DIR:" echo "" ls -lh "$OUTPUT_DIR"/*.tar.gz 2>/dev/null | while read line; do echo " $line" done echo "" # Calculate total size TOTAL_SIZE=$(du -sh "$OUTPUT_DIR" | cut -f1) log_info "Total size: $TOTAL_SIZE" echo "" # Show cache info echo "==================================================" echo " Build Cache Info" echo "==================================================" echo "" log_info "BuildKit caches (ccache, npm) are stored persistently." log_info "Subsequent builds will be faster due to compilation caching." echo "" log_info "Cache management commands:" echo " $0 --clear-cache # Clear all build caches" echo " $0 --no-cache # Build without using caches" echo "" log_success "Build completed successfully!" echo "" echo "To deploy to production:" echo " scp dist/*.tar.gz root@:/tmp/smartbotic-update/" echo " ssh root@ '/opt/smartbotic/bin/smartbotic-update --rolling --backup /tmp/smartbotic-update/'" echo ""