746 lines
16 KiB
Markdown
746 lines
16 KiB
Markdown
|
|
# aide config 设计
|
|||
|
|
|
|||
|
|
## 一、命令概述
|
|||
|
|
|
|||
|
|
### 1.1 功能定位
|
|||
|
|
|
|||
|
|
`aide config` 命令用于读取和修改项目配置文件,提供命令行方式访问配置。
|
|||
|
|
|
|||
|
|
### 1.2 执行时机
|
|||
|
|
|
|||
|
|
- 用户需要查看配置值时
|
|||
|
|
- 用户需要修改配置值时
|
|||
|
|
- Commands 中需要获取配置时(如 prep/exec 的默认文档路径)
|
|||
|
|
|
|||
|
|
### 1.3 命令格式
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
aide config get <key>
|
|||
|
|
aide config set <key> <value>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
- `<key>`:配置键,支持点号分隔(如 `task.source`)
|
|||
|
|
- `<value>`:配置值(仅 set 命令需要)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、功能需求
|
|||
|
|
|
|||
|
|
### 2.1 aide config get
|
|||
|
|
|
|||
|
|
**功能**:获取配置值
|
|||
|
|
|
|||
|
|
**输出格式**:
|
|||
|
|
|
|||
|
|
单个值:
|
|||
|
|
```
|
|||
|
|
task.source = "task-now.md"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
数组值:
|
|||
|
|
```
|
|||
|
|
flow.phases = ["flow-design", "impl", "verify", "docs", "finish"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
布尔值:
|
|||
|
|
```
|
|||
|
|
env.tools.git = true
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**错误处理**:
|
|||
|
|
|
|||
|
|
配置键不存在:
|
|||
|
|
```
|
|||
|
|
✗ 配置键不存在: invalid.key
|
|||
|
|
可用的配置键: task.source, task.spec, env.python.version
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
配置文件不存在:
|
|||
|
|
```
|
|||
|
|
✗ 配置文件不存在
|
|||
|
|
位置: .aide/config.toml
|
|||
|
|
建议: 运行 'aide init' 创建配置文件
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.2 aide config set
|
|||
|
|
|
|||
|
|
**功能**:设置配置值
|
|||
|
|
|
|||
|
|
**输出**:
|
|||
|
|
|
|||
|
|
成功时无输出(静默原则)
|
|||
|
|
|
|||
|
|
**错误处理**:
|
|||
|
|
|
|||
|
|
配置键不存在:
|
|||
|
|
```
|
|||
|
|
✗ 配置键不存在: invalid.key
|
|||
|
|
可用的配置键: task.source, task.spec, env.python.version
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
配置值类型错误:
|
|||
|
|
```
|
|||
|
|
✗ 配置值类型错误
|
|||
|
|
键: env.tools.git
|
|||
|
|
期望类型: boolean
|
|||
|
|
实际值: "yes"
|
|||
|
|
建议: 使用 true 或 false
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、实现设计
|
|||
|
|
|
|||
|
|
### 3.1 函数接口
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def cmd_config(args: list[str]) -> int:
|
|||
|
|
"""aide config 命令处理
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
args: 命令参数
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
退出码(0 表示成功)
|
|||
|
|
"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def config_get(key: str) -> int:
|
|||
|
|
"""获取配置值
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
key: 配置键
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
退出码
|
|||
|
|
"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def config_set(key: str, value: str) -> int:
|
|||
|
|
"""设置配置值
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
key: 配置键
|
|||
|
|
value: 配置值(字符串形式)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
退出码
|
|||
|
|
"""
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 实现流程
|
|||
|
|
|
|||
|
|
#### 3.2.1 cmd_config
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from core.output import err
|
|||
|
|
|
|||
|
|
def cmd_config(args: list[str]) -> int:
|
|||
|
|
"""aide config 命令处理"""
|
|||
|
|
|
|||
|
|
if not args:
|
|||
|
|
err(
|
|||
|
|
"缺少子命令",
|
|||
|
|
[
|
|||
|
|
"用法: aide config get <key>",
|
|||
|
|
" aide config set <key> <value>"
|
|||
|
|
]
|
|||
|
|
)
|
|||
|
|
return 2
|
|||
|
|
|
|||
|
|
subcommand = args[0]
|
|||
|
|
|
|||
|
|
if subcommand == "get":
|
|||
|
|
if len(args) < 2:
|
|||
|
|
err("缺少参数: key")
|
|||
|
|
return 2
|
|||
|
|
return config_get(args[1])
|
|||
|
|
|
|||
|
|
elif subcommand == "set":
|
|||
|
|
if len(args) < 3:
|
|||
|
|
err("缺少参数: key 或 value")
|
|||
|
|
return 2
|
|||
|
|
return config_set(args[1], args[2])
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
err(
|
|||
|
|
f"未知子命令: {subcommand}",
|
|||
|
|
["可用子命令: get, set"]
|
|||
|
|
)
|
|||
|
|
return 2
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.2 config_get
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from pathlib import Path
|
|||
|
|
from core.config import Config
|
|||
|
|
from core.output import err
|
|||
|
|
|
|||
|
|
def config_get(key: str) -> int:
|
|||
|
|
"""获取配置值"""
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 加载配置
|
|||
|
|
config = Config(Path.cwd())
|
|||
|
|
config.load()
|
|||
|
|
|
|||
|
|
# 获取值
|
|||
|
|
value = config.get(key)
|
|||
|
|
|
|||
|
|
if value is None:
|
|||
|
|
# 配置键不存在
|
|||
|
|
available_keys = get_available_keys(config._config)
|
|||
|
|
err(
|
|||
|
|
f"配置键不存在: {key}",
|
|||
|
|
[f"可用的配置键: {', '.join(available_keys)}"]
|
|||
|
|
)
|
|||
|
|
return 4
|
|||
|
|
|
|||
|
|
# 格式化输出
|
|||
|
|
formatted_value = format_config_value(value)
|
|||
|
|
print(f"{key} = {formatted_value}")
|
|||
|
|
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
except FileNotFoundError:
|
|||
|
|
err(
|
|||
|
|
"配置文件不存在",
|
|||
|
|
[
|
|||
|
|
"位置: .aide/config.toml",
|
|||
|
|
"建议: 运行 'aide init' 创建配置文件"
|
|||
|
|
]
|
|||
|
|
)
|
|||
|
|
return 4
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
err(
|
|||
|
|
"配置读取失败",
|
|||
|
|
[f"原因: {str(e)}"]
|
|||
|
|
)
|
|||
|
|
return 4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.3 config_set
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def config_set(key: str, value_str: str) -> int:
|
|||
|
|
"""设置配置值"""
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 加载配置
|
|||
|
|
config = Config(Path.cwd())
|
|||
|
|
config.load()
|
|||
|
|
|
|||
|
|
# 验证配置键是否存在
|
|||
|
|
if not is_valid_config_key(key):
|
|||
|
|
available_keys = get_available_keys(config._config)
|
|||
|
|
err(
|
|||
|
|
f"配置键不存在: {key}",
|
|||
|
|
[f"可用的配置键: {', '.join(available_keys)}"]
|
|||
|
|
)
|
|||
|
|
return 4
|
|||
|
|
|
|||
|
|
# 解析值
|
|||
|
|
try:
|
|||
|
|
value = parse_config_value(key, value_str, config._config)
|
|||
|
|
except ValueError as e:
|
|||
|
|
err(
|
|||
|
|
"配置值类型错误",
|
|||
|
|
[
|
|||
|
|
f"键: {key}",
|
|||
|
|
f"原因: {str(e)}"
|
|||
|
|
]
|
|||
|
|
)
|
|||
|
|
return 4
|
|||
|
|
|
|||
|
|
# 设置值
|
|||
|
|
config.set(key, value)
|
|||
|
|
|
|||
|
|
# 成功时无输出(静默原则)
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
except FileNotFoundError:
|
|||
|
|
err(
|
|||
|
|
"配置文件不存在",
|
|||
|
|
[
|
|||
|
|
"位置: .aide/config.toml",
|
|||
|
|
"建议: 运行 'aide init' 创建配置文件"
|
|||
|
|
]
|
|||
|
|
)
|
|||
|
|
return 4
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
err(
|
|||
|
|
"配置写入失败",
|
|||
|
|
[f"原因: {str(e)}"]
|
|||
|
|
)
|
|||
|
|
return 4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 辅助函数
|
|||
|
|
|
|||
|
|
#### 3.3.1 格式化配置值
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
def format_config_value(value) -> str:
|
|||
|
|
"""格式化配置值用于输出
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
value: 配置值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
格式化后的字符串
|
|||
|
|
"""
|
|||
|
|
if isinstance(value, str):
|
|||
|
|
return f'"{value}"'
|
|||
|
|
elif isinstance(value, bool):
|
|||
|
|
return "true" if value else "false"
|
|||
|
|
elif isinstance(value, (list, dict)):
|
|||
|
|
return json.dumps(value, ensure_ascii=False)
|
|||
|
|
else:
|
|||
|
|
return str(value)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.3.2 解析配置值
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def parse_config_value(key: str, value_str: str, config: dict):
|
|||
|
|
"""解析配置值
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
key: 配置键
|
|||
|
|
value_str: 值字符串
|
|||
|
|
config: 当前配置(用于推断类型)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
解析后的值
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
ValueError: 值类型错误
|
|||
|
|
"""
|
|||
|
|
# 获取当前值以推断类型
|
|||
|
|
current_value = get_config_value(config, key)
|
|||
|
|
|
|||
|
|
if current_value is None:
|
|||
|
|
# 新键,尝试自动推断类型
|
|||
|
|
return auto_parse_value(value_str)
|
|||
|
|
|
|||
|
|
# 根据当前值的类型解析
|
|||
|
|
if isinstance(current_value, bool):
|
|||
|
|
return parse_bool(value_str)
|
|||
|
|
elif isinstance(current_value, int):
|
|||
|
|
return parse_int(value_str)
|
|||
|
|
elif isinstance(current_value, float):
|
|||
|
|
return parse_float(value_str)
|
|||
|
|
elif isinstance(current_value, list):
|
|||
|
|
return parse_list(value_str)
|
|||
|
|
elif isinstance(current_value, dict):
|
|||
|
|
return parse_dict(value_str)
|
|||
|
|
else:
|
|||
|
|
# 字符串
|
|||
|
|
return value_str
|
|||
|
|
|
|||
|
|
def parse_bool(value_str: str) -> bool:
|
|||
|
|
"""解析布尔值"""
|
|||
|
|
lower = value_str.lower()
|
|||
|
|
if lower in ["true", "yes", "1"]:
|
|||
|
|
return True
|
|||
|
|
elif lower in ["false", "no", "0"]:
|
|||
|
|
return False
|
|||
|
|
else:
|
|||
|
|
raise ValueError(f"无效的布尔值: {value_str},使用 true 或 false")
|
|||
|
|
|
|||
|
|
def parse_int(value_str: str) -> int:
|
|||
|
|
"""解析整数"""
|
|||
|
|
try:
|
|||
|
|
return int(value_str)
|
|||
|
|
except ValueError:
|
|||
|
|
raise ValueError(f"无效的整数: {value_str}")
|
|||
|
|
|
|||
|
|
def parse_float(value_str: str) -> float:
|
|||
|
|
"""解析浮点数"""
|
|||
|
|
try:
|
|||
|
|
return float(value_str)
|
|||
|
|
except ValueError:
|
|||
|
|
raise ValueError(f"无效的浮点数: {value_str}")
|
|||
|
|
|
|||
|
|
def parse_list(value_str: str) -> list:
|
|||
|
|
"""解析列表(JSON 格式)"""
|
|||
|
|
try:
|
|||
|
|
value = json.loads(value_str)
|
|||
|
|
if not isinstance(value, list):
|
|||
|
|
raise ValueError("不是列表")
|
|||
|
|
return value
|
|||
|
|
except json.JSONDecodeError:
|
|||
|
|
raise ValueError(f"无效的列表格式: {value_str},使用 JSON 格式")
|
|||
|
|
|
|||
|
|
def parse_dict(value_str: str) -> dict:
|
|||
|
|
"""解析字典(JSON 格式)"""
|
|||
|
|
try:
|
|||
|
|
value = json.loads(value_str)
|
|||
|
|
if not isinstance(value, dict):
|
|||
|
|
raise ValueError("不是字典")
|
|||
|
|
return value
|
|||
|
|
except json.JSONDecodeError:
|
|||
|
|
raise ValueError(f"无效的字典格式: {value_str},使用 JSON 格式")
|
|||
|
|
|
|||
|
|
def auto_parse_value(value_str: str):
|
|||
|
|
"""自动推断并解析值"""
|
|||
|
|
# 尝试 JSON
|
|||
|
|
try:
|
|||
|
|
return json.loads(value_str)
|
|||
|
|
except json.JSONDecodeError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# 尝试布尔值
|
|||
|
|
if value_str.lower() in ["true", "false", "yes", "no"]:
|
|||
|
|
return parse_bool(value_str)
|
|||
|
|
|
|||
|
|
# 尝试数字
|
|||
|
|
try:
|
|||
|
|
if "." in value_str:
|
|||
|
|
return float(value_str)
|
|||
|
|
else:
|
|||
|
|
return int(value_str)
|
|||
|
|
except ValueError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# 默认为字符串
|
|||
|
|
return value_str
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.3.3 获取可用配置键
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def get_available_keys(config: dict, prefix: str = "") -> list[str]:
|
|||
|
|
"""获取所有可用的配置键
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
config: 配置字典
|
|||
|
|
prefix: 键前缀
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
配置键列表
|
|||
|
|
"""
|
|||
|
|
keys = []
|
|||
|
|
|
|||
|
|
for key, value in config.items():
|
|||
|
|
full_key = f"{prefix}{key}" if prefix else key
|
|||
|
|
|
|||
|
|
if isinstance(value, dict):
|
|||
|
|
# 递归获取嵌套键
|
|||
|
|
keys.extend(get_available_keys(value, f"{full_key}."))
|
|||
|
|
else:
|
|||
|
|
keys.append(full_key)
|
|||
|
|
|
|||
|
|
return sorted(keys)
|
|||
|
|
|
|||
|
|
def is_valid_config_key(key: str) -> bool:
|
|||
|
|
"""验证配置键是否有效
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
key: 配置键
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
是否有效
|
|||
|
|
"""
|
|||
|
|
# 定义允许的配置键
|
|||
|
|
valid_keys = [
|
|||
|
|
"task.source",
|
|||
|
|
"task.spec",
|
|||
|
|
"env.python.version",
|
|||
|
|
"env.python.venv",
|
|||
|
|
"env.tools.uv",
|
|||
|
|
"env.tools.git",
|
|||
|
|
"flow.phases",
|
|||
|
|
"flow.flowchart_dir",
|
|||
|
|
"decide.port",
|
|||
|
|
"decide.decisions_dir",
|
|||
|
|
"output.color",
|
|||
|
|
"output.language"
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
return key in valid_keys
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、错误处理
|
|||
|
|
|
|||
|
|
### 4.1 错误分类
|
|||
|
|
|
|||
|
|
| 错误类型 | 退出码 | 处理方式 |
|
|||
|
|
|---------|--------|---------|
|
|||
|
|
| 缺少参数 | 2 | 显示用法 |
|
|||
|
|
| 配置文件不存在 | 4 | 提示运行 aide init |
|
|||
|
|
| 配置键不存在 | 4 | 显示可用的键 |
|
|||
|
|
| 配置值类型错误 | 4 | 显示期望类型和建议 |
|
|||
|
|
| 配置文件格式错误 | 4 | 显示错误位置 |
|
|||
|
|
|
|||
|
|
### 4.2 类型验证
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def validate_config_value(key: str, value) -> list[str]:
|
|||
|
|
"""验证配置值
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
key: 配置键
|
|||
|
|
value: 配置值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
错误列表(空列表表示无错误)
|
|||
|
|
"""
|
|||
|
|
errors = []
|
|||
|
|
|
|||
|
|
# 验证特定键的值
|
|||
|
|
if key == "env.python.version":
|
|||
|
|
if not isinstance(value, str):
|
|||
|
|
errors.append("env.python.version 必须是字符串")
|
|||
|
|
elif not is_valid_version_spec(value):
|
|||
|
|
errors.append(f"无效的版本格式: {value}")
|
|||
|
|
|
|||
|
|
elif key == "env.python.venv":
|
|||
|
|
if not isinstance(value, str):
|
|||
|
|
errors.append("env.python.venv 必须是字符串")
|
|||
|
|
|
|||
|
|
elif key in ["env.tools.uv", "env.tools.git", "output.color"]:
|
|||
|
|
if not isinstance(value, bool):
|
|||
|
|
errors.append(f"{key} 必须是布尔值")
|
|||
|
|
|
|||
|
|
elif key == "flow.phases":
|
|||
|
|
if not isinstance(value, list):
|
|||
|
|
errors.append("flow.phases 必须是数组")
|
|||
|
|
elif not all(isinstance(p, str) for p in value):
|
|||
|
|
errors.append("flow.phases 的元素必须是字符串")
|
|||
|
|
|
|||
|
|
elif key == "decide.port":
|
|||
|
|
if not isinstance(value, int):
|
|||
|
|
errors.append("decide.port 必须是整数")
|
|||
|
|
elif not (1024 <= value <= 65535):
|
|||
|
|
errors.append("decide.port 必须在 1024-65535 之间")
|
|||
|
|
|
|||
|
|
return errors
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、测试用例
|
|||
|
|
|
|||
|
|
### 5.1 config get 测试
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def test_config_get_success(tmp_path, monkeypatch, capsys):
|
|||
|
|
"""测试获取配置成功"""
|
|||
|
|
monkeypatch.chdir(tmp_path)
|
|||
|
|
|
|||
|
|
# 初始化配置
|
|||
|
|
cmd_init([])
|
|||
|
|
|
|||
|
|
# 获取配置
|
|||
|
|
result = config_get("task.source")
|
|||
|
|
|
|||
|
|
# 验证退出码
|
|||
|
|
assert result == 0
|
|||
|
|
|
|||
|
|
# 验证输出
|
|||
|
|
captured = capsys.readouterr()
|
|||
|
|
assert 'task.source = "task-now.md"' in captured.out
|
|||
|
|
|
|||
|
|
def test_config_get_not_found(tmp_path, monkeypatch, capsys):
|
|||
|
|
"""测试配置键不存在"""
|
|||
|
|
monkeypatch.chdir(tmp_path)
|
|||
|
|
|
|||
|
|
# 初始化配置
|
|||
|
|
cmd_init([])
|
|||
|
|
|
|||
|
|
# 获取不存在的配置
|
|||
|
|
result = config_get("invalid.key")
|
|||
|
|
|
|||
|
|
# 验证退出码
|
|||
|
|
assert result == 4
|
|||
|
|
|
|||
|
|
# 验证输出
|
|||
|
|
captured = capsys.readouterr()
|
|||
|
|
assert "配置键不存在" in captured.out
|
|||
|
|
|
|||
|
|
def test_config_get_no_config(tmp_path, monkeypatch, capsys):
|
|||
|
|
"""测试配置文件不存在"""
|
|||
|
|
monkeypatch.chdir(tmp_path)
|
|||
|
|
|
|||
|
|
# 获取配置(未初始化)
|
|||
|
|
result = config_get("task.source")
|
|||
|
|
|
|||
|
|
# 验证退出码
|
|||
|
|
assert result == 4
|
|||
|
|
|
|||
|
|
# 验证输出
|
|||
|
|
captured = capsys.readouterr()
|
|||
|
|
assert "配置文件不存在" in captured.out
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 config set 测试
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def test_config_set_success(tmp_path, monkeypatch):
|
|||
|
|
"""测试设置配置成功"""
|
|||
|
|
monkeypatch.chdir(tmp_path)
|
|||
|
|
|
|||
|
|
# 初始化配置
|
|||
|
|
cmd_init([])
|
|||
|
|
|
|||
|
|
# 设置配置
|
|||
|
|
result = config_set("task.source", "new-task.md")
|
|||
|
|
|
|||
|
|
# 验证退出码
|
|||
|
|
assert result == 0
|
|||
|
|
|
|||
|
|
# 验证配置已更新
|
|||
|
|
config = Config(tmp_path)
|
|||
|
|
config.load()
|
|||
|
|
assert config.get("task.source") == "new-task.md"
|
|||
|
|
|
|||
|
|
def test_config_set_bool(tmp_path, monkeypatch):
|
|||
|
|
"""测试设置布尔值"""
|
|||
|
|
monkeypatch.chdir(tmp_path)
|
|||
|
|
|
|||
|
|
# 初始化配置
|
|||
|
|
cmd_init([])
|
|||
|
|
|
|||
|
|
# 设置布尔值
|
|||
|
|
result = config_set("env.tools.uv", "true")
|
|||
|
|
assert result == 0
|
|||
|
|
|
|||
|
|
# 验证
|
|||
|
|
config = Config(tmp_path)
|
|||
|
|
config.load()
|
|||
|
|
assert config.get("env.tools.uv") == True
|
|||
|
|
|
|||
|
|
def test_config_set_invalid_type(tmp_path, monkeypatch, capsys):
|
|||
|
|
"""测试设置错误类型"""
|
|||
|
|
monkeypatch.chdir(tmp_path)
|
|||
|
|
|
|||
|
|
# 初始化配置
|
|||
|
|
cmd_init([])
|
|||
|
|
|
|||
|
|
# 设置错误类型
|
|||
|
|
result = config_set("env.tools.git", "yes")
|
|||
|
|
|
|||
|
|
# 验证退出码
|
|||
|
|
assert result == 4
|
|||
|
|
|
|||
|
|
# 验证输出
|
|||
|
|
captured = capsys.readouterr()
|
|||
|
|
assert "类型错误" in captured.out
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.3 值解析测试
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def test_parse_bool():
|
|||
|
|
"""测试布尔值解析"""
|
|||
|
|
assert parse_bool("true") == True
|
|||
|
|
assert parse_bool("false") == False
|
|||
|
|
assert parse_bool("yes") == True
|
|||
|
|
assert parse_bool("no") == False
|
|||
|
|
|
|||
|
|
with pytest.raises(ValueError):
|
|||
|
|
parse_bool("invalid")
|
|||
|
|
|
|||
|
|
def test_parse_list():
|
|||
|
|
"""测试列表解析"""
|
|||
|
|
result = parse_list('["a", "b", "c"]')
|
|||
|
|
assert result == ["a", "b", "c"]
|
|||
|
|
|
|||
|
|
with pytest.raises(ValueError):
|
|||
|
|
parse_list("not a list")
|
|||
|
|
|
|||
|
|
def test_auto_parse_value():
|
|||
|
|
"""测试自动解析"""
|
|||
|
|
assert auto_parse_value("true") == True
|
|||
|
|
assert auto_parse_value("123") == 123
|
|||
|
|
assert auto_parse_value("3.14") == 3.14
|
|||
|
|
assert auto_parse_value('["a"]') == ["a"]
|
|||
|
|
assert auto_parse_value("text") == "text"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六、使用示例
|
|||
|
|
|
|||
|
|
### 6.1 查看配置
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查看任务文档路径
|
|||
|
|
aide config get task.source
|
|||
|
|
|
|||
|
|
# 查看 Python 版本要求
|
|||
|
|
aide config get env.python.version
|
|||
|
|
|
|||
|
|
# 查看流程环节列表
|
|||
|
|
aide config get flow.phases
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6.2 修改配置
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 修改任务文档路径
|
|||
|
|
aide config set task.source "my-task.md"
|
|||
|
|
|
|||
|
|
# 修改 Python 版本要求
|
|||
|
|
aide config set env.python.version ">=3.11"
|
|||
|
|
|
|||
|
|
# 启用 uv
|
|||
|
|
aide config set env.tools.uv true
|
|||
|
|
|
|||
|
|
# 修改端口
|
|||
|
|
aide config set decide.port 8080
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 七、性能要求
|
|||
|
|
|
|||
|
|
### 7.1 执行时间
|
|||
|
|
|
|||
|
|
- config get:< 100ms
|
|||
|
|
- config set:< 200ms
|
|||
|
|
|
|||
|
|
### 7.2 资源占用
|
|||
|
|
|
|||
|
|
- 内存:< 20MB
|
|||
|
|
- 磁盘 I/O:最小化
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 八、总结
|
|||
|
|
|
|||
|
|
### 8.1 核心要点
|
|||
|
|
|
|||
|
|
1. 支持 get 和 set 两个子命令
|
|||
|
|
2. 点号分隔的键访问
|
|||
|
|
3. 自动类型推断和验证
|
|||
|
|
4. 静默原则(set 成功时无输出)
|
|||
|
|
5. 清晰的错误信息
|
|||
|
|
|
|||
|
|
### 8.2 实现检查清单
|
|||
|
|
|
|||
|
|
- [ ] 实现 cmd_config 函数
|
|||
|
|
- [ ] 实现 config_get 函数
|
|||
|
|
- [ ] 实现 config_set 函数
|
|||
|
|
- [ ] 实现值格式化函数
|
|||
|
|
- [ ] 实现值解析函数
|
|||
|
|
- [ ] 实现类型验证
|
|||
|
|
- [ ] 编写单元测试
|
|||
|
|
- [ ] 编写集成测试
|
|||
|
|
- [ ] 性能测试
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**版本**:v1.0
|
|||
|
|
**更新日期**:2025-12-13
|