Files
comfyui_o1key/utils/updater.py
T

128 lines
4.8 KiB
Python

"""Safely fast-forward a Git installation of this node package."""
import ipaddress
import os
import subprocess
import sys
import tempfile
from pathlib import Path
from urllib.parse import urlsplit
PLUGIN_DIR = Path(__file__).resolve().parent.parent
class UpdateError(Exception):
pass
def is_local_management_request(request):
"""Allow software management only from the local ComfyUI browser."""
peer = request.transport.get_extra_info("peername") if request.transport else None
if not peer:
return False
try:
if not ipaddress.ip_address(peer[0]).is_loopback:
return False
host = request.headers.get("Host", "")
host_url = urlsplit(f"http://{host}")
hostname = host_url.hostname
if host_url.username or host_url.password or host_url.path or host_url.query or host_url.fragment:
return False
if hostname != "localhost" and not ipaddress.ip_address(hostname).is_loopback:
return False
origin = request.headers.get("Origin", "")
parsed_origin = urlsplit(origin)
except (ValueError, TypeError):
return False
return (
parsed_origin.scheme == request.scheme
and parsed_origin.netloc.lower() == host.lower()
and not parsed_origin.path
and not parsed_origin.query
and not parsed_origin.fragment
)
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,请先安装 Git。") from exc
except subprocess.TimeoutExpired as exc:
raise UpdateError("Git 操作超时,请检查网络后重试。") from exc
if check and result.returncode:
detail = (result.stderr or result.stdout).strip().splitlines()
raise UpdateError(detail[-1] if detail else "Git 操作失败。")
return result
def _install_requirements(requirements):
fd, filename = tempfile.mkstemp(prefix=".o1key-requirements-", suffix=".txt", dir=PLUGIN_DIR)
try:
with os.fdopen(fd, "w", encoding="utf-8") as req_file:
req_file.write(requirements)
try:
result = subprocess.run(
[sys.executable, "-m", "pip", "install", "-r", filename],
cwd=PLUGIN_DIR,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
timeout=600,
)
except subprocess.TimeoutExpired as exc:
raise UpdateError("依赖安装超时,节点代码尚未更新,请检查网络后重试。") from exc
if result.returncode:
raise UpdateError("依赖安装失败,节点代码尚未更新,请检查 ComfyUI 的 Python 环境后重试。")
finally:
Path(filename).unlink(missing_ok=True)
def update_package():
"""Update origin/main without discarding local changes or switching branches."""
if not (PLUGIN_DIR / ".git").exists():
raise UpdateError("当前节点包不是 Git 安装。请通过 Git 安装后再使用界面更新。")
branch = _git("symbolic-ref", "--quiet", "--short", "HEAD", check=False)
if branch.returncode or branch.stdout.strip() != "main":
raise UpdateError("当前不在 main 分支,请手动检查分支后更新。")
if _git("status", "--porcelain", "--untracked-files=no").stdout.strip():
raise UpdateError("节点包有本地修改,请先保存或处理修改后再更新。")
old_commit = _git("rev-parse", "HEAD").stdout.strip()
old_requirements = _git("show", "HEAD:requirements.txt", check=False).stdout
_git("fetch", "origin", "main")
new_commit = _git("rev-parse", "FETCH_HEAD").stdout.strip()
if old_commit == new_commit:
return {"updated": False, "version": old_commit[:7], "requirements_changed": False}
if _git("merge-base", "--is-ancestor", "HEAD", "FETCH_HEAD", check=False).returncode:
raise UpdateError("本地与 origin/main 已分叉,无法安全快进。请手动处理。")
new_requirements = _git("show", "FETCH_HEAD:requirements.txt", check=False).stdout
requirements_changed = old_requirements != new_requirements
if requirements_changed and new_requirements.strip():
_install_requirements(new_requirements)
_git("merge", "--ff-only", "FETCH_HEAD")
return {
"updated": True,
"version": new_commit[:7],
"requirements_changed": requirements_changed,
}