提交 6b8ca05f authored 作者: 陈泽健's avatar 陈泽健

feat(自动化更新): 实现版本自动发现和更新功能

- 新增自动版本发现配置,支持后端和前端的版本自动识别- 实现后端JAR包和前端文件的自动查找逻辑- 优化更新流程,支持自动发现并更新到最新版本
- 添加测试脚本,用于验证版本自动发现功能
上级 0e61267c
......@@ -8,7 +8,7 @@
3. 完整的异常处理流程
4. 完整的日志记录系统
"""
import fnmatch
import paramiko
import os
from datetime import datetime
......@@ -32,33 +32,30 @@ class Config:
# ===== 服务器配置 =====
SERVER_HOST = '139.159.163.86' # 默认使用兰州中石化测试环境
# ===== 后端服务配置 =====
BACKEND_PATHS = {
'inner': {
'remote': "/var/www/java/api-java-meeting2.0/",
'local': r"\\192.168.9.9\deploy\01会议预定\标准版本-长期运维\01版本管理\01后端运行服务\内部预定\COM_虹软4.0_V2.1.2526.586_2025_06_25_psl自测\ubains-meeting-inner-api-1.0-SNAPSHOT.jar",
'command': "docker exec ujava2 /var/www/java/api-java-meeting2.0/run.sh"
# 新增自动版本发现配置
VERSION_DISCOVERY = {
'backend': {
'inner': {
'search_path': r"\\192.168.9.9\deploy\01会议预定\标准版本-长期运维\01版本管理\01后端运行服务\内部预定",
'file_pattern': '*.jar'
},
'external': {
'search_path': r"\\192.168.9.9\deploy\01会议预定\标准版本-长期运维\01版本管理\01后端运行服务\对外预定",
'file_pattern': '*.jar'
}
},
'external': {
'remote': "/var/www/java/external-meeting-api/",
'local': r"\\192.168.9.9\deploy\01会议预定\标准版本-长期运维\01版本管理\01后端运行服务\对外预定\COMVhx2.0.2526.234_2025_06_25_dhh自测\ubains-meeting-api-1.0-SNAPSHOT.jar",
'command': "docker exec ujava2 /var/www/java/external-meeting-api/run.sh"
'frontend': {
'front': {
'search_path': r"\\192.168.9.9\deploy\00项目管理\2025\L 兰州中石化项目\01版本管理\02前端PC网页",
'files_to_update': ['*.worker.js', 'index.html', 'static/']
},
'admin': {
'search_path': r"\\192.168.9.9\deploy\01会议预定\标准版本-预定后台\01版本管理",
'files_to_update': ['index.html', 'static/']
}
}
}
# ===== 前端服务配置 =====
FRONTEND_CONFIG = {
'front': {
'remote_dir': "/var/www/java/ubains-web-2.0/",
'local_dir': r"\\192.168.9.9\deploy\00项目管理\2025\L 兰州中石化项目\01版本管理\02前端PC网页\2.0.2526.1148 2025-06-26",
'files_to_update': ['*.worker.js', 'index.html', 'static/']
},
'admin': {
'remote_dir': "/var/www/java/ubains-web-admin/",
'local_dir': r"\\192.168.9.9\deploy\01会议预定\标准版本-预定后台\01版本管理\2.0.2526.1109 2025-06-25",
'files_to_update': ['index.html', 'static/']
}
}
# ===== 备份配置 =====
BACKUP_CONFIG = {
......@@ -190,6 +187,105 @@ class Deployer:
self.logger(f"查找worker文件失败: {str(e)}", "ERROR")
return []
def _discover_latest_version(self, service_type, service_name):
"""自动发现最新版本(核心逻辑)"""
config = Config.VERSION_DISCOVERY[service_type][service_name]
self.logger(f"\n===== 正在发现 {service_name} 最新版本 =====", important=True)
try:
# 获取最新文件夹
latest_folder = self._get_latest_local_folder(config['search_path'])
if not latest_folder:
raise Exception("未找到有效版本文件夹")
if service_type == 'backend':
# 获取JAR包
jar_pattern = config['file_pattern']
latest_jar = self._get_latest_matching_file(latest_folder, jar_pattern)
if not latest_jar:
raise Exception(f"未找到匹配 {jar_pattern} 的文件")
self.logger(f"发现最新版本: {latest_jar}", important=True)
return {'local': latest_jar, 'remote': Config.BACKEND_PATHS[service_name]['remote']}
else:
# 获取前端文件
files_to_update = config['files_to_update']
found_files = self._get_frontend_files(latest_folder, files_to_update)
self.logger(f"发现最新版本于: {latest_folder}", important=True)
return {
'local_dir': latest_folder,
'files_to_update': files_to_update,
'found_files': found_files
}
except Exception as e:
self.logger(f"版本发现失败: {str(e)}", "ERROR")
return None
def _get_frontend_files(self, folder_path, files_to_update):
"""获取前端文件清单"""
found = {}
for item in files_to_update:
if item.endswith('/'):
dir_name = item[:-1]
dir_path = os.path.join(folder_path, dir_name)
found[dir_name] = os.path.exists(dir_path)
elif item.startswith('*.'):
ext = item[2:]
found[ext] = any(f.endswith(ext) for f in os.listdir(folder_path))
else:
found[item] = os.path.exists(os.path.join(folder_path, item))
return found
def _get_latest_matching_file(self, folder, pattern):
"""获取匹配模式的最新文件"""
try:
files = [f for f in os.listdir(folder) if fnmatch.fnmatch(f, pattern)]
if not files:
return None
# 按修改时间排序
files_with_mtime = []
for f in files:
full_path = os.path.join(folder, f)
mtime = os.path.getmtime(full_path)
files_with_mtime.append((full_path, mtime))
self.logger(f"发现文件: {f} (修改时间: {datetime.fromtimestamp(mtime)})")
latest = max(files_with_mtime, key=lambda x: x[1])[0]
return latest
except Exception as e:
self.logger(f"获取匹配文件失败: {str(e)}", "ERROR")
return None
def _get_latest_local_folder(self, path):
"""获取本地最新文件夹"""
try:
if not os.path.exists(path):
raise FileNotFoundError(f"路径不存在: {path}")
folders = [f for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
if not folders:
return None
# 按修改时间排序
folders_with_mtime = []
for f in folders:
full_path = os.path.join(path, f)
mtime = os.path.getmtime(full_path)
folders_with_mtime.append((full_path, mtime))
self.logger(f"发现版本文件夹: {f} (修改时间: {datetime.fromtimestamp(mtime)})")
latest = max(folders_with_mtime, key=lambda x: x[1])[0]
return latest
except Exception as e:
self.logger(f"获取最新文件夹失败: {str(e)}", "ERROR")
return None
def _execute_backup_move(self, src, dest, is_directory=False):
"""完整的备份移动操作"""
try:
......@@ -384,7 +480,7 @@ class Deployer:
raise
def deploy_frontend(self, frontend_type):
"""精确版前端部署流程"""
"""精确版前端更新流程"""
config = Config.FRONTEND_CONFIG[frontend_type]
backup_config = Config.BACKUP_CONFIG[frontend_type]
self.logger(f"\n===== 开始更新 {frontend_type} 前端 =====", important=True)
......@@ -474,27 +570,40 @@ class Deployer:
self.logger("连接资源已释放", "INFO")
def deploy(self):
"""主更新流程"""
try:
if not self.connect():
return False
# 更新前端(先前台再后台)
if not all(self.deploy_frontend(ft) for ft in ['front', 'admin']):
return False
# 自动发现并更新后端
for service in ['inner', 'external']:
version_info = self._discover_latest_version('backend', service)
if not version_info:
return False
# 更新后端服务
if not all(self.deploy_backend(svc) for svc in ['inner', 'external']):
return False
# 更新配置
Config.BACKEND_PATHS[service]['local'] = version_info['local']
self.logger("\n===== 所有服务更新成功 =====", important=True)
if not self.deploy_backend(service):
return False
# 自动发现并更新前端
for frontend in ['front', 'admin']:
version_info = self._discover_latest_version('frontend', frontend)
if not version_info:
return False
# 更新配置
Config.FRONTEND_CONFIG[frontend]['local_dir'] = version_info['local_dir']
if not self.deploy_frontend(frontend):
return False
self.logger("\n===== 自动发现版本并更新完成 =====", important=True)
return True
except Exception as e:
self.logger(f"更新过程异常: {str(e)}", "ERROR")
self.logger(f"自动更新失败: {str(e)}", "ERROR")
return False
finally:
self._cleanup()
def _backup_jar_file(self, service_type):
"""备份当前的JAR文件"""
......
import os
from datetime import datetime
def get_latest_folder(path):
"""获取指定路径下的最新文件夹(带调试信息)"""
print(f"\n[DEBUG] 开始查找最新文件夹,路径: {path}")
# 确保路径存在
if not os.path.exists(path):
print(f"[ERROR] 路径不存在: {path}")
raise FileNotFoundError(f"指定路径不存在: {path}")
print("[DEBUG] 路径验证通过")
# 获取所有文件夹
try:
all_items = os.listdir(path)
folders = [f for f in all_items if os.path.isdir(os.path.join(path, f))]
print(f"[DEBUG] 找到 {len(folders)} 个文件夹: {folders}")
except Exception as e:
print(f"[ERROR] 读取目录失败: {str(e)}")
raise
# 如果没有文件夹,返回None
if not folders:
print("[WARNING] 未找到任何文件夹")
return None
# 获取每个文件夹的修改时间
folder_times = []
for f in folders:
try:
full_path = os.path.join(path, f)
mtime = os.path.getmtime(full_path)
folder_times.append((f, mtime))
print(f"[DEBUG] 文件夹: {f}, 修改时间: {datetime.fromtimestamp(mtime)}")
except Exception as e:
print(f"[WARNING] 无法获取 {f} 的时间信息: {str(e)}")
continue
# 按修改时间排序,找到最新的文件夹
if not folder_times:
print("[ERROR] 没有可用的文件夹时间信息")
return None
latest_folder = max(folder_times, key=lambda x: x[1])[0]
result = os.path.join(path, latest_folder)
print(f"[DEBUG] 确定最新文件夹: {result}")
return result
def get_latest_jar_from_folder(folder_path):
"""从指定文件夹中获取最新的JAR包(带调试信息)"""
print(f"\n[DEBUG] 开始查找JAR包,路径: {folder_path}")
# 获取文件夹中的所有文件
try:
files = [f for f in os.listdir(folder_path) if f.endswith('.jar')]
print(f"[DEBUG] 找到 {len(files)} 个JAR文件: {files}")
except Exception as e:
print(f"[ERROR] 读取JAR文件失败: {str(e)}")
return None
# 如果没有JAR包,返回None
if not files:
print("[WARNING] 未找到任何JAR文件")
return None
# 获取每个JAR包的修改时间
jar_times = []
for f in files:
try:
full_path = os.path.join(folder_path, f)
mtime = os.path.getmtime(full_path)
jar_times.append((f, mtime))
print(f"[DEBUG] JAR文件: {f}, 修改时间: {datetime.fromtimestamp(mtime)}")
except Exception as e:
print(f"[WARNING] 无法获取 {f} 的时间信息: {str(e)}")
continue
# 按修改时间排序,找到最新的JAR包
if not jar_times:
print("[ERROR] 没有可用的JAR文件时间信息")
return None
latest_jar = max(jar_times, key=lambda x: x[1])[0]
result = os.path.join(folder_path, latest_jar)
print(f"[DEBUG] 确定最新JAR文件: {result}")
return result
def get_frontend_files_from_folder(folder_path, files_to_update):
"""从指定文件夹中获取前端文件(带调试信息)"""
print(f"\n[DEBUG] 开始查找前端文件,路径: {folder_path}")
print(f"[DEBUG] 需要查找的文件模式: {files_to_update}")
frontend_files = {}
for item in files_to_update:
try:
if item.endswith('/'):
dir_name = item[:-1]
dir_path = os.path.join(folder_path, dir_name)
if os.path.exists(dir_path):
files = [os.path.join(dir_name, f) for f in os.listdir(dir_path)]
frontend_files[dir_name] = files
print(f"[DEBUG] 找到目录 {dir_name}, 包含 {len(files)} 个文件")
else:
frontend_files[dir_name] = []
print(f"[WARNING] 目录不存在: {dir_path}")
elif item == '*.worker.js':
worker_files = [f for f in os.listdir(folder_path) if f.endswith('.worker.js')]
frontend_files['worker.js'] = worker_files
print(f"[DEBUG] 找到 {len(worker_files)} 个worker文件: {worker_files}")
else:
file_path = os.path.join(folder_path, item)
if os.path.exists(file_path):
frontend_files[item] = item
print(f"[DEBUG] 找到文件: {item}")
else:
frontend_files[item] = None
print(f"[WARNING] 文件不存在: {item}")
except Exception as e:
print(f"[ERROR] 处理 {item} 时出错: {str(e)}")
frontend_files[item] = None
print(f"[DEBUG] 最终获取的文件清单: {frontend_files}")
return frontend_files
def main():
"""主测试函数"""
print("=" * 50 + "\n开始测试\n" + "=" * 50)
# 测试路径配置
test_paths = {
"backend": r"\\192.168.9.9\deploy\01会议预定\标准版本-长期运维\01版本管理\01后端运行服务\内部预定",
"frontend": r"\\192.168.9.9\deploy\00项目管理\2025\L 兰州中石化项目\01版本管理\02前端PC网页",
"local_test": os.path.join(os.path.dirname(__file__), "test_data") # 本地测试路径
}
# 路径连通性测试
print("\n[TEST] 正在验证路径连通性...")
for name, path in test_paths.items():
print(f"{name}路径: {path} {'可用' if os.path.exists(path) else '不可用'}")
# 正式测试
test_cases = [
("后端服务测试", test_paths["backend"], ['*.jar']),
("前端服务测试", test_paths["frontend"], ['*.worker.js', 'index.html', 'static/']),
("本地测试", test_paths["local_test"], ['test.txt', 'subdir/'])
]
for name, path, patterns in test_cases:
print(f"\n{'=' * 20} {name} {'=' * 20}")
print(f"测试路径: {path}")
latest_folder = get_latest_folder(path)
if not latest_folder:
print(f"[RESULT] {name} - 未找到有效文件夹")
continue
print(f"[RESULT] 最新文件夹: {latest_folder}")
# 根据模式执行不同测试
if '*.jar' in patterns:
jar_file = get_latest_jar_from_folder(latest_folder)
print(f"[RESULT] 最新JAR文件: {jar_file if jar_file else '未找到'}")
else:
files = get_frontend_files_from_folder(latest_folder, patterns)
print(f"[RESULT] 找到的文件: {files}")
if __name__ == "__main__":
main()
print("\n测试完成,请检查上方调试信息")
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论