- 新增 wpmind 主面板 + 插件列表截图 (PNG+WebP) - manifest.json 补齐 wpslug/wpavatar/wpdate/wpfonts/wpmind 共 11 场景 - play-deploy 同步目标增加 screenshots 目录 - 新增 play-capture 自动截图脚本 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
258 lines
10 KiB
Bash
Executable file
258 lines
10 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
# play-deploy — play.wenpai.net 标准化部署工具
|
||
# 用法: play-deploy [command]
|
||
# deploy 从 git 拉取最新代码并部署到生产 (默认)
|
||
# status 对比 git 与生产环境差异
|
||
# rollback 回滚到上一个备份版本
|
||
# verify 验证线上站点可用性
|
||
# backup 仅备份当前生产文件
|
||
# upgrade 升级 Playground 引擎 (从 GitHub Release)
|
||
|
||
set -euo pipefail
|
||
|
||
# ── 配置 ──────────────────────────────────────────
|
||
REPO_URL="ssh://git@feicode.com:2222/WenPai-org/play.wenpai.net.git"
|
||
REPO_BRANCH="main"
|
||
LOCAL_CACHE="/tmp/play.wenpai.net-deploy"
|
||
REMOTE_HOST="feicode-prod"
|
||
REMOTE_ROOT="/www/wwwroot/play.wenpai.net"
|
||
REMOTE_OWNER="www:www"
|
||
SITE_URL="https://play.wenpai.net"
|
||
# repo 中 play/ 目录映射到生产站点根目录
|
||
PLAY_DIR="play"
|
||
# 需要同步的文件/目录(相对于 play/)
|
||
SYNC_TARGETS=(index.html plugins.json blueprints plugins screenshots)
|
||
DATE=$(date +%Y-%m-%d-%H%M)
|
||
|
||
# ── 颜色 ──────────────────────────────────────────
|
||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||
info() { echo -e "${GREEN}[✓]${NC} $*"; }
|
||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||
error() { echo -e "${RED}[✗]${NC} $*"; }
|
||
|
||
# ── 函数 ──────────────────────────────────────────
|
||
|
||
git_pull() {
|
||
if [ -d "$LOCAL_CACHE/.git" ]; then
|
||
info "更新本地缓存..."
|
||
cd "$LOCAL_CACHE"
|
||
git fetch origin "$REPO_BRANCH" --quiet
|
||
git checkout "$REPO_BRANCH" --quiet 2>/dev/null || git checkout -b "$REPO_BRANCH" "origin/$REPO_BRANCH" --quiet
|
||
git reset --hard "origin/$REPO_BRANCH" --quiet
|
||
else
|
||
info "克隆仓库..."
|
||
rm -rf "$LOCAL_CACHE"
|
||
git clone --depth 20 --branch "$REPO_BRANCH" "$REPO_URL" "$LOCAL_CACHE" 2>&1 | tail -1
|
||
fi
|
||
cd "$LOCAL_CACHE"
|
||
COMMIT=$(git log --oneline -1)
|
||
info "当前版本: $COMMIT"
|
||
}
|
||
|
||
do_backup() {
|
||
info "备份生产文件..."
|
||
local backup_dir="$REMOTE_ROOT/backups/$DATE"
|
||
ssh "$REMOTE_HOST" "mkdir -p $backup_dir"
|
||
for target in "${SYNC_TARGETS[@]}"; do
|
||
ssh "$REMOTE_HOST" "[ -e $REMOTE_ROOT/$target ] && cp -r $REMOTE_ROOT/$target $backup_dir/ || true" 2>/dev/null
|
||
done
|
||
info "备份完成: $backup_dir"
|
||
}
|
||
|
||
do_deploy() {
|
||
git_pull
|
||
do_backup
|
||
info "部署文件到生产..."
|
||
for target in "${SYNC_TARGETS[@]}"; do
|
||
local src="$LOCAL_CACHE/$PLAY_DIR/$target"
|
||
if [ ! -e "$src" ]; then
|
||
warn "跳过不存在的文件: $target"
|
||
continue
|
||
fi
|
||
if [ -d "$src" ]; then
|
||
# 目录: rsync 同步(不删除生产端多余文件,如 plugin-check.json)
|
||
rsync -az --no-perms "$src/" "$REMOTE_HOST:$REMOTE_ROOT/$target/"
|
||
else
|
||
scp -q "$src" "$REMOTE_HOST:$REMOTE_ROOT/$target"
|
||
fi
|
||
done
|
||
# 修复权限
|
||
ssh "$REMOTE_HOST" "chown -R $REMOTE_OWNER $REMOTE_ROOT/index.html $REMOTE_ROOT/plugins.json $REMOTE_ROOT/blueprints/ $REMOTE_ROOT/plugins/ 2>/dev/null || true"
|
||
local commit_hash
|
||
commit_hash=$(cd "$LOCAL_CACHE" && git rev-parse --short HEAD)
|
||
info "部署完成: $commit_hash"
|
||
do_verify
|
||
}
|
||
|
||
do_status() {
|
||
git_pull
|
||
info "对比 git 与生产环境..."
|
||
local tmpdir
|
||
tmpdir=$(mktemp -d)
|
||
for target in "${SYNC_TARGETS[@]}"; do
|
||
if [ -f "$LOCAL_CACHE/$PLAY_DIR/$target" ]; then
|
||
scp -q "$REMOTE_HOST:$REMOTE_ROOT/$target" "$tmpdir/$target" 2>/dev/null || true
|
||
if [ -f "$tmpdir/$target" ]; then
|
||
if diff -q "$LOCAL_CACHE/$PLAY_DIR/$target" "$tmpdir/$target" >/dev/null 2>&1; then
|
||
info "$target: 一致"
|
||
else
|
||
warn "$target: 有差异"
|
||
diff --color=auto "$LOCAL_CACHE/$PLAY_DIR/$target" "$tmpdir/$target" | head -20 || true
|
||
fi
|
||
else
|
||
warn "$target: 生产端不存在"
|
||
fi
|
||
fi
|
||
done
|
||
rm -rf "$tmpdir"
|
||
}
|
||
|
||
do_rollback() {
|
||
info "查找可用备份..."
|
||
local backups
|
||
backups=$(ssh "$REMOTE_HOST" "ls -d $REMOTE_ROOT/backups/*/ 2>/dev/null | sort -r | head -5")
|
||
if [ -z "$backups" ]; then
|
||
error "没有找到备份"
|
||
exit 1
|
||
fi
|
||
echo "可用备份:"
|
||
echo "$backups" | nl
|
||
read -rp "选择备份编号 (1-5): " choice
|
||
local selected
|
||
selected=$(echo "$backups" | sed -n "${choice}p")
|
||
if [ -z "$selected" ]; then
|
||
error "无效选择"
|
||
exit 1
|
||
fi
|
||
info "从 $selected 回滚..."
|
||
for target in "${SYNC_TARGETS[@]}"; do
|
||
ssh "$REMOTE_HOST" "[ -e ${selected}${target} ] && cp -r ${selected}${target} $REMOTE_ROOT/ || true" 2>/dev/null
|
||
done
|
||
ssh "$REMOTE_HOST" "chown -R $REMOTE_OWNER $REMOTE_ROOT/index.html $REMOTE_ROOT/plugins.json $REMOTE_ROOT/blueprints/ $REMOTE_ROOT/plugins/ 2>/dev/null || true"
|
||
info "回滚完成"
|
||
do_verify
|
||
}
|
||
|
||
do_verify() {
|
||
info "验证站点..."
|
||
local status
|
||
status=$(curl -sI -o /dev/null -w "%{http_code}" "$SITE_URL/" 2>/dev/null)
|
||
if [ "$status" = "200" ]; then
|
||
info "HTTP $status — 站点正常"
|
||
else
|
||
error "HTTP $status — 站点异常!"
|
||
return 1
|
||
fi
|
||
# 检查关键内容
|
||
local content
|
||
content=$(curl -s "$SITE_URL/" 2>/dev/null)
|
||
local checks=0
|
||
echo "$content" | grep -q "文派" && ((checks++)) || warn "未找到「文派」关键词"
|
||
echo "$content" | grep -q "playground" && ((checks++)) || warn "未找到 playground 引用"
|
||
info "内容检查通过: $checks/2"
|
||
}
|
||
|
||
do_localize() {
|
||
local script_path
|
||
# 优先用仓库里的 localize 脚本
|
||
if [ -f "$LOCAL_CACHE/scripts/play-localize" ]; then
|
||
script_path="$LOCAL_CACHE/scripts/play-localize"
|
||
elif [ -f "$(dirname "$0")/play-localize" ]; then
|
||
script_path="$(dirname "$0")/play-localize"
|
||
else
|
||
error "找不到 play-localize 脚本"
|
||
exit 1
|
||
fi
|
||
local extra_args=""
|
||
[[ "${1:-}" == "--dry-run" ]] && extra_args="--dry-run"
|
||
bash "$script_path" --remote $extra_args
|
||
}
|
||
|
||
do_mirror() {
|
||
info "镜像 GitHub 资源到生产服务器..."
|
||
local data_dir="$REMOTE_ROOT/data"
|
||
ssh "$REMOTE_HOST" "mkdir -p $data_dir"
|
||
|
||
# 蓝图库:从 feicode 镜像克隆,同步文件 + 索引到本地
|
||
info "同步蓝图库..."
|
||
ssh "$REMOTE_HOST" "cd /tmp && rm -rf wp-blueprints 2>/dev/null \
|
||
&& git clone --depth 1 https://feicode.com/WenPai-org/blueprints.git wp-blueprints 2>/dev/null \
|
||
&& rsync -a /tmp/wp-blueprints/blueprints/ $REMOTE_ROOT/community-blueprints/ \
|
||
&& cp /tmp/wp-blueprints/index.json $REMOTE_ROOT/blueprints-index.json \
|
||
&& chown -R $REMOTE_OWNER $REMOTE_ROOT/community-blueprints/ $REMOTE_ROOT/blueprints-index.json \
|
||
&& rm -rf /tmp/wp-blueprints" && {
|
||
# 替换截图 URL → 本地路径
|
||
ssh "$REMOTE_HOST" "cd $REMOTE_ROOT && python3 -c \"
|
||
import json, re
|
||
with open('blueprints-index.json') as f:
|
||
data = json.load(f)
|
||
for key, val in data.items():
|
||
url = val.get('screenshot_url', '')
|
||
m = re.search(r'/([0-9a-f]{40})/(blueprints/.+)', url)
|
||
if m:
|
||
path = m.group(2)
|
||
val['screenshot_url'] = '/community-' + path
|
||
with open('blueprints-index.json', 'w') as f:
|
||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||
\""
|
||
info "蓝图库已同步($(ssh "$REMOTE_HOST" "ls $REMOTE_ROOT/community-blueprints/ | wc -l") 个蓝图)"
|
||
} || warn "蓝图库同步失败"
|
||
|
||
# 主题测试数据
|
||
info "下载主题测试数据..."
|
||
ssh "$REMOTE_HOST" "curl -sL --connect-timeout 15 --max-time 60 \
|
||
'https://gh.wpcy.net/https://raw.githubusercontent.com/WordPress/theme-test-data/master/themeunittestdata.wordpress.xml' \
|
||
-o $data_dir/themeunittestdata.wordpress.xml 2>/dev/null" && info "themeunittestdata.wordpress.xml 已更新" || warn "主题测试数据下载失败"
|
||
|
||
# wp-cli.phar
|
||
if ssh "$REMOTE_HOST" "[ -f $REMOTE_ROOT/wp-cli.phar ]"; then
|
||
info "wp-cli.phar 已存在,跳过"
|
||
else
|
||
info "下载 wp-cli.phar..."
|
||
ssh "$REMOTE_HOST" "curl -sL --connect-timeout 15 --max-time 120 \
|
||
'https://gh.wpcy.net/https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar' \
|
||
-o $REMOTE_ROOT/wp-cli.phar 2>/dev/null" && info "wp-cli.phar 已下载" || warn "wp-cli.phar 下载失败"
|
||
fi
|
||
|
||
ssh "$REMOTE_HOST" "chown -R $REMOTE_OWNER $REMOTE_ROOT/blueprints-index.json $data_dir $REMOTE_ROOT/wp-cli.phar 2>/dev/null || true"
|
||
info "镜像完成"
|
||
}
|
||
|
||
do_upgrade() {
|
||
echo "Playground 引擎升级流程:"
|
||
echo " 1. play-deploy backup"
|
||
echo " 2. 从 GitHub Releases 下载最新构建,上传到 $REMOTE_ROOT"
|
||
echo " (保留 blueprints/ plugins/ index.html plugins.json data/)"
|
||
echo " 3. play-deploy localize # 自动应用所有本地化补丁"
|
||
echo " 4. play-deploy mirror # 镜像 GitHub 资源"
|
||
echo " 5. play-deploy verify # 验证站点"
|
||
echo ""
|
||
echo "完整命令:"
|
||
echo " play-deploy backup && play-deploy localize && play-deploy mirror && play-deploy verify"
|
||
}
|
||
|
||
# ── 主入口 ─────────────────────────────────────────
|
||
cmd="${1:-deploy}"
|
||
case "$cmd" in
|
||
deploy) do_deploy ;;
|
||
status) do_status ;;
|
||
rollback) do_rollback ;;
|
||
verify) do_verify ;;
|
||
backup) do_backup ;;
|
||
localize) do_localize "${2:-}" ;;
|
||
mirror) do_mirror ;;
|
||
upgrade) do_upgrade ;;
|
||
*)
|
||
echo "play-deploy — play.wenpai.net 部署工具"
|
||
echo ""
|
||
echo "用法: play-deploy [command]"
|
||
echo " deploy 拉取最新代码并部署到生产 (默认)"
|
||
echo " status 对比 git 与生产环境差异"
|
||
echo " rollback 回滚到上一个备份版本"
|
||
echo " verify 验证线上站点可用性"
|
||
echo " backup 仅备份当前生产文件"
|
||
echo " localize 应用中国本地化补丁 (--dry-run 预览)"
|
||
echo " mirror 镜像 GitHub 资源到生产服务器"
|
||
echo " upgrade Playground 引擎升级指南"
|
||
;;
|
||
esac
|