Initial commit - DesignerCEP Project with Caddy deployment
This commit is contained in:
243
AdminTool/deploy_core_only.py
Normal file
243
AdminTool/deploy_core_only.py
Normal 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()
|
||||
Reference in New Issue
Block a user