244 lines
7.4 KiB
Python
244 lines
7.4 KiB
Python
#!/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()
|