1
0
Files
DiAL/openspec/specs/config-variables/spec.md
lanyuanxiaoyao e448cb4654 feat: 重构配置布局,server.listen/storage/logging + probes.execution 分组
- 新增 server.listen (host/port)、server.storage (dataDir/retention)、
  server.logging 分组
- 新增 probes.execution (maxConcurrentChecks) 分组,替代顶层 runtime
- 旧配置入口 (runtime/logging/server.host/server.port/server.dataDir)
  启动期拒绝
- 更新 types.ts、builder.ts、config-loader.ts 适配新路径
- 更新 probe-config.schema.json、probes.example.yaml、README.md、
  DEVELOPMENT.md
- 补充 config-loader 和 variables 测试覆盖新路径和旧入口拒绝
- 同步 5 个 delta specs 到主规范 (probe-config, config-variables,
  data-retention, probe-engine, runtime-logging)
- 归档 openspec change reorganize-config-layout
2026-05-21 13:54:41 +08:00

9.7 KiB
Raw Blame History

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 段。idtype 字段 MUST NOT 参与变量替换。serverprobesdefaults 段 MUST NOT 参与变量替换。系统 SHALL 递归遍历 target 对象树中所有字符串值进行替换(包括嵌套对象和数组元素中的字符串)。

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: defaults 段不替换

  • WHEN defaults 配置 interval: "${default_interval}" 且 variables 中定义 default_interval: "30s"
  • THEN 系统 SHALL 保持 defaults.interval 为字面量 "${default_interval}",不进行替换

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}",不进行替换

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校验失败并报错