#!/usr/bin/env bash # 局域网 Mac 服务器部署:rsync 项目 → 192.168.71.100:/Users/john/Project/tkmind_go # # 前置:本机可免密 ssh 到目标机 # ssh-copy-id john@192.168.71.100 # # 用法: # ./deploy/deploy-lan-100.sh # ./deploy/deploy-lan-100.sh --skip-build # ./deploy/deploy-lan-100.sh --skip-goosed # ./deploy/deploy-lan-100.sh --skip-h5 set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" DEPLOY_ENV="${ROOT}/deploy/lan-100.env" EXCLUDE_FILE="${ROOT}/deploy/.rsync-exclude-project" LAN_DEPLOY_HOST="${LAN_DEPLOY_HOST:-john@192.168.71.100}" LAN_REMOTE_DIR="${LAN_REMOTE_DIR:-/Users/john/Project/tkmind_go}" GOOSED_PORT="${GOOSED_PORT:-18006}" H5_PORT="${H5_PORT:-8080}" SKIP_BUILD=0 SKIP_GOOSED=0 SKIP_H5=0 for arg in "$@"; do case "$arg" in --skip-build) SKIP_BUILD=1 ;; --skip-goosed) SKIP_GOOSED=1 ;; --skip-h5) SKIP_H5=1 ;; -h|--help) sed -n '2,12p' "$0" exit 0 ;; *) echo "未知参数: $arg" >&2 exit 1 ;; esac done if [[ -f "${DEPLOY_ENV}" ]]; then set -a # shellcheck disable=SC1090 source "${DEPLOY_ENV}" set +a fi ssh_cmd() { ssh -o ConnectTimeout=15 -o BatchMode=yes "${LAN_DEPLOY_HOST}" "$@" } require_ssh() { if ! ssh_cmd 'echo ok' >/dev/null 2>&1; then echo "❌ 无法 SSH 到 ${LAN_DEPLOY_HOST}" >&2 echo "请在本机终端执行一次(输入服务器密码):" >&2 echo " ssh-copy-id ${LAN_DEPLOY_HOST}" >&2 exit 1 fi } echo "======================================" echo "TKMind → 局域网部署" echo "时间: $(date '+%Y-%m-%d %H:%M:%S')" echo "本地: ${ROOT}" echo "远端: ${LAN_DEPLOY_HOST}:${LAN_REMOTE_DIR}" echo "======================================" require_ssh echo "==> 创建远端目录..." ssh_cmd "mkdir -p '${LAN_REMOTE_DIR}'" echo "==> rsync 同步项目..." rsync -az --delete --exclude-from="${EXCLUDE_FILE}" \ "${ROOT}/" \ "${LAN_DEPLOY_HOST}:${LAN_REMOTE_DIR}/" echo "==> 写入远端环境模板(不覆盖已有 .env)..." ssh_cmd "test -f '${LAN_REMOTE_DIR}/.env.local' || cat > '${LAN_REMOTE_DIR}/.env.local' <<'EOF' GOOSE_PORT=${GOOSED_PORT} GOOSE_HOST=127.0.0.1 GOOSE_SERVER__SECRET_KEY=local-dev-secret GOOSE_CODING_ROUTER=${LAN_REMOTE_DIR}/deploy/coding_router.sh GOOSE_AIDER_BIN=/Users/john/PycharmProjects/aider/.venv/bin/aider EOF" ssh_cmd "test -f '${LAN_REMOTE_DIR}/ui/h5/.env' || cp '${LAN_REMOTE_DIR}/ui/h5/.env.example' '${LAN_REMOTE_DIR}/ui/h5/.env'" if [[ "${SKIP_BUILD}" -eq 0 ]]; then if [[ "${SKIP_GOOSED}" -eq 0 ]]; then echo "==> 远端编译 goosed..." ssh_cmd "cd '${LAN_REMOTE_DIR}' && (command -v cargo >/dev/null 2>&1 || source bin/activate-hermit 2>/dev/null || true) && cargo build -p goose-server --bin goosed" fi if [[ "${SKIP_H5}" -eq 0 ]]; then echo "==> 远端构建 H5..." ssh_cmd "cd '${LAN_REMOTE_DIR}/ui/h5' && (command -v pnpm >/dev/null 2>&1 && pnpm install && pnpm run build || (npm install && npm run build))" fi fi if [[ "${SKIP_GOOSED}" -eq 0 ]]; then echo "==> 重启远端 goosed..." ssh_cmd "cd '${LAN_REMOTE_DIR}' && ./local_restart.sh" || true fi if [[ "${SKIP_H5}" -eq 0 ]]; then echo "==> 重启远端 H5..." ssh_cmd "cd '${LAN_REMOTE_DIR}/ui/h5' && (lsof -ti TCP:${H5_PORT} -s TCP:LISTEN | xargs kill 2>/dev/null || true); sleep 1; nohup node server.mjs >> '${LAN_REMOTE_DIR}/ui/h5/h5.log' 2>&1 & echo \$! > '${LAN_REMOTE_DIR}/ui/h5/.h5.pid'" fi echo "" echo "=== 健康检查 ===" ssh_cmd "curl -sk 'https://127.0.0.1:${GOOSED_PORT}/status' && echo ' ← goosed'" || true ssh_cmd "curl -s 'http://127.0.0.1:${H5_PORT}/api/status' && echo ' ← h5'" || true echo "" echo "✅ 已同步到 ${LAN_DEPLOY_HOST}:${LAN_REMOTE_DIR}" echo " H5: http://192.168.71.100:${H5_PORT}/" echo " goosed: https://127.0.0.1:${GOOSED_PORT}/ (仅服务器本机)"