feat(plugins): install plugins from private git repos using the user's stored credentials
`hermes plugins install owner/private-repo` failed for every private repository even when the same user could `git clone` it from their shell: the hardened `noninteractive_git_env` disables credential helpers, askpass and global config (so a hostile repo can't make our plumbing prompt or hang), which also blocks the user's own stored credential. The result was "could not read Username" or a 60s hang on a GUI askpass, with no hint about how to authenticate. New `hermes_cli/git_credentials.py` resolves a credential up front from sources the user already owns — GITHUB_TOKEN/GH_TOKEN (profile-scoped), `gh auth token`, then `git credential fill` against their configured helpers with prompting disabled (any host) — and hands it to git as a one-shot `http.<origin>/.extraheader` via the GIT_CONFIG_* env block. Nothing lands in the URL, `.git/config` or install metadata. The same path covers `plugins update`, catalog MCP git installs and profile-distribution staging, which share the same hardened env and the same failure. A private-repo clone with no credential now fails fast with an actionable hint.
This commit is contained in:
@@ -379,7 +379,8 @@ def _do_git_install(entry: CatalogEntry) -> Path:
|
||||
# upfront so the fast path doesn't always fail noisily before the full-clone fallback.
|
||||
is_sha_ref = bool(re.fullmatch(r"[0-9a-f]{7,40}", install.ref))
|
||||
# Never hang on a credential prompt: installs run from CLI/dashboard flows nobody can answer.
|
||||
_git_env = noninteractive_git_env()
|
||||
from hermes_cli.git_credentials import with_git_auth
|
||||
_git_env = with_git_auth(noninteractive_git_env(), install.url)
|
||||
|
||||
def _git(*args: str) -> int:
|
||||
return subprocess.run([git, *args], stdin=subprocess.DEVNULL, env=_git_env).returncode
|
||||
|
||||
Reference in New Issue
Block a user