Files
agent-aide/aide-program/docs/03-aide-env设计.md
2025-12-13 04:37:41 +08:00

14 KiB
Raw Blame History

aide env 设计

一、命令概述

1.1 功能定位

aide env 命令用于检测和修复项目开发环境,确保所有必需的工具和依赖都已正确安装。

1.2 执行时机

  • /aide:init 命令中调用(两次)
  • 用户手动检查环境时
  • 环境配置变更后

1.3 命令格式

aide env ensure [--runtime]

选项

  • --runtime:仅检查 aide 运行时环境(不依赖配置文件)

二、功能需求

2.1 aide env ensure --runtime

用途:检查 aide 程序自身运行所需的环境

检查项

  1. Python 版本(>= 3.10
  2. 必需的 Python 库tomli/tomllib, tomli-w

特点

  • 不读取项目配置文件
  • aide init 之前执行
  • 失败时给出明确的安装建议

输出示例

成功:

✓ 环境就绪 (python:3.12)

失败:

✗ Python 版本不满足要求 (需要 >=3.10, 当前 3.8)
  建议: 安装 Python 3.10+ 或使用 pyenv 管理版本
  文档: https://www.python.org/downloads/

2.2 aide env ensure

用途:检查项目开发环境

检查项

  1. Python 版本(根据配置文件)
  2. 虚拟环境(根据配置文件)
  3. 可选工具git, uv 等,根据配置文件)

特点

  • 读取 .aide/config.toml
  • aide init 之后执行
  • 可以自动修复部分问题

输出示例

成功(无问题):

✓ 环境就绪 (python:3.12, git:2.40.0)

成功(自动修复):

⚠ 已修复: 创建虚拟环境 .venv
✓ 环境就绪 (python:3.12)

失败(无法修复):

✗ Python 版本不满足要求 (需要 >=3.11, 当前 3.10)
  建议: 升级 Python 到 3.11+ 或修改配置文件
  配置: .aide/config.toml (env.python.version)

三、实现设计

3.1 函数接口

def cmd_env(args: list[str]) -> int:
    """aide env 命令处理

    Args:
        args: 命令参数

    Returns:
        退出码0 表示成功)
    """
    pass

def env_ensure(runtime_only: bool = False) -> int:
    """环境检测和修复

    Args:
        runtime_only: 是否仅检查运行时环境

    Returns:
        退出码0 表示成功)
    """
    pass

3.2 实现流程

3.2.1 aide env ensure --runtime

import sys
import subprocess
from pathlib import Path
from core.output import ok, err

def env_ensure_runtime() -> int:
    """检查 aide 运行时环境"""

    # 1. 检查 Python 版本
    python_version = sys.version_info
    if python_version.major < 3 or (python_version.major == 3 and python_version.minor < 10):
        err(
            f"Python 版本不满足要求 (需要 >=3.10, 当前 {python_version.major}.{python_version.minor})",
            [
                "建议: 安装 Python 3.10+ 或使用 pyenv 管理版本",
                "文档: https://www.python.org/downloads/"
            ]
        )
        return 3

    # 2. 检查必需的库
    try:
        # Python 3.11+ 内置 tomllib
        if python_version.minor >= 11:
            import tomllib
        else:
            import tomli as tomllib

        import tomli_w
    except ImportError as e:
        err(
            f"缺少必需的 Python 库: {e.name}",
            [
                "建议: 运行 'pip install tomli tomli-w'",
                "或使用 uv: 'uv pip install tomli tomli-w'"
            ]
        )
        return 3

    # 3. 输出成功信息
    version_str = f"{python_version.major}.{python_version.minor}.{python_version.micro}"
    ok(f"环境就绪 (python:{version_str})")

    return 0

3.2.2 aide env ensure

from core.config import Config
from core.output import ok, warn, err

