""" 企微IT智能服务台 — 部署包生成脚本(Windows 兼容版) ======================================================= 功能: 1. 构建前端(H5 + 坐席端) 2. 将前端产物 + 后端源码 + 配置文件打包为 zip 3. 输出 zip 文件,可上传到服务器直接解压部署 用法: cd 项目根目录 python deploy-server/package.py 说明: - 使用 Python 而非 bash,确保 Windows 上可直接运行 - 打包前会自动执行 npm run build 构建前端 - 如不想重新构建,传 --skip-build 参数跳过 """ import os import sys import shutil import subprocess import zipfile from pathlib import Path # ─── 配置 ─────────────────────────────────────────────────────── PROJECT_ROOT = Path(__file__).resolve().parent.parent # 项目根目录 SCRIPT_DIR = Path(__file__).resolve().parent # deploy-server 目录 PACKAGE_NAME = "it-smart-desk-server-deploy" ZIP_FILENAME = f"{PACKAGE_NAME}.zip" # 需要打包的目录和文件映射 # key: 源路径(相对于 PROJECT_ROOT),value: 目标路径(在 zip 中的相对路径,含顶层目录) PACKAGE_PREFIX = PACKAGE_NAME # zip 内顶层目录名 INCLUDE_MAP = { "deploy-server/docker-compose.yml": f"{PACKAGE_PREFIX}/docker-compose.yml", "deploy-server/.env.example": f"{PACKAGE_PREFIX}/.env.example", "deploy-server/deploy.sh": f"{PACKAGE_PREFIX}/deploy.sh", "deploy-server/README.md": f"{PACKAGE_PREFIX}/README.md", "deploy-server/nginx/nginx.conf": f"{PACKAGE_PREFIX}/nginx/nginx.conf", "frontend-h5/dist": f"{PACKAGE_PREFIX}/frontend-h5/dist", "frontend-agent/dist": f"{PACKAGE_PREFIX}/frontend-agent/dist", "backend": f"{PACKAGE_PREFIX}/backend", } # 后端目录中需要排除的文件/文件夹 BACKEND_EXCLUDE = { "__pycache__", ".pytest_cache", ".venv", "venv", "*.pyc", ".git", ".env", "*.egg-info", } # ─── 辅助函数 ─────────────────────────────────────────────────── def run_cmd(cmd: str, cwd: Path | None = None) -> bool: """执行命令,返回是否成功""" print(f" 执行: {cmd}") result = subprocess.run( cmd, shell=True, cwd=cwd, capture_output=True, encoding='utf-8', errors='replace' # Windows 下 npm 输出 UTF-8,忽略解码错误 ) if result.returncode != 0: stderr = result.stderr.strip() if result.stderr else "" # npm build 偶尔有非致命警告,只在实际失败时报错 if "error" in stderr.lower() or result.returncode != 0: print(f" ⚠ 命令返回码: {result.returncode}") if stderr: # 只打印最后几行,避免刷屏 lines = stderr.strip().split('\n') for line in lines[-3:]: print(f" {line}") return result.returncode == 0 return True def should_exclude(path: Path) -> bool: """判断文件/目录是否应排除""" name = path.name if name in {"__pycache__", ".pytest_cache", ".venv", "venv", ".git", ".env", "node_modules"}: return True if name.endswith(".pyc") or name.endswith(".egg-info"): return True return False def add_dir_to_zip(zipf: zipfile.ZipFile, src_dir: Path, arc_prefix: str): """递归添加目录到 zip""" for item in src_dir.rglob("*"): if should_exclude(item): continue if item.is_file(): # 计算在 zip 中的相对路径 rel = item.relative_to(src_dir) arc = f"{arc_prefix}/{rel}".replace("\\", "/") zipf.write(item, arc) # ─── 主流程 ───────────────────────────────────────────────────── def build_frontends(): """构建前端""" print("\n--- [1/2] 构建前端 ---") # H5 员工端 h5_dir = PROJECT_ROOT / "frontend-h5" print("构建 H5 员工端...") if not run_cmd("npm install --quiet", cwd=h5_dir): print(" ⚠ npm install 失败,尝试继续...") if not run_cmd("npm run build", cwd=h5_dir): print(" ❌ H5 构建失败!") sys.exit(1) print(" ✅ H5 员工端构建完成") # 坐席工作台 agent_dir = PROJECT_ROOT / "frontend-agent" print("构建坐席工作台...") if not run_cmd("npm install --quiet", cwd=agent_dir): print(" ⚠ npm install 失败,尝试继续...") if not run_cmd("npm run build", cwd=agent_dir): print(" ❌ 坐席端构建失败!") sys.exit(1) print(" ✅ 坐席工作台构建完成") def create_package(): """创建部署包 zip""" print("\n--- [2/2] 创建部署包 ---") zip_path = PROJECT_ROOT / ZIP_FILENAME # 检查必要文件是否存在 checks = [ (PROJECT_ROOT / "frontend-h5" / "dist" / "index.html", "H5 前端产物"), (PROJECT_ROOT / "frontend-agent" / "dist" / "index.html", "坐席端前端产物"), (PROJECT_ROOT / "backend" / "Dockerfile", "后端 Dockerfile"), (SCRIPT_DIR / "docker-compose.yml", "docker-compose.yml"), (SCRIPT_DIR / "nginx" / "nginx.conf", "nginx.conf"), ] for path, desc in checks: if not path.exists(): print(f" ❌ 缺少: {desc} ({path})") sys.exit(1) print(f" 正在打包到 {zip_path}...") with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf: for src_rel, dst_rel in INCLUDE_MAP.items(): src = PROJECT_ROOT / src_rel if not src.exists(): print(f" ⚠ 跳过不存在的路径: {src_rel}") continue if src.is_file(): zf.write(src, dst_rel) elif src.is_dir(): add_dir_to_zip(zf, src, dst_rel) # 显示结果 size_mb = zip_path.stat().st_size / (1024 * 1024) print(f" ✅ 部署包已生成: {zip_path}") print(f" 大小: {size_mb:.1f} MB") def main(): print("=" * 50) print(" IT智能服务台 — 部署包生成") print("=" * 50) # 检查是否跳过构建 skip_build = "--skip-build" in sys.argv if skip_build: print("\n ⏭ 跳过前端构建(使用现有 dist 目录)") else: build_frontends() create_package() # 输出后续步骤 print("\n" + "=" * 50) print(" 后续步骤:") print("=" * 50) print(f""" 1. 上传部署包到服务器(通过堡垒机): scp -o "ProxyJump=sxn@10.212.189.210:2222" \\ {ZIP_FILENAME} \\ sxn@10.80.0.136:/tmp/ 2. SSH 登录服务器(通过堡垒机): ssh -J sxn@10.212.189.210:2222 sxn@10.80.0.136 3. 在服务器上执行: sudo cp /tmp/{ZIP_FILENAME} /opt/ cd /opt unzip {ZIP_FILENAME} mv {PACKAGE_NAME} wecom-it-desk cd wecom-it-desk cp .env.example .env vi .env # 编辑配置(阶段一 Mock 模式默认即可) chmod +x deploy.sh ./deploy.sh 4. 配置 DNS(联系 IT 运维): itsupport.servyou.com.cn → 10.80.0.136 5. 浏览器验证: http://itsupport.servyou.com.cn/itdesk/ http://itsupport.servyou.com.cn/itagent/ """) if __name__ == "__main__": main()