Add support for SSL related interactions: (#2074)
Some checks are pending
Build-and-test / build-and-test (push) Waiting to run
docs-ci / deploy (push) Waiting to run

* Add support for SSL related interactions:
1. Inject SSL related env variables to git clone
2. Force LiteLLM to use aiohttp which honors SSL env. variables, this is done by setting configuration toml as follows:

[litellm]
disable_aiohttp = true

* Update pr_agent/git_providers/git_provider.py

Co-authored-by: qodo-merge-for-open-source[bot] <189517486+qodo-merge-for-open-source[bot]@users.noreply.github.com>

* Guard get_git_ssl_env invocation with try-catch

---------

Co-authored-by: qodo-merge-for-open-source[bot] <189517486+qodo-merge-for-open-source[bot]@users.noreply.github.com>
This commit is contained in:
sharoneyal 2025-10-21 19:06:18 +03:00 committed by GitHub
parent 8a0759f835
commit 8d3f3bca45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 4 deletions

View file

@ -33,6 +33,8 @@ class LiteLLMAIHandler(BaseAiHandler):
self.api_base = None self.api_base = None
self.repetition_penalty = None self.repetition_penalty = None
if get_settings().get("LITELLM.DISABLE_AIOHTTP", False):
litellm.disable_aiohttp_transport = True
if get_settings().get("OPENAI.KEY", None): if get_settings().get("OPENAI.KEY", None):
openai.api_key = get_settings().openai.key openai.api_key = get_settings().openai.key
litellm.openai_key = get_settings().openai.key litellm.openai_key = get_settings().openai.key

View file

@ -18,7 +18,7 @@ from ..algo.utils import (find_line_number_of_relevant_line_in_file,
load_large_diff) load_large_diff)
from ..config_loader import get_settings from ..config_loader import get_settings
from ..log import get_logger from ..log import get_logger
from .git_provider import GitProvider from .git_provider import GitProvider, get_git_ssl_env
class BitbucketServerProvider(GitProvider): class BitbucketServerProvider(GitProvider):
@ -561,5 +561,7 @@ class BitbucketServerProvider(GitProvider):
cli_args = shlex.split(f"git clone -c http.extraHeader='Authorization: Bearer {bearer_token}' " cli_args = shlex.split(f"git clone -c http.extraHeader='Authorization: Bearer {bearer_token}' "
f"--filter=blob:none --depth 1 {repo_url} {dest_folder}") f"--filter=blob:none --depth 1 {repo_url} {dest_folder}")
subprocess.run(cli_args, check=True, # check=True will raise an exception if the command fails ssl_env = get_git_ssl_env()
subprocess.run(cli_args, env=ssl_env, check=True, # check=True will raise an exception if the command fails
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=operation_timeout_in_seconds) stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=operation_timeout_in_seconds)

View file

@ -12,6 +12,65 @@ from pr_agent.log import get_logger
MAX_FILES_ALLOWED_FULL = 50 MAX_FILES_ALLOWED_FULL = 50
def get_git_ssl_env() -> dict[str, str]:
"""
Get git SSL configuration arguments for per-command use.
This fixes SSL certificate issues when cloning repos with self-signed certificates.
Returns the current environment with the addition of SSL config changes if any such SSL certificates exist.
"""
ssl_cert_file = os.environ.get('SSL_CERT_FILE')
requests_ca_bundle = os.environ.get('REQUESTS_CA_BUNDLE')
git_ssl_ca_info = os.environ.get('GIT_SSL_CAINFO')
chosen_cert_file = ""
# Try SSL_CERT_FILE first
if ssl_cert_file:
if os.path.exists(ssl_cert_file):
if ((requests_ca_bundle and requests_ca_bundle != ssl_cert_file)
or (git_ssl_ca_info and git_ssl_ca_info != ssl_cert_file)):
get_logger().warning(f"Found mismatch among: SSL_CERT_FILE, REQUESTS_CA_BUNDLE, GIT_SSL_CAINFO. "
f"Using the SSL_CERT_FILE to resolve ambiguity.",
artifact={"ssl_cert_file": ssl_cert_file, "requests_ca_bundle": requests_ca_bundle,
'git_ssl_ca_info': git_ssl_ca_info})
else:
get_logger().info(f"Using SSL certificate bundle for git operations", artifact={"ssl_cert_file": ssl_cert_file})
chosen_cert_file = ssl_cert_file
else:
get_logger().warning("SSL certificate bundle not found for git operations", artifact={"ssl_cert_file": ssl_cert_file})
# Fallback to REQUESTS_CA_BUNDLE
elif requests_ca_bundle:
if os.path.exists(requests_ca_bundle):
if (git_ssl_ca_info and git_ssl_ca_info != requests_ca_bundle):
get_logger().warning(f"Found mismatch between: REQUESTS_CA_BUNDLE, GIT_SSL_CAINFO. "
f"Using the REQUESTS_CA_BUNDLE to resolve ambiguity.",
artifact = {"requests_ca_bundle": requests_ca_bundle, 'git_ssl_ca_info': git_ssl_ca_info})
else:
get_logger().info("Using SSL certificate bundle from REQUESTS_CA_BUNDLE for git operations",
artifact={"requests_ca_bundle": requests_ca_bundle})
chosen_cert_file = requests_ca_bundle
else:
get_logger().warning("requests CA bundle not found for git operations", artifact={"requests_ca_bundle": requests_ca_bundle})
#Fallback to GIT CA:
elif git_ssl_ca_info:
if os.path.exists(git_ssl_ca_info):
get_logger().info("Using git SSL CA info from GIT_SSL_CAINFO for git operations",
artifact={"git_ssl_ca_info": git_ssl_ca_info})
chosen_cert_file = git_ssl_ca_info
else:
get_logger().warning("git SSL CA info not found for git operations", artifact={"git_ssl_ca_info": git_ssl_ca_info})
else:
get_logger().warning("Neither SSL_CERT_FILE nor REQUESTS_CA_BUNDLE nor GIT_SSL_CAINFO are defined, or they are defined but not found. Returning environment without SSL configuration")
returned_env = os.environ.copy()
if chosen_cert_file:
returned_env.update({"GIT_SSL_CAINFO": chosen_cert_file, "REQUESTS_CA_BUNDLE": chosen_cert_file})
return returned_env
class GitProvider(ABC): class GitProvider(ABC):
@abstractmethod @abstractmethod
def is_supported(self, capability: str) -> bool: def is_supported(self, capability: str) -> bool:
@ -57,12 +116,21 @@ class GitProvider(ABC):
# #Repo.clone_from(repo_url, dest_folder) # #Repo.clone_from(repo_url, dest_folder)
# , but with throwing an exception upon timeout. # , but with throwing an exception upon timeout.
# Note: This can only be used in context that supports using pipes. # Note: This can only be used in context that supports using pipes.
try:
ssl_env = get_git_ssl_env()
except Exception as e:
get_logger().exception(
"Failed to prepare SSL environment for git operations, falling back to default env",
artifact={"error": e}
)
ssl_env = os.environ.copy()
subprocess.run([ subprocess.run([
"git", "clone", "git", "clone",
"--filter=blob:none", "--filter=blob:none",
"--depth", "1", "--depth", "1",
repo_url, dest_folder repo_url, dest_folder
], check=True, # check=True will raise an exception if the command fails ], env=ssl_env, check=True, # check=True will raise an exception if the command fails
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=operation_timeout_in_seconds) stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=operation_timeout_in_seconds)
CLONE_TIMEOUT_SEC = 20 CLONE_TIMEOUT_SEC = 20