feat(deploy): openclaw-01 生产部署脚本、systemd 单元与部署文档

This commit is contained in:
2026-10-30 18:49:31 +08:00
parent 9ac3b46430
commit 57767c77a1
8 changed files with 346 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# 生产部署脚本 —— openclaw-01(用户级 systemd,全程不需要 root)
#
# 遵守 AGENTS.md 的部署铁律:
# 1. 生产只跑仓库 HEAD(git reset --hard origin/main,禁止服务器手工改)
# 2. 前端产物只在服务器上由 HEAD 构建(npm ci && npm run build)
# 3. 部署后校验 /version 的 commit == git rev-parse --short HEAD
#
# 用法(在 openclaw-01 上,openclaw 身份):
# bash ~/app/dadaotask/deploy/prod/deploy.sh
# 首次部署前准备好 ~/.config/dadaotask/env.production(见 env.production.example)。
set -euo pipefail
APP_DIR=${APP_DIR:-/home/openclaw/app/dadaotask}
CONF_DIR=${CONF_DIR:-$HOME/.config/dadaotask}
ENV_FILE=$CONF_DIR/env.production
SERVICE=${SERVICE:-dadaotask-api}
PORT=${PORT:-8200}
REPO_URL=${REPO_URL:-https://gitea.tatta.cn/Tatta/DadaoTask.git}
step() { printf '\n== %s ==\n' "$*"; }
die() { printf '!! %s\n' "$*" >&2; exit 1; }
step "0. 前置检查"
test -f "$ENV_FILE" || die "缺少 $ENV_FILE(参考 deploy/prod/env.production.example)"
if ! command -v node >/dev/null; then die "node 未安装(前端必须在服务器上构建)"; fi
node_major=$(node -v | sed 's/^v\([0-9]*\).*/\1/')
[ "$node_major" -ge 20 ] || die "node 版本过低:$(node -v),需要 20+"
step "1. 代码对齐仓库 HEAD"
if [ ! -d "$APP_DIR/.git" ]; then
git clone --branch main --single-branch "$REPO_URL" "$APP_DIR"
fi
cd "$APP_DIR"
git fetch --prune origin
git checkout -q main
git reset --hard -q origin/main # 生产不允许领先/落后仓库,也不允许脏改动
COMMIT=$(git rev-parse HEAD)
SHORT=$(git rev-parse --short HEAD)
echo " HEAD=$COMMIT ($SHORT)"
step "2. Python 依赖"
[ -x .venv/bin/python ] || python3 -m venv .venv
.venv/bin/pip install -q --upgrade pip
.venv/bin/pip install -q -r backend/requirements.txt
step "3. 数据库迁移(迁移角色,非应用角色)"
set -a; . "$ENV_FILE"; set +a
( cd backend && ../.venv/bin/alembic upgrade head )
step "4. 前端构建(只在服务器上、只用 HEAD 的源码)"
( cd web && npm ci --no-audit --no-fund && npm run build )
test -f web/dist/index.html || die "前端构建产物缺失:web/dist/index.html"
step "5. 写入构建标记并重启服务"
mkdir -p "$CONF_DIR" backend/var/uploads
chmod 700 "$CONF_DIR"
printf 'BUILD_COMMIT=%s\nBUILD_TIME=%s\n' "$SHORT" "$(date -Iseconds)" >"$CONF_DIR/env.build"
chmod 600 "$CONF_DIR/env.build" "$ENV_FILE"
systemctl --user daemon-reload
systemctl --user enable "$SERVICE" >/dev/null 2>&1 || true
systemctl --user restart "$SERVICE"
step "6. 校验(/health + /version 的 commit)"
for _ in $(seq 1 30); do
curl -fsS "http://127.0.0.1:$PORT/health" >/dev/null 2>&1 && break
sleep 1
done
HEALTH=$(curl -fsS "http://127.0.0.1:$PORT/health") || die "健康检查失败:服务没起来(journalctl --user -u $SERVICE -n 100)"
COMMIT_HTTP=$(curl -fsS "http://127.0.0.1:$PORT/version" | .venv/bin/python -c 'import json,sys; print(json.load(sys.stdin)["commit"])') \
|| die "/version 读取失败"
echo " health=$HEALTH"
echo " version.commit=$COMMIT_HTTP"
[ "$COMMIT_HTTP" = "$SHORT" ] || die "/version 的 commit($COMMIT_HTTP) 与 HEAD($SHORT) 不一致"
printf '\n部署完成:http://%s:%s (commit %s)\n' "$(hostname -I | awk '{print $1}')" "$PORT" "$SHORT"