fix(git_compat): ignore global git config in pygit2 backend

Under Desktop 2.0 the launcher sets CM_USE_PYGIT2=1, so the pygit2 backend ran clone_repository/remote.fetch with no credentials callback and honored the user's global git config. An insteadOf rewrite (https->ssh) or credential helper then forced authentication, failing with 'authentication required but no callback set'.

Blank the system/global/XDG config search path at import time so libgit2 operations are hermetic, and normalize SSH-form GitHub URLs to anonymous HTTPS on clone and when opening a repo.

Amp-Thread-ID: https://ampcode.com/threads/T-019eafa0-16a1-726e-91a4-dac1a40d4481
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Jedrzej Kosinski 2026-06-09 20:59:45 -07:00
parent 01799f8cac
commit 498e896472

View File

@ -14,11 +14,28 @@ Exports:
""" """
import os import os
import re
import sys import sys
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import deque from collections import deque
from datetime import datetime, timezone, timedelta from datetime import datetime, timezone, timedelta
def _to_https_url(url):
"""Rewrite an SSH-form git URL to its anonymous HTTPS equivalent.
Handles `git@host:owner/repo(.git)` and `ssh://git@host/owner/repo(.git)`.
Returns the URL unchanged if it is not SSH-form, so pygit2 clone/fetch
of public repos never requires SSH credentials even when a repo's own
config stores an SSH origin.
"""
if not url:
return url
m = re.match(r"^(?:ssh://)?git@([^:/]+)[:/](.+)$", url)
if m:
return "https://%s/%s" % (m.group(1), m.group(2))
return url
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Backend selection # Backend selection
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -49,6 +66,27 @@ if USE_PYGIT2:
# See CVE-2022-24765 for context on this validation. # See CVE-2022-24765 for context on this validation.
_pygit2.option(_pygit2.GIT_OPT_SET_OWNER_VALIDATION, 0) _pygit2.option(_pygit2.GIT_OPT_SET_OWNER_VALIDATION, 0)
# Ignore system/global/XDG git config for libgit2 operations. A
# user's global config can carry `insteadOf` rewrites (e.g.
# https->ssh) or credential helpers that force authentication,
# which libgit2 cannot satisfy without a credentials callback
# ("authentication required but no callback set"). Blanking the
# config search path keeps clone/fetch over anonymous HTTPS.
try:
from pygit2.enums import ConfigLevel as _ConfigLevel
_cfg_levels = [_ConfigLevel.SYSTEM, _ConfigLevel.XDG, _ConfigLevel.GLOBAL]
except (ImportError, AttributeError):
_cfg_levels = [
_pygit2.GIT_CONFIG_LEVEL_SYSTEM,
_pygit2.GIT_CONFIG_LEVEL_XDG,
_pygit2.GIT_CONFIG_LEVEL_GLOBAL,
]
for _lvl in _cfg_levels:
try:
_pygit2.settings.search_path[_lvl] = ""
except Exception:
pass
if not USE_PYGIT2: if not USE_PYGIT2:
import git as _git import git as _git
@ -387,6 +425,18 @@ class _Pygit2Repo(GitRepo):
pass pass
self._repo = _pygit2.Repository(git_dir) self._repo = _pygit2.Repository(git_dir)
self._working_dir = repo_path self._working_dir = repo_path
self._normalize_remote_urls()
def _normalize_remote_urls(self):
"""Rewrite any SSH-form remote URLs to anonymous HTTPS so fetch/pull
never require SSH credentials under the pygit2 backend."""
for remote in self._repo.remotes:
https_url = _to_https_url(remote.url)
if https_url != remote.url:
try:
self._repo.remotes.set_url(remote.name, https_url)
except Exception:
pass
@property @property
def working_dir(self): def working_dir(self):
@ -824,7 +874,7 @@ def clone_repo(url, dest, progress=None):
(checkout, clear_cache, close, etc.). (checkout, clear_cache, close, etc.).
""" """
if USE_PYGIT2: if USE_PYGIT2:
_pygit2.clone_repository(url, dest) _pygit2.clone_repository(_to_https_url(url), dest)
repo = _Pygit2Repo(dest) repo = _Pygit2Repo(dest)
repo.submodule_update() repo.submodule_update()
return repo return repo