571 lines
22 KiB
Shell
Executable file
571 lines
22 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
# Build script for Flutter Web in this repo.
|
|
# Creates a web build that uses https://hsxn.hesabix.ir/ as the API base URL by default.
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$SCRIPT_DIR"
|
|
|
|
DEFAULT_MODE="release" # debug|profile|release
|
|
DEFAULT_BUILD_DIR="build/web"
|
|
DEFAULT_API_BASE_URL="https://hsxn.hesabix.ir"
|
|
|
|
USER_PROJECT=""
|
|
MODE="$DEFAULT_MODE"
|
|
BUILD_DIR=""
|
|
API_BASE_URL="$DEFAULT_API_BASE_URL"
|
|
CLEAN_BUILD=false
|
|
INSTALL_DEPS=false
|
|
USE_OFFLINE_CACHE=false
|
|
# Optional CLI overrides for UI branding (else env / .deploy_env)
|
|
BRANDING_MODE_CLI=""
|
|
BRANDING_DIR_CLI=""
|
|
APP_NAME_FA_CLI=""
|
|
APP_NAME_EN_CLI=""
|
|
|
|
print_usage() {
|
|
cat <<EOF
|
|
Usage: ./build_web.sh [--project <path>] [--mode <debug|profile|release>] [--build-dir <dir>] [--api-base-url <url>] [--clean] [--install-deps] [--offline] [--branding <default|custom>] [--branding-dir <path>] [--app-name-fa <name>] [--app-name-en <name>] [--help]
|
|
|
|
Options:
|
|
--project PATH Flutter project path (contains pubspec.yaml). If not specified, will be auto-detected.
|
|
--mode MODE Build type: debug, profile, or release (default: $DEFAULT_MODE).
|
|
--build-dir DIR Build directory path (default: $DEFAULT_BUILD_DIR).
|
|
--api-base-url API base URL (default: $DEFAULT_API_BASE_URL).
|
|
--clean Clean build directory before building.
|
|
--install-deps Install dependencies before building.
|
|
--offline Use offline cache for pub dependencies (no network access).
|
|
--branding MODE UI branding: default (Hesabix) or custom (default: from BRANDING_MODE env / .deploy_env).
|
|
--branding-dir DIR Branding pack directory (default: BRANDING_DIR or /opt/hesabix/branding).
|
|
--app-name-fa NAME Persian display name for custom branding.
|
|
--app-name-en NAME English display name for custom branding.
|
|
-h, --help Show help.
|
|
|
|
Usage examples:
|
|
./build_web.sh
|
|
./build_web.sh --mode debug --clean
|
|
./build_web.sh --project hesabixUI/hesabix_ui
|
|
./build_web.sh --api-base-url https://hsxn.hesabix.ir
|
|
./build_web.sh --offline
|
|
./build_web.sh --branding custom --branding-dir /opt/hesabix/branding
|
|
EOF
|
|
}
|
|
|
|
warn() { echo "[warn] $*" >&2; }
|
|
die() { echo "[error] $*" >&2; exit 1; }
|
|
|
|
cmd_exists() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
ensure_flutter_in_path() {
|
|
if cmd_exists flutter; then
|
|
return 0
|
|
fi
|
|
# Check common install locations (deploy.sh may install to /opt/flutter)
|
|
for candidate in "/opt/flutter/bin" "/snap/bin" "$HOME/snap/flutter/current/flutter/bin" "$HOME/snap/flutter/common/flutter/bin"; do
|
|
if [ -x "${candidate}/flutter" ]; then
|
|
export PATH="${candidate}:$PATH"
|
|
return 0
|
|
fi
|
|
done
|
|
if ! cmd_exists flutter; then
|
|
die "Flutter not found. Please install it or configure PATH. Suggested: sudo snap install flutter --classic, or deploy.sh will install to /opt/flutter."
|
|
fi
|
|
}
|
|
|
|
is_flutter_project_dir() {
|
|
local dir="$1"
|
|
[ -f "$dir/pubspec.yaml" ] || return 1
|
|
# حداقل بررسی: وجود sdk: flutter در pubspec.yaml
|
|
if grep -qiE "sdk:\s*flutter" "$dir/pubspec.yaml"; then
|
|
return 0
|
|
fi
|
|
# برخی قالبها ممکن است شکل دیگری داشته باشند؛ صرف وجود pubspec را کافی بدانیم
|
|
return 0
|
|
}
|
|
|
|
auto_detect_project_dir() {
|
|
# Priority: user argument → environment variable → common path → search in hesabixUI
|
|
if [ -n "$USER_PROJECT" ]; then
|
|
local p="$USER_PROJECT"
|
|
[ -d "$p" ] || die "Project path does not exist: $p"
|
|
is_flutter_project_dir "$p" || die "Valid pubspec.yaml not found in path: $p"
|
|
echo "$(cd "$p" && pwd)"
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "${FLUTTER_APP_DIR:-}" ]; then
|
|
local p="$FLUTTER_APP_DIR"
|
|
if [ -d "$p" ] && is_flutter_project_dir "$p"; then
|
|
echo "$(cd "$p" && pwd)"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Common path in this repo
|
|
local common_path="$REPO_ROOT/hesabixUI/hesabix_ui"
|
|
if [ -d "$common_path" ] && is_flutter_project_dir "$common_path"; then
|
|
echo "$common_path"
|
|
return 0
|
|
fi
|
|
|
|
# Search in hesabixUI for nearest pubspec.yaml
|
|
local search_root="$REPO_ROOT/hesabixUI"
|
|
if [ -d "$search_root" ]; then
|
|
# Limited to depth 3 for speed
|
|
local found
|
|
found=$(find "$search_root" -maxdepth 3 -type f -name pubspec.yaml 2>/dev/null | head -n 1 || true)
|
|
if [ -n "$found" ]; then
|
|
echo "$(cd "$(dirname "$found")" && pwd)"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
die "Flutter project not found. Please specify path with --project."
|
|
}
|
|
|
|
# Parse args
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--project)
|
|
[[ $# -ge 2 ]] || die "Value for --project not provided"
|
|
USER_PROJECT="$2"; shift 2 ;;
|
|
--mode)
|
|
[[ $# -ge 2 ]] || die "Value for --mode not provided"
|
|
MODE="$2"; shift 2 ;;
|
|
--build-dir)
|
|
[[ $# -ge 2 ]] || die "Value for --build-dir not provided"
|
|
BUILD_DIR="$2"; shift 2 ;;
|
|
--api-base-url)
|
|
[[ $# -ge 2 ]] || die "Value for --api-base-url not provided"
|
|
API_BASE_URL="$2"; shift 2 ;;
|
|
--clean)
|
|
CLEAN_BUILD=true; shift ;;
|
|
--install-deps)
|
|
INSTALL_DEPS=true; shift ;;
|
|
--offline)
|
|
USE_OFFLINE_CACHE=true; shift ;;
|
|
--branding)
|
|
[[ $# -ge 2 ]] || die "Value for --branding not provided"
|
|
BRANDING_MODE_CLI="$2"; shift 2 ;;
|
|
--branding-dir)
|
|
[[ $# -ge 2 ]] || die "Value for --branding-dir not provided"
|
|
BRANDING_DIR_CLI="$2"; shift 2 ;;
|
|
--app-name-fa)
|
|
[[ $# -ge 2 ]] || die "Value for --app-name-fa not provided"
|
|
APP_NAME_FA_CLI="$2"; shift 2 ;;
|
|
--app-name-en)
|
|
[[ $# -ge 2 ]] || die "Value for --app-name-en not provided"
|
|
APP_NAME_EN_CLI="$2"; shift 2 ;;
|
|
-h|--help)
|
|
print_usage; exit 0 ;;
|
|
*)
|
|
warn "Unknown argument: $1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
case "$MODE" in
|
|
debug|profile|release) ;;
|
|
*) die "Invalid mode: $MODE (allowed: debug|profile|release)" ;;
|
|
esac
|
|
|
|
ensure_flutter_in_path
|
|
|
|
APP_DIR="$(auto_detect_project_dir)"
|
|
|
|
if [ -z "$BUILD_DIR" ]; then
|
|
BUILD_DIR="$DEFAULT_BUILD_DIR"
|
|
fi
|
|
|
|
# Convert to absolute path
|
|
BUILD_DIR="$(cd "$APP_DIR" && realpath -m "$BUILD_DIR")"
|
|
|
|
echo "Repo root: $REPO_ROOT"
|
|
echo "Project path: $APP_DIR"
|
|
echo "Mode: $MODE"
|
|
echo "Build path: $BUILD_DIR"
|
|
echo "API URL: $API_BASE_URL"
|
|
|
|
# Load saved deploy branding when present (hesabix -update / -domains rebuild).
|
|
if [[ -r "${APP_ROOT:-/opt/hesabix}/.deploy_env" ]]; then
|
|
# shellcheck disable=SC1090
|
|
set -a
|
|
# shellcheck source=/dev/null
|
|
source "${APP_ROOT:-/opt/hesabix}/.deploy_env"
|
|
set +a
|
|
fi
|
|
# CLI overrides win over .deploy_env
|
|
[[ -n "$BRANDING_MODE_CLI" ]] && export BRANDING_MODE_OVERRIDE="$BRANDING_MODE_CLI"
|
|
[[ -n "$BRANDING_DIR_CLI" ]] && export BRANDING_DIR_OVERRIDE="$BRANDING_DIR_CLI"
|
|
[[ -n "$APP_NAME_FA_CLI" ]] && export APP_NAME_FA_OVERRIDE="$APP_NAME_FA_CLI"
|
|
[[ -n "$APP_NAME_EN_CLI" ]] && export APP_NAME_EN_OVERRIDE="$APP_NAME_EN_CLI"
|
|
|
|
# shellcheck source=scripts/lib/hesabix_branding.sh
|
|
BRANDING_LIB="${REPO_ROOT}/scripts/lib/hesabix_branding.sh"
|
|
if [[ -r "$BRANDING_LIB" ]]; then
|
|
# shellcheck disable=SC1091
|
|
source "$BRANDING_LIB"
|
|
else
|
|
warn "Branding library missing: $BRANDING_LIB (continuing without custom branding)"
|
|
hesabix_branding_apply() { :; }
|
|
hesabix_branding_restore() { :; }
|
|
hesabix_branding_append_dart_defines() { :; }
|
|
hesabix_branding_stamp_build_output() { :; }
|
|
fi
|
|
|
|
cd "$APP_DIR"
|
|
|
|
# آینهٔ pub/storage: از محیط یا .deploy_env (deploy.sh / mirror_config.sh)
|
|
export PUB_HOSTED_URL="${PUB_HOSTED_URL:-https://f.mirror.hesabix.ir/pub}"
|
|
export FLUTTER_STORAGE_BASE_URL="${FLUTTER_STORAGE_BASE_URL:-https://f.mirror.hesabix.ir/gcs}"
|
|
# shellcheck source=scripts/mirror_config.sh
|
|
if [[ -r "${REPO_ROOT}/scripts/mirror_config.sh" ]]; then
|
|
# shellcheck disable=SC1091
|
|
source "${REPO_ROOT}/scripts/mirror_config.sh"
|
|
# Prefer a reachable pub host (pub.dev 403 → "authorization failed") and storage base.
|
|
hesabix_resolve_flutter_pub_hosted_url || true
|
|
hesabix_resolve_flutter_storage_base_url || true
|
|
fi
|
|
# اگر --offline داده نشده و آینهٔ انتخابشده در دسترس نبود، مثل حالت آفلاین رفتار کن
|
|
if [ "$USE_OFFLINE_CACHE" != true ]; then
|
|
if ! curl -fsS --connect-timeout 4 --max-time 8 "${PUB_HOSTED_URL%/}/api/packages/intl" >/dev/null 2>&1 && \
|
|
! curl -kfsS --connect-timeout 4 --max-time 8 "${PUB_HOSTED_URL%/}/api/packages/intl" >/dev/null 2>&1; then
|
|
USE_OFFLINE_CACHE=true
|
|
warn "Pub mirror (${PUB_HOSTED_URL}) is unreachable — pub get may fail without a local cache."
|
|
fi
|
|
fi
|
|
|
|
echo "Using Pub Hosted URL: $PUB_HOSTED_URL"
|
|
echo "Using Flutter Storage URL: $FLUTTER_STORAGE_BASE_URL"
|
|
if [ "$USE_OFFLINE_CACHE" = true ]; then
|
|
echo "Dependency strategy: offline cache (flutter pub get --offline)"
|
|
fi
|
|
|
|
# Install dependencies if requested
|
|
if [ "$INSTALL_DEPS" = true ]; then
|
|
echo "Installing dependencies..."
|
|
if ! flutter pub get; then
|
|
if [ "$USE_OFFLINE_CACHE" = true ]; then
|
|
warn "Online dependency install failed/no mirror. Trying offline cache..."
|
|
if ! flutter pub get --offline; then
|
|
die "Dependency install failed in offline mode too. Populate cache first on a machine with internet (copy PUB_CACHE/.pub-cache), then retry."
|
|
fi
|
|
else
|
|
die "Flutter/Dart step failed. If the process was 'Killed', the server likely ran out of memory (OOM). Add swap or use a machine with more RAM, then re-run."
|
|
fi
|
|
fi
|
|
elif [ ! -d "$APP_DIR/.dart_tool" ] || [ ! -f "$APP_DIR/pubspec.lock" ]; then
|
|
echo "Dependencies not installed. Installing..."
|
|
if ! flutter pub get; then
|
|
if [ "$USE_OFFLINE_CACHE" = true ]; then
|
|
warn "Online dependency install failed/no mirror. Trying offline cache..."
|
|
if ! flutter pub get --offline; then
|
|
warn "Offline cache install failed too. Build may fail unless dependency cache is preloaded."
|
|
warn " cd $APP_DIR && flutter pub get --offline"
|
|
fi
|
|
else
|
|
warn "Flutter/Dart step failed (if 'Killed', increase RAM or add swap). Trying to continue..."
|
|
warn " cd $APP_DIR && flutter pub get"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Clean build directory if requested
|
|
if [ "$CLEAN_BUILD" = true ]; then
|
|
echo "Cleaning build directory..."
|
|
rm -rf "$BUILD_DIR"
|
|
fi
|
|
|
|
# Configure dart-define arguments for API URL (+ optional branding names)
|
|
DART_DEFINE_ARGS=(--dart-define "API_BASE_URL=$API_BASE_URL")
|
|
hesabix_branding_append_dart_defines DART_DEFINE_ARGS
|
|
|
|
# Apply UI branding overlay (restored on EXIT so git tree stays clean)
|
|
trap 'hesabix_branding_restore "$APP_DIR" || true' EXIT
|
|
hesabix_branding_apply "$APP_DIR"
|
|
|
|
# Determine PWA strategy and optimizations based on mode
|
|
BUILD_FLAGS=()
|
|
|
|
# Memory check first (used for optimization level and workers)
|
|
TOTAL_RAM_MB=$(free -m | awk '/^Mem:/ {print $2}')
|
|
AVAILABLE_RAM_MB=$(free -m | awk '/^Mem:/ {print $7}')
|
|
LOW_RAM=0
|
|
[ "$TOTAL_RAM_MB" -lt 2500 ] && LOW_RAM=1
|
|
|
|
# For release mode, use PWA strategy and full optimizations
|
|
if [ "$MODE" = "release" ]; then
|
|
# Flutter 3.42+ deprecated PWA; offline-first now emits a SW that unregisters and reloads
|
|
# the page on activate, which breaks CanvasKit startup. Use no service worker.
|
|
BUILD_FLAGS+=(--pwa-strategy none)
|
|
BUILD_FLAGS+=(--base-href /)
|
|
# On low-RAM use -O1 to avoid dart2js OOM (exit code -9). Otherwise -O2.
|
|
if [ "$LOW_RAM" -eq 1 ]; then
|
|
BUILD_FLAGS+=(--optimization-level 1)
|
|
echo "Building Flutter for Web (Production) with low-memory settings..."
|
|
echo " - PWA Strategy: none (no Flutter service worker)"
|
|
echo " - Base Href: /"
|
|
echo " - Optimization Level: 1 (reduces memory use on <2.5GB RAM)"
|
|
else
|
|
BUILD_FLAGS+=(--optimization-level 2)
|
|
echo "Building Flutter for Web (Production) with full optimizations..."
|
|
echo " - PWA Strategy: none (no Flutter service worker)"
|
|
echo " - Base Href: /"
|
|
echo " - Optimization Level: 2 (balanced optimization)"
|
|
fi
|
|
else
|
|
BUILD_FLAGS+=(--base-href /)
|
|
echo "Building Flutter for Web ($MODE) with basic optimizations..."
|
|
echo " - Base Href: /"
|
|
fi
|
|
|
|
echo "Full command: flutter build web --$MODE ${BUILD_FLAGS[*]} ${DART_DEFINE_ARGS[*]}"
|
|
echo ""
|
|
|
|
# Configure CPU workers: on low-RAM use 1 worker only to avoid OOM
|
|
AVAILABLE_CORES=$(nproc)
|
|
if [ "$LOW_RAM" -eq 1 ]; then
|
|
BUILD_WORKERS=1
|
|
echo "CPU: 1 worker (low-memory mode to avoid OOM)"
|
|
else
|
|
BUILD_WORKERS=$((AVAILABLE_CORES * 80 / 100))
|
|
[ "$BUILD_WORKERS" -lt 1 ] && BUILD_WORKERS=1
|
|
[ "$BUILD_WORKERS" -gt 16 ] && BUILD_WORKERS=16
|
|
echo "CPU Optimization: cores=$AVAILABLE_CORES, workers=$BUILD_WORKERS"
|
|
fi
|
|
echo ""
|
|
|
|
# Heap for dart2js: on low-RAM cap at 512MB so process is not OOM-killed
|
|
if [ "$LOW_RAM" -eq 1 ]; then
|
|
HEAP_SIZE_MB=$((AVAILABLE_RAM_MB * 45 / 100))
|
|
[ "$HEAP_SIZE_MB" -lt 256 ] && HEAP_SIZE_MB=256
|
|
[ "$HEAP_SIZE_MB" -gt 512 ] && HEAP_SIZE_MB=512
|
|
else
|
|
HEAP_SIZE_MB=$((AVAILABLE_RAM_MB * 65 / 100))
|
|
[ "$HEAP_SIZE_MB" -lt 1024 ] && HEAP_SIZE_MB=1024
|
|
[ "$HEAP_SIZE_MB" -gt 16384 ] && HEAP_SIZE_MB=16384
|
|
fi
|
|
|
|
echo "Memory: Total=${TOTAL_RAM_MB}MB Available=${AVAILABLE_RAM_MB}MB Dart heap=${HEAP_SIZE_MB}MB"
|
|
echo ""
|
|
|
|
export DART_VM_OPTIONS="--old-gen-heap-size=$HEAP_SIZE_MB"
|
|
export DART_COMPILE_JS_WORKERS="$BUILD_WORKERS"
|
|
|
|
# Check available memory details
|
|
echo "System Memory Status:"
|
|
free -h | head -2
|
|
echo ""
|
|
|
|
# آینهٔ fallback موتور وب (fontFallbackBaseUrl) — بدون fonts.gstatic.com در runtime
|
|
# خانوادههای تاریخی/نادر بهصورت پیشفرض حذف میشوند؛ SYNC_FONT_INCLUDE_RARE=1 برای کامل
|
|
SYNC_FONT_MIRROR="$APP_DIR/scripts/sync_font_fallback_mirror.sh"
|
|
if [ -f "$SYNC_FONT_MIRROR" ]; then
|
|
chmod +x "$SYNC_FONT_MIRROR" 2>/dev/null || true
|
|
echo "Syncing local font fallback mirror into web/ ..."
|
|
SYNC_FONT_STRICT="${SYNC_FONT_STRICT:-0}" "$SYNC_FONT_MIRROR" "$APP_DIR/web" || warn "Font mirror incomplete — run: bash scripts/extract_flutter_gstatic_font_paths.sh && bash scripts/populate_gstatic_font_bundle.sh (see assets/gstatic_font_bundle/README.txt)"
|
|
else
|
|
warn "sync_font_fallback_mirror.sh not found; skipping local gstatic font mirror for web engine"
|
|
fi
|
|
|
|
# Run build with memory settings and parallel compilation
|
|
# Flutter always outputs to build/web inside the project; copy to BUILD_DIR if different
|
|
FLUTTER_OUTPUT="$APP_DIR/build/web"
|
|
if ! flutter build web --"$MODE" "${BUILD_FLAGS[@]}" "${DART_DEFINE_ARGS[@]}"; then
|
|
die "flutter build web failed. If you saw 'exit code -9' or 'Killed', the server ran out of memory (OOM). Add more swap or use a machine with more RAM, then re-run."
|
|
fi
|
|
# Copy to custom build-dir when user specified a path other than default
|
|
if [ "$BUILD_DIR" != "$FLUTTER_OUTPUT" ]; then
|
|
echo "Copying build output to $BUILD_DIR ..."
|
|
mkdir -p "$BUILD_DIR"
|
|
cp -r "$FLUTTER_OUTPUT"/* "$BUILD_DIR/" || die "Failed to copy build output to $BUILD_DIR"
|
|
fi
|
|
if [ ! -f "$BUILD_DIR/index.html" ]; then
|
|
die "flutter build web did not produce index.html. Flutter SDK may be broken (e.g. Dart SDK download failed). Try: rm -rf /opt/flutter && re-run deploy with mirror set."
|
|
fi
|
|
|
|
hesabix_branding_stamp_build_output "$BUILD_DIR"
|
|
|
|
# همان آینهٔ gstatic را روی خروجی نهایی هم بنویس (با هرس خانوادههای نادر مگر SYNC_FONT_INCLUDE_RARE=1)
|
|
if [ -f "$SYNC_FONT_MIRROR" ]; then
|
|
echo "Ensuring local font fallback mirror in $BUILD_DIR ..."
|
|
"$SYNC_FONT_MIRROR" "$BUILD_DIR" || warn "Font fallback mirror sync to build output failed; see assets/gstatic_font_bundle/README.txt"
|
|
fi
|
|
|
|
# Fix flutter_bootstrap.js to use local CanvasKit instead of CDN
|
|
echo ""
|
|
echo "Fixing flutter_bootstrap.js to use local CanvasKit..."
|
|
FIX_SCRIPT="$APP_DIR/scripts/fix_canvaskit_local.sh"
|
|
if [ -f "$FIX_SCRIPT" ]; then
|
|
if [ -x "$FIX_SCRIPT" ]; then
|
|
"$FIX_SCRIPT" "$BUILD_DIR" || warn "Error running CanvasKit fix script (may not be a problem)"
|
|
else
|
|
warn "fix_canvaskit_local.sh script is not executable. Setting permissions..."
|
|
chmod +x "$FIX_SCRIPT" && "$FIX_SCRIPT" "$BUILD_DIR" || warn "Error running CanvasKit fix script"
|
|
fi
|
|
else
|
|
warn "fix_canvaskit_local.sh script not found. Creating..."
|
|
mkdir -p "$APP_DIR/scripts"
|
|
cat > "$FIX_SCRIPT" << 'EOF'
|
|
#!/usr/bin/env bash
|
|
BUILD_DIR="${1:-build/web}"
|
|
if [ -f "$BUILD_DIR/flutter_bootstrap.js" ]; then
|
|
sed -i 's/_flutter\.loader\.load();/_flutter.loader.load({config: {canvasKitBaseUrl: "canvaskit\/", renderer: "canvaskit", useLocalCanvasKit: true}});/g' "$BUILD_DIR/flutter_bootstrap.js"
|
|
echo "✓ flutter_bootstrap.js fixed"
|
|
fi
|
|
EOF
|
|
chmod +x "$FIX_SCRIPT"
|
|
"$FIX_SCRIPT" "$BUILD_DIR"
|
|
fi
|
|
|
|
# فایل سبک برای تشخیص دیپلوی جدید در مرورگر (با web/index.html هماهنگ است)
|
|
echo ""
|
|
echo "Writing version.json for client-side update checks..."
|
|
GEN_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
RND_HEX="$(openssl rand -hex 4 2>/dev/null || printf '%04x%04x' "$RANDOM" "$RANDOM")"
|
|
GITREV="$(git -C "$APP_DIR" rev-parse --short HEAD 2>/dev/null || true)"
|
|
if [ -n "$GITREV" ]; then
|
|
BUILD_ID="${GEN_AT}-${RND_HEX}-${GITREV}"
|
|
else
|
|
BUILD_ID="${GEN_AT}-${RND_HEX}"
|
|
fi
|
|
printf '{"build":"%s","generated_at":"%s"}\n' "$BUILD_ID" "$GEN_AT" > "$BUILD_DIR/version.json"
|
|
echo "✓ version.json → build=\"$BUILD_ID\""
|
|
|
|
# Check icon files
|
|
echo ""
|
|
echo "Checking icon files..."
|
|
ICON_DIR="$BUILD_DIR/icons"
|
|
REQUIRED_ICONS=("Icon-192.png" "Icon-512.png" "Icon-maskable-192.png" "Icon-maskable-512.png")
|
|
MISSING_ICONS=()
|
|
|
|
if [ ! -d "$ICON_DIR" ]; then
|
|
warn "Icons folder not found in build directory: $ICON_DIR"
|
|
warn "Creating icons folder and copying files from web/icons..."
|
|
mkdir -p "$ICON_DIR"
|
|
if [ -d "$APP_DIR/web/icons" ]; then
|
|
cp -r "$APP_DIR/web/icons"/* "$ICON_DIR/" 2>/dev/null || true
|
|
else
|
|
warn "web/icons folder not found in project!"
|
|
fi
|
|
fi
|
|
|
|
for icon in "${REQUIRED_ICONS[@]}"; do
|
|
if [ ! -f "$ICON_DIR/$icon" ]; then
|
|
MISSING_ICONS+=("$icon")
|
|
fi
|
|
done
|
|
|
|
if [ ${#MISSING_ICONS[@]} -gt 0 ]; then
|
|
warn "Following icon files not found:"
|
|
for icon in "${MISSING_ICONS[@]}"; do
|
|
warn " - $icon"
|
|
done
|
|
warn "Copying icon files from web/icons..."
|
|
if [ -d "$APP_DIR/web/icons" ]; then
|
|
mkdir -p "$ICON_DIR"
|
|
cp -r "$APP_DIR/web/icons"/* "$ICON_DIR/" 2>/dev/null || true
|
|
echo "Icon files copied."
|
|
else
|
|
warn "web/icons folder not found in project! Please add icon files manually."
|
|
fi
|
|
else
|
|
echo "✓ All icon files are present."
|
|
fi
|
|
|
|
# Check manifest.json existence
|
|
if [ ! -f "$BUILD_DIR/manifest.json" ]; then
|
|
warn "manifest.json file not found! Copying from web/manifest.json..."
|
|
if [ -f "$APP_DIR/web/manifest.json" ]; then
|
|
cp "$APP_DIR/web/manifest.json" "$BUILD_DIR/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Copy built files to Apache deployment path
|
|
# DISABLED: Deployment to /var/www/arc.hesabix.ir is disabled
|
|
# DEPLOY_DIR="/var/www/arc.hesabix.ir"
|
|
# if [ -d "$BUILD_DIR" ] && [ -n "$(ls -A "$BUILD_DIR" 2>/dev/null)" ]; then
|
|
# echo ""
|
|
# echo "Copying built files to deployment path..."
|
|
# if [ -d "$DEPLOY_DIR" ] || mkdir -p "$DEPLOY_DIR" 2>/dev/null; then
|
|
# # Use optimized rsync with light compression and progress
|
|
# # --compress-level=1 for fast compression with minimal CPU overhead
|
|
# rsync -azP --compress-level=1 --delete "$BUILD_DIR/" "$DEPLOY_DIR/" 2>/dev/null || {
|
|
# warn "Error copying files to $DEPLOY_DIR"
|
|
# warn "Please copy files manually:"
|
|
# warn " rsync -azP --compress-level=1 --delete $BUILD_DIR/ $DEPLOY_DIR/"
|
|
# }
|
|
# chown -R www-data:www-data "$DEPLOY_DIR" 2>/dev/null || true
|
|
# echo "✓ Files copied to $DEPLOY_DIR"
|
|
# else
|
|
# warn "Cannot create folder $DEPLOY_DIR. Please check permissions."
|
|
# fi
|
|
# else
|
|
# warn "Build path is empty or doesn't exist: $BUILD_DIR"
|
|
# fi
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "✓ Build completed!"
|
|
echo "=========================================="
|
|
echo "Built files are located at:"
|
|
echo " $BUILD_DIR"
|
|
echo ""
|
|
if [ "$MODE" = "release" ]; then
|
|
echo "✓ Applied optimizations:"
|
|
echo " - Mode: Production (Release)"
|
|
echo " - Service Worker: disabled (pwa-strategy none)"
|
|
echo " - Optimization Level: 2 (balanced optimization)"
|
|
echo " - Parallel Workers: $BUILD_WORKERS (80% of $AVAILABLE_CORES cores)"
|
|
echo " - Heap Size: ${HEAP_SIZE_MB}MB (80% of ${TOTAL_RAM_MB}MB RAM)"
|
|
echo " - Base Href: /"
|
|
echo " - API Base URL: $API_BASE_URL"
|
|
echo " - Branding: ${BRANDING_MODE:-default}"
|
|
echo ""
|
|
echo "Note: Flutter service worker is disabled to avoid CanvasKit startup races."
|
|
else
|
|
echo "✓ Applied optimizations:"
|
|
echo " - Mode: $MODE"
|
|
echo " - Renderer: CanvasKit"
|
|
echo " - Parallel Workers: $BUILD_WORKERS (80% of $AVAILABLE_CORES cores)"
|
|
echo " - Heap Size: ${HEAP_SIZE_MB}MB (80% of ${TOTAL_RAM_MB}MB RAM)"
|
|
echo " - Base Href: /"
|
|
echo " - API Base URL: $API_BASE_URL"
|
|
echo " - Branding: ${BRANDING_MODE:-default}"
|
|
fi
|
|
echo ""
|
|
echo "To serve, you can use a web server:"
|
|
echo " cd $BUILD_DIR && python3 -m http.server 8080"
|
|
echo "or use nginx/apache for serving."
|
|
echo ""
|
|
|
|
# اگر روی همان میزبان nginx و hesabix-ui.conf وجود داشته باشد، بلاکهای version.json / SW اعمال میشود.
|
|
if [[ "${SKIP_NGINX_ENSURE:-}" != "1" ]]; then
|
|
ENSURE_NGINX_SCRIPT="$REPO_ROOT/scripts/ensure_nginx_ui_version_probe.sh"
|
|
if [[ -f "$ENSURE_NGINX_SCRIPT" ]]; then
|
|
chmod +x "$ENSURE_NGINX_SCRIPT" 2>/dev/null || true
|
|
if bash "$ENSURE_NGINX_SCRIPT"; then
|
|
:
|
|
else
|
|
warn "Nginx check/update skipped (expected on hosts without nginx or sudo). To skip: SKIP_NGINX_ENSURE=1"
|
|
fi
|
|
else
|
|
warn "Script not found: $ENSURE_NGINX_SCRIPT"
|
|
fi
|
|
fi
|
|
|
|
# پس از build/reload nginx، یونیتهای systemd را تازه کن (مثل نیاز بعد از تغییر فایل سرویس)
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
SUDO_DR=()
|
|
if [[ "$(id -u)" -ne 0 ]] && command -v sudo >/dev/null 2>&1; then
|
|
SUDO_DR=(sudo)
|
|
fi
|
|
if "${SUDO_DR[@]}" systemctl daemon-reload 2>/dev/null; then
|
|
echo "systemd daemon-reload completed."
|
|
else
|
|
warn "daemon-reload failed; if services fail to start: sudo systemctl daemon-reload"
|
|
fi
|
|
fi
|
|
|
|
|