def env_ensure_project() -> int:
    """检查项目开发环境"""

    # 1. 加载配置
    try:
        config = Config(Path.cwd())
        config.load()
    except FileNotFoundError:
        err(
            "配置文件不存在",
            [
                "位置: .aide/config.toml",
                "建议: 运行 'aide init' 创建配置文件"
            ]
        )
        return 4
    except Exception as e:
        err(
            "配置文件读取失败",
            [
                f"原因: {str(e)}",
                "建议: 检查配置文件格式"
            ]
        )
        return 4

    # 2. 检查 Python 版本
    required_version = config.get("env.python.version", ">=3.10")
    if not check_python_version(required_version):
        err(
            f"Python 版本不满足要求 (需要 {required_version}, 当前 {get_python_version()})",
            [
                "建议: 升级 Python 或修改配置文件",
                "配置: .aide/config.toml (env.python.version)"
            ]
        )
        return 3

    # 3. 检查虚拟环境
    venv_path = config.get("env.python.venv", ".venv")
    venv_result = check_venv(venv_path)

    if venv_result == "missing":
        # 尝试创建虚拟环境
        if create_venv(venv_path):
            warn(f"已修复: 创建虚拟环境 {venv_path}")
        else:
            err(
                f"虚拟环境不存在且无法创建 ({venv_path})",
                [
                    "建议: 手动创建虚拟环境",
                    f"命令: python3 -m venv {venv_path}"
                ]
            )
            return 3

    # 4. 检查可选工具
    tools_info = []

    # 检查 git
    if config.get("env.tools.git", True):
        git_version = get_tool_version("git")
        if git_version:
            tools_info.append(f"git:{git_version}")
        else:
            warn("git 未安装(可选)")

    # 检查 uv
    if config.get("env.tools.uv", False):
        uv_version = get_tool_version("uv")
        if uv_version:
            tools_info.append(f"uv:{uv_version}")
        else:
            warn("uv 未安装(可选)")

    # 5. 输出成功信息
    python_version = get_python_version()
    info_parts = [f"python:{python_version}"] + tools_info
    ok(f"环境就绪 ({', '.join(info_parts)})")

    return 0

3.3 辅助函数

3.3.1 版本检查

import re
from packaging import version

def check_python_version(requirement: str) -> bool:
    """检查 Python 版本是否满足要求

    Args:
        requirement: 版本要求(如 ">=3.10"

    Returns:
        是否满足要求
    """
    current = get_python_version()

    # 解析要求
    match = re.match(r'(>=|<=|>|<|==)?(\d+\.\d+(?:\.\d+)?)', requirement)
    if not match:
        return True  # 无法解析,假设满足

    operator, required = match.groups()
    operator = operator or "=="

    # 比较版本
    try:
        current_ver = version.parse(current)
        required_ver = version.parse(required)

        if operator == ">=":
            return current_ver >= required_ver
        elif operator == "<=":
            return current_ver <= required_ver
        elif operator == ">":
            return current_ver > required_ver
        elif operator == "<":
            return current_ver < required_ver
        elif operator == "==":
            return current_ver == required_ver
    except Exception:
        return True

    return True

def get_python_version() -> str:
    """获取当前 Python 版本

    Returns:
        版本字符串(如 "3.12.0"
    """
    v = sys.version_info
    return f"{v.major}.{v.minor}.{v.micro}"

3.3.2 虚拟环境检查

import subprocess

def check_venv(venv_path: str) -> str:
    """检查虚拟环境状态

    Args:
        venv_path: 虚拟环境路径

    Returns:
        状态:'ok', 'missing', 'invalid'
    """
    venv_dir = Path(venv_path)

    if not venv_dir.exists():
        return "missing"

    # 检查是否是有效的虚拟环境
    if sys.platform == "win32":
        python_exe = venv_dir / "Scripts" / "python.exe"
    else:
        python_exe = venv_dir / "bin" / "python"

    if python_exe.exists():
        return "ok"
    else:
        return "invalid"

def create_venv(venv_path: str) -> bool:
    """创建虚拟环境

    Args:
        venv_path: 虚拟环境路径

    Returns:
        是否创建成功
    """
    try:
        subprocess.run(
            [sys.executable, "-m", "venv", venv_path],
            check=True,
            capture_output=True,
            text=True
        )
        return True
    except subprocess.CalledProcessError:
        return False
    except Exception:
        return False

3.3.3 工具版本检查

def get_tool_version(tool: str) -> str | None:
    """获取工具版本

    Args:
        tool: 工具名称(如 "git", "uv"

    Returns:
        版本字符串,未安装则返回 None
    """
    try:
        result = subprocess.run(
            [tool, "--version"],
            capture_output=True,
            text=True,
            timeout=5
        )

        if result.returncode == 0:
            # 解析版本号
            output = result.stdout.strip()
            match = re.search(r'(\d+\.\d+\.\d+)', output)
            if match:
                return match.group(1)
            return "unknown"
    except (subprocess.TimeoutExpired, FileNotFoundError):
        pass

    return None

四、错误处理

4.1 错误分类

