#!/usr/bin/env bash set -euo pipefail # Build script for minimal Debian base image with Erlang/OTP and Elixir # This script can be re-run to rebuild with latest packages # Detect container engine (podman or docker) if command -v podman &> /dev/null; then CONTAINER_CMD="podman" elif command -v docker &> /dev/null; then CONTAINER_CMD="docker" else echo "Error: Neither podman nor docker found. Please install one of them." exit 1 fi # Configuration REGISTRY="${REGISTRY:-registry.gitlab.com/towerops/towerops}" DOCKERHUB_REGISTRY="${DOCKERHUB_REGISTRY:-docker.io/gmcintire}" IMAGE_NAME="towerops-base" ELIXIR_VERSION="${ELIXIR_VERSION:-1.19.4}" OTP_VERSION="${OTP_VERSION:-28.3}" DEBIAN_VERSION="${DEBIAN_VERSION:-trixie-20251229-slim}" # Build timestamp for tracking BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") GIT_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") # Image tags (separate tags for builder and runtime variants) BUILDER_TAG="builder" RUNTIME_TAG="runtime" TAG_FULL="${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}" TAG_DATED="${BUILD_DATE//[:-]*/}" # YYYY-MM-DD format echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Building Base Images for Towerops" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Container: ${CONTAINER_CMD}" echo "GitLab: ${REGISTRY}/${IMAGE_NAME}" echo "Docker Hub: ${DOCKERHUB_REGISTRY}/${IMAGE_NAME}" echo "Elixir: ${ELIXIR_VERSION}" echo "Erlang: ${OTP_VERSION}" echo "Debian: ${DEBIAN_VERSION}" echo "Build Date: ${BUILD_DATE}" echo "Git SHA: ${GIT_SHA}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Build builder image (no cache to ensure latest packages) echo "" echo "🔨 Building builder image (no cache, pulling latest packages)..." $CONTAINER_CMD build \ --no-cache \ --pull \ --target builder \ --build-arg ELIXIR_VERSION="${ELIXIR_VERSION}" \ --build-arg OTP_VERSION="${OTP_VERSION}" \ --build-arg DEBIAN_VERSION="${DEBIAN_VERSION}" \ --label "build.date=${BUILD_DATE}" \ --label "build.git-sha=${GIT_SHA}" \ --label "elixir.version=${ELIXIR_VERSION}" \ --label "erlang.version=${OTP_VERSION}" \ --label "debian.version=${DEBIAN_VERSION}" \ -t "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" \ -t "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}-${TAG_DATED}" \ -f Dockerfile \ . # Build runtime image echo "" echo "🔨 Building runtime image..." $CONTAINER_CMD build \ --no-cache \ --pull \ --target runtime \ --build-arg DEBIAN_VERSION="${DEBIAN_VERSION}" \ --label "build.date=${BUILD_DATE}" \ --label "build.git-sha=${GIT_SHA}" \ --label "debian.version=${DEBIAN_VERSION}" \ -t "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" \ -t "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}-${TAG_DATED}" \ -t "${REGISTRY}/${IMAGE_NAME}:latest" \ -f Dockerfile \ . echo "" echo "✅ Build complete!" echo "" echo "Image tags created:" echo " - ${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" echo " - ${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}-${TAG_DATED}" echo " - ${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" echo " - ${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}-${TAG_DATED}" echo " - ${REGISTRY}/${IMAGE_NAME}:latest (runtime)" echo "" # Show image sizes echo "📦 Image sizes:" $CONTAINER_CMD images "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" $CONTAINER_CMD images "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" echo "" # Verify images work echo "🔍 Verifying builder image..." $CONTAINER_CMD run --rm "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" mix --version echo "" echo "🔍 Verifying runtime image..." $CONTAINER_CMD run --rm "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" locale | grep "en_US.UTF-8" echo "" # Ask if user wants to push read -p "Push to registries? (y/N) " -n 1 -r echo "" if [[ $REPLY =~ ^[Yy]$ ]]; then echo "🚀 Pushing to GitLab registry..." $CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" $CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}-${TAG_DATED}" $CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" $CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}-${TAG_DATED}" $CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:latest" echo "" echo "🚀 Tagging for Docker Hub..." $CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" $CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}-${TAG_DATED}" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}-${TAG_DATED}" $CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" $CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}-${TAG_DATED}" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}-${TAG_DATED}" $CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:latest" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:latest" echo "🚀 Pushing to Docker Hub..." $CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" $CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}-${TAG_DATED}" $CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" $CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}-${TAG_DATED}" $CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:latest" echo "" echo "✅ Pushed successfully to both registries!" echo "" echo "Update your main Dockerfile to use:" echo " Builder: ARG BUILDER_IMAGE=\"${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}\"" echo " Runtime: ARG RUNNER_IMAGE=\"${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}\"" else echo "⏭️ Skipping push. To push later:" echo " GitLab:" echo " $CONTAINER_CMD push ${REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" echo " $CONTAINER_CMD push ${REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" echo " $CONTAINER_CMD push ${REGISTRY}/${IMAGE_NAME}:latest" echo " Docker Hub:" echo " $CONTAINER_CMD push ${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${BUILDER_TAG}" echo " $CONTAINER_CMD push ${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${RUNTIME_TAG}" echo " $CONTAINER_CMD push ${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:latest" fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Done!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"