119 lines
4.4 KiB
Python
119 lines
4.4 KiB
Python
"""Fast-forward a clean Git installation from the public O1Key release repo."""
|
|
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
PLUGIN_DIR = Path(__file__).resolve().parent.parent
|
|
RELEASE_REPOSITORY_URL = "https://git.o1key.com/publisher/comfyui_o1key.git"
|
|
|
|
|
|
class UpdateError(Exception):
|
|
def __init__(self, code, message, suggestion, status=409):
|
|
super().__init__(message)
|
|
self.code = code
|
|
self.suggestion = suggestion
|
|
self.status = status
|
|
|
|
def as_dict(self):
|
|
return {
|
|
"code": self.code,
|
|
"error": str(self),
|
|
"suggestion": self.suggestion,
|
|
}
|
|
|
|
|
|
def _git(*args, timeout=60, check=True):
|
|
env = os.environ.copy()
|
|
env["GIT_TERMINAL_PROMPT"] = "0"
|
|
env["GCM_INTERACTIVE"] = "Never"
|
|
try:
|
|
result = subprocess.run(
|
|
["git", *args], cwd=PLUGIN_DIR, env=env,
|
|
capture_output=True, text=True, encoding="utf-8",
|
|
errors="replace", timeout=timeout,
|
|
)
|
|
except FileNotFoundError as exc:
|
|
raise UpdateError(
|
|
"git_missing", "未找到 Git。", "安装 Git 后重启 ComfyUI,再重新检查更新。", 503,
|
|
) from exc
|
|
except subprocess.TimeoutExpired as exc:
|
|
raise UpdateError(
|
|
"timeout", "连接发布仓库超时。", "检查网络连接,稍后再试。", 504,
|
|
) from exc
|
|
if check and result.returncode:
|
|
raise UpdateError(
|
|
"git_failed", "Git 操作未完成。", "检查插件目录的 Git 状态后重试。",
|
|
)
|
|
return result
|
|
|
|
|
|
def _check_release():
|
|
"""Inspect the release before considering any local file changes."""
|
|
if not (PLUGIN_DIR / ".git").exists():
|
|
raise UpdateError(
|
|
"not_git", "当前插件不是 Git 安装。",
|
|
"请从发布仓库重新以 Git 安装;现有配置文件先单独备份。",
|
|
)
|
|
|
|
branch = _git("symbolic-ref", "--quiet", "--short", "HEAD", check=False)
|
|
if branch.returncode or branch.stdout.strip() != "main":
|
|
raise UpdateError(
|
|
"wrong_branch", "当前不在 main 分支。",
|
|
"请先检查并切换分支;本地分支上的修改不会自动合并。",
|
|
)
|
|
|
|
old_commit = _git("rev-parse", "HEAD").stdout.strip()
|
|
fetched = _git("fetch", "--no-tags", RELEASE_REPOSITORY_URL, "main", timeout=90, check=False)
|
|
if fetched.returncode:
|
|
raise UpdateError(
|
|
"fetch_failed", "无法获取发布仓库的 main 分支。",
|
|
"检查 git.o1key.com 的网络连接与仓库读取权限,稍后重试。", 503,
|
|
)
|
|
new_commit = _git("rev-parse", "FETCH_HEAD").stdout.strip()
|
|
if old_commit == new_commit:
|
|
return old_commit, new_commit
|
|
|
|
if _git("merge-base", "--is-ancestor", "HEAD", "FETCH_HEAD", check=False).returncode:
|
|
raise UpdateError(
|
|
"diverged", "本地提交与发布仓库已分叉,无法自动快进。",
|
|
"请手动比较两个分支并合并,不要强制重置本地文件。",
|
|
)
|
|
|
|
if _git("status", "--porcelain", "--untracked-files=no").stdout.strip():
|
|
raise UpdateError(
|
|
"local_changes", "插件目录有未提交的代码修改。",
|
|
"请先保存、提交或暂存修改;更新不会覆盖这些文件。",
|
|
)
|
|
|
|
return old_commit, new_commit
|
|
|
|
|
|
def check_for_update():
|
|
"""Check whether the installed version can be updated without changing files."""
|
|
old_commit, new_commit = _check_release()
|
|
return {"update_available": old_commit != new_commit}
|
|
|
|
|
|
def update_package():
|
|
"""Fetch the release main branch and fast-forward only a clean local main."""
|
|
old_commit, new_commit = _check_release()
|
|
if old_commit == new_commit:
|
|
return {"updated": False, "version": old_commit[:7], "requirements_changed": False}
|
|
|
|
old_requirements = _git("show", "HEAD:requirements.txt", check=False).stdout
|
|
new_requirements = _git("show", "FETCH_HEAD:requirements.txt", check=False).stdout
|
|
merged = _git("merge", "--ff-only", "FETCH_HEAD", check=False)
|
|
if merged.returncode:
|
|
raise UpdateError(
|
|
"merge_blocked", "更新被本地文件阻止。",
|
|
"检查是否有与新版本重名的未跟踪文件,保留文件后手动处理。",
|
|
)
|
|
requirements_changed = old_requirements != new_requirements
|
|
return {
|
|
"updated": True,
|
|
"version": new_commit[:7],
|
|
"requirements_changed": requirements_changed,
|
|
}
|