docker-publish.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. #!/bin/bash
  2. # Build and push multi-architecture Docker image to GitHub Container Registry AND Docker Hub
  3. #
  4. # Usage:
  5. # ./docker-publish.sh [version] [--parallel] [--ghcr-only] [--dockerhub-only]
  6. #
  7. # Examples:
  8. # ./docker-publish.sh 0.1.9b # Sequential build, push to both registries
  9. # ./docker-publish.sh 0.1.9b --parallel # Build both archs simultaneously
  10. # ./docker-publish.sh 0.1.9b --ghcr-only # Only push to GHCR
  11. # ./docker-publish.sh 0.1.9b --dockerhub-only # Only push to Docker Hub
  12. #
  13. # Note: Stable versions are also tagged as 'latest'. Beta versions (ending in 'b') are not.
  14. #
  15. # Prerequisites:
  16. # 1. Log in to ghcr.io:
  17. # echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin
  18. #
  19. # 2. Log in to Docker Hub:
  20. # docker login -u YOUR_USERNAME
  21. #
  22. # 3. Create a GitHub Personal Access Token with 'write:packages' scope:
  23. # https://github.com/settings/tokens/new?scopes=write:packages
  24. #
  25. # Supported architectures:
  26. # - linux/amd64 (x86_64, most servers/desktops)
  27. # - linux/arm64 (Raspberry Pi 4/5, Apple Silicon via emulation)
  28. set -e
  29. # Configuration
  30. GHCR_REGISTRY="ghcr.io"
  31. DOCKERHUB_REGISTRY="docker.io"
  32. IMAGE_NAME="maziggy/bambuddy"
  33. GHCR_IMAGE="${GHCR_REGISTRY}/${IMAGE_NAME}"
  34. DOCKERHUB_IMAGE="${DOCKERHUB_REGISTRY}/${IMAGE_NAME}"
  35. PLATFORMS="linux/amd64,linux/arm64"
  36. BUILDER_NAME="bambuddy-builder"
  37. # Colors for output
  38. RED='\033[0;31m'
  39. GREEN='\033[0;32m'
  40. YELLOW='\033[1;33m'
  41. BLUE='\033[0;34m'
  42. NC='\033[0m' # No Color
  43. # Parse arguments
  44. VERSION=""
  45. PARALLEL=false
  46. PUSH_GHCR=true
  47. PUSH_DOCKERHUB=true
  48. SKIP_SIDECARS=false
  49. for arg in "$@"; do
  50. case $arg in
  51. --parallel)
  52. PARALLEL=true
  53. ;;
  54. --ghcr-only)
  55. PUSH_DOCKERHUB=false
  56. ;;
  57. --dockerhub-only)
  58. PUSH_GHCR=false
  59. ;;
  60. --skip-sidecars)
  61. SKIP_SIDECARS=true
  62. ;;
  63. *)
  64. if [ -z "$VERSION" ]; then
  65. VERSION="$arg"
  66. fi
  67. ;;
  68. esac
  69. done
  70. if [ -z "$VERSION" ]; then
  71. echo -e "${YELLOW}Usage: $0 <version> [--parallel] [--ghcr-only] [--dockerhub-only]${NC}"
  72. echo "Example: $0 0.1.9b"
  73. echo " $0 0.1.9b --parallel # Build both architectures simultaneously"
  74. echo " $0 0.1.9b --ghcr-only # Only push to GitHub Container Registry"
  75. echo " $0 0.1.9b --dockerhub-only # Only push to Docker Hub"
  76. exit 1
  77. fi
  78. # Get CPU count
  79. CPU_COUNT=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
  80. echo -e "${GREEN}================================================${NC}"
  81. echo -e "${GREEN} Building multi-arch image${NC}"
  82. echo -e "${GREEN} Version: ${VERSION}${NC}"
  83. echo -e "${GREEN} Platforms: ${PLATFORMS}${NC}"
  84. echo -e "${GREEN} CPU cores: ${CPU_COUNT}${NC}"
  85. if [ "$PARALLEL" = true ]; then
  86. echo -e "${GREEN} Mode: PARALLEL (both archs simultaneously)${NC}"
  87. else
  88. echo -e "${GREEN} Mode: Sequential (amd64 → arm64)${NC}"
  89. fi
  90. echo -e "${GREEN} Registries:${NC}"
  91. if [ "$PUSH_GHCR" = true ]; then
  92. echo -e "${GREEN} - ${GHCR_IMAGE}${NC}"
  93. fi
  94. if [ "$PUSH_DOCKERHUB" = true ]; then
  95. echo -e "${GREEN} - ${DOCKERHUB_IMAGE}${NC}"
  96. fi
  97. echo -e "${GREEN}================================================${NC}"
  98. echo ""
  99. # Check registry logins
  100. if [ "$PUSH_GHCR" = true ]; then
  101. if ! grep -q "ghcr.io" ~/.docker/config.json 2>/dev/null; then
  102. echo -e "${YELLOW}Warning: You may not be logged in to ghcr.io${NC}"
  103. echo "Run: echo \$GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin"
  104. echo ""
  105. fi
  106. fi
  107. if [ "$PUSH_DOCKERHUB" = true ]; then
  108. if ! grep -q "index.docker.io\|docker.io" ~/.docker/config.json 2>/dev/null; then
  109. echo -e "${RED}Error: You are not logged in to Docker Hub${NC}"
  110. echo "Run: docker login -u YOUR_USERNAME"
  111. echo ""
  112. exit 1
  113. fi
  114. fi
  115. # Setup buildx builder if not exists
  116. echo -e "${BLUE}[1/4] Setting up Docker Buildx...${NC}"
  117. if ! docker buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then
  118. echo "Creating new buildx builder: $BUILDER_NAME (optimized for ${CPU_COUNT} cores)"
  119. docker buildx create \
  120. --name "$BUILDER_NAME" \
  121. --driver docker-container \
  122. --driver-opt network=host \
  123. --driver-opt "env.BUILDKIT_STEP_LOG_MAX_SIZE=10000000" \
  124. --buildkitd-flags "--allow-insecure-entitlement network.host --oci-worker-gc=false" \
  125. --config /dev/stdin <<EOF
  126. [worker.oci]
  127. max-parallelism = ${CPU_COUNT}
  128. EOF
  129. docker buildx inspect --bootstrap "$BUILDER_NAME"
  130. fi
  131. docker buildx use "$BUILDER_NAME"
  132. # Verify builder supports multi-platform
  133. echo -e "${BLUE}[2/4] Verifying multi-platform support...${NC}"
  134. if ! docker buildx inspect --bootstrap | grep -q "linux/arm64"; then
  135. echo -e "${YELLOW}Installing QEMU for cross-platform builds...${NC}"
  136. docker run --privileged --rm tonistiigi/binfmt --install all
  137. fi
  138. # Only tag as 'latest' for stable releases (not beta versions ending in 'b')
  139. TAG_LATEST=true
  140. if [[ "$VERSION" =~ b[0-9]*$ ]]; then
  141. TAG_LATEST=false
  142. echo -e "${YELLOW}Beta version detected — skipping 'latest' tag${NC}"
  143. fi
  144. # Build tags for all target registries
  145. TAGS=""
  146. if [ "$PUSH_GHCR" = true ]; then
  147. TAGS="$TAGS -t ${GHCR_IMAGE}:${VERSION}"
  148. [ "$TAG_LATEST" = true ] && TAGS="$TAGS -t ${GHCR_IMAGE}:latest"
  149. fi
  150. if [ "$PUSH_DOCKERHUB" = true ]; then
  151. TAGS="$TAGS -t ${DOCKERHUB_IMAGE}:${VERSION}"
  152. [ "$TAG_LATEST" = true ] && TAGS="$TAGS -t ${DOCKERHUB_IMAGE}:latest"
  153. fi
  154. echo -e "${BLUE}[3/4] Building and pushing...${NC}"
  155. # Common build args (no cache to ensure clean builds)
  156. BUILD_ARGS="--provenance=false --sbom=false --no-cache --pull"
  157. if [ "$PARALLEL" = true ]; then
  158. # Parallel build: Build each architecture separately then combine manifests
  159. echo -e "${YELLOW}Building amd64 and arm64 in parallel (${CPU_COUNT} cores each, no cache)...${NC}"
  160. # Build per-arch staging tags for each target registry
  161. ARCH_TAGS_AMD64=""
  162. ARCH_TAGS_ARM64=""
  163. if [ "$PUSH_GHCR" = true ]; then
  164. ARCH_TAGS_AMD64="$ARCH_TAGS_AMD64 -t ${GHCR_IMAGE}:${VERSION}-amd64"
  165. ARCH_TAGS_ARM64="$ARCH_TAGS_ARM64 -t ${GHCR_IMAGE}:${VERSION}-arm64"
  166. fi
  167. if [ "$PUSH_DOCKERHUB" = true ]; then
  168. ARCH_TAGS_AMD64="$ARCH_TAGS_AMD64 -t ${DOCKERHUB_IMAGE}:${VERSION}-amd64"
  169. ARCH_TAGS_ARM64="$ARCH_TAGS_ARM64 -t ${DOCKERHUB_IMAGE}:${VERSION}-arm64"
  170. fi
  171. # Build amd64 in background
  172. (
  173. echo -e "${BLUE}[amd64] Starting build...${NC}"
  174. docker buildx build \
  175. --platform linux/amd64 \
  176. ${ARCH_TAGS_AMD64} \
  177. ${BUILD_ARGS} \
  178. --push \
  179. . 2>&1 | sed 's/^/[amd64] /'
  180. echo -e "${GREEN}[amd64] Complete!${NC}"
  181. ) &
  182. PID_AMD64=$!
  183. # Build arm64 in background
  184. (
  185. echo -e "${BLUE}[arm64] Starting build...${NC}"
  186. docker buildx build \
  187. --platform linux/arm64 \
  188. ${ARCH_TAGS_ARM64} \
  189. ${BUILD_ARGS} \
  190. --push \
  191. . 2>&1 | sed 's/^/[arm64] /'
  192. echo -e "${GREEN}[arm64] Complete!${NC}"
  193. ) &
  194. PID_ARM64=$!
  195. # Wait for both builds
  196. echo "Waiting for parallel builds to complete..."
  197. wait $PID_AMD64
  198. wait $PID_ARM64
  199. # Create multi-arch manifests per registry (no cross-registry blob copies)
  200. echo -e "${BLUE}Creating multi-arch manifests...${NC}"
  201. if [ "$PUSH_GHCR" = true ]; then
  202. echo -e "${BLUE} Creating GHCR manifest...${NC}"
  203. GHCR_MANIFEST_TAGS="-t ${GHCR_IMAGE}:${VERSION}"
  204. [ "$TAG_LATEST" = true ] && GHCR_MANIFEST_TAGS="$GHCR_MANIFEST_TAGS -t ${GHCR_IMAGE}:latest"
  205. docker buildx imagetools create \
  206. $GHCR_MANIFEST_TAGS \
  207. "${GHCR_IMAGE}:${VERSION}-amd64" \
  208. "${GHCR_IMAGE}:${VERSION}-arm64"
  209. fi
  210. if [ "$PUSH_DOCKERHUB" = true ]; then
  211. echo -e "${BLUE} Creating Docker Hub manifest...${NC}"
  212. DH_MANIFEST_TAGS="-t ${DOCKERHUB_IMAGE}:${VERSION}"
  213. [ "$TAG_LATEST" = true ] && DH_MANIFEST_TAGS="$DH_MANIFEST_TAGS -t ${DOCKERHUB_IMAGE}:latest"
  214. docker buildx imagetools create \
  215. $DH_MANIFEST_TAGS \
  216. "${DOCKERHUB_IMAGE}:${VERSION}-amd64" \
  217. "${DOCKERHUB_IMAGE}:${VERSION}-arm64"
  218. fi
  219. else
  220. # Sequential build (default): Build both platforms in one command
  221. echo -e "${YELLOW}Building sequentially with ${CPU_COUNT} cores (no cache)...${NC}"
  222. DOCKER_BUILDKIT=1 docker buildx build \
  223. --platform "$PLATFORMS" \
  224. ${BUILD_ARGS} \
  225. $TAGS \
  226. --push \
  227. .
  228. fi
  229. echo -e "${BLUE}[4/4] Verifying manifests...${NC}"
  230. if [ "$PUSH_GHCR" = true ]; then
  231. echo -e "${BLUE}GHCR:${NC}"
  232. docker buildx imagetools inspect "${GHCR_IMAGE}:${VERSION}"
  233. fi
  234. if [ "$PUSH_DOCKERHUB" = true ]; then
  235. echo -e "${BLUE}Docker Hub:${NC}"
  236. docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:${VERSION}"
  237. fi
  238. echo ""
  239. echo -e "${GREEN}================================================${NC}"
  240. echo -e "${GREEN} Successfully pushed multi-arch image:${NC}"
  241. echo -e "${GREEN}================================================${NC}"
  242. if [ "$PUSH_GHCR" = true ]; then
  243. echo " GHCR:"
  244. echo " - ${GHCR_IMAGE}:${VERSION}"
  245. [ "$TAG_LATEST" = true ] && echo " - ${GHCR_IMAGE}:latest"
  246. fi
  247. if [ "$PUSH_DOCKERHUB" = true ]; then
  248. echo " Docker Hub:"
  249. echo " - ${DOCKERHUB_IMAGE}:${VERSION}"
  250. [ "$TAG_LATEST" = true ] && echo " - ${DOCKERHUB_IMAGE}:latest"
  251. fi
  252. echo ""
  253. echo -e "${BLUE}Supported platforms:${NC}"
  254. echo " - linux/amd64 (Intel/AMD servers, desktops)"
  255. echo " - linux/arm64 (Raspberry Pi 4/5, Apple Silicon)"
  256. echo ""
  257. echo -e "${GREEN}Users can now run:${NC}"
  258. if [ "$PUSH_GHCR" = true ]; then
  259. echo " docker pull ${GHCR_IMAGE}:${VERSION}"
  260. fi
  261. if [ "$PUSH_DOCKERHUB" = true ]; then
  262. echo " docker pull ${DOCKERHUB_IMAGE}:${VERSION}"
  263. echo " docker pull ${IMAGE_NAME}:${VERSION} # shorthand"
  264. fi
  265. # ============================================================
  266. # Sidecar images (orca-slicer-api + bambu-studio-api)
  267. # ============================================================
  268. SIDECAR_SCRIPT="/opt/claude/projects/orca-slicer-api/docker-publish-sidecars.sh"
  269. if [ "$SKIP_SIDECARS" = true ]; then
  270. echo ""
  271. echo -e "${YELLOW}Skipping sidecar images (--skip-sidecars).${NC}"
  272. elif [ ! -x "$SIDECAR_SCRIPT" ]; then
  273. echo ""
  274. echo -e "${YELLOW}Sidecar helper not found at ${SIDECAR_SCRIPT} — skipping sidecar build.${NC}"
  275. else
  276. echo ""
  277. echo -e "${GREEN}================================================${NC}"
  278. echo -e "${GREEN} Publishing sidecar images (stable channel)${NC}"
  279. echo -e "${GREEN}================================================${NC}"
  280. SIDECAR_ARGS="--channel stable --version ${VERSION}"
  281. [ "$PARALLEL" = true ] && SIDECAR_ARGS="$SIDECAR_ARGS --parallel"
  282. [ "$PUSH_GHCR" = false ] && SIDECAR_ARGS="$SIDECAR_ARGS --dockerhub-only"
  283. [ "$PUSH_DOCKERHUB" = false ] && SIDECAR_ARGS="$SIDECAR_ARGS --ghcr-only"
  284. "$SIDECAR_SCRIPT" $SIDECAR_ARGS
  285. fi