- Change the build process to allow cache busting - Optimisations to the build process - Several improvements of UI geared towards mobile experience -
389 lines
12 KiB
Bash
Executable File
389 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Safer bash: we manage errors manually (no -e) but detect pipeline failures
|
|
set -o pipefail
|
|
|
|
# Quiet mode default; enable verbose with --verbose
|
|
QUIET=${QUIET:-true}
|
|
|
|
# Parse --verbose early (we'll reparse fully later as well)
|
|
for arg in "$@"; do
|
|
if [[ "$arg" == "--verbose" ]]; then QUIET=false; fi
|
|
done
|
|
|
|
# Per-run logs directory
|
|
RUN_TS=$(date +%Y%m%d_%H%M%S)
|
|
LOG_DIR="./build_logs/$RUN_TS"
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
# Error aggregation
|
|
ERRORS=()
|
|
ERROR_LINES=()
|
|
EXIT_CODE=0
|
|
|
|
# Helper: run_quiet SERVICE STEP -- CMD ARGS...
|
|
run_quiet() {
|
|
local SERVICE="$1"; shift
|
|
local STEP="$1"; shift
|
|
# Expect a literal "--" separator before the command
|
|
if [[ "$1" == "--" ]]; then shift; fi
|
|
local LOG_FILE="$LOG_DIR/${SERVICE}.${STEP}.log"
|
|
if [[ "$QUIET" == "true" ]]; then
|
|
"$@" > /dev/null 2> >(tee -a "$LOG_FILE" >&2)
|
|
else
|
|
"$@" > >(tee -a "$LOG_FILE") 2> >(tee -a "$LOG_FILE" >&2)
|
|
fi
|
|
local RC=$?
|
|
echo "$LOG_FILE" > "$LOG_DIR/.last_${SERVICE}_${STEP}.path"
|
|
return $RC
|
|
}
|
|
|
|
record_error() {
|
|
local SERVICE="$1"; local STEP="$2"; local MESSAGE="$3"; local LOG_FILE="$4"
|
|
ERRORS+=("$SERVICE|$STEP|$LOG_FILE|$MESSAGE")
|
|
ERROR_LINES+=("$MESSAGE")
|
|
EXIT_CODE=1
|
|
}
|
|
|
|
source ./podman_env_switch.sh dev
|
|
|
|
# Load environment variables
|
|
source .env
|
|
|
|
# Check if podman is available
|
|
if ! command -v podman &> /dev/null; then
|
|
echo "Error: podman not found"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Using container runtime: podman"
|
|
|
|
# Local registry
|
|
REGISTRY="registry.ask-eve-ai-local.com"
|
|
# Account prefix voor consistency met Docker Hub
|
|
ACCOUNT="josakola"
|
|
|
|
# Tag (you might want to use a version or git commit hash)
|
|
TAG="latest"
|
|
|
|
# Single platform - AMD64 only for simplicity
|
|
PLATFORM="linux/amd64"
|
|
|
|
# Default action
|
|
ACTION="both"
|
|
|
|
# Default build options
|
|
NO_CACHE=""
|
|
PROGRESS=""
|
|
DEBUG=""
|
|
BUILD_BASE=""
|
|
BASE_ONLY=""
|
|
|
|
# Function to display usage information
|
|
usage() {
|
|
echo "Usage: $0 [-b|-p|-bb|--base-only] [--no-cache] [--progress=plain] [--debug] [--verbose] [service1 service2 ...]"
|
|
echo " -b: Build only"
|
|
echo " -p: Push only"
|
|
echo " -bb: Build base image (in addition to services)"
|
|
echo " --base-only: Build only base image (skip services)"
|
|
echo " --no-cache: Perform a clean build without using cache"
|
|
echo " --progress=plain: Show detailed progress of the build"
|
|
echo " --debug: Enable debug mode for the build"
|
|
echo " --verbose: Show full output of build/push (default is quiet; logs always saved under ./build_logs/<timestamp>)"
|
|
echo " If no option is provided, both build and push will be performed."
|
|
echo " If no services are specified, all eveai_ services and nginx will be processed."
|
|
echo " All images are built for AMD64 platform (compatible with both x86_64 and Apple Silicon via emulation)."
|
|
}
|
|
|
|
# Parse command-line options
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--verbose)
|
|
QUIET=false
|
|
shift
|
|
;;
|
|
-b)
|
|
ACTION="build"
|
|
shift
|
|
;;
|
|
-p)
|
|
ACTION="push"
|
|
shift
|
|
;;
|
|
-bb)
|
|
BUILD_BASE="true"
|
|
shift
|
|
;;
|
|
--base-only)
|
|
BASE_ONLY="true"
|
|
shift
|
|
;;
|
|
--no-cache)
|
|
NO_CACHE="--no-cache"
|
|
shift
|
|
;;
|
|
--progress=plain)
|
|
PROGRESS="--progress=plain"
|
|
shift
|
|
;;
|
|
--debug)
|
|
DEBUG="--debug"
|
|
shift
|
|
;;
|
|
-*)
|
|
echo "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Function to build base image
|
|
build_base_image() {
|
|
echo "🏗️ Building base image... =============================================================="
|
|
local BASE_IMAGE_NAME="$REGISTRY/$ACCOUNT/eveai-base:$TAG"
|
|
|
|
echo "Building base image for platform: $PLATFORM"
|
|
echo "Base image tag: $BASE_IMAGE_NAME"
|
|
|
|
run_quiet base build -- podman build \
|
|
--platform "$PLATFORM" \
|
|
$NO_CACHE \
|
|
$PROGRESS \
|
|
$DEBUG \
|
|
-t "$ACCOUNT/eveai-base:$TAG" \
|
|
-t "$BASE_IMAGE_NAME" \
|
|
-f Dockerfile.base \
|
|
..
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/base.build.log"
|
|
echo "❌ Failed to build base image"
|
|
record_error base build "❌ Failed to build base image" "$LOG_FILE"
|
|
return 1
|
|
fi
|
|
|
|
if [ "$ACTION" = "push" ] || [ "$ACTION" = "both" ]; then
|
|
echo "Pushing base image to registry..."
|
|
run_quiet base push -- podman push "$BASE_IMAGE_NAME"
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/base.push.log"
|
|
echo "❌ Failed to push base image"
|
|
record_error base push "❌ Failed to push base image" "$LOG_FILE"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
echo "✅ Base image built successfully"
|
|
}
|
|
|
|
# Function to check if we should build base image
|
|
should_build_base() {
|
|
if [ "$BUILD_BASE" = "true" ] || [ "$BASE_ONLY" = "true" ]; then
|
|
return 0 # true
|
|
else
|
|
return 1 # false
|
|
fi
|
|
}
|
|
|
|
# Function to build and/or push a service
|
|
process_service() {
|
|
local SERVICE="$1"
|
|
echo "Processing $SERVICE... =================================================================="
|
|
|
|
# Extract the build context and dockerfile from the compose file
|
|
CONTEXT=$(yq e ".services.$SERVICE.build.context" compose_dev.yaml)
|
|
DOCKERFILE=$(yq e ".services.$SERVICE.build.dockerfile" compose_dev.yaml)
|
|
|
|
# Check if context directory exists
|
|
if [ ! -d "$CONTEXT" ]; then
|
|
echo "Error: Build context directory '$CONTEXT' for service '$SERVICE' does not exist."
|
|
return 1
|
|
fi
|
|
|
|
# Check if Dockerfile exists
|
|
if [ ! -f "$CONTEXT/$DOCKERFILE" ]; then
|
|
echo "Error: Dockerfile '$DOCKERFILE' for service '$SERVICE' does not exist in context '$CONTEXT'."
|
|
return 1
|
|
fi
|
|
|
|
# Construct image names
|
|
LOCAL_IMAGE_NAME="$ACCOUNT/$SERVICE:$TAG"
|
|
REGISTRY_IMAGE_NAME="$REGISTRY/$ACCOUNT/$SERVICE:$TAG"
|
|
|
|
echo "Building for platform: $PLATFORM"
|
|
echo "Local tag: $LOCAL_IMAGE_NAME"
|
|
echo "Registry tag: $REGISTRY_IMAGE_NAME"
|
|
|
|
# Build and/or push based on ACTION
|
|
if [ "$ACTION" = "build" ]; then
|
|
echo "🛠️ Building $SERVICE for $PLATFORM..."
|
|
run_quiet "$SERVICE" build -- podman build \
|
|
--platform "$PLATFORM" \
|
|
$NO_CACHE \
|
|
$PROGRESS \
|
|
$DEBUG \
|
|
-t "$LOCAL_IMAGE_NAME" \
|
|
-t "$REGISTRY_IMAGE_NAME" \
|
|
-f "$CONTEXT/$DOCKERFILE" \
|
|
"$CONTEXT"
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/${SERVICE}.build.log"
|
|
echo "❌ Failed to build $SERVICE"
|
|
record_error "$SERVICE" build "❌ Failed to build $SERVICE" "$LOG_FILE"
|
|
return 1
|
|
fi
|
|
|
|
elif [ "$ACTION" = "push" ]; then
|
|
echo "📤 Pushing $SERVICE to registry..."
|
|
run_quiet "$SERVICE" push -- podman push "$REGISTRY_IMAGE_NAME"
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/${SERVICE}.push.log"
|
|
echo "❌ Failed to push $SERVICE"
|
|
record_error "$SERVICE" push "❌ Failed to push $SERVICE" "$LOG_FILE"
|
|
return 1
|
|
fi
|
|
|
|
else
|
|
# Both build and push
|
|
echo "🛠️ Building $SERVICE for $PLATFORM..."
|
|
run_quiet "$SERVICE" build -- podman build \
|
|
--platform "$PLATFORM" \
|
|
$NO_CACHE \
|
|
$PROGRESS \
|
|
$DEBUG \
|
|
-t "$LOCAL_IMAGE_NAME" \
|
|
-t "$REGISTRY_IMAGE_NAME" \
|
|
-f "$CONTEXT/$DOCKERFILE" \
|
|
"$CONTEXT"
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/${SERVICE}.build.log"
|
|
echo "❌ Failed to build $SERVICE"
|
|
record_error "$SERVICE" build "❌ Failed to build $SERVICE" "$LOG_FILE"
|
|
return 1
|
|
fi
|
|
|
|
echo "📤 Pushing $SERVICE to registry..."
|
|
run_quiet "$SERVICE" push -- podman push "$REGISTRY_IMAGE_NAME"
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/${SERVICE}.push.log"
|
|
echo "❌ Failed to push $SERVICE"
|
|
record_error "$SERVICE" push "❌ Failed to push $SERVICE" "$LOG_FILE"
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# If no arguments are provided, process all services
|
|
if [ $# -eq 0 ]; then
|
|
SERVICES=()
|
|
while IFS= read -r line; do
|
|
SERVICES+=("$line")
|
|
done < <(yq e '.services | keys | .[]' compose_dev.yaml | grep -E '^(nginx|eveai_|prometheus|grafana)')
|
|
else
|
|
SERVICES=("$@")
|
|
fi
|
|
|
|
# Handle base-only mode
|
|
if [ "$BASE_ONLY" = "true" ]; then
|
|
echo "🎯 Base-only mode: Building only base image"
|
|
build_base_image
|
|
echo -e "\033[32m✅ Base image build completed!\033[0m"
|
|
exit 0
|
|
fi
|
|
|
|
# Build base image if requested
|
|
if should_build_base; then
|
|
build_base_image
|
|
echo "" # Empty line for readability
|
|
fi
|
|
|
|
echo "Using simplified AMD64-only approach for maximum compatibility..."
|
|
echo "Images will be tagged as: $REGISTRY/$ACCOUNT/[service]:$TAG"
|
|
|
|
# Reorder to ensure nginx builds before eveai_* if both are present
|
|
HAS_NGINX=false
|
|
HAS_APPS=false
|
|
for S in "${SERVICES[@]}"; do
|
|
if [[ "$S" == "nginx" ]]; then HAS_NGINX=true; fi
|
|
if [[ "$S" == eveai_* ]]; then HAS_APPS=true; fi
|
|
done
|
|
|
|
if $HAS_NGINX && $HAS_APPS; then
|
|
ORDERED_SERVICES=("nginx")
|
|
for S in "${SERVICES[@]}"; do
|
|
if [[ "$S" != "nginx" ]]; then ORDERED_SERVICES+=("$S"); fi
|
|
done
|
|
SERVICES=("${ORDERED_SERVICES[@]}")
|
|
fi
|
|
|
|
# Loop through services
|
|
for SERVICE in "${SERVICES[@]}"; do
|
|
if [[ "$SERVICE" == "nginx" ]]; then
|
|
run_quiet nginx copy-specialist-svgs -- ./copy_specialist_svgs.sh ../config ../nginx/static/assets
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/nginx.copy-specialist-svgs.log"
|
|
echo "⚠️ copy_specialist_svgs.sh not found or failed"
|
|
record_error nginx copy-specialist-svgs "⚠️ copy_specialist_svgs.sh not found or failed" "$LOG_FILE"
|
|
fi
|
|
run_quiet nginx rebuild-chat-client -- ./rebuild_chat_client.sh
|
|
if [ $? -ne 0 ]; then
|
|
LOG_FILE="$LOG_DIR/nginx.rebuild-chat-client.log"
|
|
echo "❌ rebuild_chat_client.sh failed"
|
|
record_error nginx rebuild-chat-client "❌ rebuild_chat_client.sh failed" "$LOG_FILE"
|
|
fi
|
|
MANIFEST_SRC="../nginx/static/dist/manifest.json"
|
|
MANIFEST_DST_DIR="../config/static-manifest"
|
|
MANIFEST_DST="$MANIFEST_DST_DIR/manifest.json"
|
|
if [ ! -f "$MANIFEST_SRC" ]; then
|
|
if $HAS_NGINX; then
|
|
echo "⚠️ manifest.json not found at $MANIFEST_SRC yet. nginx should be built first in this run."
|
|
else
|
|
echo "❌ manifest.json not found at $MANIFEST_SRC. Please build nginx (assets) first."
|
|
exit 1
|
|
fi
|
|
fi
|
|
mkdir -p "$MANIFEST_DST_DIR"
|
|
if [ -f "$MANIFEST_SRC" ]; then
|
|
cp -f "$MANIFEST_SRC" "$MANIFEST_DST"
|
|
echo "📄 Staged manifest at $MANIFEST_DST"
|
|
fi
|
|
fi
|
|
|
|
if [[ "$SERVICE" == "nginx" || "$SERVICE" == eveai_* || "$SERVICE" == "prometheus" || "$SERVICE" == "grafana" ]]; then
|
|
if process_service "$SERVICE"; then
|
|
echo "✅ Successfully processed $SERVICE"
|
|
else
|
|
echo "❌ Failed to process $SERVICE"
|
|
ERROR_LINES+=("❌ Failed to process $SERVICE")
|
|
EXIT_CODE=1
|
|
fi
|
|
else
|
|
echo "⏭️ Skipping $SERVICE as it's not nginx, prometheus, grafana or doesn't start with eveai_"
|
|
fi
|
|
done
|
|
|
|
if [ ${#ERRORS[@]} -eq 0 ]; then
|
|
echo -e "\033[32m✅ All specified services processed successfully!\033[0m"
|
|
echo -e "\033[32m📦 Images are available locally and in registry\033[0m"
|
|
else
|
|
echo -e "\033[31m❌ One or more errors occurred during build/push\033[0m"
|
|
# Reprint short failure lines (your concise messages)
|
|
for LINE in "${ERROR_LINES[@]}"; do
|
|
echo "$LINE"
|
|
done
|
|
echo ""
|
|
echo "Details (see logs for full output):"
|
|
for ITEM in "${ERRORS[@]}"; do
|
|
SERVICE_STEP_MSG_LOG=$(echo "$ITEM")
|
|
IFS='|' read -r SVC STEP LOGFILE MSG <<< "$SERVICE_STEP_MSG_LOG"
|
|
echo "- Service: $SVC | Step: $STEP"
|
|
echo " ↳ Log: $LOGFILE"
|
|
done
|
|
EXIT_CODE=1
|
|
fi
|
|
# Always print finished timestamp
|
|
echo -e "\033[32m🕐 Finished at $(date +"%d/%m/%Y %H:%M:%S")\033[0m"
|
|
exit $EXIT_CODE |