2026-05-09 10:40:17 +08:00

345 lines
9.4 KiB
Bash

#!/usr/bin/env bash
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE_FILE="$ROOT_DIR/local-services.compose.yml"
ENV_FILE="${LIKEI_ENV_FILE:-$ROOT_DIR/.env.local}"
DOCKER_APP_BIN_DIR="/Applications/Docker.app/Contents/Resources/bin"
resolve_default_workspace_dir() {
local candidate
for candidate in "$ROOT_DIR/.." "$ROOT_DIR/../chatapp3" "$ROOT_DIR/../../chatapp3" "$ROOT_DIR/../.."; do
if [ -d "$candidate/chatapp3-java" ] && [ -d "$candidate/chatapp3-golang" ]; then
cd "$candidate" && pwd
return 0
fi
done
cd "$ROOT_DIR/.." && pwd
}
DEFAULT_WORKSPACE_DIR="$(resolve_default_workspace_dir)"
WORKSPACE_DIR="$DEFAULT_WORKSPACE_DIR"
JAVA_REPO_DIR="$WORKSPACE_DIR/chatapp3-java"
GOLANG_REPO_DIR="$WORKSPACE_DIR/chatapp3-golang"
configure_local_repo_paths() {
if [ -n "${LIKEI_WORKSPACE_DIR:-}" ]; then
WORKSPACE_DIR="$(cd "$LIKEI_WORKSPACE_DIR" && pwd)"
else
WORKSPACE_DIR="$DEFAULT_WORKSPACE_DIR"
fi
JAVA_REPO_DIR="${LIKEI_LOCAL_JAVA_REPO_DIR:-$WORKSPACE_DIR/chatapp3-java}"
GOLANG_REPO_DIR="${LIKEI_LOCAL_GOLANG_REPO_DIR:-$WORKSPACE_DIR/chatapp3-golang}"
export WORKSPACE_DIR
export JAVA_REPO_DIR
export GOLANG_REPO_DIR
export LIKEI_LOCAL_JAVA_REPO_DIR="$JAVA_REPO_DIR"
export LIKEI_LOCAL_GOLANG_REPO_DIR="$GOLANG_REPO_DIR"
}
configure_local_repo_paths
trim_spaces() {
local value="${1:-}"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}
strip_wrapping_quotes() {
local value="${1:-}"
if [ "${#value}" -ge 2 ]; then
case "$value" in
\"*\")
printf '%s' "${value:1:${#value}-2}"
return 0
;;
\'*\')
printf '%s' "${value:1:${#value}-2}"
return 0
;;
esac
fi
printf '%s' "$value"
}
is_truthy() {
case "${1:-}" in
1|true|TRUE|True|yes|YES|Yes|y|Y|on|ON|On)
return 0
;;
*)
return 1
;;
esac
}
local_rocketmq_disabled() {
is_truthy "${LIKEI_ROCKETMQ_LOCAL_DISABLE:-true}"
}
configure_local_runtime_defaults() {
if [ -z "${LIKEI_LOCAL_OTHER_JAVA_OPTS_EXTRA+x}" ]; then
if local_rocketmq_disabled; then
export LIKEI_LOCAL_OTHER_JAVA_OPTS_EXTRA="-Dspring.autoconfigure.exclude=com.red.circle.component.mq.config.RocketMQAutoConfiguration"
else
export LIKEI_LOCAL_OTHER_JAVA_OPTS_EXTRA=""
fi
fi
}
load_local_env() {
if [ -f "$ENV_FILE" ]; then
while IFS= read -r raw_line || [ -n "$raw_line" ]; do
local line key value
line="$(trim_spaces "$raw_line")"
[ -n "$line" ] || continue
case "$line" in
\#*)
continue
;;
export\ *)
line="${line#export }"
;;
esac
case "$line" in
*=*)
key="${line%%=*}"
value="${line#*=}"
key="$(trim_spaces "$key")"
value="$(trim_spaces "$value")"
value="$(strip_wrapping_quotes "$value")"
export "$key=$value"
;;
esac
done < "$ENV_FILE"
fi
export LIKEI_ENV_FILE="$ENV_FILE"
export LIKEI_ENV_MODE="local"
configure_local_repo_paths
configure_local_runtime_defaults
}
ensure_docker_cli() {
if command -v docker >/dev/null 2>&1; then
return 0
fi
if [ -d "$DOCKER_APP_BIN_DIR" ]; then
export PATH="$DOCKER_APP_BIN_DIR:$PATH"
fi
command -v docker >/dev/null 2>&1
}
wait_for_docker() {
ensure_docker_cli || {
echo "docker CLI not found" >&2
exit 1
}
if docker info >/dev/null 2>&1; then
return 0
fi
if [ -d "/Applications/Docker.app" ]; then
open -a Docker >/dev/null 2>&1 || true
fi
for _ in $(seq 1 120); do
if docker info >/dev/null 2>&1; then
return 0
fi
sleep 2
done
echo "docker daemon did not become ready in time" >&2
exit 1
}
compose() {
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" "$@"
}
wait_http() {
local url="$1"
local retries="${2:-120}"
for _ in $(seq 1 "$retries"); do
if curl -fsS "$url" >/dev/null 2>&1; then
return 0
fi
sleep 2
done
echo "service did not become healthy in time: $url" >&2
exit 1
}
wait_tcp_port() {
local host="$1"
local port="$2"
local retries="${3:-120}"
for _ in $(seq 1 "$retries"); do
if nc -z "$host" "$port" >/dev/null 2>&1; then
return 0
fi
sleep 2
done
echo "tcp port did not become ready in time: $host:$port" >&2
exit 1
}
wait_container_healthy() {
local container_name="$1"
local retries="${2:-120}"
for _ in $(seq 1 "$retries"); do
local state
state="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_name" 2>/dev/null || true)"
if [ "$state" = "healthy" ] || [ "$state" = "running" ]; then
return 0
fi
sleep 2
done
echo "container did not become healthy in time: $container_name" >&2
exit 1
}
wait_rocketmq_cluster() {
local retries="${1:-120}"
for _ in $(seq 1 "$retries"); do
if docker exec chatapp3-local-rocketmq-broker sh mqadmin clusterList -n rocketmq-namesrv:9876 2>/dev/null | grep -q "DefaultCluster"; then
return 0
fi
sleep 2
done
echo "rocketmq cluster did not become ready in time: DefaultCluster" >&2
exit 1
}
wait_local_rocketmq() {
if local_rocketmq_disabled; then
return 0
fi
require_env LIKEI_LOCAL_ROCKETMQ_NAMESRV_PORT
require_env LIKEI_LOCAL_ROCKETMQ_PROXY_GRPC_PORT
wait_container_healthy chatapp3-local-rocketmq-namesrv 120
wait_container_healthy chatapp3-local-rocketmq-broker 120
wait_container_healthy chatapp3-local-rocketmq-proxy 120
wait_tcp_port 127.0.0.1 "${LIKEI_LOCAL_ROCKETMQ_NAMESRV_PORT}" 120
wait_rocketmq_cluster 120
wait_tcp_port 127.0.0.1 "${LIKEI_LOCAL_ROCKETMQ_PROXY_GRPC_PORT}" 120
}
require_env() {
local key="$1"
if [ -z "${!key:-}" ]; then
echo "required environment variable is missing: $key" >&2
exit 1
fi
}
java_major_version() {
if ! command -v java >/dev/null 2>&1; then
return 1
fi
local raw_version major
raw_version="$(java -version 2>&1 | awk -F '"' '/version/ {print $2; exit}')"
[ -n "$raw_version" ] || return 1
major="${raw_version%%.*}"
if [ "$major" = "1" ]; then
major="$(printf '%s\n' "$raw_version" | awk -F. '{print $2}')"
fi
printf '%s\n' "$major"
}
require_java_17() {
local major
major="$(java_major_version || true)"
if [ -z "$major" ]; then
echo "java not found; rebuilding local Java services requires JDK 17" >&2
exit 1
fi
if [ "$major" != "17" ]; then
echo "rebuilding local Java services requires JDK 17; current java major version is $major" >&2
exit 1
fi
}
is_java_service() {
case "$1" in
gateway|auth|external|other|live|order|wallet|console|visual-monitor|scheduling|game-fruit)
return 0
;;
*)
return 1
;;
esac
}
java_artifact_path_for_service() {
case "$1" in
gateway) printf '%s\n' "$JAVA_REPO_DIR/rc-gateway/target/rc-gateway-1.0.0-local.jar" ;;
auth) printf '%s\n' "$JAVA_REPO_DIR/rc-auth/target/rc-auth-1.0.0-local.jar" ;;
external) printf '%s\n' "$JAVA_REPO_DIR/rc-service/rc-service-external/external-start/target/rc-service-external-1.0.0-local.jar" ;;
other) printf '%s\n' "$JAVA_REPO_DIR/rc-service/rc-service-other/other-start/target/rc-service-other-1.0.0-local.jar" ;;
live) printf '%s\n' "$JAVA_REPO_DIR/rc-service/rc-service-live/live-start/target/rc-service-live-1.0.0-local.jar" ;;
order) printf '%s\n' "$JAVA_REPO_DIR/rc-service/rc-service-order/order-start/target/rc-service-order-1.0.0-local.jar" ;;
wallet) printf '%s\n' "$JAVA_REPO_DIR/rc-service/rc-service-wallet/wallet-start/target/rc-service-wallet-1.0.0-local.jar" ;;
console) printf '%s\n' "$JAVA_REPO_DIR/rc-service/rc-service-console/console-start/target/rc-service-console-1.0.0-local.jar" ;;
visual-monitor) printf '%s\n' "$JAVA_REPO_DIR/rc-visual/visual-monitor/target/visual-monitor-1.0.0-local.jar" ;;
scheduling) printf '%s\n' "$JAVA_REPO_DIR/rc-scheduling/target/rc-scheduling-1.0.0-local.jar" ;;
game-fruit) printf '%s\n' "$JAVA_REPO_DIR/rc-service-game/game-fruit/target/game-fruit-1.0.0-local.jar" ;;
*)
return 1
;;
esac
}
all_local_java_jars_present() {
local service artifact_path
for service in gateway auth external other live order wallet console visual-monitor scheduling game-fruit; do
artifact_path="$(java_artifact_path_for_service "$service")" || return 1
if [ ! -f "$artifact_path" ]; then
echo "missing local jar: $artifact_path" >&2
return 1
fi
done
}
service_port() {
local env_key
case "$1" in
gateway) env_key="LIKEI_LOCAL_GATEWAY_PORT" ;;
auth) env_key="LIKEI_LOCAL_AUTH_PORT" ;;
wallet) env_key="LIKEI_LOCAL_WALLET_PORT" ;;
other) env_key="LIKEI_LOCAL_OTHER_PORT" ;;
live) env_key="LIKEI_LOCAL_LIVE_PORT" ;;
order) env_key="LIKEI_LOCAL_ORDER_PORT" ;;
console) env_key="LIKEI_LOCAL_CONSOLE_PORT" ;;
scheduling) env_key="LIKEI_LOCAL_SCHEDULING_PORT" ;;
golang) env_key="LIKEI_LOCAL_GOLANG_PORT" ;;
external) env_key="LIKEI_LOCAL_EXTERNAL_PORT" ;;
visual-monitor) env_key="LIKEI_LOCAL_VISUAL_MONITOR_PORT" ;;
game-fruit) env_key="LIKEI_LOCAL_GAME_FRUIT_PORT" ;;
*)
return 1
;;
esac
require_env "$env_key"
printf '%s\n' "${!env_key}"
}
wait_service_port() {
local service="$1"
local retries="${2:-120}"
local port
port="$(service_port "$service")" || {
echo "unknown app service port: $service" >&2
exit 1
}
wait_tcp_port 127.0.0.1 "$port" "$retries"
}