#!/bin/bash # 拉取式部署 watcher - 从 Gitea Release 拉取最新部署包并自动部署 # 设计原则: # - CI 在任意 runner(hk 等)构建,产物以 Release 附件发布到 Gitea # - 本脚本由服务器本地定时任务(1Panel 计划任务)每分钟调用,通过 HTTPS 拉取, # 任何 AI/CI 均不通过 SSH 操作服务器 # 安装方式见 .gitea/docs/PULL_DEPLOY.md(1Panel 原生方式,一次性配置) # # 可选环境变量(建议写入 /opt/lunar/production/.env 并在计划任务里 source): # GITEA_DEPLOY_TOKEN 只读令牌(仓库公开时可省略,匿名即可下载) set -u GITEA_URL="${GITEA_URL:-https://gitea.neatcn.com}" REPO="${REPO:-gouki/lunar-mini}" STATE_FILE="${STATE_FILE:-/opt/lunar/production/.last-deployed-release}" DEPLOY_SCRIPT="${DEPLOY_SCRIPT:-/opt/lunar/scripts/deploy.sh}" LOG_FILE="${LOG_FILE:-/opt/lunar/deploy-watch.log}" WORK_DIR="$(mktemp -d)" trap 'rm -rf "$WORK_DIR"' EXIT log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" } AUTH_ARGS=() if [ -n "${GITEA_DEPLOY_TOKEN:-}" ]; then AUTH_ARGS=(-H "Authorization: token ${GITEA_DEPLOY_TOKEN}") fi # 查询最新 20 个 release,取 tag 前缀为 server-v 且带 deploy.tar.gz 附件的最新一个 RELEASE_JSON=$(curl -fsS --connect-timeout 10 --max-time 30 \ "${AUTH_ARGS[@]}" \ "$GITEA_URL/api/v1/repos/$REPO/releases?limit=20" \ -o "$WORK_DIR/releases.json" && cat "$WORK_DIR/releases.json") || { log "查询 release 列表失败,等待下次轮询" exit 1 } PARSED=$(python3 - "$WORK_DIR/releases.json" <<'PY' import json, sys releases = json.load(open(sys.argv[1])) for r in releases: if not r.get("tag_name", "").startswith("server-v"): continue if r.get("draft") or r.get("prerelease"): continue for a in r.get("assets", []): if a.get("name") == "deploy.tar.gz": print(r["id"]) print(r["tag_name"]) print(a["id"]) print(a["browser_download_url"]) sys.exit(0) PY ) || true if [ -z "$PARSED" ]; then # 尚无可部署的发布版本,静默等待 exit 0 fi RELEASE_ID=$(echo "$PARSED" | sed -n 1p) TAG=$(echo "$PARSED" | sed -n 2p) ASSET_ID=$(echo "$PARSED" | sed -n 3p) DOWNLOAD_URL=$(echo "$PARSED" | sed -n 4p) # 幂等:已部署过该 release 则跳过 LAST="$(cat "$STATE_FILE" 2>/dev/null || true)" if [ "$LAST" = "$RELEASE_ID" ]; then exit 0 fi log "发现新版本 $TAG (release #$RELEASE_ID),开始拉取部署" # 下载部署包(公开仓库可匿名;-L 跟随重定向) if ! curl -fsSL --connect-timeout 10 --max-time 300 \ "${AUTH_ARGS[@]}" \ "$DOWNLOAD_URL" -o "$WORK_DIR/deploy.tar.gz"; then log "下载部署包失败,等待下次轮询重试" exit 1 fi # 包结构预检(deploy.sh 内部还会再校验) if ! tar -tzf "$WORK_DIR/deploy.tar.gz" | grep -q "^deploy/bin/server$"; then log "部署包结构异常(缺少 deploy/bin/server),中止" exit 1 fi # 执行部署;成功后才记录 release id(失败时下次轮询会重试) if bash "$DEPLOY_SCRIPT" "$WORK_DIR/deploy.tar.gz" >> "$LOG_FILE" 2>&1; then mkdir -p "$(dirname "$STATE_FILE")" printf '%s' "$RELEASE_ID" > "$STATE_FILE" log "部署成功: $TAG" else log "部署失败($TAG),保留旧版本,等待下次轮询重试或人工介入" exit 1 fi