#!/bin/bash # Docker Buildx Helper Script # Provides multi-platform Docker image building and pushing capabilities 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 BUILDER_NAME="rxminder-builder" PLATFORMS="linux/amd64,linux/arm64" DOCKERFILE_PATH="docker/Dockerfile" DOCKER_CONTEXT="." IMAGE_NAME="${DOCKER_IMAGE_NAME:-rxminder}" REGISTRY="${DOCKER_REGISTRY:-}" TAG="${DOCKER_TAG:-latest}" # Function to print colored output print_status() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # Function to show usage show_usage() { echo "Docker Buildx Helper Script" echo "" echo "Usage: $0 [options]" echo "" echo "Commands:" echo " setup Setup buildx builder instance" echo " build-local Build image for local platform only" echo " build-multi Build multi-platform images (production)" echo " build-multi-dev Build multi-platform images (development)" echo " build-push Build and push multi-platform images" echo " push Push existing images to registry" echo " inspect Inspect builder instance" echo " bake Build using docker-bake.hcl" echo " cleanup Remove builder instance and cleanup" echo " list List available builders" echo "" echo "Environment Variables:" echo " DOCKER_IMAGE_NAME Image name (default: rxminder)" echo " DOCKER_REGISTRY Registry URL (e.g., ghcr.io/username)" echo " DOCKER_TAG Image tag (default: latest)" echo " APP_NAME Application name for build args" echo " NODE_ENV Build environment (development/production)" echo "" echo "Examples:" echo " $0 setup" echo " $0 build-local" echo " DOCKER_REGISTRY=ghcr.io/myuser $0 build-push" echo " DOCKER_TAG=v1.0.0 $0 build-multi" } # Function to setup buildx builder setup_builder() { print_status "Setting up Docker Buildx builder..." # Check if buildx is available if ! docker buildx version >/dev/null 2>&1; then print_error "Docker Buildx is not available. Please update Docker." exit 1 fi # Remove existing builder if it exists if docker buildx ls | grep -q "$BUILDER_NAME"; then print_status "Removing existing builder instance..." docker buildx rm "$BUILDER_NAME" 2>/dev/null || true fi # Create new builder instance print_status "Creating new buildx builder instance: $BUILDER_NAME" docker buildx create \ --name "$BUILDER_NAME" \ --driver docker-container \ --platform "$PLATFORMS" \ --bootstrap # Use the builder docker buildx use "$BUILDER_NAME" # Inspect the builder docker buildx inspect --bootstrap print_success "Buildx builder setup completed!" } # Function to get build arguments get_build_args() { local build_env="${1:-${NODE_ENV:-production}}" echo "--build-arg APP_NAME=${APP_NAME:-RxMinder}" echo "--build-arg NODE_ENV=${build_env}" echo "--build-arg JWT_SECRET=${JWT_SECRET:-demo_jwt_secret_for_frontend_only}" echo "--build-arg SESSION_SECRET=${SESSION_SECRET:-demo_session_secret_for_frontend_only}" echo "--build-arg VITE_COUCHDB_URL=${VITE_COUCHDB_URL:-http://couchdb:5984}" echo "--build-arg VITE_COUCHDB_USER=${VITE_COUCHDB_USER:-admin}" echo "--build-arg VITE_COUCHDB_PASSWORD=${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" echo "--build-arg APP_BASE_URL=${APP_BASE_URL:-http://localhost:8080}" echo "--build-arg VITE_GOOGLE_CLIENT_ID=${VITE_GOOGLE_CLIENT_ID:-}" echo "--build-arg VITE_GITHUB_CLIENT_ID=${VITE_GITHUB_CLIENT_ID:-}" echo "--build-arg MAILGUN_API_KEY=${MAILGUN_API_KEY:-}" echo "--build-arg MAILGUN_DOMAIN=${MAILGUN_DOMAIN:-}" echo "--build-arg MAILGUN_FROM_EMAIL=${MAILGUN_FROM_EMAIL:-}" } # Function to get image tags get_image_tags() { local base_name="$1" local tags="" # Always include the specified tag tags="$tags -t $base_name:$TAG" # Add latest tag if not already latest if [ "$TAG" != "latest" ]; then tags="$tags -t $base_name:latest" fi # Add git-based tags if in git repo if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then local git_hash=$(git rev-parse --short HEAD) local git_branch=$(git rev-parse --abbrev-ref HEAD | sed 's/[^a-zA-Z0-9.-]/-/g') tags="$tags -t $base_name:$git_hash" if [ "$git_branch" != "HEAD" ] && [ "$git_branch" != "main" ] && [ "$git_branch" != "master" ]; then tags="$tags -t $base_name:$git_branch" fi fi echo "$tags" } # Function to build for local platform only build_local() { print_status "Building Docker image for local platform..." # Ensure builder is available if ! docker buildx ls | grep -q "$BUILDER_NAME"; then print_warning "Builder not found, setting up..." setup_builder fi # Use the builder docker buildx use "$BUILDER_NAME" # Get the current platform local platform=$(docker version --format '{{.Server.Os}}/{{.Server.Arch}}') print_status "Building for platform: $platform" # Determine image name local image_name="$IMAGE_NAME" if [ -n "$REGISTRY" ]; then image_name="$REGISTRY/$IMAGE_NAME" fi # Get build arguments and tags (use development for local builds) local build_args=$(get_build_args "development") local tags=$(get_image_tags "$image_name") print_status "Building image: $image_name:$TAG (development mode)" # Build the image docker buildx build \ --platform "$platform" \ $build_args \ $tags \ --load \ -f "$DOCKERFILE_PATH" \ "$DOCKER_CONTEXT" print_success "Local build completed successfully!" # Show image info docker images "$image_name" | head -2 } # Function to build multi-platform images build_multi() { print_status "Building multi-platform Docker images..." # Ensure builder is available if ! docker buildx ls | grep -q "$BUILDER_NAME"; then print_warning "Builder not found, setting up..." setup_builder fi # Use the builder docker buildx use "$BUILDER_NAME" # Determine image name local image_name="$IMAGE_NAME" if [ -n "$REGISTRY" ]; then image_name="$REGISTRY/$IMAGE_NAME" fi # Get build arguments and tags (use production for multi-platform) local build_args=$(get_build_args "production") local tags=$(get_image_tags "$image_name") print_status "Building for platforms: $PLATFORMS" print_status "Image: $image_name:$TAG (production mode)" # Build the images docker buildx build \ --platform "$PLATFORMS" \ $build_args \ $tags \ -f "$DOCKERFILE_PATH" \ "$DOCKER_CONTEXT" print_success "Multi-platform build completed successfully!" } # Function to build multi-platform images for development build_multi_dev() { print_status "Building multi-platform Docker images for development..." # Ensure builder is available if ! docker buildx ls | grep -q "$BUILDER_NAME"; then print_warning "Builder not found, setting up..." setup_builder fi # Use the builder docker buildx use "$BUILDER_NAME" # Determine image name local image_name="$IMAGE_NAME" if [ -n "$REGISTRY" ]; then image_name="$REGISTRY/$IMAGE_NAME" fi # Get build arguments and tags (use development for multi-platform dev) local build_args=$(get_build_args "development") local tags=$(get_image_tags "$image_name") print_status "Building for platforms: $PLATFORMS" print_status "Image: $image_name:$TAG (development mode)" # Build the images docker buildx build \ --platform "$PLATFORMS" \ $build_args \ $tags \ -f "$DOCKERFILE_PATH" \ "$DOCKER_CONTEXT" print_success "Multi-platform development build completed successfully!" } # Function to build and push multi-platform images build_push() { print_status "Building and pushing multi-platform Docker images..." if [ -z "$REGISTRY" ]; then print_error "DOCKER_REGISTRY environment variable must be set for pushing" print_status "Example: DOCKER_REGISTRY=ghcr.io/username $0 build-push" exit 1 fi # Ensure builder is available if ! docker buildx ls | grep -q "$BUILDER_NAME"; then print_warning "Builder not found, setting up..." setup_builder fi # Use the builder docker buildx use "$BUILDER_NAME" # Determine image name local image_name="$REGISTRY/$IMAGE_NAME" # Get build arguments and tags (use production for build-push) local build_args=$(get_build_args "production") local tags=$(get_image_tags "$image_name") print_status "Building and pushing for platforms: $PLATFORMS" print_status "Registry: $REGISTRY" print_status "Image: $image_name:$TAG (production mode)" # Build and push the images docker buildx build \ --platform "$PLATFORMS" \ $build_args \ $tags \ --push \ -f "$DOCKERFILE_PATH" \ "$DOCKER_CONTEXT" print_success "Multi-platform build and push completed successfully!" # Show pushed images echo "" print_status "Pushed images:" echo "$image_name:$TAG" if [ "$TAG" != "latest" ]; then echo "$image_name:latest" fi if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then local git_hash=$(git rev-parse --short HEAD) echo "$image_name:$git_hash" fi } # Function to push existing images push_images() { print_status "Pushing existing images to registry..." if [ -z "$REGISTRY" ]; then print_error "DOCKER_REGISTRY environment variable must be set for pushing" exit 1 fi local image_name="$REGISTRY/$IMAGE_NAME" print_status "Pushing $image_name:$TAG" docker push "$image_name:$TAG" if [ "$TAG" != "latest" ]; then print_status "Pushing $image_name:latest" docker push "$image_name:latest" fi print_success "Push completed successfully!" } # Function to inspect builder inspect_builder() { print_status "Inspecting buildx builder..." if docker buildx ls | grep -q "$BUILDER_NAME"; then docker buildx inspect "$BUILDER_NAME" else print_warning "Builder '$BUILDER_NAME' not found" print_status "Available builders:" docker buildx ls fi } # Function to build using docker-bake.hcl build_bake() { print_status "Building using docker-bake.hcl..." if [ ! -f "docker-bake.hcl" ]; then print_error "docker-bake.hcl not found" exit 1 fi # Ensure builder is available if ! docker buildx ls | grep -q "$BUILDER_NAME"; then print_warning "Builder not found, setting up..." setup_builder fi # Use the builder docker buildx use "$BUILDER_NAME" # Build using bake docker buildx bake -f docker-bake.hcl print_success "Bake build completed successfully!" } # Function to cleanup builder cleanup_builder() { print_status "Cleaning up buildx builder..." if docker buildx ls | grep -q "$BUILDER_NAME"; then docker buildx rm "$BUILDER_NAME" print_success "Builder '$BUILDER_NAME' removed" else print_warning "Builder '$BUILDER_NAME' not found" fi # Cleanup unused build cache print_status "Cleaning up build cache..." docker buildx prune -f print_success "Cleanup completed!" } # Function to list builders list_builders() { print_status "Available buildx builders:" docker buildx ls } # Main script logic case "${1:-}" in "setup") setup_builder ;; "build-local") build_local ;; "build-multi") build_multi ;; "build-multi-dev") build_multi_dev ;; "build-push") build_push ;; "push") push_images ;; "inspect") inspect_builder ;; "bake") build_bake ;; "cleanup") cleanup_builder ;; "list") list_builders ;; "help"|"--help"|"-h") show_usage ;; "") print_error "No command specified" show_usage exit 1 ;; *) print_error "Unknown command: $1" show_usage exit 1 ;; esac