blob: 386f10256e6091e941fe37532d8b2682eaff5c83 [file]
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default mode
MODE=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--check)
MODE="check"
shift
;;
--fix)
MODE="fix"
shift
;;
--help|-h)
echo "Usage: $0 [--check|--fix]"
echo ""
echo "Sync Rust version from rust-toolchain.toml to all Dockerfiles"
echo ""
echo "Options:"
echo " --check Check if all Dockerfiles have the correct Rust version"
echo " --fix Update all Dockerfiles to use the correct Rust version"
echo " --help Show this help message"
exit 0
;;
*)
echo -e "${RED}Error: Unknown option $1${NC}"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Require mode to be specified
if [ -z "$MODE" ]; then
echo -e "${RED}Error: Please specify either --check or --fix${NC}"
echo "Use --help for usage information"
exit 1
fi
# Get the repository root (two levels up from scripts/ci/)
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$REPO_ROOT"
# Extract Rust version from rust-toolchain.toml
RUST_VERSION=$(grep 'channel' rust-toolchain.toml | sed 's/.*"\(.*\)".*/\1/')
if [ -z "$RUST_VERSION" ]; then
echo -e "${RED}Error: Could not extract Rust version from rust-toolchain.toml${NC}"
exit 1
fi
# Strip trailing ".0" -> e.g., 1.89.0 -> 1.89 (no change if it doesn't end in .0)
RUST_VERSION_SHORT=$(echo "$RUST_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+)\.0$/\1.\2/')
RUST_IMAGE_VARIANT="slim-trixie"
RUST_IMAGE_PATTERN="FROM[[:space:]].*\\brust:"
RUST_IMAGE_TAG_PATTERN="(rust:[^-[:space:]]+)"
echo "Rust version from rust-toolchain.toml: ${GREEN}$RUST_VERSION${NC} (using ${GREEN}$RUST_VERSION_SHORT${NC} for Dockerfiles)"
echo ""
# Find all Dockerfiles
DOCKERFILES=$(find . -name "Dockerfile*" -type f | grep -v node_modules | grep -v target | sort)
# Track misaligned files
MISALIGNED_FILES=()
TOTAL_FILES=0
FIXED_FILES=0
for dockerfile in $DOCKERFILES; do
SOURCE=""
CURRENT_VERSION=""
EXPECTED_VERSION=""
HAS_RUST_IMAGE="false"
RUST_IMAGE_MISMATCH="false"
# Two ways a Dockerfile pins the toolchain:
# 1. `ARG RUST_VERSION=1.96` (+ `FROM rust:${RUST_VERSION}...`)
# 2. hardcoded `FROM rust:1.96-slim` / `FROM rust:1.96.0-alpine`
# Both must stay in sync; a Dockerfile that pins neither is skipped.
if grep -q "^ARG RUST_VERSION=" "$dockerfile" 2>/dev/null; then
SOURCE="arg"
CURRENT_VERSION=$(grep "^ARG RUST_VERSION=" "$dockerfile" | head -1 | sed 's/^ARG RUST_VERSION=//')
EXPECTED_VERSION="$RUST_VERSION_SHORT"
elif grep -qE "FROM[[:space:]].*\brust:[0-9]" "$dockerfile" 2>/dev/null; then
SOURCE="from"
CURRENT_VERSION=$(grep -E "FROM[[:space:]].*\brust:[0-9]" "$dockerfile" | head -1 | sed -nE 's/.*\brust:([0-9]+\.[0-9]+(\.[0-9]+)?).*/\1/p')
# Preserve the file's precision: full patch (1.96.0) or short (1.96).
if [[ "$CURRENT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
EXPECTED_VERSION="$RUST_VERSION"
else
EXPECTED_VERSION="$RUST_VERSION_SHORT"
fi
fi
if grep -qE "$RUST_IMAGE_PATTERN" "$dockerfile" 2>/dev/null; then
HAS_RUST_IMAGE="true"
if grep -E "$RUST_IMAGE_PATTERN" "$dockerfile" | grep -qvE -- "-$RUST_IMAGE_VARIANT([[:space:]]|$)"; then
RUST_IMAGE_MISMATCH="true"
fi
fi
if [ -z "$SOURCE" ] && [ "$HAS_RUST_IMAGE" = "false" ]; then
continue
fi
TOTAL_FILES=$((TOTAL_FILES + 1))
STATUS_DETAILS="$CURRENT_VERSION"
if [ "$HAS_RUST_IMAGE" = "true" ]; then
STATUS_DETAILS="${STATUS_DETAILS:+$STATUS_DETAILS, }$RUST_IMAGE_VARIANT"
fi
if [ "$MODE" = "check" ]; then
if { [ -n "$SOURCE" ] && [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; } || [ "$RUST_IMAGE_MISMATCH" = "true" ]; then
MISALIGNED_FILES+=("$dockerfile")
MESSAGE=""
if [ -n "$SOURCE" ] && [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; then
MESSAGE="Rust version ${RED}$CURRENT_VERSION${NC} (expected: ${GREEN}$EXPECTED_VERSION${NC})"
fi
if [ "$RUST_IMAGE_MISMATCH" = "true" ]; then
if [ -n "$MESSAGE" ]; then
MESSAGE="$MESSAGE; "
fi
MESSAGE="${MESSAGE}Rust base image must use ${GREEN}$RUST_IMAGE_VARIANT${NC}"
fi
echo -e "${RED}✗${NC} $dockerfile: $MESSAGE"
else
echo -e "${GREEN}✓${NC} $dockerfile${STATUS_DETAILS:+: $STATUS_DETAILS}"
fi
elif [ "$MODE" = "fix" ]; then
if { [ -n "$SOURCE" ] && [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; } || [ "$RUST_IMAGE_MISMATCH" = "true" ]; then
if [ -n "$SOURCE" ] && [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ] && [ "$SOURCE" = "arg" ]; then
sed -i "s/^ARG RUST_VERSION=.*/ARG RUST_VERSION=$EXPECTED_VERSION/" "$dockerfile"
elif [ -n "$SOURCE" ] && [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; then
sed -i -E "/FROM[[:space:]].*\\brust:[0-9]/ s#(\\brust:)[0-9]+\\.[0-9]+(\\.[0-9]+)?#\\1$EXPECTED_VERSION#g" "$dockerfile"
fi
if [ "$RUST_IMAGE_MISMATCH" = "true" ]; then
sed -i -E "/$RUST_IMAGE_PATTERN/ s#$RUST_IMAGE_TAG_PATTERN-[^[:space:]]+#\\1-$RUST_IMAGE_VARIANT#g" "$dockerfile"
sed -i -E "/$RUST_IMAGE_PATTERN/ { /-$RUST_IMAGE_VARIANT([[:space:]]|$)/! s#$RUST_IMAGE_TAG_PATTERN([[:space:]]|$)#\\1-$RUST_IMAGE_VARIANT\\2#g; }" "$dockerfile"
fi
FIXED_FILES=$((FIXED_FILES + 1))
MESSAGE=""
if [ -n "$SOURCE" ] && [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; then
MESSAGE="Rust version ${RED}$CURRENT_VERSION${NC} -> ${GREEN}$EXPECTED_VERSION${NC}"
fi
if [ "$RUST_IMAGE_MISMATCH" = "true" ]; then
if [ -n "$MESSAGE" ]; then
MESSAGE="$MESSAGE; "
fi
MESSAGE="${MESSAGE}Rust base image -> ${GREEN}$RUST_IMAGE_VARIANT${NC}"
fi
echo -e "${GREEN}Fixed${NC} $dockerfile: $MESSAGE"
else
echo -e "${GREEN}✓${NC} $dockerfile${STATUS_DETAILS:+: already correct ($STATUS_DETAILS)}"
fi
fi
done
echo ""
echo "────────────────────────────────────────────────"
if [ "$MODE" = "check" ]; then
if [ ${#MISALIGNED_FILES[@]} -eq 0 ]; then
echo -e "${GREEN}✓ All $TOTAL_FILES Dockerfiles are aligned with Rust version $RUST_VERSION_SHORT${NC}"
exit 0
else
echo -e "${RED}✗ Found ${#MISALIGNED_FILES[@]} misaligned Dockerfile(s) out of $TOTAL_FILES:${NC}"
for file in "${MISALIGNED_FILES[@]}"; do
echo -e " ${RED}• $file${NC}"
done
echo ""
echo -e "${YELLOW}Run '$0 --fix' to fix these files${NC}"
exit 1
fi
elif [ "$MODE" = "fix" ]; then
if [ $FIXED_FILES -eq 0 ]; then
echo -e "${GREEN}✓ All $TOTAL_FILES Dockerfiles were already aligned with Rust version $RUST_VERSION_SHORT${NC}"
else
echo -e "${GREEN}✓ Fixed $FIXED_FILES out of $TOTAL_FILES Dockerfiles to use Rust version $RUST_VERSION_SHORT${NC}"
fi
exit 0
fi