From a87e9aa0b8b8d8113d2d1eb00fe0434c10066063 Mon Sep 17 00:00:00 2001 From: lxh2875931338 Date: Wed, 29 Jul 2026 23:17:19 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=99main=E7=AB=AF=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E7=9A=84=E8=87=AA=E5=8A=A8=E5=8C=96=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E8=BF=9B=E8=A1=8C=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy.sh | 396 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 328 insertions(+), 68 deletions(-) diff --git a/deploy.sh b/deploy.sh index b34e82c..955593a 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,15 +1,10 @@ #!/bin/bash # ============================================================ -# frpc-console 一键部署脚本(Docker 优先) -# 支持:Linux x86_64 / ARM64 / ARMv7 -# 自动安装:git / curl / wget / Docker -# +# frpc-console 一键部署脚本 (Docker 优先) # 用法: -# ./deploy.sh # 完整交互流程 -# ./deploy.sh --yes # 跳过确认,直接执行 -# ./deploy.sh --check # 只检测环境,不执行 -# ./deploy.sh --dry-run # 显示将执行的操作,不实际执行 +# curl -sSL .../deploy.sh | sudo bash +# curl -sSL .../deploy.sh | sudo bash -s -- --channel preview # ============================================================ set -e @@ -24,13 +19,7 @@ if [ -t 1 ]; then MAGENTA='\033[0;35m' NC='\033[0m' else - RED='' - GREEN='' - YELLOW='' - BLUE='' - CYAN='' - MAGENTA='' - NC='' + RED=''; GREEN=''; YELLOW=''; BLUE=''; CYAN=''; MAGENTA=''; NC='' fi # ---------- 配置 ---------- @@ -60,6 +49,10 @@ CONTAINER_RUNNING=false SKIP_CONFIRM=false CHECK_ONLY=false DRY_RUN=false +CHANNEL="" +CURRENT_VERSION="" +TARGET_VERSION="" +IMAGE_TAG="" # ---------- 打印函数 ---------- print_info() { echo -e "${BLUE}[INFO]${NC} $1"; } @@ -74,22 +67,38 @@ print_subtitle() { echo -e "${MAGENTA} $1${NC}"; } parse_args() { for arg in "$@"; do case $arg in - --yes|-y) SKIP_CONFIRM=true ;; - --check) CHECK_ONLY=true ;; - --dry-run) DRY_RUN=true ;; + --yes|-y) + SKIP_CONFIRM=true + ;; + --check) + CHECK_ONLY=true + ;; + --dry-run) + DRY_RUN=true + ;; + --channel) + shift + CHANNEL="$1" + ;; + --channel=*) + CHANNEL="${arg#*=}" + ;; --help|-h) echo "用法: ./deploy.sh [选项]" echo "" echo "选项:" - echo " --yes, -y 跳过所有确认提示,直接执行" - echo " --check 只检测环境,不执行部署" - echo " --dry-run 显示将执行的操作,不实际执行" - echo " --help, -h 显示帮助信息" + echo " --channel lts 使用 LTS 通道 (main 分支)" + echo " --channel preview 使用 Preview 通道 (test 分支)" + echo " --yes, -y 跳过所有确认提示" + echo " --check 只检测环境,不执行部署" + echo " --dry-run 显示将执行的操作,不实际执行" + echo " --help, -h 显示帮助信息" echo "" echo "示例:" - echo " ./deploy.sh # 完整交互流程" - echo " ./deploy.sh --yes # 无人值守部署" - echo " ./deploy.sh --check # 只检测环境" + echo " ./deploy.sh # 交互式选择通道" + echo " ./deploy.sh --channel lts # 部署 LTS 版本" + echo " ./deploy.sh --channel preview # 部署 Preview 版本" + echo " ./deploy.sh --yes # 无人值守部署" exit 0 ;; *) @@ -154,14 +163,165 @@ check_container() { fi } +# ---------- 版本管理函数 ---------- + +read_version_file() { + local version_file="./version.ini" + if [ ! -f "$version_file" ]; then + print_error "version.ini 文件不存在: $version_file" + return 1 + fi + SEMVER=$(grep -v "^[;#]" "$version_file" | head -1 | awk '{print $1}') + LTS_DATE=$(grep -v "^[;#]" "$version_file" | head -1 | awk '{print $2}') + echo "$SEMVER $LTS_DATE" +} + +get_version_type() { + local semver="$1" + local minor="${semver#*.}" + if [ "$minor" = "0" ] || [ "$minor" = "5" ]; then + echo "lts" + else + echo "preview" + fi +} + +generate_target_version() { + local channel="$1" + local semver_lts_date + semver_lts_date=$(read_version_file) + SEMVER=$(echo "$semver_lts_date" | awk '{print $1}') + LTS_DATE=$(echo "$semver_lts_date" | awk '{print $2}') + + if [ -z "$SEMVER" ]; then + print_error "无法从 version.ini 读取语义版本" + exit 1 + fi + + local vtype + vtype=$(get_version_type "$SEMVER") + + if [ "$channel" = "lts" ] || [ "$vtype" = "lts" ]; then + if [ -z "$LTS_DATE" ]; then + LTS_DATE=$(date +%Y%m%d) + print_warn "LTS 版本未指定日期,使用今天: $LTS_DATE" + fi + TARGET_VERSION="${SEMVER}-lts-${LTS_DATE}" + IMAGE_TAG="${SEMVER}-lts-${LTS_DATE}" + else + TIMESTAMP=$(date +%Y%m%d-%H%M%S) + TARGET_VERSION="${SEMVER}-preview-${TIMESTAMP}" + IMAGE_TAG="preview-${TIMESTAMP}" + fi + echo "$TARGET_VERSION $IMAGE_TAG" +} + +get_current_version() { + local version_file="${DEPLOY_DIR}/version.ini" + if [ -f "$version_file" ]; then + grep -v "^[;#]" "$version_file" | head -1 | tr -d '\n' + else + if [ "$CONTAINER_EXISTS" = true ]; then + docker exec frpc-console cat /app/version.ini 2>/dev/null | grep -v "^[;#]" | head -1 | tr -d '\n' || echo "0.0.0-dev" + else + echo "0.0.0-dev" + fi + fi +} + +extract_semver() { + local version="$1" + echo "$version" | sed -E 's/^([0-9]+\.[0-9]+).*$/\1/' +} + +extract_timestamp() { + local version="$1" + echo "$version" | sed -E 's/^[0-9]+\.[0-9]+-(lts|preview)-//' +} + +compare_version() { + local current="$1" + local target="$2" + + local cur_semver=$(extract_semver "$current") + local tgt_semver=$(extract_semver "$target") + + if [ "$tgt_semver" -gt "$cur_semver" ] 2>/dev/null; then + return 1 + fi + if [ "$tgt_semver" -lt "$cur_semver" ] 2>/dev/null; then + return 2 + fi + + local cur_ts=$(extract_timestamp "$current") + local tgt_ts=$(extract_timestamp "$target") + + if [ -z "$cur_ts" ]; then + cur_ts="0" + fi + if [ -z "$tgt_ts" ]; then + tgt_ts="0" + fi + + if [ "$tgt_ts" -gt "$cur_ts" ] 2>/dev/null; then + return 1 + elif [ "$tgt_ts" -eq "$cur_ts" ] 2>/dev/null; then + return 0 + else + return 2 + fi +} + +# ---------- 通道选择(含分支切换)---------- +select_channel() { + if [ -n "$CHANNEL" ]; then + if [ "$CHANNEL" != "lts" ] && [ "$CHANNEL" != "preview" ]; then + print_error "无效通道: $CHANNEL (仅支持 lts / preview)" + exit 1 + fi + if [ "$CHANNEL" = "preview" ]; then + BRANCH="test" + else + BRANCH="main" + fi + print_info "使用命令行参数: ${CHANNEL} 通道 (${BRANCH} 分支)" + return + fi + + echo "" + echo -e "${CYAN}请选择部署通道:${NC}" + echo " 1. LTS (稳定版,生产推荐) [默认] → main 分支" + echo " 2. Preview (技术预览版,包含新特性) → test 分支" + echo "" + read -p "请选择 [1]: " CHANNEL_INPUT /dev/null; then - if ! sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='users';" 2>/dev/null | grep -q "^1$"; then - print_warn "数据库文件无效,将删除,由容器全新初始化" - rm -f "$DB_FILE" - else - print_info "数据库有效,保留" + # ----- 事务前钩子:备份数据库(仅降级时备份) ----- + BACKUP_DIR="" + if [ -n "$CURRENT_VERSION" ] && [ "$CURRENT_VERSION" != "0.0.0-dev" ]; then + compare_version "$CURRENT_VERSION" "$TARGET_VERSION" + COMPARE_RESULT=$? + if [ $COMPARE_RESULT -eq 2 ]; then + print_warn "检测到降级操作: $CURRENT_VERSION → $TARGET_VERSION" + echo "" + echo -e "${YELLOW}⚠ 降级可能导致数据不兼容${NC}" + echo " 当前版本: $CURRENT_VERSION" + echo " 目标版本: $TARGET_VERSION" + echo "" + echo -e "${CYAN}将备份完整数据目录到:${NC}" + TIMESTAMP=$(date +%Y%m%d_%H%M%S) + BACKUP_DIR="/opt/frpc-console-backups/${TIMESTAMP}_${CHANNEL}" + echo " $BACKUP_DIR" + echo "" + + read -p "确认执行降级? [y/N]: " CONFIRM_DOWNGRADE /dev/null | grep -q "[f]rpc -c"; then print_success "frpc 进程运行正常" @@ -389,6 +616,24 @@ do_deploy() { print_warn "数据库为空或不可用,请通过 WebUI 注册管理员账户" fi + # ----- 验证版本写入 ----- + print_step "验证版本..." + sleep 1 + CONTAINER_VERSION=$(docker exec frpc-console cat /app/version.ini 2>/dev/null | grep -v "^[;#]" | head -1 | tr -d '\n' || echo "unknown") + echo -e " 容器版本: ${CONTAINER_VERSION}" + echo -e " 目标版本: ${TARGET_VERSION}" + if [ "$CONTAINER_VERSION" = "$TARGET_VERSION" ]; then + print_success "版本写入成功" + else + print_warn "版本不匹配,请检查" + fi + + if [ -n "$BACKUP_DIR" ]; then + echo "" + print_warn "降级前数据已备份到: $BACKUP_DIR" + echo " 如需回退,请恢复: cp -r $BACKUP_DIR/frpc-console/* $DEPLOY_DIR/" + fi + # ----- 清理临时文件 ----- print_step "清理临时文件..." rm -rf "$WORK_DIR" @@ -398,17 +643,21 @@ do_deploy() { print_title print_success "frpc-console 部署完成!" print_title + echo "" echo -e " ${CYAN}📍 访问地址:${NC} http://$(hostname -I | awk '{print $1}'):${PORT}" echo -e " ${CYAN}📂 数据目录:${NC} ${DEPLOY_DIR}" echo -e " ${CYAN}📦 备份目录:${NC} /tmp/frpc-console/db-backups" echo -e " ${CYAN}🐳 容器名称:${NC} frpc-console" + echo -e " ${CYAN}🏷️ 版本:${NC} ${TARGET_VERSION}" + echo -e " ${CYAN}🌿 分支:${NC} ${BRANCH}" echo "" echo -e " ${CYAN}常用命令:${NC}" echo " docker logs frpc-console # 查看日志" echo " docker restart frpc-console # 重启服务" echo " docker stop frpc-console # 停止服务" echo " docker start frpc-console # 启动服务" + echo " cat ${DEPLOY_DIR}/version.ini # 查看当前版本" echo "" echo -e " ${YELLOW}首次访问需要注册管理员账户${NC}" echo "" @@ -427,6 +676,16 @@ main() { check_tools check_container + select_channel + + generate_target_version "$CHANNEL" + + if [ "$CONTAINER_EXISTS" = true ]; then + CURRENT_VERSION=$(get_current_version) + else + CURRENT_VERSION="(首次部署)" + fi + print_environment_summary if [ "$CHECK_ONLY" = true ]; then @@ -449,6 +708,7 @@ main() { if ! confirm_deploy; then custom_config + generate_plan print_deployment_plan if ! confirm_deploy; then print_info "已取消部署"