#!/bin/bash set -euo pipefail # Required environment variables (set by CI) if [ -z "${REPO_OWNER:-}" ] || [ -z "${GITHUB_IO_TOKEN:-}" ]; then echo "๐Ÿงจ Error: REPO_OWNER and GITHUB_IO_TOKEN env vars are required" exit 1 fi # ----------------------------- # Rust / Cargo Setup (Assuming toolchain is handled by CI) # ----------------------------- export RUSTUP_NO_UPDATE_CHECK=1 export CARGO_NET_GIT_FETCH_WITH_CLI=true export RUSTFLAGS="--remap-path-prefix $HOME=~" # Validate arguments if [ $# -ne 1 ]; then echo "Usage: $(basename "$0") " exit 1 fi BRANCH="$1" case "$BRANCH" in master) TAG_SUFFIX="latest" ;; develop) TAG_SUFFIX="dev" ;; experimental) TAG_SUFFIX="experimental" ;; *) echo "๐Ÿงจ Error: Branch '$BRANCH' not supported"; exit 1 ;; esac echo "๐Ÿš€ Building for branch: $BRANCH (tag: $TAG_SUFFIX)" # Directories WORKING_DIR=$(pwd) DOCKER_DIR="${WORKING_DIR}/docker" #BACKEND_DIR="${WORKING_DIR}/backend" FRONTEND_DIR="${WORKING_DIR}/frontend" FRONTEND_BUILD_DIR="${FRONTEND_DIR}/dist" ARTIFACT_DIR="${WORKING_DIR}/artifacts" # Config declare -A ARCHITECTURES=([LINUX]=x86_64-unknown-linux-musl [AARCH64]=aarch64-unknown-linux-musl) declare -A MULTI_PLATFORM_IMAGES=([tuliprox]="scratch-final" [tuliprox-alpine]="alpine-final") # Version detection VERSION=$(grep -Po '^version\s*=\s*"\K[0-9\.]+' "${WORKING_DIR}/Cargo.toml") echo "๐Ÿ“ฆ Version: ${VERSION}" # Prepare artifact directory (clean per run to avoid stale files) rm -rf "${ARTIFACT_DIR}" mkdir -p "${ARTIFACT_DIR}" write_checksum() { local file_path="$1" local file_dir local file_name file_dir="$(dirname "${file_path}")" file_name="$(basename "${file_path}")" if command -v sha256sum >/dev/null 2>&1; then (cd "${file_dir}" && sha256sum "${file_name}") > "${file_path}.sha256" else (cd "${file_dir}" && shasum -a 256 "${file_name}") > "${file_path}.sha256" fi } create_release_bundle() { local architecture="$1" local binary_path="$2" local bundle_root="" local bundle_name local bundle_path bundle_root="$(mktemp -d)" trap 'if [ -n "${bundle_root:-}" ] && [ -d "${bundle_root}" ]; then rm -rf "${bundle_root}"; fi' RETURN ERR INT mkdir -p "${bundle_root}/tuliprox" cp "${binary_path}" "${bundle_root}/tuliprox/tuliprox" chmod +x "${bundle_root}/tuliprox/tuliprox" cp -r "${FRONTEND_BUILD_DIR}" "${bundle_root}/tuliprox/web" cp -r "${WORKING_DIR}/resources" "${bundle_root}/tuliprox/resources" bundle_name="tuliprox-v${VERSION}-${architecture}.tar.gz" bundle_path="${ARTIFACT_DIR}/${bundle_name}" tar -C "${bundle_root}" -czf "${bundle_path}" tuliprox write_checksum "${bundle_path}" } # ----------------------------------------- # 1. Documentation + Frontend Build # ----------------------------------------- echo "๐ŸŽจ Building frontend..." "${WORKING_DIR}/bin/build_fe.sh" release cd "$WORKING_DIR" # ----------------------------------------- # 2. Binary Compilation (Multi-Arch) # ----------------------------------------- echo "๐Ÿ—๏ธ Building binaries..." mkdir -p "${DOCKER_DIR}/binaries" for PLATFORM in "${!ARCHITECTURES[@]}"; do ARCHITECTURE=${ARCHITECTURES[$PLATFORM]} echo "๐Ÿ”จ Building for $ARCHITECTURE" # Using cross for compilation cross build -p tuliprox --release --target "$ARCHITECTURE" --locked SOURCE_BIN_PATH="target/${ARCHITECTURE}/release/tuliprox" cp "${SOURCE_BIN_PATH}" "${DOCKER_DIR}/binaries/tuliprox-${ARCHITECTURE}" create_release_bundle "${ARCHITECTURE}" "${SOURCE_BIN_PATH}" done echo "๐Ÿ“ฆ Release bundles prepared in ${ARTIFACT_DIR}" # ----------------------------------------- # 3. Docker Build & Push (Optimized Caching) # ----------------------------------------- echo "๐Ÿ“‹ Preparing Docker context..." cp -r "${FRONTEND_BUILD_DIR}" "${DOCKER_DIR}/web" cp -r "resources" "${DOCKER_DIR}/resources" cd "${DOCKER_DIR}" # Login echo "๐Ÿ”‘ Logging into GHCR..." echo "${GITHUB_IO_TOKEN}" | docker login ghcr.io -u "${REPO_OWNER}" --password-stdin REPO_OWNER_LC="${REPO_OWNER,,}" for IMAGE_NAME in "${!MULTI_PLATFORM_IMAGES[@]}"; do BUILD_TARGET="${MULTI_PLATFORM_IMAGES[$IMAGE_NAME]}" TAG_BRANCH="ghcr.io/${REPO_OWNER_LC}/${IMAGE_NAME}:${TAG_SUFFIX}" if [ "$BRANCH" = "experimental" ]; then # Experimental images must not overwrite official release version tags TAG_VERSION="ghcr.io/${REPO_OWNER_LC}/${IMAGE_NAME}:experimental-${VERSION}" else TAG_VERSION="ghcr.io/${REPO_OWNER_LC}/${IMAGE_NAME}:${VERSION}" fi echo "๐ŸŽฏ Building multi-platform image: ${IMAGE_NAME}" # THE FIX: Using type=gha for automatic GitHub Actions cache management. # Scoped per branch/tag_suffix to avoid cache pollution between develop and experimental. docker buildx build -f Dockerfile.manual \ -t "${TAG_VERSION}" \ -t "${TAG_BRANCH}" \ --target "$BUILD_TARGET" \ --platform "linux/amd64,linux/arm64" \ --cache-from "type=gha,scope=${IMAGE_NAME}-${TAG_SUFFIX}" \ --cache-to "type=gha,mode=max,scope=${IMAGE_NAME}-${TAG_SUFFIX}" \ --push \ . done # ----------------------------------------- # Cleanup # ----------------------------------------- echo "๐Ÿ—‘๏ธ Final cleanup..." rm -rf "${DOCKER_DIR}/binaries" "${DOCKER_DIR}/web" "${DOCKER_DIR}/resources"