#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")" && pwd)" RUN_DIR="$ROOT_DIR/run" LOG_DIR="$ROOT_DIR/logs" API_PID_FILE="$RUN_DIR/api.pid" WORKER_PID_FILE="$RUN_DIR/worker.pid" API_LOG_FILE="$LOG_DIR/api.log" WORKER_LOG_FILE="$LOG_DIR/worker.log" API_BIN="$RUN_DIR/api" WORKER_BIN="$RUN_DIR/worker" HTTP_PORT=16811 WORKER_HTTP_PORT=16812 mkdir -p "$RUN_DIR" "$LOG_DIR" load_env_config() { local env_file for env_file in ".env"; do if [[ -f "$ROOT_DIR/$env_file" ]]; then # shellcheck disable=SC1090 source "$ROOT_DIR/$env_file" fi done if [[ -n "${APP_ENV:-}" && -f "$ROOT_DIR/.env.$APP_ENV" ]]; then # shellcheck disable=SC1090 source "$ROOT_DIR/.env.$APP_ENV" elif [[ -n "${GO_ENV:-}" && -f "$ROOT_DIR/.env.$GO_ENV" ]]; then # shellcheck disable=SC1090 source "$ROOT_DIR/.env.$GO_ENV" elif [[ -n "${ENV:-}" && -f "$ROOT_DIR/.env.$ENV" ]]; then # shellcheck disable=SC1090 source "$ROOT_DIR/.env.$ENV" fi if [[ -f "$ROOT_DIR/.env.local" ]]; then # shellcheck disable=SC1090 source "$ROOT_DIR/.env.local" fi } port_pid() { local port="$1" lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true } is_scheduler_process() { local pid="$1" local cmd cmd="$(ps -p "$pid" -o command= 2>/dev/null || true)" [[ "$cmd" == *"/scheduler-backend/run/api"* || "$cmd" == *"/scheduler-backend/run/worker"* || "$cmd" == *"go run ./cmd/api"* || "$cmd" == *"go run ./cmd/worker"* || "$cmd" == *"/scheduler-backend/api"* || "$cmd" == *"/scheduler-backend/worker"* || "$cmd" == *"/exe/api"* || "$cmd" == *"/exe/worker"* ]] } is_running() { local pid_file="$1" if [[ ! -f "$pid_file" ]]; then return 1 fi local pid pid="$(cat "$pid_file")" if [[ -z "$pid" ]]; then return 1 fi kill -0 "$pid" 2>/dev/null } ensure_port_free() { local name="$1" local port="$2" local pid pid="$(port_pid "$port")" if [[ -z "$pid" ]]; then return 0 fi if is_scheduler_process "$pid"; then kill "$pid" 2>/dev/null || true sleep 1 pid="$(port_pid "$port")" if [[ -z "$pid" ]]; then echo "$name previous process stopped, port=$port" return 0 fi fi echo "$name port $port is already occupied by pid=$pid" >&2 ps -p "$pid" -o pid,ppid,command >&2 || true return 1 } start_process() { local name="$1" local pid_file="$2" local log_file="$3" shift 3 if is_running "$pid_file"; then echo "$name is already running, restarting pid=$(cat "$pid_file")" kill "$(cat "$pid_file")" 2>/dev/null || true sleep 1 fi rm -f "$pid_file" ( cd "$ROOT_DIR" nohup "$@" >>"$log_file" 2>&1 & echo $! >"$pid_file" ) sleep 1 if is_running "$pid_file"; then echo "$name started, pid=$(cat "$pid_file"), log=$log_file" return 0 fi echo "failed to start $name, check $log_file" >&2 rm -f "$pid_file" return 1 } build_binary() { local output="$1" local target="$2" ( cd "$ROOT_DIR" GOWORK=off go build -o "$output" "$target" ) } stop_process() { local name="$1" local pid_file="$2" if ! is_running "$pid_file"; then rm -f "$pid_file" return 0 fi kill "$(cat "$pid_file")" 2>/dev/null || true rm -f "$pid_file" echo "$name stopped" } build_binary "$API_BIN" ./cmd/api build_binary "$WORKER_BIN" ./cmd/worker load_env_config ensure_port_free "api" "${HTTP_PORT}" ensure_port_free "worker" "${WORKER_HTTP_PORT}" start_process "api" "$API_PID_FILE" "$API_LOG_FILE" "$API_BIN" if ! start_process "worker" "$WORKER_PID_FILE" "$WORKER_LOG_FILE" "$WORKER_BIN"; then stop_process "api" "$API_PID_FILE" exit 1 fi echo "scheduler-backend started" echo "api pid: $(cat "$API_PID_FILE")" echo "worker pid: $(cat "$WORKER_PID_FILE")" echo "api log: $API_LOG_FILE" echo "worker log: $WORKER_LOG_FILE"