mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-12-12 02:45:18 +00:00
Add support for SSL related interactions: (#2074)
* 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:
parent
8a0759f835
commit
8d3f3bca45
3 changed files with 76 additions and 4 deletions
|
|
@ -32,7 +32,9 @@ class LiteLLMAIHandler(BaseAiHandler):
|
||||||
self.azure = False
|
self.azure = False
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue