Files
wecom_it_smart_desk/deploy-server/package.py

214 lines
7.4 KiB
Python

"""
企微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()