错误类型 退出码 处理方式
Python 版本不满足 3 显示当前版本和要求,给出升级建议
配置文件不存在 4 提示运行 aide init
配置文件格式错误 4 显示错误位置和原因
虚拟环境无法创建 3 显示创建命令
必需工具未安装 3 显示安装建议

4.2 错误恢复

def env_ensure_with_retry(runtime_only: bool = False, max_retries: int = 3) -> int:
    """带重试的环境检测

    Args:
        runtime_only: 是否仅检查运行时
        max_retries: 最大重试次数

    Returns:
        退出码
    """
    for attempt in range(max_retries):
        result = env_ensure(runtime_only)

        if result == 0:
            return 0

        if attempt < max_retries - 1:
            info(f"重试 ({attempt + 1}/{max_retries})...")

    return result

五、测试用例

5.1 runtime 模式测试

def test_env_ensure_runtime_success():
    """测试运行时环境检测成功"""
    result = env_ensure_runtime()
    assert result == 0

def test_env_ensure_runtime_old_python(monkeypatch):
    """测试 Python 版本过低"""
    # 模拟 Python 3.8
    monkeypatch.setattr(sys, "version_info", (3, 8, 0, "final", 0))

    result = env_ensure_runtime()
    assert result == 3

def test_env_ensure_runtime_missing_lib(monkeypatch):
    """测试缺少必需库"""
    # 模拟 import 失败
    def mock_import(name, *args, **kwargs):
        if name in ["tomli", "tomllib"]:
            raise ImportError(f"No module named '{name}'")
        return __import__(name, *args, **kwargs)

    monkeypatch.setattr("builtins.__import__", mock_import)

    result = env_ensure_runtime()
    assert result == 3

5.2 项目环境测试

def test_env_ensure_project_success(tmp_path, monkeypatch):
    """测试项目环境检测成功"""
    monkeypatch.chdir(tmp_path)

    # 创建配置文件
    cmd_init([])

    # 检测环境
    result = env_ensure_project()
    assert result == 0

def test_env_ensure_project_no_config(tmp_path, monkeypatch):
    """测试配置文件不存在"""
    monkeypatch.chdir(tmp_path)

    result = env_ensure_project()
    assert result == 4

def test_env_ensure_project_create_venv(tmp_path, monkeypatch):
    """测试自动创建虚拟环境"""
    monkeypatch.chdir(tmp_path)

    # 创建配置文件
    cmd_init([])

    # 确保虚拟环境不存在
    venv_path = tmp_path / ".venv"
    if venv_path.exists():
        shutil.rmtree(venv_path)

    # 检测环境(应该自动创建虚拟环境)
    result = env_ensure_project()
    assert result == 0
    assert venv_path.exists()

5.3 版本检查测试

def test_check_python_version():
    """测试 Python 版本检查"""
    # 测试各种版本要求
    assert check_python_version(">=3.10") == True  # 假设当前是 3.12
    assert check_python_version(">=3.15") == False
    assert check_python_version("==3.12") == True
    assert check_python_version("<4.0") == True

def test_get_python_version():
    """测试获取 Python 版本"""
    version = get_python_version()
    assert re.match(r'\d+\.\d+\.\d+', version)

六、性能要求

6.1 执行时间

  • --runtime 模式:< 100ms
  • 项目环境检测(无修复):< 500ms
  • 项目环境检测(创建虚拟环境):< 10s

6.2 资源占用

  • 内存:< 50MB
  • CPU主要是 I/O 操作)

七、集成测试

7.1 完整工作流

def test_env_workflow(tmp_path, monkeypatch):
    """测试完整的环境检测工作流"""
    monkeypatch.chdir(tmp_path)

    # 1. 检查运行时环境
    result = env_ensure_runtime()
    assert result == 0

    # 2. 初始化项目
    result = cmd_init([])
    assert result == 0

    # 3. 检查项目环境
    result = env_ensure_project()
    assert result == 0

    # 4. 验证虚拟环境已创建
    assert (tmp_path / ".venv").exists()

八、总结

8.1 核心要点

  1. 两种模式runtime 和 project
  2. 自动修复部分问题(如创建虚拟环境)
  3. 清晰的错误信息和建议
  4. 完善的版本检查逻辑

8.2 实现检查清单

  • 实现 cmd_env 函数
  • 实现 env_ensure_runtime 函数
  • 实现 env_ensure_project 函数
  • 实现版本检查函数
  • 实现虚拟环境检查和创建
  • 实现工具版本检查
  • 编写单元测试
  • 编写集成测试
  • 性能测试

版本v1.0 更新日期2025-12-13