#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" INSTALL_ROOT="${ERP_INSTALL_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd -P)}" CONFIG_FILE="${ERP_CONFIG_FILE:-$INSTALL_ROOT/config/erp.env}" say() { printf '[ERP] %s\n' "$*"; } fail() { printf '[ERP] ERROR: %s\n' "$*" >&2; exit 1; } load_configuration() { [[ -r "$CONFIG_FILE" ]] || fail "configuration not readable: $CONFIG_FILE" set -a # The installer writes this file with POSIX shell-safe single-quoted values. # shellcheck disable=SC1090 source "$CONFIG_FILE" set +a RUN_DIR="${ERP_RUN_DIR:-$INSTALL_ROOT/run}" JAR_PATH="${ERP_JAR_PATH:-$INSTALL_ROOT/current/app/kaidi-erp.jar}" INSTALLER_JAR="${ERP_INSTALLER_JAR:-$INSTALL_ROOT/installer/kaidi-erp-installer.jar}" PENDING_FILE="${ERP_INSTALL_PENDING_FILE:-$INSTALL_ROOT/state/install.pending}" INSTALL_LOCK_FILE="${ERP_INSTALL_LOCK_FILE:-$INSTALL_ROOT/state/install.lock}" INSTALL_LOG_FILE="${ERP_INSTALL_LOG_FILE:-$(dirname "$PENDING_FILE")/install-formal.log}" HEALTH_URL="${ERP_HEALTH_URL:-http://127.0.0.1:${SERVER_PORT:-8091}/api/oa/health}" HEALTH_TIMEOUT_SECONDS="${ERP_INSTALL_HEALTH_TIMEOUT_SECONDS:-240}" HEALTH_POLL_SECONDS="${ERP_UPDATE_HEALTH_POLL_SECONDS:-2}" } resolve_java() { JAVA_BIN="${ERP_JAVA_BIN:-}" if [[ -z "$JAVA_BIN" ]]; then if [[ -n "${JAVA_HOME:-}" && -x "$JAVA_HOME/bin/java" ]]; then JAVA_BIN="$JAVA_HOME/bin/java" else JAVA_BIN="$(command -v java || true)" fi fi [[ -n "$JAVA_BIN" && -x "$JAVA_BIN" ]] || fail 'Java 17+ is not installed' local java_major java_major="$("$JAVA_BIN" -version 2>&1 | awk -F'[".]' '/version/ {print $2; exit}')" [[ "$java_major" =~ ^[0-9]+$ && "$java_major" -ge 17 ]] || fail 'Java 17+ is required' } split_java_options() { IFS=$' \t' read -r -a JAVA_OPTS <<< "${ERP_JAVA_OPTS:--Xms512m -Xmx2g}" IFS=$' \t' read -r -a INSTALLER_JAVA_OPTS <<< "${ERP_INSTALLER_JAVA_OPTS:--Xms128m -Xmx512m}" } write_pid() { mkdir -p "$RUN_DIR" printf '%s\n' "$1" > "$RUN_DIR/app.pid" } clear_pid() { local expected="$1" if [[ -r "$RUN_DIR/app.pid" && "$(<"$RUN_DIR/app.pid")" == "$expected" ]]; then rm -f "$RUN_DIR/app.pid" fi } run_formal_application_direct() { [[ -r "$JAR_PATH" ]] || fail "application jar not readable: $JAR_PATH" write_pid "$$" exec "$JAVA_BIN" "${JAVA_OPTS[@]}" \ -jar "$JAR_PATH" \ --spring.profiles.active=postgres \ --server.port="${SERVER_PORT:-8091}" } run_web_installer() { [[ -r "$INSTALLER_JAR" ]] || fail "installer jar not readable: $INSTALLER_JAR" say "Starting first-run installer on port ${SERVER_PORT:-8091}" printf '%s\n' "$$" > "$RUN_DIR/installer.pid" set +e "$JAVA_BIN" "${INSTALLER_JAVA_OPTS[@]}" \ -jar "$INSTALLER_JAR" \ --server.port="${SERVER_PORT:-8091}" local status=$? set -e rm -f "$RUN_DIR/installer.pid" [[ "$status" == "0" ]] || fail "web installer exited with status $status" [[ -f "$PENDING_FILE" ]] || fail 'web installer exited before installation was completed' } finalize_installation() { [[ -f "$PENDING_FILE" ]] || fail "pending installation marker is missing: $PENDING_FILE" [[ ! -e "$INSTALL_LOCK_FILE" ]] || fail "installation lock already exists: $INSTALL_LOCK_FILE" mkdir -p "$(dirname "$INSTALL_LOCK_FILE")" mv "$PENDING_FILE" "$INSTALL_LOCK_FILE" chmod 600 "$INSTALL_LOCK_FILE" rm -rf "$INSTALL_ROOT/installer" say "Installation lock written; the web installer has been removed" } run_pending_formal_application() { [[ -r "$JAR_PATH" ]] || fail "application jar not readable: $JAR_PATH" [[ "$HEALTH_TIMEOUT_SECONDS" =~ ^[1-9][0-9]*$ ]] || fail 'invalid installation health timeout' [[ "$HEALTH_POLL_SECONDS" =~ ^[1-9][0-9]*$ ]] || fail 'invalid health poll interval' say 'Starting the formal PostgreSQL application' mkdir -p "$(dirname "$INSTALL_LOG_FILE")" : > "$INSTALL_LOG_FILE" chmod 600 "$INSTALL_LOG_FILE" "$JAVA_BIN" "${JAVA_OPTS[@]}" \ -jar "$JAR_PATH" \ --spring.profiles.active=postgres \ --server.port="${SERVER_PORT:-8091}" \ > >(tee -a "$INSTALL_LOG_FILE") 2>&1 & APP_PID=$! write_pid "$APP_PID" forward_signal() { kill -TERM "$APP_PID" 2>/dev/null || true } trap forward_signal TERM INT local deadline=$((SECONDS + HEALTH_TIMEOUT_SECONDS)) while (( SECONDS < deadline )); do if ! kill -0 "$APP_PID" 2>/dev/null; then set +e wait "$APP_PID" local status=$? set -e clear_pid "$APP_PID" say "Formal application diagnostics: $INSTALL_LOG_FILE" fail "formal application exited before becoming healthy (status $status)" fi if curl -fsS --connect-timeout 2 --max-time 5 "$HEALTH_URL" >/dev/null 2>&1; then finalize_installation set +e wait "$APP_PID" local status=$? set -e clear_pid "$APP_PID" return "$status" fi sleep "$HEALTH_POLL_SECONDS" done kill -TERM "$APP_PID" 2>/dev/null || true set +e wait "$APP_PID" set -e clear_pid "$APP_PID" say "Formal application diagnostics: $INSTALL_LOG_FILE" fail "formal application did not become healthy within ${HEALTH_TIMEOUT_SECONDS} seconds" } main() { load_configuration resolve_java split_java_options mkdir -p "$RUN_DIR" if [[ -f "$INSTALL_LOCK_FILE" ]]; then run_formal_application_direct fi if [[ ! -f "$PENDING_FILE" ]]; then run_web_installer load_configuration resolve_java split_java_options fi run_pending_formal_application } main "$@"