#!/usr/bin/env bash # # packaging/build.sh — Top-level build orchestrator for smartbotic-vectorapi # # Supports two modes: # Default: Docker build targeting Debian 13 (production .debs) # --local: Native build on current system (development/testing) # set -euo pipefail # --------------------------------------------------------------------------- # Paths # --------------------------------------------------------------------------- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" BASE_IMAGE_NAME="smartbotic-vectorapi-build-base:debian13" BUILD_JOBS="${BUILD_JOBS:-$(nproc)}" DEB_REPO_DIR="${DEB_REPO_DIR:-/data/smartbotics-deb-repo}" # Output directories — separate to prevent local/Docker overwriting each other # dist/local/ — native build for current system # dist/debian13/ — Docker build for Debian 13 production DIST_BASE="${PROJECT_DIR}/dist" VERSION="$(cat "$PROJECT_DIR/VERSION")" GIT_COMMIT="$(git -C "$PROJECT_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown")" # --------------------------------------------------------------------------- # Defaults # --------------------------------------------------------------------------- LOCAL_MODE=0 INSTALL_AFTER=0 SKIP_TESTS=0 DEB_REVISION=1 REBUILD_BASE=0 NO_CACHE=0 DO_REPO=0 SUITE="" DO_SYNC=0 # --------------------------------------------------------------------------- # Colored logging # --------------------------------------------------------------------------- log_info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; } log_success() { printf '\033[1;32m[OK]\033[0m %s\n' "$*"; } log_warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; } log_error() { printf '\033[1;31m[ERROR]\033[0m %s\n' "$*"; } # --------------------------------------------------------------------------- # Usage # --------------------------------------------------------------------------- usage() { cat </dev/null; then log_error "Docker is not installed or not in PATH" exit 1 fi # Read repo credentials from the .env file (key names: RepoUser / RepoPass) local env_file="${DEB_REPO_DIR}/.env" if [[ ! -f "$env_file" ]]; then log_error "Repository .env not found: $env_file" exit 1 fi local REPO_USER REPO_PASS REPO_USER="$(grep '^RepoUser:' "$env_file" | awk '{print $2}')" REPO_PASS="$(grep '^RepoPass:' "$env_file" | awk '{print $2}')" if [[ -z "$REPO_USER" || -z "$REPO_PASS" ]]; then log_error "Could not parse RepoUser/RepoPass from $env_file" exit 1 fi # Check / build base image if [[ $REBUILD_BASE -eq 1 ]] || ! docker image inspect "$BASE_IMAGE_NAME" &>/dev/null; then if [[ $REBUILD_BASE -eq 1 ]]; then log_info "Rebuilding base image (--rebuild-base)..." else log_info "Base image not found, building ${BASE_IMAGE_NAME}..." fi local base_cache_args=() if [[ $NO_CACHE -eq 1 ]]; then base_cache_args+=("--no-cache") fi docker buildx build \ -f "$PROJECT_DIR/packaging/Dockerfile.base" \ -t "$BASE_IMAGE_NAME" \ --build-arg "REPO_USER=${REPO_USER}" \ --build-arg "REPO_PASS=${REPO_PASS}" \ "${base_cache_args[@]+"${base_cache_args[@]}"}" \ "$PROJECT_DIR" log_success "Base image built: ${BASE_IMAGE_NAME}" else log_info "Using existing base image: ${BASE_IMAGE_NAME}" fi # Build packages log_info "Building packages in Docker..." local build_cache_args=() if [[ $NO_CACHE -eq 1 ]]; then build_cache_args+=("--no-cache") fi docker buildx build \ -f "$PROJECT_DIR/packaging/Dockerfile.build" \ --build-arg "BASE_IMAGE=${BASE_IMAGE_NAME}" \ --build-arg "BUILD_VERSION=${VERSION}" \ --build-arg "BUILD_DEB_REVISION=${DEB_REVISION}" \ --build-arg "BUILD_JOBS=${BUILD_JOBS}" \ --build-arg "BUILD_GIT_COMMIT=${GIT_COMMIT}" \ --target packages \ --output "type=local,dest=$OUTPUT_DIR" \ "${build_cache_args[@]+"${build_cache_args[@]}"}" \ "$PROJECT_DIR" log_success "Docker build completed" } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- if [[ $LOCAL_MODE -eq 1 ]]; then build_local else build_docker fi # --------------------------------------------------------------------------- # List created packages # --------------------------------------------------------------------------- echo "" log_info "Created packages:" ls -lh "$OUTPUT_DIR"/*.deb 2>/dev/null || { log_error "No .deb files found in $OUTPUT_DIR" exit 1 } # --------------------------------------------------------------------------- # Repository operations # --------------------------------------------------------------------------- if [[ $DO_REPO -eq 1 ]]; then echo "" log_info "Adding packages to repository (suite: ${SUITE})..." if [[ ! -x "$DEB_REPO_DIR/scripts/add-packages.sh" ]]; then log_error "Repository tool not found: $DEB_REPO_DIR/scripts/add-packages.sh" exit 1 fi "$DEB_REPO_DIR/scripts/add-packages.sh" "$OUTPUT_DIR" log_success "Packages added to staging" "$DEB_REPO_DIR/scripts/create-repo.sh" --suite "$SUITE" log_success "Repository created for suite: ${SUITE}" if [[ $DO_SYNC -eq 1 ]]; then log_info "Syncing repository to repository.smartbotics.ai..." "$DEB_REPO_DIR/scripts/sync-repo.sh" log_success "Repository synced" fi fi # --------------------------------------------------------------------------- # Next steps # --------------------------------------------------------------------------- echo "" log_success "Build complete! (v${VERSION}-${DEB_REVISION})" echo "" if [[ $LOCAL_MODE -eq 1 && $INSTALL_AFTER -eq 0 ]]; then log_info "To install locally:" echo " sudo dpkg -i ${OUTPUT_DIR}/*.deb || sudo apt-get install -f -y" echo "" fi if [[ $DO_REPO -eq 0 ]]; then log_info "To publish to repository:" echo " packaging/build.sh --repo --suite trixie" echo "" fi if [[ $DO_REPO -eq 1 && $DO_SYNC -eq 0 ]]; then log_info "To sync repository to remote:" echo " packaging/build.sh --repo --suite ${SUITE} --sync" echo "" fi