fix(git_helper): Windows subprocess crash fix — reinstall URL handling + tqdm pipe deadlock

Two fixes for Windows E2E failures:

1. cm_cli reinstall_node(): resolve_node_spec() returns the full URL
   as node_name, but internal dicts are keyed by repo basename or
   cnr_id. Use get_cnr_by_repo() for CNR-aware lookup with correct
   is_unknown flag, falling back to basename for unknown nodes.

2. git_helper.py gitclone(): disable tqdm progress when stderr is
   piped (sys.stderr.isatty() gate). When a parent process captures
   stderr via PIPE, tqdm output fills the 4KB Windows pipe buffer,
   blocking GitPython's progress reader and causing git clone exit 128.
This commit is contained in:
Dr.Lt.Data 2026-03-21 21:03:34 +09:00
parent 099aed1ad4
commit 41ab628f99
2 changed files with 25 additions and 10 deletions

View File

@ -238,18 +238,29 @@ def install_node(node_spec_str, is_all=False, cnt_msg='', **kwargs):
def reinstall_node(node_spec_str, is_all=False, cnt_msg=''):
node_spec = unified_manager.resolve_node_spec(node_spec_str)
if core.is_valid_url(node_spec_str):
# URL-based: resolve_node_spec returns the full URL as node_name,
# but internal dicts are keyed by repo basename or cnr_id.
url = node_spec_str.rstrip('/')
cnr = unified_manager.get_cnr_by_repo(url)
if cnr:
node_id = cnr['id']
unified_manager.unified_uninstall(node_id, False)
unified_manager.purge_node_state(node_id)
else:
repo_name = os.path.splitext(os.path.basename(url))[0]
unified_manager.unified_uninstall(repo_name, True)
unified_manager.purge_node_state(repo_name)
node_name, version_spec, _ = node_spec
install_node(node_spec_str, is_all=is_all, cnt_msg=cnt_msg, raise_on_fail=True)
else:
node_spec = unified_manager.resolve_node_spec(node_spec_str)
node_name, version_spec, _ = node_spec
# Best-effort uninstall via normal path
unified_manager.unified_uninstall(node_name, version_spec == 'unknown')
unified_manager.unified_uninstall(node_name, version_spec == 'unknown')
unified_manager.purge_node_state(node_name)
# Fallback: purge all state and directories regardless of categorization
# Handles categorization mismatch between cm_cli invocations (e.g. unknown→nightly)
unified_manager.purge_node_state(node_name)
install_node(node_name, is_all=is_all, cnt_msg=cnt_msg, raise_on_fail=True)
install_node(node_name, is_all=is_all, cnt_msg=cnt_msg, raise_on_fail=True)
def fix_node(node_spec_str, is_all=False, cnt_msg=''):

View File

@ -101,7 +101,11 @@ def gitclone(custom_nodes_path, url, target_hash=None, repo_path=None):
repo_path = os.path.join(custom_nodes_path, repo_name)
# Clone the repository from the remote URL
repo = git.Repo.clone_from(url, repo_path, recursive=True, progress=GitProgress())
# Disable tqdm progress when stderr is piped to avoid deadlock on Windows.
# When a parent process captures stderr via PIPE, tqdm output can fill the
# 4KB Windows pipe buffer, blocking GitPython's progress reader and git itself.
progress = GitProgress() if sys.stderr.isatty() else None
repo = git.Repo.clone_from(url, repo_path, recursive=True, progress=progress)
if target_hash is not None:
print(f"CHECKOUT: {repo_name} [{target_hash}]")