[patch] 新增自动更新功能 模块Test-center
This commit is contained in:
164
auto_updater.py
164
auto_updater.py
@@ -133,12 +133,14 @@ class UpdateDownloader(QThread):
|
|||||||
|
|
||||||
def install_update_and_restart(installer_path):
|
def install_update_and_restart(installer_path):
|
||||||
"""
|
"""
|
||||||
启动静默安装并自动重启程序
|
简化版更新安装(文件替换方式,不使用NSIS安装)
|
||||||
|
|
||||||
工作流程:
|
工作流程:
|
||||||
1. 启动NSIS安装包(静默模式)
|
1. 将安装包移动到程序目录
|
||||||
2. 启动重启启动器(等待安装完成后重启)
|
2. 创建替换脚本
|
||||||
3. 当前程序退出
|
3. 脚本等待程序退出
|
||||||
|
4. 脚本解压并替换文件
|
||||||
|
5. 重启程序
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
installer_path: 安装包路径
|
installer_path: 安装包路径
|
||||||
@@ -156,55 +158,153 @@ def install_update_and_restart(installer_path):
|
|||||||
else:
|
else:
|
||||||
# 开发环境
|
# 开发环境
|
||||||
current_exe = os.path.abspath("main.py")
|
current_exe = os.path.abspath("main.py")
|
||||||
current_dir = os.getcwd()
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
exe_name = "main.exe"
|
exe_name = "main.exe"
|
||||||
|
|
||||||
print(f"[Updater] 当前程序: {current_exe}")
|
print(f"[Updater] 当前程序: {current_exe}")
|
||||||
print(f"[Updater] 安装目录: {current_dir}")
|
print(f"[Updater] 程序目录: {current_dir}")
|
||||||
print(f"[Updater] 主程序名: {exe_name}")
|
print(f"[Updater] 主程序名: {exe_name}")
|
||||||
|
print(f"[Updater] 安装包路径: {installer_path}")
|
||||||
|
|
||||||
# 创建重启启动器脚本
|
# 🔥 关键改进:将安装包移动到程序目录(避免跨盘符问题)
|
||||||
restart_script_path = create_restart_launcher(current_dir, exe_name)
|
installer_name = os.path.basename(installer_path)
|
||||||
|
target_installer_path = os.path.join(current_dir, installer_name)
|
||||||
|
|
||||||
# 启动NSIS安装包
|
# 如果安装包不在程序目录,移动过去
|
||||||
# 参数说明:
|
if os.path.abspath(installer_path) != os.path.abspath(target_installer_path):
|
||||||
# /S - 静默安装
|
try:
|
||||||
# /D=目录 - 指定安装目录(必须是最后一个参数)
|
import shutil
|
||||||
install_cmd = [
|
shutil.move(installer_path, target_installer_path)
|
||||||
|
print(f"[Updater] 安装包已移动到程序目录: {target_installer_path}")
|
||||||
|
installer_path = target_installer_path
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Updater] 移动安装包失败,使用原路径: {e}")
|
||||||
|
|
||||||
|
# 创建更新脚本
|
||||||
|
update_script_path = create_update_installer_script(
|
||||||
installer_path,
|
installer_path,
|
||||||
'/S', # 静默安装
|
current_dir,
|
||||||
f'/D={current_dir}' # 安装到当前目录(覆盖)
|
exe_name
|
||||||
]
|
)
|
||||||
|
|
||||||
print(f"[Updater] 启动安装命令: {' '.join(install_cmd)}")
|
# 启动更新脚本(独立进程)
|
||||||
|
print(f"[Updater] 启动更新脚本: {update_script_path}")
|
||||||
# 启动安装进程(独立进程,不受父进程影响)
|
|
||||||
subprocess.Popen(
|
subprocess.Popen(
|
||||||
install_cmd,
|
['cmd', '/c', update_script_path],
|
||||||
creationflags=subprocess.CREATE_NO_WINDOW | subprocess.DETACHED_PROCESS,
|
creationflags=subprocess.CREATE_NO_WINDOW | subprocess.DETACHED_PROCESS,
|
||||||
close_fds=True
|
close_fds=True
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"[Updater] ✅ 安装包已启动")
|
print(f"[Updater] ✅ 更新脚本已启动")
|
||||||
|
|
||||||
# 同时启动重启启动器(延迟15秒启动,确保安装完成)
|
|
||||||
subprocess.Popen(
|
|
||||||
['cmd', '/c', restart_script_path],
|
|
||||||
creationflags=subprocess.CREATE_NO_WINDOW | subprocess.DETACHED_PROCESS,
|
|
||||||
close_fds=True
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"[Updater] ✅ 重启启动器已启动")
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Updater] ❌ 启动安装失败: {e}")
|
print(f"[Updater] ❌ 启动更新失败: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def create_update_installer_script(installer_path, install_dir, exe_name):
|
||||||
|
"""
|
||||||
|
创建更新安装脚本(在程序目录下执行)
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. 等待主程序退出
|
||||||
|
2. 在程序目录下执行NSIS安装
|
||||||
|
3. 清理安装包
|
||||||
|
4. 重启程序
|
||||||
|
|
||||||
|
Args:
|
||||||
|
installer_path: 安装包路径(应该已经在程序目录下)
|
||||||
|
install_dir: 程序安装目录
|
||||||
|
exe_name: 主程序文件名
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 脚本路径
|
||||||
|
"""
|
||||||
|
# 脚本放在程序目录下,避免权限问题
|
||||||
|
script_path = os.path.join(install_dir, "update_installer.bat")
|
||||||
|
|
||||||
|
# 转换为绝对路径(处理引号问题)
|
||||||
|
installer_path = os.path.abspath(installer_path)
|
||||||
|
install_dir = os.path.abspath(install_dir)
|
||||||
|
|
||||||
|
script_content = f"""@echo off
|
||||||
|
chcp 65001 > nul
|
||||||
|
title 水滴AI客服智能助手 - 自动更新
|
||||||
|
|
||||||
|
echo ============================================
|
||||||
|
echo 水滴AI客服智能助手 - 自动更新
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 切换到程序目录
|
||||||
|
cd /d "{install_dir}"
|
||||||
|
echo [INFO] 程序目录: {install_dir}
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 等待主程序退出(最多等待30秒)
|
||||||
|
echo [INFO] 等待主程序退出...
|
||||||
|
set count=0
|
||||||
|
:wait_main_exit
|
||||||
|
tasklist /FI "IMAGENAME eq {exe_name}" 2>NUL | find /I "{exe_name}" >NUL
|
||||||
|
if NOT ERRORLEVEL 1 (
|
||||||
|
if %count% LSS 30 (
|
||||||
|
timeout /t 1 /nobreak > nul
|
||||||
|
set /a count+=1
|
||||||
|
goto wait_main_exit
|
||||||
|
) else (
|
||||||
|
echo [WARN] 主程序未正常退出,强制继续安装
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo [OK] 主程序已退出
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 执行静默安装(在当前目录下)
|
||||||
|
echo [INFO] 开始安装更新...
|
||||||
|
echo [INFO] 安装包: "{installer_path}"
|
||||||
|
echo [INFO] 目标目录: "{install_dir}"
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 使用完整路径和引号避免空格问题
|
||||||
|
"{installer_path}" /S /D="{install_dir}"
|
||||||
|
|
||||||
|
REM 等待安装完成
|
||||||
|
echo [INFO] 等待安装完成...
|
||||||
|
timeout /t 20 /nobreak > nul
|
||||||
|
|
||||||
|
REM 清理安装包
|
||||||
|
echo [INFO] 清理安装包...
|
||||||
|
del /f /q "{installer_path}" 2>nul
|
||||||
|
|
||||||
|
REM 启动新版本程序
|
||||||
|
echo [INFO] 正在启动新版本...
|
||||||
|
if exist "{install_dir}\\{exe_name}" (
|
||||||
|
start "" "{install_dir}\\{exe_name}"
|
||||||
|
echo [OK] ✅ 程序已启动
|
||||||
|
) else (
|
||||||
|
echo [ERROR] ❌ 找不到程序文件: {exe_name}
|
||||||
|
echo [INFO] 请手动启动程序
|
||||||
|
pause
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 延迟后删除自己
|
||||||
|
timeout /t 2 /nobreak > nul
|
||||||
|
del /f /q "%~f0" 2>nul
|
||||||
|
exit
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(script_path, 'w', encoding='utf-8-sig') as f:
|
||||||
|
f.write(script_content)
|
||||||
|
print(f"[Updater] 更新脚本已创建: {script_path}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Updater] 创建更新脚本失败: {e}")
|
||||||
|
|
||||||
|
return script_path
|
||||||
|
|
||||||
|
|
||||||
def create_restart_launcher(install_dir, exe_name):
|
def create_restart_launcher(install_dir, exe_name):
|
||||||
"""
|
"""
|
||||||
创建重启启动器脚本
|
创建重启启动器脚本
|
||||||
|
|||||||
3
main.py
3
main.py
@@ -1233,7 +1233,8 @@ class LoginWindow(QMainWindow):
|
|||||||
"""显示更新对话框(信号槽函数,始终在主线程中执行)"""
|
"""显示更新对话框(信号槽函数,始终在主线程中执行)"""
|
||||||
try:
|
try:
|
||||||
self.add_log(f"🎯 主线程收到更新信号: v{latest_version}", "INFO")
|
self.add_log(f"🎯 主线程收到更新信号: v{latest_version}", "INFO")
|
||||||
self.show_update_notification(latest_version, download_url)
|
# 🔥 修改:调用新的trigger_update方法(支持自动更新)
|
||||||
|
self.trigger_update(download_url, latest_version)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.add_log(f"❌ 显示更新对话框失败: {e}", "ERROR")
|
self.add_log(f"❌ 显示更新对话框失败: {e}", "ERROR")
|
||||||
import traceback
|
import traceback
|
||||||
|
|||||||
Reference in New Issue
Block a user