mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
fix(windows): sweep remaining unguarded text-mode subprocess sites codebase-wide
AST-driven pass over every subprocess.run/Popen/check_output/check_call/call with text=True (or universal_newlines=True) and no explicit encoding=: append encoding='utf-8', errors='replace' at the kwarg site. 136 call sites across 28 files (cli.py, hermes_cli/main.py, tools_config.py, environments, computer_use, gateway, scripts, skills helpers, agent/*). Together with the salvaged #55339/#60741 commits this closes out issue #53428's bug class; the salvaged #60751 linter rule in check-windows-footguns.py now enforces it repo-wide (verified: 807 files scanned, zero findings).
This commit is contained in:
parent
051217342b
commit
d4b867cf9f
28 changed files with 138 additions and 136 deletions
48
cli.py
48
cli.py
|
|
@ -1432,7 +1432,7 @@ def _git_repo_root() -> Optional[str]:
|
|||
try:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=5,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return _normalize_git_bash_path(result.stdout.strip())
|
||||
|
|
@ -1479,7 +1479,7 @@ def _resolve_worktree_base(repo_root: str) -> tuple:
|
|||
def _git(args, timeout=20):
|
||||
return subprocess.run(
|
||||
["git", *args],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=repo_root,
|
||||
)
|
||||
|
||||
# 1. Current branch's upstream, if it tracks one.
|
||||
|
|
@ -1578,7 +1578,7 @@ def _setup_worktree(repo_root: str = None, sync_base: bool = True) -> Optional[D
|
|||
try:
|
||||
result = subprocess.run(
|
||||
["git", "worktree", "add", str(wt_path), "-b", branch_name, base_ref],
|
||||
capture_output=True, text=True, timeout=30, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=30, cwd=repo_root,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
# If branching from the resolved remote ref failed for any reason
|
||||
|
|
@ -1592,7 +1592,7 @@ def _setup_worktree(repo_root: str = None, sync_base: bool = True) -> Optional[D
|
|||
base_ref, base_label = "HEAD", "HEAD (fallback — remote base failed)"
|
||||
result = subprocess.run(
|
||||
["git", "worktree", "add", str(wt_path), "-b", branch_name, base_ref],
|
||||
capture_output=True, text=True, timeout=30, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=30, cwd=repo_root,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(f"\033[31m✗ Failed to create worktree: {result.stderr.strip()}\033[0m")
|
||||
|
|
@ -1673,7 +1673,7 @@ def _setup_worktree(repo_root: str = None, sync_base: bool = True) -> Optional[D
|
|||
try:
|
||||
subprocess.run(
|
||||
["git", "worktree", "lock", "--reason", f"hermes pid={os.getpid()}", str(wt_path)],
|
||||
capture_output=True, text=True, timeout=10, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10, cwd=repo_root,
|
||||
)
|
||||
logger.debug("Worktree locked: %s (pid=%s)", wt_path, os.getpid())
|
||||
except Exception as e:
|
||||
|
|
@ -1706,7 +1706,7 @@ def _worktree_has_unpushed_commits(worktree_path: str, timeout: int = 10) -> boo
|
|||
try:
|
||||
remote_refs = subprocess.run(
|
||||
["git", "for-each-ref", "--format=%(refname)", "refs/remotes"],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=worktree_path,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=worktree_path,
|
||||
)
|
||||
if remote_refs.returncode != 0:
|
||||
return True
|
||||
|
|
@ -1715,7 +1715,7 @@ def _worktree_has_unpushed_commits(worktree_path: str, timeout: int = 10) -> boo
|
|||
|
||||
result = subprocess.run(
|
||||
["git", "log", "--oneline", "HEAD", "--not", "--remotes"],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=worktree_path,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=worktree_path,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return True
|
||||
|
|
@ -1736,7 +1736,7 @@ def _worktree_is_dirty(worktree_path: str, timeout: int = 10) -> bool:
|
|||
try:
|
||||
result = subprocess.run(
|
||||
["git", "status", "--porcelain"],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=worktree_path,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=worktree_path,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return True
|
||||
|
|
@ -1769,7 +1769,7 @@ def _worktree_commits_all_merged_upstream(
|
|||
try:
|
||||
probe = subprocess.run(
|
||||
["git", "rev-parse", "--verify", "--quiet", candidate],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=worktree_path,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=worktree_path,
|
||||
)
|
||||
if probe.returncode == 0 and probe.stdout.strip():
|
||||
base = candidate
|
||||
|
|
@ -1782,7 +1782,7 @@ def _worktree_commits_all_merged_upstream(
|
|||
try:
|
||||
ahead = subprocess.run(
|
||||
["git", "rev-list", "--count", f"{base}..HEAD"],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=worktree_path,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=worktree_path,
|
||||
)
|
||||
if ahead.returncode != 0:
|
||||
return False
|
||||
|
|
@ -1794,7 +1794,7 @@ def _worktree_commits_all_merged_upstream(
|
|||
|
||||
cherry = subprocess.run(
|
||||
["git", "cherry", base, "HEAD"],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=worktree_path,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=worktree_path,
|
||||
)
|
||||
if cherry.returncode != 0:
|
||||
return False
|
||||
|
|
@ -1829,7 +1829,7 @@ def _worktree_lock_is_live(repo_root: str, worktree_path: str, timeout: int = 10
|
|||
try:
|
||||
result = subprocess.run(
|
||||
["git", "worktree", "list", "--porcelain"],
|
||||
capture_output=True, text=True, timeout=timeout, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, cwd=repo_root,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return "live"
|
||||
|
|
@ -1904,7 +1904,7 @@ def _cleanup_worktree(info: Dict[str, str] = None) -> None:
|
|||
try:
|
||||
subprocess.run(
|
||||
["git", "worktree", "unlock", wt_path],
|
||||
capture_output=True, text=True, timeout=10, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10, cwd=repo_root,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("git worktree unlock failed (non-fatal): %s", e)
|
||||
|
|
@ -1912,7 +1912,7 @@ def _cleanup_worktree(info: Dict[str, str] = None) -> None:
|
|||
try:
|
||||
subprocess.run(
|
||||
["git", "worktree", "remove", wt_path, "--force"],
|
||||
capture_output=True, text=True, timeout=15, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=15, cwd=repo_root,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("Failed to remove worktree: %s", e)
|
||||
|
|
@ -1921,7 +1921,7 @@ def _cleanup_worktree(info: Dict[str, str] = None) -> None:
|
|||
try:
|
||||
subprocess.run(
|
||||
["git", "branch", "-D", branch],
|
||||
capture_output=True, text=True, timeout=10, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10, cwd=repo_root,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("Failed to delete branch %s: %s", branch, e)
|
||||
|
|
@ -2122,7 +2122,7 @@ def _prune_stale_worktrees(repo_root: str, max_age_hours: int = 24) -> None:
|
|||
try:
|
||||
subprocess.run(
|
||||
["git", "worktree", "unlock", str(entry)],
|
||||
capture_output=True, text=True, timeout=10, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10, cwd=repo_root,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("Failed to unlock dead worktree %s: %s", entry.name, e)
|
||||
|
|
@ -2131,13 +2131,13 @@ def _prune_stale_worktrees(repo_root: str, max_age_hours: int = 24) -> None:
|
|||
try:
|
||||
branch_result = subprocess.run(
|
||||
["git", "branch", "--show-current"],
|
||||
capture_output=True, text=True, timeout=5, cwd=str(entry),
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=5, cwd=str(entry),
|
||||
)
|
||||
branch = branch_result.stdout.strip()
|
||||
|
||||
remove_result = subprocess.run(
|
||||
["git", "worktree", "remove", str(entry), "--force"],
|
||||
capture_output=True, text=True, timeout=15, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=15, cwd=repo_root,
|
||||
)
|
||||
if remove_result.returncode != 0:
|
||||
# Removal failed — keep the branch so any commits stay
|
||||
|
|
@ -2150,7 +2150,7 @@ def _prune_stale_worktrees(repo_root: str, max_age_hours: int = 24) -> None:
|
|||
if branch:
|
||||
subprocess.run(
|
||||
["git", "branch", "-D", branch],
|
||||
capture_output=True, text=True, timeout=10, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10, cwd=repo_root,
|
||||
)
|
||||
logger.debug("Pruned stale worktree: %s (force=%s)", entry.name, force)
|
||||
except Exception as e:
|
||||
|
|
@ -2178,7 +2178,7 @@ def _prune_orphaned_branches(repo_root: str) -> None:
|
|||
try:
|
||||
result = subprocess.run(
|
||||
["git", "branch", "--format=%(refname:short)"],
|
||||
capture_output=True, text=True, timeout=10, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10, cwd=repo_root,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return
|
||||
|
|
@ -2191,7 +2191,7 @@ def _prune_orphaned_branches(repo_root: str) -> None:
|
|||
try:
|
||||
wt_result = subprocess.run(
|
||||
["git", "worktree", "list", "--porcelain"],
|
||||
capture_output=True, text=True, timeout=10, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10, cwd=repo_root,
|
||||
)
|
||||
for line in wt_result.stdout.split("\n"):
|
||||
if line.startswith("branch refs/heads/"):
|
||||
|
|
@ -2203,7 +2203,7 @@ def _prune_orphaned_branches(repo_root: str) -> None:
|
|||
try:
|
||||
head_result = subprocess.run(
|
||||
["git", "branch", "--show-current"],
|
||||
capture_output=True, text=True, timeout=5, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=5, cwd=repo_root,
|
||||
)
|
||||
current = head_result.stdout.strip()
|
||||
if current:
|
||||
|
|
@ -2227,7 +2227,7 @@ def _prune_orphaned_branches(repo_root: str) -> None:
|
|||
try:
|
||||
subprocess.run(
|
||||
["git", "branch", "-D"] + batch,
|
||||
capture_output=True, text=True, timeout=30, cwd=repo_root,
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=30, cwd=repo_root,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("Failed to prune orphaned branches: %s", e)
|
||||
|
|
@ -9528,7 +9528,7 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
|||
from hermes_cli._subprocess_compat import windows_hide_flags
|
||||
result = subprocess.run(
|
||||
exec_cmd, shell=True, capture_output=True,
|
||||
text=True, timeout=30, env=sanitized_env,
|
||||
text=True, encoding="utf-8", errors="replace", timeout=30, env=sanitized_env,
|
||||
# No console flash on Windows (#56747).
|
||||
creationflags=windows_hide_flags(),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue