139 lines
5.0 KiB
Python
139 lines
5.0 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
一键部署脚本
|
|||
|
|
============
|
|||
|
|
用途:根据配置文件一键部署Node项目到宝塔服务器
|
|||
|
|
|
|||
|
|
使用方法:
|
|||
|
|
python3 一键部署.py 项目名称 本地项目路径
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
python3 一键部署.py soul /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import sys
|
|||
|
|
import os
|
|||
|
|
import subprocess
|
|||
|
|
import time
|
|||
|
|
|
|||
|
|
# 默认服务器配置
|
|||
|
|
默认配置 = {
|
|||
|
|
"服务器IP": "42.194.232.22",
|
|||
|
|
"SSH用户": "root",
|
|||
|
|
"SSH密码": "Zhiqun1984",
|
|||
|
|
"服务器根目录": "/www/wwwroot"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def 执行命令(命令: str, 显示输出: bool = True) -> tuple:
|
|||
|
|
"""执行shell命令"""
|
|||
|
|
result = subprocess.run(命令, shell=True, capture_output=True, text=True)
|
|||
|
|
if 显示输出 and result.stdout:
|
|||
|
|
print(result.stdout)
|
|||
|
|
if result.stderr and "Warning" not in result.stderr:
|
|||
|
|
print(f"错误: {result.stderr}")
|
|||
|
|
return result.returncode, result.stdout
|
|||
|
|
|
|||
|
|
def 部署项目(项目名称: str, 本地路径: str):
|
|||
|
|
"""执行部署流程"""
|
|||
|
|
服务器路径 = f"{默认配置['服务器根目录']}/{项目名称}"
|
|||
|
|
压缩文件 = f"/tmp/{项目名称}_update.tar.gz"
|
|||
|
|
|
|||
|
|
print(f"\n{'='*60}")
|
|||
|
|
print(f"开始部署: {项目名称}")
|
|||
|
|
print(f"本地路径: {本地路径}")
|
|||
|
|
print(f"服务器路径: {服务器路径}")
|
|||
|
|
print(f"{'='*60}\n")
|
|||
|
|
|
|||
|
|
# 步骤1: 压缩项目
|
|||
|
|
print("📦 步骤1: 压缩项目文件...")
|
|||
|
|
排除项 = "--exclude='node_modules' --exclude='.next' --exclude='.git' --exclude='android' --exclude='out'"
|
|||
|
|
压缩命令 = f"cd '{本地路径}' && tar {排除项} -czf {压缩文件} ."
|
|||
|
|
code, _ = 执行命令(压缩命令, False)
|
|||
|
|
if code != 0:
|
|||
|
|
print("❌ 压缩失败")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 获取文件大小
|
|||
|
|
大小 = os.path.getsize(压缩文件) / 1024 / 1024
|
|||
|
|
print(f" ✅ 压缩完成,大小: {大小:.2f} MB")
|
|||
|
|
|
|||
|
|
# 步骤2: 上传到服务器
|
|||
|
|
print("\n📤 步骤2: 上传到服务器...")
|
|||
|
|
上传命令 = f"sshpass -p '{默认配置['SSH密码']}' scp -o StrictHostKeyChecking=no {压缩文件} {默认配置['SSH用户']}@{默认配置['服务器IP']}:/tmp/"
|
|||
|
|
code, _ = 执行命令(上传命令, False)
|
|||
|
|
if code != 0:
|
|||
|
|
print("❌ 上传失败")
|
|||
|
|
return False
|
|||
|
|
print(" ✅ 上传完成")
|
|||
|
|
|
|||
|
|
# 步骤3-6: SSH远程执行
|
|||
|
|
print("\n🔧 步骤3-6: 服务器端操作...")
|
|||
|
|
|
|||
|
|
SSH前缀 = f"sshpass -p '{默认配置['SSH密码']}' ssh -o StrictHostKeyChecking=no {默认配置['SSH用户']}@{默认配置['服务器IP']}"
|
|||
|
|
|
|||
|
|
# 清理旧文件
|
|||
|
|
清理命令 = f"{SSH前缀} 'cd {服务器路径} && rm -rf app components lib public styles *.json *.js *.ts *.mjs *.md .next 2>/dev/null || true'"
|
|||
|
|
执行命令(清理命令, False)
|
|||
|
|
print(" ✅ 清理旧文件")
|
|||
|
|
|
|||
|
|
# 解压
|
|||
|
|
解压命令 = f"{SSH前缀} 'cd {服务器路径} && tar -xzf /tmp/{项目名称}_update.tar.gz'"
|
|||
|
|
执行命令(解压命令, False)
|
|||
|
|
print(" ✅ 解压新代码")
|
|||
|
|
|
|||
|
|
# 安装依赖
|
|||
|
|
print("\n📚 安装依赖 (这可能需要几分钟)...")
|
|||
|
|
安装命令 = f"{SSH前缀} 'export PATH=/www/server/nodejs/v22.14.0/bin:$PATH && cd {服务器路径} && npm install --legacy-peer-deps 2>&1'"
|
|||
|
|
执行命令(安装命令, True)
|
|||
|
|
|
|||
|
|
# 构建
|
|||
|
|
print("\n🏗️ 构建项目...")
|
|||
|
|
构建命令 = f"{SSH前缀} 'export PATH=/www/server/nodejs/v22.14.0/bin:$PATH && cd {服务器路径} && npm run build 2>&1'"
|
|||
|
|
执行命令(构建命令, True)
|
|||
|
|
|
|||
|
|
# 重启PM2
|
|||
|
|
print("\n🔄 重启服务...")
|
|||
|
|
重启命令 = f"{SSH前缀} 'export PATH=/www/server/nodejs/v22.14.0/bin:$PATH && pm2 restart {项目名称} 2>&1'"
|
|||
|
|
执行命令(重启命令, True)
|
|||
|
|
|
|||
|
|
# 清理临时文件
|
|||
|
|
清理临时命令 = f"{SSH前缀} 'rm -f /tmp/{项目名称}_update.tar.gz'"
|
|||
|
|
执行命令(清理临时命令, False)
|
|||
|
|
os.remove(压缩文件)
|
|||
|
|
|
|||
|
|
print(f"\n{'='*60}")
|
|||
|
|
print("✅ 部署完成!")
|
|||
|
|
print(f"{'='*60}")
|
|||
|
|
print("\n⚠️ 请在宝塔面板手动重启项目:")
|
|||
|
|
print(f" 1. 登录 https://42.194.232.22:9988/ckbpanel")
|
|||
|
|
print(f" 2. 进入【网站】→【Node项目】")
|
|||
|
|
print(f" 3. 找到 {项目名称},点击【重启】")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
if len(sys.argv) < 3:
|
|||
|
|
print("用法: python3 一键部署.py <项目名称> <本地项目路径>")
|
|||
|
|
print("\n示例:")
|
|||
|
|
print(" python3 一键部署.py soul /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验")
|
|||
|
|
print(" python3 一键部署.py kr_wb /Users/karuo/Documents/开发/4、小工具/whiteboard")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
项目名称 = sys.argv[1]
|
|||
|
|
本地路径 = sys.argv[2]
|
|||
|
|
|
|||
|
|
if not os.path.exists(本地路径):
|
|||
|
|
print(f"❌ 本地路径不存在: {本地路径}")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
确认 = input(f"\n确认部署 {项目名称} 到服务器? (y/n): ")
|
|||
|
|
if 确认.lower() != 'y':
|
|||
|
|
print("已取消部署")
|
|||
|
|
sys.exit(0)
|
|||
|
|
|
|||
|
|
部署项目(项目名称, 本地路径)
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|