M4 runtime story Rust migration wrap-up
This commit is contained in:
280
scripts/dev-rust-stack.sh
Normal file
280
scripts/dev-rust-stack.sh
Normal file
@@ -0,0 +1,280 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法:
|
||||
npm run dev:rust:sh
|
||||
./scripts/dev-rust-stack.sh --api-port 8090 --spacetime-port 3110
|
||||
./scripts/dev-rust-stack.sh --skip-spacetime --skip-publish
|
||||
./scripts/dev-rust-stack.sh --clear-database
|
||||
|
||||
说明:
|
||||
1. 默认同时启动 SpacetimeDB standalone、Rust api-server 与 Vite 前端。
|
||||
2. 默认会 publish server-rs/crates/spacetime-module,但不会清空数据库。
|
||||
3. 只有显式传入 --clear-database 时,才会追加 spacetime publish --clear-database。
|
||||
EOF
|
||||
}
|
||||
|
||||
require_command() {
|
||||
local command_name="$1"
|
||||
|
||||
if ! command -v "${command_name}" >/dev/null 2>&1; then
|
||||
echo "[dev:rust] 缺少命令: ${command_name}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
resolve_client_host() {
|
||||
local host_name="$1"
|
||||
|
||||
if [[ "${host_name}" == "0.0.0.0" || "${host_name}" == "::" ]]; then
|
||||
echo "127.0.0.1"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "${host_name}"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
local index
|
||||
|
||||
for ((index = ${#PIDS[@]} - 1; index >= 0; index--)); do
|
||||
local pid="${PIDS[index]}"
|
||||
local name="${NAMES[index]}"
|
||||
|
||||
if [[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null; then
|
||||
echo "[dev:rust] 停止 ${name} (pid=${pid})"
|
||||
if command -v pgrep >/dev/null 2>&1; then
|
||||
while read -r child_pid; do
|
||||
if [[ -n "${child_pid}" ]]; then
|
||||
kill "${child_pid}" 2>/dev/null || true
|
||||
fi
|
||||
done < <(pgrep -P "${pid}" 2>/dev/null || true)
|
||||
fi
|
||||
kill "${pid}" 2>/dev/null || true
|
||||
sleep 0.2
|
||||
if kill -0 "${pid}" 2>/dev/null; then
|
||||
kill -9 "${pid}" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
wait_for_spacetime() {
|
||||
local server="$1"
|
||||
local timeout_seconds="$2"
|
||||
local process_pid="${3:-}"
|
||||
local deadline=$((SECONDS + timeout_seconds))
|
||||
|
||||
while ((SECONDS < deadline)); do
|
||||
if [[ -n "${process_pid}" ]] && ! kill -0 "${process_pid}" 2>/dev/null; then
|
||||
echo "[dev:rust] SpacetimeDB 进程在就绪前退出。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if spacetime server ping "${server}" >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
echo "[dev:rust] 等待 SpacetimeDB 就绪超时: ${server}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
|
||||
SERVER_RS_DIR="${REPO_ROOT}/server-rs"
|
||||
MANIFEST_PATH="${SERVER_RS_DIR}/Cargo.toml"
|
||||
MODULE_PATH="${SERVER_RS_DIR}/crates/spacetime-module"
|
||||
VITE_CLI_PATH="${REPO_ROOT}/scripts/vite-cli.mjs"
|
||||
|
||||
API_HOST="127.0.0.1"
|
||||
API_PORT="8082"
|
||||
WEB_HOST="0.0.0.0"
|
||||
WEB_PORT="3000"
|
||||
SPACETIME_HOST="127.0.0.1"
|
||||
SPACETIME_PORT="3101"
|
||||
SPACETIME_ROOT_DIR="${SERVER_RS_DIR}/.spacetimedb/local"
|
||||
DATABASE="genarrative-dev"
|
||||
API_LOG="info,tower_http=info"
|
||||
SPACETIME_TIMEOUT_SECONDS="60"
|
||||
SKIP_SPACETIME=0
|
||||
SKIP_PUBLISH=0
|
||||
CLEAR_DATABASE=0
|
||||
PIDS=()
|
||||
NAMES=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--api-host)
|
||||
API_HOST="${2:?缺少 --api-host 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--api-port)
|
||||
API_PORT="${2:?缺少 --api-port 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--web-host)
|
||||
WEB_HOST="${2:?缺少 --web-host 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--web-port)
|
||||
WEB_PORT="${2:?缺少 --web-port 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--spacetime-host)
|
||||
SPACETIME_HOST="${2:?缺少 --spacetime-host 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--spacetime-port)
|
||||
SPACETIME_PORT="${2:?缺少 --spacetime-port 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--spacetime-root-dir)
|
||||
SPACETIME_ROOT_DIR="${2:?缺少 --spacetime-root-dir 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--database)
|
||||
DATABASE="${2:?缺少 --database 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--log)
|
||||
API_LOG="${2:?缺少 --log 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--spacetime-timeout-seconds)
|
||||
SPACETIME_TIMEOUT_SECONDS="${2:?缺少 --spacetime-timeout-seconds 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--skip-spacetime)
|
||||
SKIP_SPACETIME=1
|
||||
shift
|
||||
;;
|
||||
--skip-publish)
|
||||
SKIP_PUBLISH=1
|
||||
shift
|
||||
;;
|
||||
--clear-database)
|
||||
CLEAR_DATABASE=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "[dev:rust] 未知参数: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -f "${MANIFEST_PATH}" ]]; then
|
||||
echo "[dev:rust] 未找到 ${MANIFEST_PATH},无法启动 Rust 本地栈。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${MODULE_PATH}/Cargo.toml" ]]; then
|
||||
echo "[dev:rust] 未找到 ${MODULE_PATH}/Cargo.toml,无法发布 SpacetimeDB 模块。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${VITE_CLI_PATH}" ]]; then
|
||||
echo "[dev:rust] 未找到 ${VITE_CLI_PATH},无法启动 Web 前端。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_command cargo
|
||||
require_command node
|
||||
|
||||
if [[ "${SKIP_SPACETIME}" -ne 1 || "${SKIP_PUBLISH}" -ne 1 ]]; then
|
||||
require_command spacetime
|
||||
fi
|
||||
|
||||
SPACETIME_SERVER="http://${SPACETIME_HOST}:${SPACETIME_PORT}"
|
||||
API_TARGET_HOST="$(resolve_client_host "${API_HOST}")"
|
||||
RUST_SERVER_TARGET="http://${API_TARGET_HOST}:${API_PORT}"
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
echo "[dev:rust] repo: ${REPO_ROOT}"
|
||||
echo "[dev:rust] web: http://127.0.0.1:${WEB_PORT}"
|
||||
echo "[dev:rust] rust api: ${RUST_SERVER_TARGET}"
|
||||
echo "[dev:rust] spacetime: ${SPACETIME_SERVER}"
|
||||
echo "[dev:rust] database: ${DATABASE}"
|
||||
|
||||
if [[ "${SKIP_SPACETIME}" -ne 1 ]]; then
|
||||
mkdir -p "${SPACETIME_ROOT_DIR}"
|
||||
echo "[dev:rust] 启动 spacetimedb"
|
||||
(
|
||||
cd "${SERVER_RS_DIR}"
|
||||
exec spacetime \
|
||||
--root-dir "${SPACETIME_ROOT_DIR}" \
|
||||
start \
|
||||
--edition standalone \
|
||||
--listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"
|
||||
) &
|
||||
PIDS+=("$!")
|
||||
NAMES+=("spacetimedb")
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_PUBLISH}" -ne 1 ]]; then
|
||||
echo "[dev:rust] 等待 SpacetimeDB 就绪"
|
||||
wait_for_spacetime "${SPACETIME_SERVER}" "${SPACETIME_TIMEOUT_SECONDS}" "${PIDS[0]:-}"
|
||||
|
||||
PUBLISH_ARGS=(
|
||||
publish
|
||||
"${DATABASE}"
|
||||
--server "${SPACETIME_SERVER}"
|
||||
--module-path "${MODULE_PATH}"
|
||||
)
|
||||
|
||||
if [[ "${CLEAR_DATABASE}" -eq 1 ]]; then
|
||||
PUBLISH_ARGS+=(--clear-database)
|
||||
fi
|
||||
|
||||
PUBLISH_ARGS+=(--yes)
|
||||
|
||||
echo "[dev:rust] 发布 SpacetimeDB 模块: ${DATABASE}"
|
||||
spacetime "${PUBLISH_ARGS[@]}"
|
||||
fi
|
||||
|
||||
echo "[dev:rust] 启动 api-server"
|
||||
(
|
||||
cd "${REPO_ROOT}"
|
||||
GENARRATIVE_API_HOST="${API_HOST}" \
|
||||
GENARRATIVE_API_PORT="${API_PORT}" \
|
||||
GENARRATIVE_API_LOG="${API_LOG}" \
|
||||
GENARRATIVE_SPACETIME_SERVER_URL="${SPACETIME_SERVER}" \
|
||||
GENARRATIVE_SPACETIME_DATABASE="${DATABASE}" \
|
||||
exec cargo run -p api-server --manifest-path "${MANIFEST_PATH}"
|
||||
) &
|
||||
PIDS+=("$!")
|
||||
NAMES+=("api-server")
|
||||
|
||||
echo "[dev:rust] 启动 vite"
|
||||
(
|
||||
cd "${REPO_ROOT}"
|
||||
GENARRATIVE_BACKEND_STACK="rust" \
|
||||
RUST_SERVER_TARGET="${RUST_SERVER_TARGET}" \
|
||||
GENARRATIVE_RUNTIME_SERVER_TARGET="${RUST_SERVER_TARGET}" \
|
||||
VITE_DEV_HOST="${WEB_HOST}" \
|
||||
exec node "${VITE_CLI_PATH}" "--port=${WEB_PORT}" "--host=${WEB_HOST}"
|
||||
) &
|
||||
PIDS+=("$!")
|
||||
NAMES+=("vite")
|
||||
|
||||
echo "[dev:rust] 本地 Rust 栈已启动。按 Ctrl+C 停止全部子进程。"
|
||||
|
||||
set +e
|
||||
wait -n "${PIDS[@]}"
|
||||
EXIT_CODE="$?"
|
||||
set -e
|
||||
|
||||
echo "[dev:rust] 子进程已退出,开始回收本地 Rust 栈,退出码: ${EXIT_CODE}"
|
||||
exit "${EXIT_CODE}"
|
||||
Reference in New Issue
Block a user