Initial commit - DesignerCEP Project with Caddy deployment

This commit is contained in:
zuowei1216
2025-12-19 21:27:17 +08:00
commit 8ea58fe480
170 changed files with 47469 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Core 快速发布脚本
仅执行:构建 Core -> 上传 Core -> 更新数据库
跳过 Shell 的构建和上传
"""
import os
import sys
import shutil
import subprocess
import argparse
import zipfile
import json
from pathlib import Path
from datetime import datetime
import paramiko
# ==================== 配置区域 ====================
PROJECT_ROOT = Path(__file__).parent.parent.absolute()
DESIGNER_DIR = PROJECT_ROOT / "Designer"
DIST_CORE_DIR = DESIGNER_DIR / "dist_core" # Core 构建输出目录
CONFIG_FILE = Path(__file__).parent / "deploy_config.json"
# 颜色输出
try:
import colorama
colorama.init()
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
RESET = '\033[0m'
except ImportError:
GREEN = YELLOW = RED = BLUE = RESET = ''
# ==================== 工具函数 ====================
def print_step(message):
print(f"\n{BLUE}{'='*60}{RESET}")
print(f"{GREEN}{message}{RESET}")
print(f"{BLUE}{'='*60}{RESET}\n")
def print_success(message):
print(f"{GREEN}{message}{RESET}")
def print_error(message):
print(f"{RED}{message}{RESET}")
def run_command(command, cwd=None):
print(f" 执行: {command}")
try:
result = subprocess.run(
command,
cwd=cwd,
shell=True,
check=True,
capture_output=True,
text=True,
encoding='utf-8',
errors='replace'
)
if result.stdout:
print(f" 输出: {result.stdout.strip()[:200]}...") # 只打印前200字符
return result
except subprocess.CalledProcessError as e:
print_error(f"命令执行失败: {e}")
if e.stderr:
print(f" 错误: {e.stderr}")
sys.exit(1)
def load_config():
if not CONFIG_FILE.exists():
print_error("未找到配置文件 deploy_config.json")
sys.exit(1)
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
# ==================== 核心步骤 ====================
def step1_build_core():
print_step("步骤 1: 构建 Core")
# 检查目录
if not DESIGNER_DIR.exists():
print_error(f"Designer 目录不存在: {DESIGNER_DIR}")
sys.exit(1)
# 执行构建
print(" 正在构建 Core (npm run build:core)...")
os.chdir(DESIGNER_DIR)
run_command("npm run build:core", cwd=DESIGNER_DIR)
# 验证
if not DIST_CORE_DIR.exists():
print_error(f"Core 构建失败,输出目录不存在: {DIST_CORE_DIR}")
sys.exit(1)
print_success("Core 构建完成")
def step2_upload_core(version, config):
print_step("步骤 2: 上传 Core 到服务器")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
print(f" 连接服务器: {config['host']}")
ssh.connect(
hostname=config['host'],
port=int(config.get('port', 22)),
username=config['username'],
password=config.get('password', ''),
timeout=30
)
print_success("SSH 连接成功")
sftp = ssh.open_sftp()
remote_base = config['remote_path']
remote_core_dir = f"{remote_base}/core/{version}"
# 创建远程目录
print(f" 创建远程目录: {remote_core_dir}")
ssh.exec_command(f"mkdir -p {remote_core_dir}")
# 递归上传
print(" 开始上传文件...")
upload_count = 0
for root, dirs, files in os.walk(DIST_CORE_DIR):
relative_root = Path(root).relative_to(DIST_CORE_DIR)
remote_root = f"{remote_core_dir}/{relative_root}".replace("\\", "/").rstrip("/")
if str(relative_root) == ".":
remote_root = remote_core_dir
# 确保子目录存在
try:
sftp.stat(remote_root)
except IOError:
sftp.mkdir(remote_root)
for file in files:
local_file = Path(root) / file
remote_file = f"{remote_root}/{file}"
sftp.put(str(local_file), remote_file)
upload_count += 1
if upload_count % 10 == 0:
print(f" 已上传 {upload_count} 个文件...", end='\r')
print(f"\n 共上传 {upload_count} 个文件")
print_success("Core 上传完成")
sftp.close()
ssh.close()
except Exception as e:
print_error(f"上传失败: {e}")
raise
def step3_update_db(version, config):
print_step("步骤 3: 更新数据库")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect(
hostname=config['host'],
port=int(config.get('port', 22)),
username=config['username'],
password=config.get('password', ''),
timeout=30
)
# 构造 SQL
sql = f"UPDATE plugin_groups SET current_version_file = 'core-v{version}.zip' WHERE name = 'default';"
# 数据库配置
db_conf = config.get('mysql', {})
db_user = db_conf.get('username', 'designer_user')
db_pass = db_conf.get('password', 'DesignerPass123!')
db_name = db_conf.get('database', 'designer_db')
container_name = "designercep_db"
print(f" 更新 'default' 分组版本为: core-v{version}.zip")
docker_cmd = f'docker exec -i {container_name} mysql -u{db_user} -p{db_pass} {db_name} -e "{sql}"'
stdin, stdout, stderr = ssh.exec_command(docker_cmd)
exit_status = stdout.channel.recv_exit_status()
if exit_status == 0:
print_success("数据库更新成功")
else:
print_error(f"数据库更新失败: {stderr.read().decode().strip()}")
raise Exception("DB Update Failed")
ssh.close()
except Exception as e:
print_error(f"数据库操作失败: {e}")
raise
def step4_clean_cache():
print_step("步骤 4: 清除本地缓存")
cache_dir = Path.home() / "AppData" / "Roaming" / "DesignerCache"
if cache_dir.exists():
try:
shutil.rmtree(cache_dir)
print_success(f"已清除: {cache_dir}")
except Exception as e:
print_error(f"清除失败: {e}")
else:
print(" 无需清除")
# ==================== 主入口 ====================
def main():
parser = argparse.ArgumentParser(description='Core 快速发布脚本')
parser.add_argument('--version', '-v', required=True, help='版本号 (如 1.0.7)')
args = parser.parse_args()
version = args.version
print(f"{BLUE}开始发布 Core - 版本: {version}{RESET}")
try:
config = load_config()
step1_build_core()
step2_upload_core(version, config)
step3_update_db(version, config)
step4_clean_cache()
print_step("🎉 发布全部完成!")
print(f" 版本: {version}")
print(f" 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
except Exception as e:
print_error(f"\n发布过程中止: {e}")
sys.exit(1)
if __name__ == "__main__":
main()