630 lines
26 KiB
YAML
630 lines
26 KiB
YAML
name: 🏛️ 铸渊主权服务器 · 部署
|
||
|
||
# ═══════════════════════════════════════════════════════════
|
||
# 铸渊主权服务器部署工作流
|
||
# 编号: ZY-WF-铸体 (新器官 · 服务器部署引擎)
|
||
# 守护: 铸渊 · ICE-GL-ZY001
|
||
# 版权: 国作登字-2026-A-00037559
|
||
# ═══════════════════════════════════════════════════════════
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
paths:
|
||
- 'server/**'
|
||
- 'docs/index.html'
|
||
- 'docs/css/**'
|
||
- 'docs/js/**'
|
||
- 'docs/zh/**'
|
||
workflow_dispatch:
|
||
inputs:
|
||
action:
|
||
description: '部署动作'
|
||
required: true
|
||
default: 'deploy'
|
||
type: choice
|
||
options:
|
||
- deploy
|
||
- init
|
||
- health-check
|
||
- promote
|
||
- setup-ssl
|
||
deploy_target:
|
||
description: '部署目标站点'
|
||
required: false
|
||
default: 'preview'
|
||
type: choice
|
||
options:
|
||
- preview
|
||
- production
|
||
ssl_domain:
|
||
description: 'SSL域名 (仅setup-ssl时需要,如: guanghulab.online)'
|
||
required: false
|
||
type: string
|
||
|
||
concurrency:
|
||
group: zhuyuan-server-deploy
|
||
cancel-in-progress: false
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
jobs:
|
||
# ═══ §1 初始化 (仅首次) ═══
|
||
init:
|
||
name: 🔧 服务器初始化
|
||
if: github.event.inputs.action == 'init'
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: 🔐 配置SSH
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
# 验证私钥格式完整性
|
||
if head -1 ~/.ssh/zy_key | grep -q "BEGIN" && tail -1 ~/.ssh/zy_key | grep -q "END"; then
|
||
echo "✅ SSH私钥格式正确(BEGIN/END标记完整)"
|
||
else
|
||
echo "❌ SSH私钥格式异常 — 请检查 GitHub Secrets 中 ZY_SERVER_KEY 的内容"
|
||
echo " 应以 '-----BEGIN' 开头,以 '-----END' 结尾"
|
||
exit 1
|
||
fi
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: 🔍 SSH连接测试
|
||
run: |
|
||
echo "🔍 测试SSH连接到 ${{ secrets.ZY_SERVER_HOST }}..."
|
||
ssh -i ~/.ssh/zy_key -o BatchMode=yes -o ConnectTimeout=10 \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
'echo "✅ SSH连接成功 · $(hostname) · $(date)"' || {
|
||
echo ""
|
||
echo "═══════════════════════════════════════════════"
|
||
echo "❌ SSH连接失败 · 排查指南"
|
||
echo "═══════════════════════════════════════════════"
|
||
echo ""
|
||
echo "可能原因:"
|
||
echo " 1. 服务器未配置对应的SSH公钥"
|
||
echo " 2. GitHub Secrets中的私钥不完整或格式错误"
|
||
echo " 3. SSH用户名不正确"
|
||
echo ""
|
||
echo "修复步骤(冰朔操作):"
|
||
echo " ① 登录腾讯云控制台 → 轻量应用服务器 → 选择新加坡服务器"
|
||
echo " ② 点击「远程登录」→ 使用网页终端登录"
|
||
echo " ③ 在终端中执行以下命令生成新密钥:"
|
||
echo " ssh-keygen -t ed25519 -f ~/.ssh/github_deploy -N ''"
|
||
echo " cat ~/.ssh/github_deploy.pub >> ~/.ssh/authorized_keys"
|
||
echo " chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
|
||
echo " cat ~/.ssh/github_deploy"
|
||
echo " ④ 复制输出的私钥内容(从 -----BEGIN 到 -----END 完整复制)"
|
||
echo " ⑤ 打开 GitHub → Settings → Secrets → 更新 ZY_SERVER_KEY"
|
||
echo "═══════════════════════════════════════════════"
|
||
exit 1
|
||
}
|
||
|
||
- name: 📦 上传初始化脚本
|
||
run: |
|
||
scp -i ~/.ssh/zy_key \
|
||
server/setup/zhuyuan-server-init.sh \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/tmp/
|
||
|
||
- name: 🚀 执行初始化
|
||
run: |
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
'chmod +x /tmp/zhuyuan-server-init.sh && sudo /tmp/zhuyuan-server-init.sh'
|
||
|
||
# ═══ §2 部署应用 (默认部署到预览站) ═══
|
||
deploy:
|
||
name: 🚀 部署应用代码
|
||
if: github.event.inputs.action == 'deploy' || github.event.inputs.action == '' || github.event_name == 'push'
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: 🔐 配置SSH
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: 📦 同步应用代码
|
||
run: |
|
||
# 确定部署目标: preview (默认) 或 production
|
||
TARGET="${{ github.event.inputs.deploy_target || 'preview' }}"
|
||
TARGET_DIR="/opt/zhuyuan/sites/${TARGET}"
|
||
echo "📦 部署目标: ${TARGET} → ${TARGET_DIR}"
|
||
|
||
# 确保目标目录存在
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
"mkdir -p ${TARGET_DIR} /opt/zhuyuan/app /opt/age-os/mcp-server /opt/age-os/agents /opt/age-os/logs"
|
||
|
||
# 同步后端应用代码
|
||
rsync -avz --delete \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
server/app/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/zhuyuan/app/
|
||
|
||
# 同步 AGE OS MCP Server 代码
|
||
rsync -avz --delete \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
--exclude='node_modules' \
|
||
server/age-os/mcp-server/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/mcp-server/
|
||
|
||
# 同步 AGE OS Agents 代码(活模块 · S5新增)
|
||
rsync -avz --delete \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
server/age-os/agents/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/agents/
|
||
|
||
# 同步 AGE OS Schema
|
||
rsync -avz \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
server/age-os/schema/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/schema/
|
||
|
||
# 同步 AGE OS PM2 配置
|
||
scp -i ~/.ssh/zy_key \
|
||
server/age-os/ecosystem.config.js \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/
|
||
|
||
# 同步 AGE OS package.json
|
||
scp -i ~/.ssh/zy_key \
|
||
server/age-os/package.json \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/
|
||
|
||
- name: 🌐 同步前端内容到站点
|
||
run: |
|
||
TARGET="${{ github.event.inputs.deploy_target || 'preview' }}"
|
||
TARGET_DIR="/opt/zhuyuan/sites/${TARGET}"
|
||
echo "🌐 同步前端 → ${TARGET_DIR}"
|
||
|
||
# 确保零点原核频道目录存在
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
"mkdir -p /opt/zhuyuan/sites/yaoming"
|
||
|
||
# 同步主站门户 (server/sites/portal/) 到目标站点目录
|
||
if [ -d "server/sites/portal" ]; then
|
||
rsync -avz --delete \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
server/sites/portal/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:${TARGET_DIR}/
|
||
echo "✅ 主站门户已部署到 ${TARGET_DIR}"
|
||
else
|
||
# 降级: 使用 docs/ 目录
|
||
rsync -avz --delete \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
--exclude='CNAME' \
|
||
--exclude='*.md' \
|
||
--exclude='.nojekyll' \
|
||
--exclude='ontology/' \
|
||
--exclude='daily-reports/' \
|
||
--exclude='dashboard/' \
|
||
--exclude='dev-portal/' \
|
||
docs/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:${TARGET_DIR}/
|
||
echo "✅ 前端内容(docs/)已部署到 ${TARGET_DIR}"
|
||
fi
|
||
|
||
# 同步零点原核频道 (server/sites/yaoming/) 到 yaoming 目录
|
||
if [ -d "server/sites/yaoming" ]; then
|
||
rsync -avz --delete \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
server/sites/yaoming/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/zhuyuan/sites/yaoming/
|
||
echo "✅ 零点原核频道已部署到 /opt/zhuyuan/sites/yaoming/"
|
||
fi
|
||
|
||
- name: 📦 同步配置文件
|
||
env:
|
||
ZY_DOMAIN_MAIN: ${{ secrets.ZY_DOMAIN_MAIN }}
|
||
ZY_DOMAIN_PREVIEW: ${{ secrets.ZY_DOMAIN_PREVIEW }}
|
||
ZY_FACE_DOMAIN: ${{ secrets.ZY_FACE_DOMAIN }}
|
||
run: |
|
||
# PM2 配置
|
||
scp -i ~/.ssh/zy_key \
|
||
server/ecosystem.config.js \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/zhuyuan/config/pm2/
|
||
|
||
# Nginx 配置 — 主站 · 注入域名
|
||
cp server/nginx/zhuyuan-sovereign.conf /tmp/zhuyuan-sovereign.conf
|
||
if [ -n "$ZY_DOMAIN_MAIN" ]; then
|
||
sed -i "s/ZY_DOMAIN_MAIN_PLACEHOLDER/$ZY_DOMAIN_MAIN/g" /tmp/zhuyuan-sovereign.conf
|
||
echo "✅ 主域名已注入: $ZY_DOMAIN_MAIN"
|
||
else
|
||
echo "⚠️ ZY_DOMAIN_MAIN 未配置,使用IP访问"
|
||
sed -i "s/ZY_DOMAIN_MAIN_PLACEHOLDER//g" /tmp/zhuyuan-sovereign.conf
|
||
fi
|
||
if [ -n "$ZY_DOMAIN_PREVIEW" ]; then
|
||
sed -i "s/ZY_DOMAIN_PREVIEW_PLACEHOLDER/$ZY_DOMAIN_PREVIEW/g" /tmp/zhuyuan-sovereign.conf
|
||
echo "✅ 预览域名已注入: $ZY_DOMAIN_PREVIEW"
|
||
else
|
||
echo "⚠️ ZY_DOMAIN_PREVIEW 未配置,预览站使用默认server_name"
|
||
# Nginx中 _ 是标准的catch-all server_name,不匹配任何实际域名
|
||
sed -i "s/ZY_DOMAIN_PREVIEW_PLACEHOLDER/_/g" /tmp/zhuyuan-sovereign.conf
|
||
fi
|
||
ZY_BRAIN_HOST="${{ secrets.ZY_BRAIN_HOST }}"
|
||
if [ -n "$ZY_BRAIN_HOST" ]; then
|
||
sed -i "s/ZY_BRAIN_HOST_PLACEHOLDER/$ZY_BRAIN_HOST/g" /tmp/zhuyuan-sovereign.conf
|
||
echo "✅ 大脑服务器已注入V3桥接: $ZY_BRAIN_HOST"
|
||
else
|
||
echo "⚠️ ZY_BRAIN_HOST 未配置,V3桥接不可用"
|
||
sed -i '/ZY_BRAIN_HOST_PLACEHOLDER/d' /tmp/zhuyuan-sovereign.conf
|
||
fi
|
||
scp -i ~/.ssh/zy_key \
|
||
/tmp/zhuyuan-sovereign.conf \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/zhuyuan/config/nginx/zhuyuan-sovereign.conf
|
||
|
||
# Nginx 配置 — 零点原核频道 · 注入域名
|
||
if [ -f "server/nginx/guanghuyaoming.conf" ]; then
|
||
cp server/nginx/guanghuyaoming.conf /tmp/guanghuyaoming.conf
|
||
if [ -n "$ZY_FACE_DOMAIN" ]; then
|
||
sed -i "s/ZY_FACE_DOMAIN_PLACEHOLDER/$ZY_FACE_DOMAIN/g" /tmp/guanghuyaoming.conf
|
||
echo "✅ 零点原核域名已注入: $ZY_FACE_DOMAIN"
|
||
else
|
||
echo "⚠️ ZY_FACE_DOMAIN 未配置,零点原核频道使用默认server_name"
|
||
sed -i "s/ZY_FACE_DOMAIN_PLACEHOLDER/_/g" /tmp/guanghuyaoming.conf
|
||
fi
|
||
scp -i ~/.ssh/zy_key \
|
||
/tmp/guanghuyaoming.conf \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/zhuyuan/config/nginx/guanghuyaoming.conf
|
||
echo "✅ 零点原核Nginx配置已上传"
|
||
fi
|
||
|
||
- name: 📦 同步迁移脚本
|
||
run: |
|
||
# 同步数据库迁移运行器
|
||
rsync -avz \
|
||
-e "ssh -i ~/.ssh/zy_key" \
|
||
server/age-os/scripts/ \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/scripts/
|
||
|
||
- name: 📥 安装依赖 & 重启
|
||
env:
|
||
ZHUYUAN_API_KEY: ${{ secrets.ZHUYUAN_API_KEY }}
|
||
ZY_DB_HOST: ${{ secrets.ZY_DB_HOST }}
|
||
ZY_DB_USER: ${{ secrets.ZY_DB_USER }}
|
||
ZY_DB_PASS: ${{ secrets.ZY_DB_PASS }}
|
||
ZY_DB_NAME: ${{ secrets.ZY_DB_NAME }}
|
||
ZY_DB_PORT: ${{ secrets.ZY_DB_PORT }}
|
||
ZY_DEEPSEEK_API_KEY: ${{ secrets.ZY_DEEPSEEK_API_KEY }}
|
||
ZY_QIANWEN_API_KEY: ${{ secrets.ZY_QIANWEN_API_KEY }}
|
||
ZY_KIMI_API_KEY: ${{ secrets.ZY_KIMI_API_KEY }}
|
||
ZY_QINGYAN_API_KEY: ${{ secrets.ZY_QINGYAN_API_KEY }}
|
||
ZY_OSS_KEY: ${{ secrets.ZY_OSS_KEY }}
|
||
ZY_OSS_SECRET: ${{ secrets.ZY_OSS_SECRET }}
|
||
ZY_COS_REGION: ${{ secrets.ZY_COS_REGION }}
|
||
ZY_NOTION_TOKEN: ${{ secrets.ZY_NOTION_TOKEN }}
|
||
ZY_CN_LLM_RELAY_HOST: ${{ secrets.ZY_CN_LLM_RELAY_HOST }}
|
||
ZY_CN_LLM_RELAY_KEY: ${{ secrets.ZY_CN_LLM_RELAY_KEY }}
|
||
run: |
|
||
COMMIT_SHA="${{ github.sha }}"
|
||
REF_NAME="${{ github.ref_name }}"
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << DEPLOY_CMD
|
||
|
||
set -e
|
||
cd /opt/zhuyuan/app
|
||
|
||
# 安装依赖
|
||
npm install --production 2>&1
|
||
|
||
# ─── 写入主应用环境变量配置 ───
|
||
# 注意: PORT 和 ZY_SITE_MODE 不写入 .env.app
|
||
# 这两个值由 ecosystem.config.js 按实例设置 (主站3800/预览站3801)
|
||
# 写入 .env.app 会导致 loadEnvFile() 覆盖预览实例的端口 → 两实例抢端口 → 502
|
||
echo "NODE_ENV=production" > /opt/zhuyuan/app/.env.app
|
||
echo "ZY_ROOT=/opt/zhuyuan" >> /opt/zhuyuan/app/.env.app
|
||
echo "ZY_DEEPSEEK_API_KEY=${ZY_DEEPSEEK_API_KEY}" >> /opt/zhuyuan/app/.env.app
|
||
echo "ZY_QIANWEN_API_KEY=${ZY_QIANWEN_API_KEY}" >> /opt/zhuyuan/app/.env.app
|
||
echo "ZY_KIMI_API_KEY=${ZY_KIMI_API_KEY}" >> /opt/zhuyuan/app/.env.app
|
||
echo "ZY_QINGYAN_API_KEY=${ZY_QINGYAN_API_KEY}" >> /opt/zhuyuan/app/.env.app
|
||
echo "ZY_NOTION_TOKEN=${ZY_NOTION_TOKEN}" >> /opt/zhuyuan/app/.env.app
|
||
# ─── 广州CN中继配置(可选·配置后国内API走广州中继) ───
|
||
if [ -n "${ZY_CN_LLM_RELAY_HOST}" ]; then
|
||
echo "ZY_CN_LLM_RELAY_HOST=${ZY_CN_LLM_RELAY_HOST}" >> /opt/zhuyuan/app/.env.app
|
||
echo "ZY_CN_LLM_RELAY_KEY=${ZY_CN_LLM_RELAY_KEY}" >> /opt/zhuyuan/app/.env.app
|
||
echo "ZY_CN_LLM_RELAY_PORT=3900" >> /opt/zhuyuan/app/.env.app
|
||
echo "✅ 广州CN中继已配置: ${ZY_CN_LLM_RELAY_HOST}:3900"
|
||
else
|
||
echo "ℹ️ 广州CN中继未配置·国内API走直连"
|
||
fi
|
||
chmod 600 /opt/zhuyuan/app/.env.app
|
||
|
||
# 复制Nginx配置
|
||
sudo cp /opt/zhuyuan/config/nginx/zhuyuan-sovereign.conf /etc/nginx/sites-available/zhuyuan.conf
|
||
sudo ln -sf /etc/nginx/sites-available/zhuyuan.conf /etc/nginx/sites-enabled/zhuyuan.conf
|
||
|
||
# 零点原核频道Nginx配置
|
||
if [ -f "/opt/zhuyuan/config/nginx/guanghuyaoming.conf" ]; then
|
||
sudo cp /opt/zhuyuan/config/nginx/guanghuyaoming.conf /etc/nginx/sites-available/guanghuyaoming.conf
|
||
sudo ln -sf /etc/nginx/sites-available/guanghuyaoming.conf /etc/nginx/sites-enabled/guanghuyaoming.conf
|
||
echo "✅ 零点原核Nginx配置已启用"
|
||
fi
|
||
|
||
# 移除默认配置文件,避免 duplicate default_server 冲突
|
||
# zhuyuan.conf 已声明 listen 80 default_server,不能与 default 文件共存
|
||
sudo rm -f /etc/nginx/sites-enabled/default
|
||
|
||
sudo nginx -t && sudo systemctl reload nginx
|
||
|
||
# PM2 重启 · 主应用 + 预览进程 (两个都要清理·否则残留errored进程导致502)
|
||
pm2 delete zhuyuan-server 2>/dev/null || true
|
||
pm2 delete zhuyuan-preview 2>/dev/null || true
|
||
pm2 start /opt/zhuyuan/config/pm2/ecosystem.config.js
|
||
pm2 save
|
||
|
||
# ─── AGE OS MCP Server 部署 ───
|
||
cd /opt/age-os
|
||
npm install --production 2>&1
|
||
|
||
# 写入 MCP Server 环境变量配置
|
||
echo "NODE_ENV=production" > /opt/age-os/.env.mcp
|
||
echo "MCP_PORT=3100" >> /opt/age-os/.env.mcp
|
||
echo "MCP_BIND_HOST=127.0.0.1" >> /opt/age-os/.env.mcp
|
||
|
||
# 注入密钥(从 GitHub Secrets)
|
||
echo "ZHUYUAN_API_KEY=${ZHUYUAN_API_KEY}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_DB_HOST=${ZY_DB_HOST:-127.0.0.1}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_DB_USER=${ZY_DB_USER:-zy_admin}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_DB_PASS=${ZY_DB_PASS}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_DB_NAME=${ZY_DB_NAME:-age_os}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_DB_PORT=${ZY_DB_PORT:-5432}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_DEEPSEEK_API_KEY=${ZY_DEEPSEEK_API_KEY}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_QIANWEN_API_KEY=${ZY_QIANWEN_API_KEY}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_KIMI_API_KEY=${ZY_KIMI_API_KEY}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_QINGYAN_API_KEY=${ZY_QINGYAN_API_KEY}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_OSS_KEY=${ZY_OSS_KEY}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_OSS_SECRET=${ZY_OSS_SECRET}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_COS_REGION=${ZY_COS_REGION:-ap-guangzhou}" >> /opt/age-os/.env.mcp
|
||
echo "ZY_NOTION_TOKEN=${ZY_NOTION_TOKEN}" >> /opt/age-os/.env.mcp
|
||
chmod 600 /opt/age-os/.env.mcp
|
||
|
||
# ─── 自动执行数据库迁移 ───
|
||
echo ""
|
||
echo "═══ 数据库迁移 ═══"
|
||
cd /opt/age-os
|
||
# 加载环境变量供迁移脚本使用
|
||
export ZY_DB_HOST="${ZY_DB_HOST:-127.0.0.1}"
|
||
export ZY_DB_USER="${ZY_DB_USER:-zy_admin}"
|
||
export ZY_DB_PASS="${ZY_DB_PASS}"
|
||
export ZY_DB_NAME="${ZY_DB_NAME:-age_os}"
|
||
export ZY_DB_PORT="${ZY_DB_PORT:-5432}"
|
||
node scripts/db-migrate.js || echo "⚠️ 数据库迁移有警告(非致命·应用仍会启动)"
|
||
echo "═══════════════════"
|
||
echo ""
|
||
|
||
# PM2 重启 MCP Server(读取 .env.mcp)
|
||
pm2 delete age-os-mcp 2>/dev/null || true
|
||
pm2 delete age-os-agents 2>/dev/null || true
|
||
cd /opt/age-os && pm2 start ecosystem.config.js
|
||
pm2 save
|
||
|
||
# 健康检查 · 主应用(3800) + 预览进程(3801) + MCP(3100)
|
||
sleep 3
|
||
curl -sf http://localhost:3800/api/health || echo "⚠️ 主应用健康检查失败,等待更长时间..."
|
||
curl -sf http://localhost:3801/api/health || echo "⚠️ 预览进程健康检查失败,等待更长时间..."
|
||
curl -sf http://localhost:3100/health || echo "⚠️ MCP Server健康检查失败,等待更长时间..."
|
||
sleep 5
|
||
curl -sf http://localhost:3800/api/health && echo "✅ 主应用已上线 (3800)" || echo "❌ 主应用启动失败 (3800)"
|
||
curl -sf http://localhost:3801/api/health && echo "✅ 预览进程已上线 (3801)" || echo "❌ 预览进程启动失败 (3801) · 若 ZY_DOMAIN_PREVIEW 域名指向此端口则该域名可能502"
|
||
curl -sf http://localhost:3100/health && echo "✅ MCP Server已上线 (3100)" || echo "❌ MCP Server启动失败 (3100)"
|
||
|
||
# 记录部署操作
|
||
curl -sf -X POST http://localhost:3800/api/operations \
|
||
-H "Content-Type: application/json" \
|
||
-d "{
|
||
\"operator\": \"铸渊 · GitHub Actions\",
|
||
\"action\": \"自动部署\",
|
||
\"details\": \"commit: ${COMMIT_SHA}, branch: ${REF_NAME}\"
|
||
}" || true
|
||
|
||
DEPLOY_CMD
|
||
|
||
# ═══ §3 健康检查 ═══
|
||
health-check:
|
||
name: 💚 健康检查
|
||
if: github.event.inputs.action == 'health-check'
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: 🔐 配置SSH
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: 💚 执行健康检查
|
||
run: |
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << 'HEALTH_CMD'
|
||
|
||
echo "═══ 铸渊主权服务器健康报告 ═══"
|
||
echo ""
|
||
|
||
echo "§1 系统状态:"
|
||
uptime
|
||
echo ""
|
||
|
||
echo "§2 磁盘使用:"
|
||
df -h /
|
||
echo ""
|
||
|
||
echo "§3 内存使用:"
|
||
free -m
|
||
echo ""
|
||
|
||
echo "§4 PM2 进程:"
|
||
pm2 list 2>/dev/null || echo "PM2 未运行"
|
||
echo ""
|
||
|
||
echo "§5 Nginx 状态:"
|
||
systemctl is-active nginx && echo "Nginx: 运行中" || echo "Nginx: 已停止"
|
||
echo ""
|
||
|
||
echo "§6 应用健康:"
|
||
curl -sf http://localhost:3800/api/health | jq . 2>/dev/null || echo "应用未响应"
|
||
echo ""
|
||
|
||
echo "§7 大脑状态:"
|
||
curl -sf http://localhost:3800/api/brain | jq '.files_present, .files_total' 2>/dev/null || echo "大脑API未响应"
|
||
echo ""
|
||
|
||
echo "§8 双站点状态:"
|
||
curl -sf http://localhost:3800/api/sites | jq . 2>/dev/null || echo "站点API未响应"
|
||
|
||
HEALTH_CMD
|
||
|
||
# ═══ §4 一键推送: 预览站 → 主站 ═══
|
||
promote:
|
||
name: 🚀 预览站→主站 一键推送
|
||
if: github.event.inputs.action == 'promote'
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: 🔐 配置SSH
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: 🚀 执行推送
|
||
run: |
|
||
echo "🚀 一键推送: 预览站 → 主站"
|
||
RUN_ID="${{ github.run_id }}"
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << PROMOTE_CMD
|
||
|
||
# 调用铸渊 API 执行推送
|
||
RESULT=\$(curl -sf -X POST http://localhost:3800/api/sites/promote \
|
||
-H "Content-Type: application/json" \
|
||
-d "{
|
||
\"operator\": \"冰朔 · 通过 GitHub Actions 授权推送\",
|
||
\"note\": \"workflow_dispatch promote · run_id: ${RUN_ID}\"
|
||
}" 2>/dev/null)
|
||
|
||
if [ \$? -eq 0 ]; then
|
||
echo "\$RESULT" | jq . 2>/dev/null || echo "\$RESULT"
|
||
echo ""
|
||
echo "✅ 预览站已推送到主站"
|
||
else
|
||
echo "❌ 推送失败"
|
||
echo "尝试手动执行..."
|
||
if [ -d /opt/zhuyuan/sites/preview ] && [ -f /opt/zhuyuan/sites/preview/index.html ]; then
|
||
rsync -a --delete /opt/zhuyuan/sites/preview/ /opt/zhuyuan/sites/production/
|
||
echo "✅ 手动推送完成"
|
||
else
|
||
echo "❌ 预览站无内容,无法推送"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
PROMOTE_CMD
|
||
|
||
- name: 💚 推送后验证
|
||
run: |
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << 'VERIFY_CMD'
|
||
|
||
echo "═══ 推送后验证 ═══"
|
||
echo ""
|
||
echo "主站内容:"
|
||
ls -la /opt/zhuyuan/sites/production/ | head -10
|
||
echo ""
|
||
echo "站点状态:"
|
||
curl -sf http://localhost:3800/api/sites | jq . 2>/dev/null || echo "API未响应"
|
||
|
||
VERIFY_CMD
|
||
|
||
# ═══ §5 SSL证书配置 ═══
|
||
setup-ssl:
|
||
name: 🔒 SSL证书配置
|
||
if: github.event.inputs.action == 'setup-ssl'
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: 🔐 配置SSH
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
if head -1 ~/.ssh/zy_key | grep -q "BEGIN" && tail -1 ~/.ssh/zy_key | grep -q "END"; then
|
||
echo "✅ SSH私钥格式正确"
|
||
else
|
||
echo "❌ SSH私钥格式异常"
|
||
exit 1
|
||
fi
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: 🔍 SSH连接测试
|
||
run: |
|
||
ssh -i ~/.ssh/zy_key -o BatchMode=yes -o ConnectTimeout=10 \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
'echo "✅ SSH连接成功 · $(hostname)"'
|
||
|
||
- name: 📦 上传SSL配置脚本
|
||
run: |
|
||
scp -i ~/.ssh/zy_key \
|
||
server/setup/setup-ssl.sh \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/tmp/setup-ssl.sh
|
||
|
||
- name: 🔒 执行SSL配置
|
||
run: |
|
||
SSL_DOMAIN="${{ github.event.inputs.ssl_domain }}"
|
||
ZY_DOMAIN_PREVIEW="${{ secrets.ZY_DOMAIN_PREVIEW }}"
|
||
|
||
# 如果未指定域名,使用预览域名
|
||
if [ -z "$SSL_DOMAIN" ]; then
|
||
SSL_DOMAIN="$ZY_DOMAIN_PREVIEW"
|
||
fi
|
||
|
||
if [ -z "$SSL_DOMAIN" ]; then
|
||
echo "❌ 未指定SSL域名"
|
||
echo ""
|
||
echo "请在触发工作流时填写 ssl_domain 参数"
|
||
echo "例如: guanghulab.online"
|
||
exit 1
|
||
fi
|
||
|
||
echo "🔒 配置SSL: $SSL_DOMAIN"
|
||
echo ""
|
||
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
"chmod +x /tmp/setup-ssl.sh && sudo bash /tmp/setup-ssl.sh $SSL_DOMAIN"
|
||
|
||
- name: 💚 SSL验证
|
||
run: |
|
||
SSL_DOMAIN="${{ github.event.inputs.ssl_domain || secrets.ZY_DOMAIN_PREVIEW }}"
|
||
if [ -n "$SSL_DOMAIN" ]; then
|
||
echo "🔍 验证HTTPS: https://$SSL_DOMAIN"
|
||
sleep 3
|
||
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" "https://${SSL_DOMAIN}/" 2>/dev/null || echo "000")
|
||
echo "HTTPS状态码: $HTTP_CODE"
|
||
if [ "$HTTP_CODE" != "000" ]; then
|
||
echo "✅ HTTPS可访问"
|
||
else
|
||
echo "⚠️ HTTPS暂时不可访问 (可能需要等待DNS传播)"
|
||
fi
|
||
fi
|