📃 docs: 部分完成
This commit is contained in:
599
aide-program/docs/03-aide-env设计.md
Normal file
599
aide-program/docs/03-aide-env设计.md
Normal file
@@ -0,0 +1,599 @@
|
||||
# aide env 设计
|
||||
|
||||
## 一、命令概述
|
||||
|
||||
### 1.1 功能定位
|
||||
|
||||
`aide env` 命令用于检测和修复项目开发环境,确保所有必需的工具和依赖都已正确安装。
|
||||
|
||||
### 1.2 执行时机
|
||||
|
||||
- `/aide:init` 命令中调用(两次)
|
||||
- 用户手动检查环境时
|
||||
- 环境配置变更后
|
||||
|
||||
### 1.3 命令格式
|
||||
|
||||
```bash
|
||||
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 函数接口
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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 版本检查
|
||||
|
||||
```python
|
||||
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 虚拟环境检查
|
||||
|
||||
```python
|
||||
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 工具版本检查
|
||||
|
||||
```python
|
||||
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 错误恢复
|
||||
|
||||
```python
|
||||
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 模式测试
|
||||
|
||||
```python
|
||||
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 项目环境测试
|
||||
|
||||
```python
|
||||
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 版本检查测试
|
||||
|
||||
```python
|
||||
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 完整工作流
|
||||
|
||||
```python
|
||||
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
|
||||
Reference in New Issue
Block a user