feat: release 1.4.0 with isolated upgrades and device discovery

This commit is contained in:
chenmingxuan committed 2026-10-09 15:46:28 +08:00
1 parent a6a3ee2eb0
commit df66897ff4
41 files changed
+1564 -126

No files matched your search

+40 -42
View File
@@ -13,7 +13,6 @@ import subprocess
import sys
import tempfile
import tomllib
import urllib.request
from urllib.parse import urlsplit
ROOT = Path(__file__).resolve().parents[1]
@@ -42,34 +41,16 @@ def git(root: Path, *args: str, timeout: int = 15) -> str:
return result.stdout.strip()
def update_source(root: Path, port: int = 8000) -> int:
"""Return 10 after a fast-forward (caller reloads launcher); never overwrite local work."""
try:
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
with opener.open(f"http://127.0.0.1:{port}/api/status", timeout=2) as response:
status = json.load(response)
if (
status.get("active_tasks")
or status.get("queue")
or (status.get("status") == "running" and status.get("pid"))
):
print(
launch_text(
"[WAIT] Tasks are active. Finish/stop them before updating dependencies or restarting.",
"[等待] 有任务正在执行或排队。请先处理任务,再更新依赖或重启。",
)
)
return 20
except (OSError, ValueError):
pass
def update_source(root: Path, *, check_only: bool = False) -> int:
"""Return 10 for an available/applied fast-forward, 1 on failure; preserve local work."""
if not shutil.which("git"):
print(
launch_text(
"[SKIP] Git is not installed. Continuing with local source; install Git for automatic source updates.",
"[跳过] 未安装 Git,继续使用当前源码;安装 Git 后可自动检查更新。",
"[ERROR] Install Git before upgrading; local source is preserved.",
"[提示] 未安装 Git,无法升级;当前源码保持不变。",
)
)
return 0
return 1
if not (root / ".git").exists():
print(
launch_text(
@@ -77,8 +58,16 @@ def update_source(root: Path, port: int = 8000) -> int:
"[跳过] 旧版源码包不含 Git 信息。请使用新版源码包或 Git 克隆;当前文件保持不变。",
)
)
return 0
return 1
try:
if not check_only and git(root, "status", "--porcelain"):
print(
launch_text(
"[ERROR] Local changes exist; commit or back up first.",
"[错误] 发现本地修改;请先提交或备份后再升级。",
)
)
return 1
branch = git(root, "symbolic-ref", "--quiet", "--short", "HEAD")
remote = git(root, "config", "--get", f"branch.{branch}.remote")
merge_ref = git(root, "config", "--get", f"branch.{branch}.merge")
@@ -98,45 +87,53 @@ def update_source(root: Path, port: int = 8000) -> int:
"[检查] 正在检查当前分支的远程更新...",
)
)
git(root, "fetch", "--no-tags", remote, merge_ref, timeout=45)
git(root, "fetch", "--no-tags", remote, merge_ref, timeout=5 if check_only else 45)
target = git(root, "rev-parse", "FETCH_HEAD")
current = git(root, "rev-parse", "HEAD")
if current == target:
print(launch_text("[OK] Source is up to date.", "[完成] 源码已是最新版。"))
return 0
if check_only:
print(
launch_text(
"[UPDATE] A different upstream revision is available. Run update.bat (update-cn.bat for China mirrors).",
"[更新] 远程版本有变化;请运行 update-cn.bat 或 update.bat 升级。",
)
)
return 10
if git(root, "status", "--porcelain"):
print(
launch_text(
"[SKIP] Upstream differs, but local changes exist. Commit/back up your work before updating; continuing locally.",
"[跳过] 发现本地修改;请先提交或备份。此次继续使用本地源码。",
"[ERROR] Local changes appeared during the check. Upgrade stopped; files preserved.",
"[错误] 检查期间出现本地修改;已停止升级,保留现有文件。",
)
)
return 0
return 1
ancestor = git(root, "merge-base", "HEAD", target)
if ancestor != current:
print(
launch_text(
"[SKIP] Local branch is ahead or diverged. No automatic merge/rebase; continuing locally.",
"[跳过] 本地分支领先或分叉,不自动合并;继续使用本地源码。",
"[ERROR] Local branch is ahead or diverged. Upgrade stopped; no merge/rebase performed.",
"[错误] 本地分支领先或分叉;已停止升级,不自动合并。",
)
)
return 0
return 1
git(root, "merge", "--ff-only", target, timeout=30)
print(
launch_text(
f"[UPDATED] Source {current[:12]} -> {target[:12]}. Reloading launcher and checking dependencies.",
f"[已更新] 源码 {current[:12]} -> {target[:12]},重新载入启动脚本并检查依赖。",
f"[UPDATED] Source {current[:12]} -> {target[:12]}. Preparing the updated installation.",
f"[已更新] 源码 {current[:12]} -> {target[:12]},准备同步新版环境。",
)
)
return 10
except (RuntimeError, subprocess.TimeoutExpired) as exc:
except (OSError, RuntimeError, subprocess.TimeoutExpired) as exc:
print(
launch_text(
f"[WARN] {exc if isinstance(exc, RuntimeError) else 'Git update timed out.'} Continuing with local source.",
"[提示] Git 更新失败或超时;请检查网络、凭据和仓库状态。继续使用当前源码。",
f"[WARN] {exc if isinstance(exc, RuntimeError) else 'Git check failed or timed out.'} Local source is preserved.",
"[提示] Git 检查/更新失败或超时;请检查网络、凭据和仓库状态。当前源码保持不变。",
)
)
return 0
return 1
def _atomic_write(path: Path, text: str) -> None:
@@ -406,12 +403,13 @@ def frontend_fingerprint(root: Path) -> str:
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("action", choices=["source", "mcp", "frontend-check", "frontend-mark"])
parser.add_argument(
"action", choices=["source", "source-check", "mcp", "frontend-check", "frontend-mark"]
)
parser.add_argument("--root", type=Path, default=ROOT)
parser.add_argument("--port", type=int, default=8000)
args = parser.parse_args()
if args.action == "source":
sys.exit(update_source(args.root, args.port))
if args.action in {"source", "source-check"}:
sys.exit(update_source(args.root, check_only=args.action == "source-check"))
elif args.action == "mcp":
sys.exit(refresh_mcp(args.root))
else: