docker-publish.sh 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #!/bin/bash
  2. # Build and push multi-architecture Docker image to GitHub Container Registry
  3. #
  4. # Usage:
  5. # ./scripts/docker-publish.sh [version] [--parallel]
  6. #
  7. # Examples:
  8. # ./scripts/docker-publish.sh 0.1.6 # Sequential build (default)
  9. # ./scripts/docker-publish.sh 0.1.6 --parallel # Build both archs simultaneously
  10. # ./scripts/docker-publish.sh 0.1.6-beta # Pre-release (no latest tag)
  11. #
  12. # Prerequisites:
  13. # 1. Log in to ghcr.io first:
  14. # echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin
  15. #
  16. # 2. Create a GitHub Personal Access Token with 'write:packages' scope:
  17. # https://github.com/settings/tokens/new?scopes=write:packages
  18. #
  19. # Supported architectures:
  20. # - linux/amd64 (x86_64, most servers/desktops)
  21. # - linux/arm64 (Raspberry Pi 4/5, Apple Silicon via emulation)
  22. set -e
  23. # Configuration
  24. REGISTRY="ghcr.io"
  25. IMAGE_NAME="maziggy/bambuddy"
  26. FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}"
  27. PLATFORMS="linux/amd64,linux/arm64"
  28. BUILDER_NAME="bambuddy-builder"
  29. # Colors for output
  30. RED='\033[0;31m'
  31. GREEN='\033[0;32m'
  32. YELLOW='\033[1;33m'
  33. BLUE='\033[0;34m'
  34. NC='\033[0m' # No Color
  35. # Parse arguments
  36. VERSION=""
  37. PARALLEL=false
  38. for arg in "$@"; do
  39. case $arg in
  40. --parallel)
  41. PARALLEL=true
  42. ;;
  43. *)
  44. if [ -z "$VERSION" ]; then
  45. VERSION="$arg"
  46. fi
  47. ;;
  48. esac
  49. done
  50. if [ -z "$VERSION" ]; then
  51. echo -e "${YELLOW}Usage: $0 <version> [--parallel]${NC}"
  52. echo "Example: $0 0.1.6"
  53. echo " $0 0.1.6 --parallel # Build both architectures simultaneously"
  54. exit 1
  55. fi
  56. # Get CPU count
  57. CPU_COUNT=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
  58. echo -e "${GREEN}================================================${NC}"
  59. echo -e "${GREEN} Building multi-arch image${NC}"
  60. echo -e "${GREEN} ${FULL_IMAGE}:${VERSION}${NC}"
  61. echo -e "${GREEN} Platforms: ${PLATFORMS}${NC}"
  62. echo -e "${GREEN} CPU cores: ${CPU_COUNT}${NC}"
  63. if [ "$PARALLEL" = true ]; then
  64. echo -e "${GREEN} Mode: PARALLEL (both archs simultaneously)${NC}"
  65. else
  66. echo -e "${GREEN} Mode: Sequential (amd64 → arm64)${NC}"
  67. fi
  68. echo -e "${GREEN}================================================${NC}"
  69. echo ""
  70. # Check if logged in to registry
  71. if ! docker info 2>/dev/null | grep -q "Username"; then
  72. echo -e "${YELLOW}Warning: You may not be logged in to Docker registry${NC}"
  73. echo "Run: echo \$GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin"
  74. echo ""
  75. fi
  76. # Determine if this is a release version (includes betas for now)
  77. IS_RELEASE=false
  78. if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(b[0-9]+)?$ ]]; then
  79. IS_RELEASE=true
  80. fi
  81. # Setup buildx builder if not exists
  82. echo -e "${BLUE}[1/4] Setting up Docker Buildx...${NC}"
  83. if ! docker buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then
  84. echo "Creating new buildx builder: $BUILDER_NAME (optimized for ${CPU_COUNT} cores)"
  85. docker buildx create \
  86. --name "$BUILDER_NAME" \
  87. --driver docker-container \
  88. --driver-opt network=host \
  89. --driver-opt "env.BUILDKIT_STEP_LOG_MAX_SIZE=10000000" \
  90. --buildkitd-flags "--allow-insecure-entitlement network.host --oci-worker-gc=false" \
  91. --config /dev/stdin <<EOF
  92. [worker.oci]
  93. max-parallelism = ${CPU_COUNT}
  94. EOF
  95. docker buildx inspect --bootstrap "$BUILDER_NAME"
  96. fi
  97. docker buildx use "$BUILDER_NAME"
  98. # Verify builder supports multi-platform
  99. echo -e "${BLUE}[2/4] Verifying multi-platform support...${NC}"
  100. if ! docker buildx inspect --bootstrap | grep -q "linux/arm64"; then
  101. echo -e "${YELLOW}Installing QEMU for cross-platform builds...${NC}"
  102. docker run --privileged --rm tonistiigi/binfmt --install all
  103. fi
  104. # Build tags
  105. TAGS="-t ${FULL_IMAGE}:${VERSION}"
  106. if [ "$IS_RELEASE" = true ]; then
  107. TAGS="$TAGS -t ${FULL_IMAGE}:latest"
  108. echo -e "${BLUE}[3/4] Building and pushing (version + latest)...${NC}"
  109. else
  110. echo -e "${BLUE}[3/4] Building and pushing (version only, no latest)...${NC}"
  111. fi
  112. # Common build args for speed
  113. BUILD_ARGS="--provenance=false --sbom=false"
  114. CACHE_ARGS="--cache-from type=registry,ref=${FULL_IMAGE}:buildcache --cache-to type=registry,ref=${FULL_IMAGE}:buildcache,mode=max"
  115. if [ "$PARALLEL" = true ]; then
  116. # Parallel build: Build each architecture separately then combine
  117. echo -e "${YELLOW}Building amd64 and arm64 in parallel (${CPU_COUNT} cores each)...${NC}"
  118. # Build amd64 in background
  119. (
  120. echo -e "${BLUE}[amd64] Starting build...${NC}"
  121. docker buildx build \
  122. --platform linux/amd64 \
  123. -t "${FULL_IMAGE}:${VERSION}-amd64" \
  124. ${BUILD_ARGS} \
  125. --cache-from type=registry,ref=${FULL_IMAGE}:cache-amd64 \
  126. --cache-to type=registry,ref=${FULL_IMAGE}:cache-amd64,mode=max \
  127. --push \
  128. . 2>&1 | sed 's/^/[amd64] /'
  129. echo -e "${GREEN}[amd64] Complete!${NC}"
  130. ) &
  131. PID_AMD64=$!
  132. # Build arm64 in background
  133. (
  134. echo -e "${BLUE}[arm64] Starting build...${NC}"
  135. docker buildx build \
  136. --platform linux/arm64 \
  137. -t "${FULL_IMAGE}:${VERSION}-arm64" \
  138. ${BUILD_ARGS} \
  139. --cache-from type=registry,ref=${FULL_IMAGE}:cache-arm64 \
  140. --cache-to type=registry,ref=${FULL_IMAGE}:cache-arm64,mode=max \
  141. --push \
  142. . 2>&1 | sed 's/^/[arm64] /'
  143. echo -e "${GREEN}[arm64] Complete!${NC}"
  144. ) &
  145. PID_ARM64=$!
  146. # Wait for both builds
  147. echo "Waiting for parallel builds to complete..."
  148. wait $PID_AMD64
  149. wait $PID_ARM64
  150. # Create and push multi-arch manifest
  151. echo -e "${BLUE}Creating multi-arch manifest...${NC}"
  152. docker buildx imagetools create \
  153. -t "${FULL_IMAGE}:${VERSION}" \
  154. "${FULL_IMAGE}:${VERSION}-amd64" \
  155. "${FULL_IMAGE}:${VERSION}-arm64"
  156. if [ "$IS_RELEASE" = true ]; then
  157. docker buildx imagetools create \
  158. -t "${FULL_IMAGE}:latest" \
  159. "${FULL_IMAGE}:${VERSION}-amd64" \
  160. "${FULL_IMAGE}:${VERSION}-arm64"
  161. fi
  162. else
  163. # Sequential build (default): Build both platforms in one command
  164. echo -e "${YELLOW}Building sequentially with ${CPU_COUNT} cores...${NC}"
  165. DOCKER_BUILDKIT=1 docker buildx build \
  166. --platform "$PLATFORMS" \
  167. ${BUILD_ARGS} \
  168. ${CACHE_ARGS} \
  169. $TAGS \
  170. --push \
  171. .
  172. fi
  173. echo -e "${BLUE}[4/4] Verifying manifest...${NC}"
  174. docker buildx imagetools inspect "${FULL_IMAGE}:${VERSION}"
  175. echo ""
  176. echo -e "${GREEN}================================================${NC}"
  177. echo -e "${GREEN}✓ Successfully pushed multi-arch image:${NC}"
  178. echo -e "${GREEN}================================================${NC}"
  179. echo " - ${FULL_IMAGE}:${VERSION}"
  180. if [ "$IS_RELEASE" = true ]; then
  181. echo " - ${FULL_IMAGE}:latest"
  182. fi
  183. echo ""
  184. echo -e "${BLUE}Supported platforms:${NC}"
  185. echo " - linux/amd64 (Intel/AMD servers, desktops)"
  186. echo " - linux/arm64 (Raspberry Pi 4/5, Apple Silicon)"
  187. echo ""
  188. echo -e "${GREEN}Users can now run:${NC}"
  189. echo " docker pull ${FULL_IMAGE}:${VERSION}"