feat: 升级下载 zip 后解压还原 jar

上传端为绕过中间设备对 jar 的拦截,升级包以 zip 下发;Agent 下载 zip 并 sha256 校验后,解压取内层 jar 再走替换/重启/回滚。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-09-09 14:32:29 +08:00
co-authored by Claude Code
parent 0b13de14eb
commit f7fbc054df
+26
View File
@@ -21,6 +21,7 @@ import shutil
import subprocess
import threading
import time
import zipfile
import requests
@@ -118,6 +119,18 @@ class UpgradeCommand:
return self._fail(version, f"SHA-256 校验失败:expected={sha256}, actual={actual}")
log.info("SHA-256 校验通过")
# ===== 2.5 解压 zip 得到内层 jar(上传端为绕过中间设备拦截,jar 被打成 zip) =====
extracted_jar = os.path.join("tmp", f"upgrade-{version}.jar.extracted")
try:
result = extract_jar_from_zip(tmp_jar, extracted_jar)
if result is None:
os.remove(tmp_jar)
return self._fail(version, "zip 包内未找到 jar 文件")
os.remove(tmp_jar) # 删除临时 zip
tmp_jar = extracted_jar # 后续替换使用解压出的真 jar
except Exception as e:
return self._fail(version, f"解压 zip 失败:{e}")
# ===== 3. 备份 =====
# jar 路径来自配置(main.jar.path),适配不同部署结构
#(如 Agent 与主程序分目录、jar 文件名带版本号等)
@@ -333,6 +346,19 @@ def copy_with_retry(src: str, dst: str, retries: int):
) from last_error
def extract_jar_from_zip(zip_path: str, dst_jar: str) -> str | None:
"""从 zip 中取出第一个 .jar 文件写到 dst_jar,返回 dst_jar;找不到返回 None。"""
with zipfile.ZipFile(zip_path) as z:
jars = [n for n in z.namelist()
if n.lower().endswith(".jar") and not n.endswith("/")]
if not jars:
return None
with z.open(jars[0]) as src, open(dst_jar, "wb") as dst:
shutil.copyfileobj(src, dst)
log.info("解压 zip 得到 jar%s%s", jars[0], os.path.basename(dst_jar))
return dst_jar
def sha256_file(path: str) -> str:
"""文件 SHA-256(分块读取,支持大文件)。"""
digest = hashlib.sha256()