diff --git a/claude/uclaude_updater.py b/claude/uclaude_updater.py index 046f6ce..7fdc81c 100644 --- a/claude/uclaude_updater.py +++ b/claude/uclaude_updater.py @@ -69,6 +69,13 @@ def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) +def run_cmd(cmd, **kwargs): + """run_cmd wrapper: uses shell=True on Windows for .cmd/.bat commands (npm, claude, etc.).""" + if IS_WINDOWS: + kwargs.setdefault("shell", True) + return subprocess.run(cmd, **kwargs) + + # ============================================================ # Node.js check and auto-install # ============================================================ @@ -79,7 +86,7 @@ MIN_NODE_VERSION = (24, 13, 0) def get_node_version(): """Get installed Node.js version as tuple, or None.""" try: - result = subprocess.run( + result = run_cmd( ["node", "--version"], capture_output=True, text=True, timeout=10, ) @@ -103,7 +110,7 @@ def install_node(): if IS_MACOS: print(f" Installing Node.js via Homebrew...") try: - result = subprocess.run( + result = run_cmd( ["brew", "install", "node"], timeout=120, capture_output=True, text=True, ) @@ -125,7 +132,7 @@ def install_node(): if os.path.isfile(old_list): os.remove(old_list) - result = subprocess.run( + result = run_cmd( ["bash", "-c", "curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && apt-get install -y nodejs"], timeout=180, capture_output=True, text=True, ) @@ -140,7 +147,7 @@ def install_node(): # Try dnf/yum fallback for RHEL/Fedora for pkg_mgr in ["dnf", "yum"]: if shutil.which(pkg_mgr): - result = subprocess.run( + result = run_cmd( ["bash", "-c", f"curl -fsSL https://rpm.nodesource.com/setup_24.x | bash - && {pkg_mgr} install -y nodejs"], timeout=180, capture_output=True, text=True, ) @@ -213,7 +220,7 @@ def ensure_claude_code(target_version=None): pkg = "@anthropic-ai/claude-code" + (f"@{target_version}" if target_version else "") try: - result = subprocess.run( + result = run_cmd( ["npm", "install", "-g", pkg], capture_output=True, text=True, timeout=300, ) @@ -291,7 +298,7 @@ def get_installed_version(): # 2. claude --version try: - result = subprocess.run( + result = run_cmd( ["claude", "--version"], capture_output=True, text=True, timeout=10, ) @@ -362,13 +369,13 @@ def git_pull(): """Pull latest changes from remote (shallow fetch for minimal download).""" try: # Shallow fetch + reset — downloads only latest commit, not full history - result = subprocess.run( + result = run_cmd( ["git", "fetch", "--depth", "1", "origin", "master"], cwd=REPO_ROOT, capture_output=True, text=True, timeout=60, ) if result.returncode != 0: # Fallback to regular pull - result = subprocess.run( + result = run_cmd( ["git", "pull", "--quiet"], cwd=REPO_ROOT, capture_output=True, text=True, timeout=60, ) @@ -377,7 +384,7 @@ def git_pull(): return True # Reset to fetched state - subprocess.run( + run_cmd( ["git", "reset", "--hard", "origin/master"], cwd=REPO_ROOT, capture_output=True, text=True, timeout=10, ) @@ -414,7 +421,7 @@ def _setup_sparse_checkout(): return # Enable sparse checkout - subprocess.run( + run_cmd( ["git", "config", "core.sparseCheckout", "true"], cwd=REPO_ROOT, capture_output=True, ) @@ -432,7 +439,7 @@ def _setup_sparse_checkout(): f.write("\n".join(patterns) + "\n") # Apply sparse checkout - subprocess.run( + run_cmd( ["git", "checkout", "HEAD", "--", "."], cwd=REPO_ROOT, capture_output=True, timeout=30, ) @@ -461,7 +468,7 @@ def install_cli_js(version, cli_js_path): os.chmod(cli_js_path, 0o755) # Syntax check - result = subprocess.run( + result = run_cmd( ["node", "--check", cli_js_path], capture_output=True, text=True, timeout=30, ) @@ -748,7 +755,7 @@ def _set_user_env_windows(config): for key, val in env_vars.items(): try: - subprocess.run(["setx", key, val], capture_output=True, check=True) + run_cmd(["setx", key, val], capture_output=True, check=True) os.environ[key] = val except Exception: pass