Assist_Design/scripts/plesk/build-images.sh
barsa 9e27380069 Update TypeScript configurations, improve module imports, and clean up Dockerfiles
- Adjusted TypeScript settings in tsconfig files for better alignment with ESNext standards.
- Updated pnpm-lock.yaml to reflect dependency changes and improve package management.
- Cleaned up Dockerfiles for both BFF and Portal applications to enhance build processes.
- Modified import statements across various modules to include file extensions for consistency.
- Removed outdated SHA256 files for backend and frontend tarballs to streamline project structure.
- Enhanced health check mechanisms in Dockerfiles for improved application startup reliability.
2025-12-10 16:08:34 +09:00

272 lines
9.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# 🐳 Build production Docker images for Plesk deployment
# Features: Parallel builds, BuildKit, compressed tarballs, multi-platform support
set -Eeuo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Configuration (override via env vars or flags)
IMAGE_FRONTEND="${IMAGE_FRONTEND_NAME:-portal-frontend}"
IMAGE_BACKEND="${IMAGE_BACKEND_NAME:-portal-backend}"
IMAGE_TAG="${IMAGE_TAG:-}"
OUTPUT_DIR="${OUTPUT_DIR:-$PROJECT_ROOT}"
PUSH_REMOTE="${PUSH_REMOTE:-}"
PARALLEL="${PARALLEL_BUILD:-1}"
COMPRESS="${COMPRESS:-1}"
USE_LATEST_FILENAME="${USE_LATEST_FILENAME:-1}" # Default: save as .latest.tar.gz
SAVE_TARS=1
PLATFORM="${PLATFORM:-linux/amd64}" # Override for ARM: linux/arm64
PROGRESS="${PROGRESS:-auto}" # "plain" for CI, "auto" for interactive
# Colors
G='\033[0;32m' Y='\033[1;33m' R='\033[0;31m' B='\033[0;34m' C='\033[0;36m' N='\033[0m'
log() { echo -e "${G}[BUILD]${N} $*"; }
info() { echo -e "${B}[INFO]${N} $*"; }
warn() { echo -e "${Y}[WARN]${N} $*"; }
fail() { echo -e "${R}[ERROR]${N} $*"; exit 1; }
step() { echo -e "${C}[STEP]${N} $*"; }
usage() {
cat <<EOF
Build Docker images and save tarballs for Plesk.
Usage: $0 [OPTIONS]
Options:
--tag <tag> Version tag for image (default: YYYYMMDD-gitsha)
--output <dir> Output directory (default: project root)
--push <registry> Push to registry after build
--no-save Build only, no tar files
--no-compress Save as .tar instead of .tar.gz
--versioned Name files with version tag (default: .latest.tar.gz)
--sequential Build one at a time (default: parallel)
--platform <p> Target platform (default: linux/amd64, use linux/arm64 for ARM)
--ci CI mode: plain progress output, no colors
-h, --help Show this help
Examples:
$0 # Output: portal-frontend.latest.tar.gz (default)
$0 --versioned # Output: portal-frontend.20251201-abc123.tar.gz
$0 --tag v1.2.3 --versioned # Output: portal-frontend.v1.2.3.tar.gz
$0 --sequential --no-save # Debug build
$0 --platform linux/arm64 # Build for ARM64 (Apple Silicon)
$0 --ci # CI-friendly output
EOF
exit 0
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--tag) IMAGE_TAG="${2:-}"; shift 2 ;;
--output) OUTPUT_DIR="${2:-}"; shift 2 ;;
--push) PUSH_REMOTE="${2:-}"; shift 2 ;;
--no-save) SAVE_TARS=0; shift ;;
--no-compress) COMPRESS=0; shift ;;
--versioned) USE_LATEST_FILENAME=0; shift ;;
--sequential) PARALLEL=0; shift ;;
--platform) PLATFORM="${2:-linux/amd64}"; shift 2 ;;
--ci) PROGRESS="plain"; G=''; Y=''; R=''; B=''; C=''; N=''; shift ;;
-h|--help) usage ;;
*) fail "Unknown option: $1" ;;
esac
done
# Validation
command -v docker >/dev/null 2>&1 || fail "Docker required"
cd "$PROJECT_ROOT"
[[ -f apps/portal/Dockerfile ]] || fail "Missing apps/portal/Dockerfile"
[[ -f apps/bff/Dockerfile ]] || fail "Missing apps/bff/Dockerfile"
# Auto-generate tag if not provided
[[ -z "$IMAGE_TAG" ]] && IMAGE_TAG="$(date +%Y%m%d)-$(git rev-parse --short HEAD 2>/dev/null || echo 'local')"
# Enable BuildKit
export DOCKER_BUILDKIT=1
# Extract PNPM version from package.json (packageManager field)
# Format: "pnpm@10.25.0+sha512..."
PNPM_VERSION_FROM_PKG=$(grep -oP '"packageManager":\s*"pnpm@\K[0-9.]+' package.json 2>/dev/null || echo "")
PNPM_VERSION="${PNPM_VERSION:-${PNPM_VERSION_FROM_PKG:-10.25.0}}"
# Build args (can be overridden via env vars)
NEXT_PUBLIC_API_BASE="${NEXT_PUBLIC_API_BASE:-/api}"
NEXT_PUBLIC_APP_NAME="${NEXT_PUBLIC_APP_NAME:-Customer Portal}"
GIT_SOURCE="$(git config --get remote.origin.url 2>/dev/null || echo unknown)"
GIT_COMMIT="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
log "🏷️ Tag: ${IMAGE_TAG}"
info "📦 PNPM: ${PNPM_VERSION} | Platform: ${PLATFORM}"
LOG_DIR="${OUTPUT_DIR}/.build-logs"
mkdir -p "$LOG_DIR"
build_frontend() {
local logfile="$LOG_DIR/frontend.log"
step "Building frontend image..."
docker build -f apps/portal/Dockerfile \
--platform "${PLATFORM}" \
--progress "${PROGRESS}" \
--build-arg "PNPM_VERSION=${PNPM_VERSION}" \
--build-arg "NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE}" \
--build-arg "NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}" \
--build-arg "NEXT_PUBLIC_APP_VERSION=${IMAGE_TAG}" \
-t "${IMAGE_FRONTEND}:latest" \
-t "${IMAGE_FRONTEND}:${IMAGE_TAG}" \
--label "org.opencontainers.image.version=${IMAGE_TAG}" \
--label "org.opencontainers.image.source=${GIT_SOURCE}" \
--label "org.opencontainers.image.revision=${GIT_COMMIT}" \
--label "org.opencontainers.image.created=${BUILD_DATE}" \
. > "$logfile" 2>&1
local exit_code=$?
if [[ $exit_code -eq 0 ]]; then
local size=$(docker image inspect "${IMAGE_FRONTEND}:latest" --format='{{.Size}}' 2>/dev/null | numfmt --to=iec 2>/dev/null || echo "?")
log "✅ Frontend built (${size})"
else
warn "❌ Frontend FAILED - see $logfile"
tail -30 "$logfile"
fi
return $exit_code
}
build_backend() {
local logfile="$LOG_DIR/backend.log"
step "Building backend image..."
docker build -f apps/bff/Dockerfile \
--platform "${PLATFORM}" \
--progress "${PROGRESS}" \
--build-arg "PNPM_VERSION=${PNPM_VERSION}" \
-t "${IMAGE_BACKEND}:latest" \
-t "${IMAGE_BACKEND}:${IMAGE_TAG}" \
--label "org.opencontainers.image.version=${IMAGE_TAG}" \
--label "org.opencontainers.image.source=${GIT_SOURCE}" \
--label "org.opencontainers.image.revision=${GIT_COMMIT}" \
--label "org.opencontainers.image.created=${BUILD_DATE}" \
. > "$logfile" 2>&1
local exit_code=$?
if [[ $exit_code -eq 0 ]]; then
local size=$(docker image inspect "${IMAGE_BACKEND}:latest" --format='{{.Size}}' 2>/dev/null | numfmt --to=iec 2>/dev/null || echo "?")
log "✅ Backend built (${size})"
else
warn "❌ Backend FAILED - see $logfile"
tail -30 "$logfile"
fi
return $exit_code
}
# Build images
START=$(date +%s)
echo ""
log "🐳 Starting Docker builds..."
info "📁 Build logs: $LOG_DIR/"
echo ""
if [[ "$PARALLEL" -eq 1 ]]; then
log "🚀 Building frontend & backend in parallel..."
build_frontend & FE_PID=$!
build_backend & BE_PID=$!
# Track progress
ELAPSED=0
while kill -0 $FE_PID 2>/dev/null || kill -0 $BE_PID 2>/dev/null; do
sleep 10
ELAPSED=$((ELAPSED + 10))
info "⏳ Building... (${ELAPSED}s elapsed)"
done
# Check results
FE_EXIT=0; BE_EXIT=0
wait $FE_PID || FE_EXIT=$?
wait $BE_PID || BE_EXIT=$?
[[ $FE_EXIT -ne 0 ]] && fail "Frontend build failed (exit $FE_EXIT) - check $LOG_DIR/frontend.log"
[[ $BE_EXIT -ne 0 ]] && fail "Backend build failed (exit $BE_EXIT) - check $LOG_DIR/backend.log"
else
log "🔧 Sequential build mode..."
build_frontend || fail "Frontend build failed - check $LOG_DIR/frontend.log"
build_backend || fail "Backend build failed - check $LOG_DIR/backend.log"
fi
BUILD_TIME=$(($(date +%s) - START))
echo ""
log "⏱️ Build completed in ${BUILD_TIME}s"
# Save tarballs
if [[ "$SAVE_TARS" -eq 1 ]]; then
mkdir -p "$OUTPUT_DIR"
SAVE_START=$(date +%s)
# Determine filename suffix
if [[ "$USE_LATEST_FILENAME" -eq 1 ]]; then
FILE_TAG="latest"
else
FILE_TAG="$IMAGE_TAG"
fi
if [[ "$COMPRESS" -eq 1 ]]; then
# Pick fastest available compressor: pigz (parallel) > gzip
if command -v pigz >/dev/null 2>&1; then
COMPRESSOR="pigz -p $(nproc)" # Use all CPU cores
COMP_NAME="pigz"
else
COMPRESSOR="gzip -1" # Fast mode if no pigz
COMP_NAME="gzip"
fi
FE_TAR="$OUTPUT_DIR/${IMAGE_FRONTEND}.${FILE_TAG}.tar.gz"
BE_TAR="$OUTPUT_DIR/${IMAGE_BACKEND}.${FILE_TAG}.tar.gz"
log "💾 Compressing with $COMP_NAME..."
(docker save "${IMAGE_FRONTEND}:latest" | $COMPRESSOR > "$FE_TAR") &
(docker save "${IMAGE_BACKEND}:latest" | $COMPRESSOR > "$BE_TAR") &
wait
else
FE_TAR="$OUTPUT_DIR/${IMAGE_FRONTEND}.${FILE_TAG}.tar"
BE_TAR="$OUTPUT_DIR/${IMAGE_BACKEND}.${FILE_TAG}.tar"
log "💾 Saving uncompressed tarballs..."
docker save -o "$FE_TAR" "${IMAGE_FRONTEND}:latest" &
docker save -o "$BE_TAR" "${IMAGE_BACKEND}:latest" &
wait
fi
SAVE_TIME=$(($(date +%s) - SAVE_START))
sha256sum "$FE_TAR" > "${FE_TAR}.sha256"
sha256sum "$BE_TAR" > "${BE_TAR}.sha256"
log "✅ Saved in ${SAVE_TIME}s:"
printf " %-50s %s\n" "$FE_TAR" "$(du -h "$FE_TAR" | cut -f1)"
printf " %-50s %s\n" "$BE_TAR" "$(du -h "$BE_TAR" | cut -f1)"
fi
# Push to registry
if [[ -n "$PUSH_REMOTE" ]]; then
log "📤 Pushing to ${PUSH_REMOTE}..."
for img in "${IMAGE_FRONTEND}" "${IMAGE_BACKEND}"; do
for tag in "latest" "${IMAGE_TAG}"; do
docker tag "${img}:${tag}" "${PUSH_REMOTE}/${img}:${tag}"
docker push "${PUSH_REMOTE}/${img}:${tag}" &
done
done
wait
log "✅ Pushed"
fi
TOTAL_TIME=$(($(date +%s) - START))
log "🎉 Complete in ${TOTAL_TIME}s"
echo ""
info "Next: Upload to Plesk, then:"
if [[ "$COMPRESS" -eq 1 ]]; then
echo " gunzip -c ${IMAGE_FRONTEND}.${FILE_TAG}.tar.gz | docker load"
echo " gunzip -c ${IMAGE_BACKEND}.${FILE_TAG}.tar.gz | docker load"
else
echo " docker load -i ${IMAGE_FRONTEND}.${FILE_TAG}.tar"
echo " docker load -i ${IMAGE_BACKEND}.${FILE_TAG}.tar"
fi
if [[ "$USE_LATEST_FILENAME" -eq 0 ]]; then
echo " Update Portainer with tag: ${IMAGE_TAG}"
fi