fix: resolve git/gh from PATH instead of hardcoded /usr/local/bin

Closes #2289.

Some workspace template images ship `/usr/local/bin/{git,gh}` wrappers
that bake `GH_TOKEN` into argv handling (preferred — auto-PR creation
authenticates without explicit token plumbing); other templates have
plain `/usr/bin/git` installed via apt with no wrapper. The hardcoded
`_GIT = "/usr/local/bin/git"` crashed every auto-push attempt on the
latter image class:

    FileNotFoundError: [Errno 2] No such file or directory: '/usr/local/bin/git'
      File "/app/molecule_runtime/executor_helpers.py", line 524, in _auto_push_and_pr_sync
        subprocess.run(['/usr/local/bin/git', 'rev-parse', '--is-inside-work-tree'], ...)

`shutil.which("git")` walks PATH in order — finds the `/usr/local/bin/`
wrapper first when it exists, falls back to `/usr/bin/git` otherwise.
GH_TOKEN injection still wins on wrapper-equipped images; auto-push
no longer crashes on bare-apt images.

Verified locally: `shutil.which("git")` resolves to `/usr/bin/git` on
the bug-reporter's image; `shutil.which("gh")` resolves to the
homebrew path on dev. Both paths exist + are executable on respective
hosts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-29 02:07:19 -07:00
parent 42cf4f444c
commit 59d65ba557

View File

@ -26,6 +26,7 @@ import logging
import mimetypes
import os
import re
import shutil
import subprocess
import uuid as _uuid
from pathlib import Path
@ -513,9 +514,16 @@ def sanitize_agent_error(
# Auto-push hook — push unpushed commits and open PR after task completion
# ========================================================================
# Git/gh wrappers at /usr/local/bin have GH_TOKEN baked in.
_GIT = "/usr/local/bin/git"
_GH = "/usr/local/bin/gh"
# Resolve git/gh from PATH so the runtime works regardless of which
# image the workspace is on. Some templates ship a /usr/local/bin/{git,gh}
# wrapper with GH_TOKEN baked in (preferred — picks up auth automatically);
# other templates have plain /usr/bin/git installed by apt. Hardcoding
# /usr/local/bin/git crashed every auto-push attempt on the latter image
# class with `FileNotFoundError: '/usr/local/bin/git'` (issue #2289).
# `shutil.which` finds the wrapper first if it's earlier in PATH, so the
# GH_TOKEN injection still wins where it exists.
_GIT = shutil.which("git") or "/usr/bin/git"
_GH = shutil.which("gh") or "/usr/bin/gh"
_PROTECTED_BRANCHES = frozenset({"staging", "main", "master"})