#!/bin/bash # Abort if anything fails set -e # Source common functions BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/lib/message-helpers.sh" # Configuration BUILD_DIR="$BUILD_ROOT/build" OUTPUT_DIR="$BUILD_ROOT/output/ruby" # Default values ARCHITECTURES=("x86_64") RUBY_VERSIONS=() RAILS_VERSIONS=("8.0.2.1") # Current stable Rails version # Function to fetch last N Ruby releases fetch_ruby_releases() { local count=$1 print_info "Fetching last $count Ruby releases..." # Fetch releases from Ruby's release page curl -s https://api.github.com/repos/ruby/ruby/releases | \ jq -r '.[].tag_name' | \ grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | \ sed 's/^v//' | \ head -n "$count" } # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in --ruby) IFS=',' read -ra RUBY_VERSIONS <<< "$2" shift 2 ;; --rails) IFS=',' read -ra RAILS_VERSIONS <<< "$2" shift 2 ;; --arch) IFS=',' read -ra ARCHITECTURES <<< "$2" shift 2 ;; --last) # Fetch last N Ruby releases RUBY_VERSIONS=($(fetch_ruby_releases "$2")) shift 2 ;; -h | --help) echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --ruby Comma-separated Ruby versions (e.g., 3.4.5,3.3.0,edge)" echo " --rails Comma-separated Rails versions (e.g., 8.0.2.1,7.2.0,edge)" echo " --arch Comma-separated architectures (e.g., x86_64,aarch64)" echo " --last Build last N Ruby releases" echo " -h, --help Show this help message" echo "" echo "Examples:" echo " $0 --ruby 3.4.5,edge --rails 8.0.2.1" echo " $0 --last 10 --rails 8.0.2.1" echo " $0 --ruby 3.4.5 --rails 8.0.2.1,7.2.0 --arch x86_64,aarch64" echo "" echo "Default behavior:" echo " - Builds Ruby 3.4.5, edge, and last 10 releases" echo " - Uses Rails 8.0.2.1" echo " - Builds for x86_64 architecture" exit 0 ;; *) print_error "Unknown option: $1" exit 1 ;; esac done # If no Ruby versions specified, use defaults if [[ ${#RUBY_VERSIONS[@]} -eq 0 ]]; then print_info "No Ruby versions specified, using defaults..." # Get last 10 releases LAST_10=($(fetch_ruby_releases 10)) # Combine with specific versions RUBY_VERSIONS=("3.4.5" "edge" "${LAST_10[@]}") # Remove duplicates RUBY_VERSIONS=($(echo "${RUBY_VERSIONS[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) fi print_header "Ruby Bundle Builder" print_info "Ruby versions: ${RUBY_VERSIONS[*]}" print_info "Rails versions: ${RAILS_VERSIONS[*]}" print_info "Architectures: ${ARCHITECTURES[*]}" # Check for Docker if ! command -v docker &>/dev/null; then print_error "Docker is not installed" exit 1 fi # Check if Docker daemon is running if ! docker info &>/dev/null; then print_error "Docker daemon is not running" print_warning "Start Docker with: sudo systemctl start docker" exit 1 fi # Setup QEMU for multi-platform builds if aarch64 is requested if [[ " ${ARCHITECTURES[@]} " =~ " aarch64 " ]]; then print_info "Setting up QEMU for aarch64 emulation..." # Install QEMU user static for cross-platform emulation if ! docker run --rm --privileged multiarch/qemu-user-static --reset -p yes; then print_warning "Failed to setup QEMU, aarch64 builds may fail" print_info "You may need to install qemu-user-static on your host" else print_success "QEMU emulation enabled for multi-arch builds" fi fi # Create output directories for arch in "${ARCHITECTURES[@]}"; do mkdir -p "$OUTPUT_DIR/$arch" done # Build the Docker image using buildx for multi-platform support print_info "Building Docker image..." # Determine platforms needed PLATFORMS="" for arch in "${ARCHITECTURES[@]}"; do case "$arch" in x86_64) [[ -n "$PLATFORMS" ]] && PLATFORMS+="," PLATFORMS+="linux/amd64" ;; aarch64) [[ -n "$PLATFORMS" ]] && PLATFORMS+="," PLATFORMS+="linux/arm64" ;; *) print_error "Unsupported architecture: $arch" continue ;; esac done # Use buildx to build multi-platform image if command -v docker buildx &>/dev/null; then # Ensure we have a builder instance BUILDER_NAME="ruby-multiarch" if ! docker buildx ls | grep -q "$BUILDER_NAME"; then print_info "Creating buildx builder instance..." docker buildx create --name "$BUILDER_NAME" --driver docker-container --use else docker buildx use "$BUILDER_NAME" fi print_info "Building multi-platform image for: $PLATFORMS" # Build for all platforms at once # We can't use --load with multiple platforms, so we build separately for each for arch in "${ARCHITECTURES[@]}"; do case "$arch" in x86_64) PLATFORM="linux/amd64" ;; aarch64) PLATFORM="linux/arm64" ;; esac print_info "Building for $arch ($PLATFORM)..." if ! docker buildx build \ --platform "$PLATFORM" \ -t "ruby-builder:$arch" \ --load \ -f "$BUILD_DIR/Dockerfile.ruby-builder" \ "$BUILD_DIR"; then print_error "Failed to build Docker image for $arch" exit 1 fi print_success "Built image for $arch" done else print_error "Docker buildx is required for multi-platform builds" print_info "Install with: docker buildx install" exit 1 fi print_success "Docker image built successfully" print_info "Starting Ruby builds..." # Track results SUCCESSFUL_BUILDS=() FAILED_BUILDS=() # Build each combination TOTAL_BUILDS=$((${#RUBY_VERSIONS[@]} * ${#RAILS_VERSIONS[@]} * ${#ARCHITECTURES[@]})) CURRENT_BUILD=0 print_info "Total builds to perform: $TOTAL_BUILDS" for ruby_version in "${RUBY_VERSIONS[@]}"; do print_info "Processing Ruby version: $ruby_version" for rails_version in "${RAILS_VERSIONS[@]}"; do print_info "Processing Rails version: $rails_version" for arch in "${ARCHITECTURES[@]}"; do print_info "Processing architecture: $arch" CURRENT_BUILD=$((CURRENT_BUILD + 1)) echo "" print_info "[$CURRENT_BUILD/$TOTAL_BUILDS] Building Ruby $ruby_version + Rails $rails_version for $arch..." # Check if this combination already exists if [[ "$ruby_version" == "edge" ]]; then TARBALL_PATTERN="ruby-edge-rails-*-${arch}.tar.gz" else if [[ "$rails_version" == "edge" ]]; then TARBALL_PATTERN="ruby-${ruby_version}-rails-edge-*-${arch}.tar.gz" else TARBALL_PATTERN="ruby-${ruby_version}-rails-${rails_version}-${arch}.tar.gz" fi fi if ls "$OUTPUT_DIR/$arch"/$TARBALL_PATTERN 1>/dev/null 2>&1; then print_warning "Skipping: $TARBALL_PATTERN already exists" continue fi # Map architecture to Docker platform case "$arch" in x86_64) PLATFORM="linux/amd64" ;; aarch64) PLATFORM="linux/arm64" ;; *) print_error "Unsupported architecture: $arch" continue ;; esac # Run the build in Docker using the architecture-specific image print_info "Running Ruby build in container for $arch..." if docker run --rm \ --platform "$PLATFORM" \ -e RUBY_VERSION="$ruby_version" \ -e RAILS_VERSION="$rails_version" \ -e ARCH="$arch" \ -v "$OUTPUT_DIR:/output/ruby" \ -v "$BUILD_DIR/build-ruby.sh:/build/build-ruby.sh:ro" \ "ruby-builder:$arch"; then print_success "Successfully built Ruby $ruby_version + Rails $rails_version for $arch" SUCCESSFUL_BUILDS+=("ruby-$ruby_version-rails-$rails_version-$arch") else print_error "Failed to build Ruby $ruby_version + Rails $rails_version for $arch" FAILED_BUILDS+=("ruby-$ruby_version-rails-$rails_version-$arch") fi done done done # Summary echo "" print_header "Build Summary" print_info "Total builds attempted: $TOTAL_BUILDS" print_success "Successful: ${#SUCCESSFUL_BUILDS[@]}" if [[ ${#FAILED_BUILDS[@]} -gt 0 ]]; then print_error "Failed: ${#FAILED_BUILDS[@]}" echo "Failed builds:" for build in "${FAILED_BUILDS[@]}"; do echo " - $build" done exit 1 else print_success "All builds completed successfully!" fi # Show output directory contents echo "" print_info "Output directory contents:" for arch in "${ARCHITECTURES[@]}"; do echo " $OUTPUT_DIR/$arch:" ls -lh "$OUTPUT_DIR/$arch/" 2>/dev/null | tail -n +2 | head -5 | sed 's/^/ /' TOTAL_FILES=$(ls "$OUTPUT_DIR/$arch/" 2>/dev/null | wc -l) if [[ $TOTAL_FILES -gt 5 ]]; then echo " ... and $((TOTAL_FILES - 5)) more files" fi done