fix(pygit2): address review findings + bump version to 4.2b1

- C1: add submodule_update() after pygit2 clone for recursive parity
- C2: check subprocess returncode in submodule_update fallback
- C3: move GIT_OPT_SET_OWNER_VALIDATION to module-level (once at import)
- H1: use context manager in git_pull() to prevent resource leaks
- Bump version to 4.2b1, version_code to [4, 2]
- Add pygit2 to dev dependencies and requirements.txt
This commit is contained in:
Dr.Lt.Data 2026-03-27 08:33:15 +09:00
parent 8831f1ffcf
commit 73f4dd5637
5 changed files with 43 additions and 33 deletions

View File

@ -42,6 +42,12 @@ if USE_PYGIT2:
USE_PYGIT2 = False USE_PYGIT2 = False
_PYGIT2_REQUESTED = False _PYGIT2_REQUESTED = False
import git as _git import git as _git
else:
# Disable owner validation once at import time.
# Required for Desktop 2.0 standalone installs where repo directories
# may be owned by a different user (e.g., system-installed paths).
# See CVE-2022-24765 for context on this validation.
_pygit2.option(_pygit2.GIT_OPT_SET_OWNER_VALIDATION, 0)
if not USE_PYGIT2: if not USE_PYGIT2:
import git as _git import git as _git
@ -372,7 +378,6 @@ class _GitPythonRepo(GitRepo):
class _Pygit2Repo(GitRepo): class _Pygit2Repo(GitRepo):
def __init__(self, path): def __init__(self, path):
_pygit2.option(_pygit2.GIT_OPT_SET_OWNER_VALIDATION, 0)
repo_path = os.path.abspath(path) repo_path = os.path.abspath(path)
git_dir = os.path.join(repo_path, '.git') git_dir = os.path.join(repo_path, '.git')
for sub in ['refs/heads', 'refs/tags', 'refs/remotes']: for sub in ['refs/heads', 'refs/tags', 'refs/remotes']:
@ -750,18 +755,24 @@ class _Pygit2Repo(GitRepo):
try: try:
self._repo.submodules.init() self._repo.submodules.init()
self._repo.submodules.update() self._repo.submodules.update()
except (AttributeError, Exception) as e: except Exception as e:
import subprocess import subprocess
try: try:
subprocess.run( result = subprocess.run(
['git', 'submodule', 'update', '--init', '--recursive'], ['git', 'submodule', 'update', '--init', '--recursive'],
cwd=self._working_dir, cwd=self._working_dir,
capture_output=True, timeout=120, capture_output=True, timeout=120,
) )
if result.returncode != 0:
raise GitCommandError(
f"submodule update failed (exit {result.returncode}): "
f"{result.stderr.decode(errors='replace')}")
except FileNotFoundError: except FileNotFoundError:
print(f"[ComfyUI-Manager] pygit2: submodule update requires system git (not installed): {e}", file=sys.stderr) print(f"[ComfyUI-Manager] pygit2: submodule update requires system git (not installed)", file=sys.stderr)
except Exception: except GitCommandError:
print(f"[ComfyUI-Manager] pygit2: submodule update failed: {e}", file=sys.stderr) raise
except Exception as sub_e:
print(f"[ComfyUI-Manager] pygit2: submodule update failed: {sub_e}", file=sys.stderr)
def clear_cache(self): def clear_cache(self):
pass pass
@ -813,9 +824,10 @@ def clone_repo(url, dest, progress=None):
(checkout, clear_cache, close, etc.). (checkout, clear_cache, close, etc.).
""" """
if USE_PYGIT2: if USE_PYGIT2:
_pygit2.option(_pygit2.GIT_OPT_SET_OWNER_VALIDATION, 0)
_pygit2.clone_repository(url, dest) _pygit2.clone_repository(url, dest)
return _Pygit2Repo(dest) repo = _Pygit2Repo(dest)
repo.submodule_update()
return repo
else: else:
if progress is None: if progress is None:
r = _git.Repo.clone_from(url, dest, recursive=True) r = _git.Repo.clone_from(url, dest, recursive=True)

View File

@ -44,7 +44,7 @@ from ..common.enums import NetworkMode, SecurityLevel, DBMode
from ..common import context from ..common import context
version_code = [4, 1] version_code = [4, 2]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '') version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
@ -2282,31 +2282,28 @@ def git_pull(path):
if platform.system() == "Windows": if platform.system() == "Windows":
return __win_check_git_pull(path) return __win_check_git_pull(path)
else: else:
repo = open_repo(path) with open_repo(path) as repo:
if repo.is_dirty():
print(f"STASH: '{path}' is dirty.")
repo.stash()
if repo.is_dirty(): if repo.head_is_detached:
print(f"STASH: '{path}' is dirty.") if not switch_to_default_branch(repo):
repo.stash() raise ValueError(f"Failed to switch to default branch while pulling: {path}")
if repo.head_is_detached: branch_name = repo.active_branch_name
if not switch_to_default_branch(repo): remote_name = repo.get_tracking_remote_name()
raise ValueError(f"Failed to switch to default branch while pulling: {path}")
branch_name = repo.active_branch_name try:
remote_name = repo.get_tracking_remote_name() repo.pull_ff_only()
except GitCommandError:
backup_name = get_backup_branch_name(repo)
repo.create_backup_branch(backup_name)
logging.info(f"[ComfyUI-Manager] Cannot fast-forward. Backup created: {backup_name}")
repo.reset_hard(f'{remote_name}/{branch_name}')
logging.info(f"[ComfyUI-Manager] Reset to {remote_name}/{branch_name}")
try: repo.submodule_update()
repo.pull_ff_only()
except GitCommandError:
backup_name = get_backup_branch_name(repo)
repo.create_backup_branch(backup_name)
logging.info(f"[ComfyUI-Manager] Cannot fast-forward. Backup created: {backup_name}")
repo.reset_hard(f'{remote_name}/{branch_name}')
logging.info(f"[ComfyUI-Manager] Reset to {remote_name}/{branch_name}")
repo.submodule_update()
repo.close()
return True return True

View File

@ -42,7 +42,7 @@ from ..common.enums import NetworkMode, SecurityLevel, DBMode
from ..common import context from ..common import context
version_code = [4, 1] version_code = [4, 2]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '') version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')

View File

@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "comfyui-manager" name = "comfyui-manager"
license = { text = "GPL-3.0-only" } license = { text = "GPL-3.0-only" }
version = "4.1" version = "4.2b1"
requires-python = ">= 3.9" requires-python = ">= 3.9"
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI." description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
readme = "README.md" readme = "README.md"
@ -39,7 +39,7 @@ dependencies = [
] ]
[project.optional-dependencies] [project.optional-dependencies]
dev = ["pre-commit", "pytest", "ruff", "pytest-cov"] dev = ["pre-commit", "pytest", "ruff", "pytest-cov", "pygit2"]
[project.urls] [project.urls]
Repository = "https://github.com/ltdrdata/ComfyUI-Manager" Repository = "https://github.com/ltdrdata/ComfyUI-Manager"

View File

@ -1,4 +1,5 @@
GitPython GitPython
pygit2
PyGithub PyGithub
# matrix-nio # matrix-nio
transformers transformers