#!/bin/bash # Build and push multi-architecture Docker image to GitHub Container Registry # # Usage: # ./scripts/docker-publish.sh [version] [--parallel] # # Examples: # ./scripts/docker-publish.sh 0.1.6 # Sequential build (default) # ./scripts/docker-publish.sh 0.1.6 --parallel # Build both archs simultaneously # ./scripts/docker-publish.sh 0.1.6-beta # Pre-release (no latest tag) # # Prerequisites: # 1. Log in to ghcr.io first: # echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin # # 2. Create a GitHub Personal Access Token with 'write:packages' scope: # https://github.com/settings/tokens/new?scopes=write:packages # # Supported architectures: # - linux/amd64 (x86_64, most servers/desktops) # - linux/arm64 (Raspberry Pi 4/5, Apple Silicon via emulation) set -e # Configuration REGISTRY="ghcr.io" IMAGE_NAME="maziggy/bambuddy" FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}" PLATFORMS="linux/amd64,linux/arm64" BUILDER_NAME="bambuddy-builder" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Parse arguments VERSION="" PARALLEL=false for arg in "$@"; do case $arg in --parallel) PARALLEL=true ;; *) if [ -z "$VERSION" ]; then VERSION="$arg" fi ;; esac done if [ -z "$VERSION" ]; then echo -e "${YELLOW}Usage: $0 [--parallel]${NC}" echo "Example: $0 0.1.6" echo " $0 0.1.6 --parallel # Build both architectures simultaneously" exit 1 fi # Get CPU count CPU_COUNT=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) echo -e "${GREEN}================================================${NC}" echo -e "${GREEN} Building multi-arch image${NC}" echo -e "${GREEN} ${FULL_IMAGE}:${VERSION}${NC}" echo -e "${GREEN} Platforms: ${PLATFORMS}${NC}" echo -e "${GREEN} CPU cores: ${CPU_COUNT}${NC}" if [ "$PARALLEL" = true ]; then echo -e "${GREEN} Mode: PARALLEL (both archs simultaneously)${NC}" else echo -e "${GREEN} Mode: Sequential (amd64 → arm64)${NC}" fi echo -e "${GREEN}================================================${NC}" echo "" # Check if logged in to registry if ! docker info 2>/dev/null | grep -q "Username"; then echo -e "${YELLOW}Warning: You may not be logged in to Docker registry${NC}" echo "Run: echo \$GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin" echo "" fi # Determine if this is a release version (no pre-release suffix) IS_RELEASE=false if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then IS_RELEASE=true fi # Setup buildx builder if not exists echo -e "${BLUE}[1/4] Setting up Docker Buildx...${NC}" if ! docker buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then echo "Creating new buildx builder: $BUILDER_NAME" docker buildx create \ --name "$BUILDER_NAME" \ --driver docker-container \ --driver-opt network=host \ --buildkitd-flags '--allow-insecure-entitlement network.host' \ --bootstrap fi docker buildx use "$BUILDER_NAME" # Verify builder supports multi-platform echo -e "${BLUE}[2/4] Verifying multi-platform support...${NC}" if ! docker buildx inspect --bootstrap | grep -q "linux/arm64"; then echo -e "${YELLOW}Installing QEMU for cross-platform builds...${NC}" docker run --privileged --rm tonistiigi/binfmt --install all fi # Build tags TAGS="-t ${FULL_IMAGE}:${VERSION}" if [ "$IS_RELEASE" = true ]; then TAGS="$TAGS -t ${FULL_IMAGE}:latest" echo -e "${BLUE}[3/4] Building and pushing (version + latest)...${NC}" else echo -e "${BLUE}[3/4] Building and pushing (version only, no latest)...${NC}" fi if [ "$PARALLEL" = true ]; then # Parallel build: Build each architecture separately then combine echo -e "${YELLOW}Building amd64 and arm64 in parallel...${NC}" # Build amd64 in background ( echo -e "${BLUE}[amd64] Starting build...${NC}" docker buildx build \ --platform linux/amd64 \ -t "${FULL_IMAGE}:${VERSION}-amd64" \ --provenance=false \ --push \ . 2>&1 | sed 's/^/[amd64] /' echo -e "${GREEN}[amd64] Complete!${NC}" ) & PID_AMD64=$! # Build arm64 in background ( echo -e "${BLUE}[arm64] Starting build...${NC}" docker buildx build \ --platform linux/arm64 \ -t "${FULL_IMAGE}:${VERSION}-arm64" \ --provenance=false \ --push \ . 2>&1 | sed 's/^/[arm64] /' echo -e "${GREEN}[arm64] Complete!${NC}" ) & PID_ARM64=$! # Wait for both builds echo "Waiting for parallel builds to complete..." wait $PID_AMD64 wait $PID_ARM64 # Create and push multi-arch manifest echo -e "${BLUE}Creating multi-arch manifest...${NC}" docker buildx imagetools create \ -t "${FULL_IMAGE}:${VERSION}" \ "${FULL_IMAGE}:${VERSION}-amd64" \ "${FULL_IMAGE}:${VERSION}-arm64" if [ "$IS_RELEASE" = true ]; then docker buildx imagetools create \ -t "${FULL_IMAGE}:latest" \ "${FULL_IMAGE}:${VERSION}-amd64" \ "${FULL_IMAGE}:${VERSION}-arm64" fi else # Sequential build (default): Build both platforms in one command DOCKER_BUILDKIT=1 docker buildx build \ --platform "$PLATFORMS" \ --build-arg BUILDKIT_INLINE_CACHE=1 \ --provenance=false \ $TAGS \ --push \ . fi echo -e "${BLUE}[4/4] Verifying manifest...${NC}" docker buildx imagetools inspect "${FULL_IMAGE}:${VERSION}" echo "" echo -e "${GREEN}================================================${NC}" echo -e "${GREEN}✓ Successfully pushed multi-arch image:${NC}" echo -e "${GREEN}================================================${NC}" echo " - ${FULL_IMAGE}:${VERSION}" if [ "$IS_RELEASE" = true ]; then echo " - ${FULL_IMAGE}:latest" fi echo "" echo -e "${BLUE}Supported platforms:${NC}" echo " - linux/amd64 (Intel/AMD servers, desktops)" echo " - linux/arm64 (Raspberry Pi 4/5, Apple Silicon)" echo "" echo -e "${GREEN}Users can now run:${NC}" echo " docker pull ${FULL_IMAGE}:${VERSION}"