Files
agent-aide/aide-program/aide/env/ensure.py
2025-12-13 05:01:28 +08:00

120 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""环境检测与修复逻辑。"""
from __future__ import annotations
import platform
import subprocess
import sys
from pathlib import Path
from aide.core import output
from aide.core.config import ConfigManager
class EnvManager:
def __init__(self, root: Path):
self.root = root
def ensure(self, runtime_only: bool, cfg: ConfigManager) -> bool:
"""运行环境检测入口。"""
required_py = self._get_required_python(cfg, runtime_only)
if not self._check_python_version(required_py):
return False
uv_version = self._check_uv()
if uv_version is None:
return False
if runtime_only:
output.ok(f"运行时环境就绪 (python:{platform.python_version()}, uv:{uv_version})")
return True
config = cfg.ensure_config()
cfg.ensure_gitignore()
env_config = config.get("env", {})
venv_path = self.root / env_config.get("venv", ".venv")
req_path = self.root / env_config.get("requirements", "requirements.txt")
self._ensure_requirements_file(req_path)
if not self._ensure_venv(venv_path):
return False
if not self._install_requirements(venv_path, req_path):
return False
task_config = config.get("task", {})
output.info(f"任务原文档: {task_config.get('source', 'task-now.md')}")
output.info(f"任务细则文档: {task_config.get('spec', 'task-spec.md')}")
output.ok(f"环境就绪 (python:{platform.python_version()}, uv:{uv_version}, venv:{venv_path})")
return True
@staticmethod
def _get_required_python(cfg: ConfigManager, runtime_only: bool) -> str:
if runtime_only:
return "3.11"
data = cfg.load_config()
runtime = data.get("runtime", {})
return str(runtime.get("python_min", "3.11"))
@staticmethod
def _parse_version(version: str) -> tuple[int, ...]:
parts = []
for part in version.split("."):
try:
parts.append(int(part))
except ValueError:
break
return tuple(parts)
def _check_python_version(self, required: str) -> bool:
current = self._parse_version(platform.python_version())
target = self._parse_version(required)
if current >= target:
return True
output.err(f"Python 版本不足,要求>={required},当前 {platform.python_version()}")
return False
def _check_uv(self) -> str | None:
try:
result = subprocess.run(
["uv", "--version"],
check=True,
capture_output=True,
text=True,
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError) as exc:
output.err(f"未检测到 uv请先安装{exc}")
return None
def _ensure_venv(self, venv_path: Path) -> bool:
if venv_path.exists():
return True
output.info(f"创建虚拟环境: {venv_path}")
try:
subprocess.run(["uv", "venv", str(venv_path)], check=True)
output.ok("已创建虚拟环境")
return True
except subprocess.CalledProcessError as exc:
output.err(f"创建虚拟环境失败: {exc}")
return False
@staticmethod
def _ensure_requirements_file(req_path: Path) -> None:
if req_path.exists():
return
req_path.write_text("# 在此添加依赖\n", encoding="utf-8")
output.warn(f"未找到 {req_path.name},已创建空文件")
def _install_requirements(self, venv_path: Path, req_path: Path) -> bool:
if not req_path.exists():
output.err(f"缺少 {req_path}")
return False
cmd = ["uv", "pip", "install", "-r", str(req_path), "--python", str(venv_path)]
output.info("安装依赖uv pip install -r requirements.txt")
try:
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
return True
except subprocess.CalledProcessError as exc:
output.err(f"安装依赖失败: {exc}")
return False