fix: use shell=True on Windows for subprocess calls (npm/claude are .cmd)
Without shell=True, subprocess.run(["npm", ...]) fails on Windows because npm and claude are .cmd batch files, not .exe. Added run_cmd() wrapper that sets shell=True only on Windows, keeping Linux/macOS behavior unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,13 @@ def eprint(*args, **kwargs):
|
|||||||
print(*args, file=sys.stderr, **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
|
# Node.js check and auto-install
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -79,7 +86,7 @@ MIN_NODE_VERSION = (24, 13, 0)
|
|||||||
def get_node_version():
|
def get_node_version():
|
||||||
"""Get installed Node.js version as tuple, or None."""
|
"""Get installed Node.js version as tuple, or None."""
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = run_cmd(
|
||||||
["node", "--version"],
|
["node", "--version"],
|
||||||
capture_output=True, text=True, timeout=10,
|
capture_output=True, text=True, timeout=10,
|
||||||
)
|
)
|
||||||
@@ -103,7 +110,7 @@ def install_node():
|
|||||||
if IS_MACOS:
|
if IS_MACOS:
|
||||||
print(f" Installing Node.js via Homebrew...")
|
print(f" Installing Node.js via Homebrew...")
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = run_cmd(
|
||||||
["brew", "install", "node"],
|
["brew", "install", "node"],
|
||||||
timeout=120, capture_output=True, text=True,
|
timeout=120, capture_output=True, text=True,
|
||||||
)
|
)
|
||||||
@@ -125,7 +132,7 @@ def install_node():
|
|||||||
if os.path.isfile(old_list):
|
if os.path.isfile(old_list):
|
||||||
os.remove(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"],
|
["bash", "-c", "curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && apt-get install -y nodejs"],
|
||||||
timeout=180, capture_output=True, text=True,
|
timeout=180, capture_output=True, text=True,
|
||||||
)
|
)
|
||||||
@@ -140,7 +147,7 @@ def install_node():
|
|||||||
# Try dnf/yum fallback for RHEL/Fedora
|
# Try dnf/yum fallback for RHEL/Fedora
|
||||||
for pkg_mgr in ["dnf", "yum"]:
|
for pkg_mgr in ["dnf", "yum"]:
|
||||||
if shutil.which(pkg_mgr):
|
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"],
|
["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,
|
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 "")
|
pkg = "@anthropic-ai/claude-code" + (f"@{target_version}" if target_version else "")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = run_cmd(
|
||||||
["npm", "install", "-g", pkg],
|
["npm", "install", "-g", pkg],
|
||||||
capture_output=True, text=True, timeout=300,
|
capture_output=True, text=True, timeout=300,
|
||||||
)
|
)
|
||||||
@@ -291,7 +298,7 @@ def get_installed_version():
|
|||||||
|
|
||||||
# 2. claude --version
|
# 2. claude --version
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = run_cmd(
|
||||||
["claude", "--version"],
|
["claude", "--version"],
|
||||||
capture_output=True, text=True, timeout=10,
|
capture_output=True, text=True, timeout=10,
|
||||||
)
|
)
|
||||||
@@ -362,13 +369,13 @@ def git_pull():
|
|||||||
"""Pull latest changes from remote (shallow fetch for minimal download)."""
|
"""Pull latest changes from remote (shallow fetch for minimal download)."""
|
||||||
try:
|
try:
|
||||||
# Shallow fetch + reset — downloads only latest commit, not full history
|
# Shallow fetch + reset — downloads only latest commit, not full history
|
||||||
result = subprocess.run(
|
result = run_cmd(
|
||||||
["git", "fetch", "--depth", "1", "origin", "master"],
|
["git", "fetch", "--depth", "1", "origin", "master"],
|
||||||
cwd=REPO_ROOT, capture_output=True, text=True, timeout=60,
|
cwd=REPO_ROOT, capture_output=True, text=True, timeout=60,
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
# Fallback to regular pull
|
# Fallback to regular pull
|
||||||
result = subprocess.run(
|
result = run_cmd(
|
||||||
["git", "pull", "--quiet"],
|
["git", "pull", "--quiet"],
|
||||||
cwd=REPO_ROOT, capture_output=True, text=True, timeout=60,
|
cwd=REPO_ROOT, capture_output=True, text=True, timeout=60,
|
||||||
)
|
)
|
||||||
@@ -377,7 +384,7 @@ def git_pull():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# Reset to fetched state
|
# Reset to fetched state
|
||||||
subprocess.run(
|
run_cmd(
|
||||||
["git", "reset", "--hard", "origin/master"],
|
["git", "reset", "--hard", "origin/master"],
|
||||||
cwd=REPO_ROOT, capture_output=True, text=True, timeout=10,
|
cwd=REPO_ROOT, capture_output=True, text=True, timeout=10,
|
||||||
)
|
)
|
||||||
@@ -414,7 +421,7 @@ def _setup_sparse_checkout():
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Enable sparse checkout
|
# Enable sparse checkout
|
||||||
subprocess.run(
|
run_cmd(
|
||||||
["git", "config", "core.sparseCheckout", "true"],
|
["git", "config", "core.sparseCheckout", "true"],
|
||||||
cwd=REPO_ROOT, capture_output=True,
|
cwd=REPO_ROOT, capture_output=True,
|
||||||
)
|
)
|
||||||
@@ -432,7 +439,7 @@ def _setup_sparse_checkout():
|
|||||||
f.write("\n".join(patterns) + "\n")
|
f.write("\n".join(patterns) + "\n")
|
||||||
|
|
||||||
# Apply sparse checkout
|
# Apply sparse checkout
|
||||||
subprocess.run(
|
run_cmd(
|
||||||
["git", "checkout", "HEAD", "--", "."],
|
["git", "checkout", "HEAD", "--", "."],
|
||||||
cwd=REPO_ROOT, capture_output=True, timeout=30,
|
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)
|
os.chmod(cli_js_path, 0o755)
|
||||||
|
|
||||||
# Syntax check
|
# Syntax check
|
||||||
result = subprocess.run(
|
result = run_cmd(
|
||||||
["node", "--check", cli_js_path],
|
["node", "--check", cli_js_path],
|
||||||
capture_output=True, text=True, timeout=30,
|
capture_output=True, text=True, timeout=30,
|
||||||
)
|
)
|
||||||
@@ -748,7 +755,7 @@ def _set_user_env_windows(config):
|
|||||||
|
|
||||||
for key, val in env_vars.items():
|
for key, val in env_vars.items():
|
||||||
try:
|
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
|
os.environ[key] = val
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user