做安装包版本

This commit is contained in:
2026-04-07 23:21:58 +08:00
parent d583e711f0
commit 920d142d9c
11 changed files with 669 additions and 42 deletions
+73 -5
View File
@@ -7,12 +7,15 @@ import os
import sys
import subprocess
import tempfile
import json
from PyQt6.QtCore import QThread, pyqtSignal, QObject
from PyQt6.QtWidgets import QProgressDialog, QMessageBox, QApplication
import urllib.request
UPDATE_CHECK_URL = "https://api.yunzer.cn/api/softwareupgrade/check?code=niumasortware"
APP_NAME = "CleanDesktopOrganizer"
UPDATE_TASK_NAME = r"CleanDesktopOrganizer\Update"
def _current_version() -> str:
@@ -74,6 +77,51 @@ class _DownloadWorker(QThread):
self.error.emit(str(e))
def _programdata_dir() -> str:
base = os.environ.get("PROGRAMDATA") or r"C:\ProgramData"
return os.path.join(base, APP_NAME)
def _ensure_dir(p: str):
try:
os.makedirs(p, exist_ok=True)
except Exception:
pass
def _update_work_dir() -> str:
"""
全局安装场景下,Program Files 不可写;更新文件与请求文件统一放 ProgramData。
该目录应由安装器创建并赋予 Users Modify 权限。
"""
d = os.path.join(_programdata_dir(), "updates")
_ensure_dir(d)
if os.path.isdir(d):
return d
return tempfile.gettempdir()
def _request_file_path() -> str:
d = _programdata_dir()
_ensure_dir(d)
return os.path.join(d, "update_request.json")
def _run_update_task() -> bool:
try:
r = subprocess.run(
["schtasks", "/Run", "/TN", UPDATE_TASK_NAME],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
creationflags=subprocess.CREATE_NO_WINDOW,
)
return r.returncode == 0
except Exception:
return False
def _replace_and_restart(new_exe: str):
"""
onedir 模式:只替换 exe 本身,dll 等文件不变。
@@ -178,11 +226,8 @@ class Updater(QObject):
QMessageBox.warning(self._parent_widget, "检查更新失败", f"无法连接更新服务器:\n{err}")
def _download(self, url: str):
# 下载到原 exe 同目录,避免跨盘 move 失败
if getattr(sys, "frozen", False):
dest_dir = os.path.dirname(sys.executable)
else:
dest_dir = tempfile.gettempdir()
# 全局安装:不要下载到安装目录(通常在 Program Files,不可写)
dest_dir = _update_work_dir() if getattr(sys, "frozen", False) else tempfile.gettempdir()
dest = os.path.join(dest_dir, "_update_new.exe")
self._progress = QProgressDialog("正在下载更新...", "取消", 0, 100, self._parent_widget)
@@ -199,6 +244,29 @@ class Updater(QObject):
def _on_download_done(self, path: str):
self._progress.close()
# frozen 且全局安装:交给计划任务(最高权限)覆盖安装目录 exe
if getattr(sys, "frozen", False):
try:
req = {
"src": path,
"dst": sys.executable,
"pid": os.getpid(),
"restart": True,
}
with open(_request_file_path(), "w", encoding="utf-8") as f:
json.dump(req, f, ensure_ascii=False)
except Exception as e:
QMessageBox.critical(self._parent_widget, "更新失败", f"无法准备更新任务:\n{e}")
return
if _run_update_task():
QApplication.quit()
return
# 兜底:如果计划任务不存在/失败,尝试旧的自替换方式(仅当安装目录可写时有效)
_replace_and_restart(path)
return
_replace_and_restart(path)
def _on_download_error(self, err: str):