1
0
Files
DiAL/openspec/specs/config-variables/spec.md
lanyuanxiaoyao 79358ba50d refactor: 移除顶层 defaults 配置段,简化为 target 显式字段 > 代码内置默认值
- 移除 DefaultsConfig 类型、ProbeConfig.defaults 字段
- 移除 CheckerSchemas.defaults、ResolveContext.defaults、CheckerValidationInput.defaults
- 更新所有 checker schema/resolve/validate 删除 defaults 合并逻辑
- 更新 config-loader 不再读取传递 defaults
- 更新测试、README、DEVELOPMENT、probes.example.yaml
- 重新生成 probe-config.schema.json(不含 defaults)
- 同步 delta specs 到主规范
- 归档 openspec change
2026-05-21 16:53:12 +08:00

175 lines
9.7 KiB
Markdown
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.
## Purpose
定义配置文件的变量定义、引用、解析和替换机制,支持集中管理共享值和环境变量注入。
## Requirements
### Requirement: variables 段定义
配置文件 SHALL 支持可选的顶层 `variables`用于定义变量键值对。variables 的 key SHALL 符合 `[a-zA-Z_][a-zA-Z0-9_]*` 命名规则。variables 的 value SHALL 仅支持 string、number、boolean 三种类型MUST NOT 支持 null、array、object。variables 段自身 MUST NOT 支持引用其他变量或环境变量(值为纯字面量)。
#### Scenario: 定义字符串变量
- **WHEN** 配置文件包含 `variables: { base_url: "https://api.example.com" }`
- **THEN** 系统 SHALL 解析 base_url 为字符串类型变量,值为 "https://api.example.com"
#### Scenario: 定义数字变量
- **WHEN** 配置文件包含 `variables: { port: 5432 }`
- **THEN** 系统 SHALL 解析 port 为 number 类型变量,值为 5432
#### Scenario: 定义布尔变量
- **WHEN** 配置文件包含 `variables: { ssl_enabled: true }`
- **THEN** 系统 SHALL 解析 ssl_enabled 为 boolean 类型变量,值为 true
#### Scenario: 变量值为 null 报错
- **WHEN** 配置文件包含 `variables: { empty: null }`
- **THEN** 系统 SHALL 以配置错误退出,提示 variables 的值不允许为 null
#### Scenario: 变量值为数组报错
- **WHEN** 配置文件包含 `variables: { list: [1, 2, 3] }`
- **THEN** 系统 SHALL 以配置错误退出,提示 variables 的值不允许为 array
#### Scenario: 变量值为对象报错
- **WHEN** 配置文件包含 `variables: { obj: { a: 1 } }`
- **THEN** 系统 SHALL 以配置错误退出,提示 variables 的值不允许为 object
#### Scenario: 变量 key 不合法报错
- **WHEN** 配置文件包含 `variables: { "123start": "value" }`
- **THEN** 系统 SHALL 以配置错误退出,提示变量名不符合命名规则
#### Scenario: 不定义 variables 段
- **WHEN** 配置文件不包含 variables 段
- **THEN** 系统 SHALL 正常启动targets 中的 `${...}` 引用仅从环境变量查找
### Requirement: 变量引用语法
targets 中的字符串值 SHALL 支持 `${key}` 语法引用变量。系统 SHALL 支持 `${key|default}` 语法设置默认值,其中第一个 `|` 为分隔符,后续 `|` 属于默认值内容。系统 SHALL 支持 `$${...}` 转义语法输出字面量 `${...}`
#### Scenario: 简单变量引用
- **WHEN** target 字段值为 `"${base_url}/health"` 且 variables 中定义 `base_url: "https://api.example.com"`
- **THEN** 系统 SHALL 将该字段替换为 `"https://api.example.com/health"`
#### Scenario: 带默认值的变量引用
- **WHEN** target 字段值为 `"${DB_PORT|5432}"` 且 variables 和环境变量中均不存在 DB_PORT
- **THEN** 系统 SHALL 将该字段替换为使用默认值(类型推断后为 number 5432
#### Scenario: 默认值包含管道符
- **WHEN** target 字段值为 `"${PATTERN|foo|bar}"` 且变量不存在
- **THEN** 系统 SHALL 使用 `"foo|bar"` 作为默认值(第一个 `|` 为分隔符)
#### Scenario: 转义语法
- **WHEN** target 字段值为 `"Hello $${name}"`
- **THEN** 系统 SHALL 输出 `"Hello ${name}"`,不进行变量替换
#### Scenario: 多个变量引用
- **WHEN** target 字段值为 `"${protocol}://${host}:${port}/api"`
- **THEN** 系统 SHALL 逐个解析并替换所有变量引用,结果为拼接后的字符串
#### Scenario: 无变量引用的字符串
- **WHEN** target 字段值为 `"https://example.com"` 且不含 `${...}` 模式
- **THEN** 系统 SHALL 保持原样,不做任何处理
### Requirement: 变量解析优先级
系统 SHALL 按以下优先级解析变量引用variables 定义 → 环境变量 → 默认值。如果三者均不存在,系统 SHALL 以配置错误退出。
#### Scenario: variables 优先于环境变量
- **WHEN** variables 中定义 `port: 5432` 且环境变量 `port=3000` 也存在
- **THEN** 系统 SHALL 使用 variables 中的值 5432
#### Scenario: 环境变量作为 fallback
- **WHEN** variables 中未定义 `DB_HOST` 但环境变量 `DB_HOST=localhost` 存在
- **THEN** 系统 SHALL 使用环境变量的值 "localhost"
#### Scenario: 默认值作为最终 fallback
- **WHEN** variables 和环境变量中均不存在 `CACHE_TTL`,且引用为 `${CACHE_TTL|60}`
- **THEN** 系统 SHALL 使用默认值(类型推断后为 number 60
#### Scenario: 变量未定义且无默认值报错
- **WHEN** target 字段引用 `${MISSING_VAR}` 且 variables、环境变量中均不存在也未设置默认值
- **THEN** 系统 SHALL 以配置错误退出,提示该变量未定义
### Requirement: 完整引用类型保留
当字段值仅包含单个变量引用(完整引用)时,系统 SHALL 保留变量的原始类型。完整引用的判定为:字段值去掉首尾空白后严格匹配单个 `${key}``${key|default}` 模式且无其他字符。
#### Scenario: 完整引用 number 变量
- **WHEN** target 字段值为 `"${port}"` 且 variables 中 `port: 5432`
- **THEN** 系统 SHALL 将该字段替换为 number 类型的 5432
#### Scenario: 完整引用 boolean 变量
- **WHEN** target 字段值为 `"${ssl}"` 且 variables 中 `ssl: true`
- **THEN** 系统 SHALL 将该字段替换为 boolean 类型的 true
#### Scenario: 完整引用 string 变量
- **WHEN** target 字段值为 `"${host}"` 且 variables 中 `host: "example.com"`
- **THEN** 系统 SHALL 将该字段替换为 string 类型的 "example.com"
#### Scenario: 部分引用强制为字符串
- **WHEN** target 字段值为 `"port: ${port}"` 且 variables 中 `port: 5432`
- **THEN** 系统 SHALL 将该字段替换为 string 类型的 "port: 5432"
#### Scenario: 环境变量完整引用类型推断
- **WHEN** target 字段值为 `"${MAX_REDIRECTS}"` 且环境变量 `MAX_REDIRECTS=5`
- **THEN** 系统 SHALL 将该字段替换为 number 类型的 5类型推断
#### Scenario: 默认值完整引用类型推断
- **WHEN** target 字段值为 `"${TIMEOUT|30}"` 且变量不存在
- **THEN** 系统 SHALL 将该字段替换为 number 类型的 30类型推断
#### Scenario: 默认值推断为 boolean
- **WHEN** target 字段值为 `"${IGNORE_SSL|false}"` 且变量不存在
- **THEN** 系统 SHALL 将该字段替换为 boolean 类型的 false
#### Scenario: 默认值无法推断保持字符串
- **WHEN** target 字段值为 `"${HOST|localhost}"` 且变量不存在
- **THEN** 系统 SHALL 将该字段替换为 string 类型的 "localhost"
### Requirement: 替换范围限制
变量替换 SHALL 仅作用于 targets 段。`id``type` 字段 MUST NOT 参与变量替换。`server``probes` 段 MUST NOT 参与变量替换。系统 SHALL 递归遍历 target 对象树中所有字符串值进行替换(包括嵌套对象和数组元素中的字符串)。顶层 `defaults` 不再是合法配置段,因此不属于变量替换范围。
#### Scenario: target 嵌套对象中的变量替换
- **WHEN** target 配置 `http.headers.Authorization: "${token}"` 且 variables 中定义 `token: "Bearer abc"`
- **THEN** 系统 SHALL 将该 header 值替换为 "Bearer abc"
#### Scenario: target 数组元素中的变量替换
- **WHEN** target 配置 `cmd.args: ["--host", "${host}"]` 且 variables 中定义 `host: "localhost"`
- **THEN** 系统 SHALL 将数组第二个元素替换为 "localhost"
#### Scenario: id 字段不替换
- **WHEN** target 配置 `id: "${my_id}"` 且 variables 中定义 `my_id: "test"`
- **THEN** 系统 SHALL 保持 id 字段值为字面量 `"${my_id}"`,不进行替换
#### Scenario: type 字段不替换
- **WHEN** target 配置 `type: "${checker_type}"` 且 variables 中定义 `checker_type: "http"`
- **THEN** 系统 SHALL 保持 type 字段值为字面量 `"${checker_type}"`,不进行替换
#### Scenario: server 段不替换
- **WHEN** server 配置 `listen.host: "${server_host}"` 且 variables 中定义 `server_host: "0.0.0.0"`
- **THEN** 系统 SHALL 保持 server.listen.host 为字面量 `"${server_host}"`,不进行替换
#### Scenario: probes 段不替换
- **WHEN** probes 配置 `execution.maxConcurrentChecks: "${max_checks}"` 且 variables 中定义 `max_checks: 5`
- **THEN** 系统 SHALL 保持 probes.execution.maxConcurrentChecks 为字面量 `"${max_checks}"`,不进行替换
#### Scenario: defaults 段被拒绝
- **WHEN** 配置文件声明顶层 `defaults`
- **THEN** 系统 SHALL 在契约校验阶段拒绝该未知字段,而不是尝试变量替换
### Requirement: 变量替换错误报告
变量替换阶段的错误 SHALL 作为 `ConfigValidationIssue` 输出code 为 `unresolved-variable`。错误信息 SHALL 包含 target 索引、target id、字段路径和变量名。
#### Scenario: 单个变量缺失报错
- **WHEN** targets[0] (id: "api-health") 的 http.url 引用 `${base_url}` 但变量不存在
- **THEN** 系统 SHALL 输出包含 target 索引 0、id "api-health"、路径 "http.url"、变量名 "base_url" 的错误信息
#### Scenario: 多个变量缺失批量报错
- **WHEN** 多个 target 的多个字段引用了不存在的变量
- **THEN** 系统 SHALL 收集所有缺失变量错误后统一输出,而非遇到第一个就退出
### Requirement: 变量替换执行时机
变量替换 SHALL 在 YAML 解析之后、schema 契约校验AJV之前执行。替换完成后的配置对象 SHALL 传入后续校验流程。
#### Scenario: 替换后通过 schema 校验
- **WHEN** target 配置 `http.maxRedirects: "${MAX_REDIRECTS}"` 且环境变量 `MAX_REDIRECTS=5`
- **THEN** 系统 SHALL 先将该字段替换为 number 5再进入 AJV 校验(期望 integer校验通过
#### Scenario: 替换后未通过 schema 校验
- **WHEN** target 配置 `http.maxRedirects: "${MAX_REDIRECTS}"` 且环境变量 `MAX_REDIRECTS=abc`
- **THEN** 系统 SHALL 先将该字段替换为 string "abc",再进入 AJV 校验(期望 integer校验失败并报错