mcpField
A
adder

adder

github.com/lwlee2608开发257 次安装3 周前更新
代码仓库

关于 adder

面向 lwlee2608/adder 的 Go 应用配置技能,指导开发者处理 YAML 与结构体绑定、环境变量覆盖和默认值,并检查静默绑定失败。

主要功能

  1. 01让 YAML 键匹配小写 Go 字段名
  2. 02在 application.yml 中配置默认值
  3. 03使用自动环境变量映射
  4. 04按索引处理切片覆盖
  5. 05按包组织配置类型
  6. 06验证绑定并排查零值

适用场景

  1. 01为 Go 服务添加配置字段
  2. 02诊断静默加载为零值的字段
  3. 03设置本地默认值和部署覆盖项
  4. 04通过环境变量配置列表值

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lwlee2608/adder --skill adder

安装后在 Claude Code 中输入/adder确认已加载。

详细介绍

使用 lwlee2608/adder

Adder 会将 YAML 读入 Go 结构体,并叠加环境变量。它只进行不区分大小写的键匹配,不会转换 snake_case ↔ CamelCase。以下每条规则都是为了避免字段悄无声息地绑定到零值。

规则

  1. 优先使用与 Go 字段名转为小写后相匹配的 YAML 键。 Adder 会查找 strings.ToLower(field.Name),因此字段 SessionTtl 会与 YAML 键 sessionttl 匹配。小写键和 camelCase 键都可用,因为 adder 会将两侧都转为小写;camelCase 只是更易读。

    # good — matches field name when lowercased
    auth:
      username: admin
      sessionTtl: 24h
    
    type AuthConfig struct {
        Username   string
        SessionTtl time.Duration  // no tag required
    }
    

    符合 Go 习惯的写法与不使用标签的绑定之间存在取舍。 Go 风格要求首字母缩写全部大写(SessionTTL)。但 strings.ToLower("SessionTTL") 会得到 "sessionttl",这就只能使用难看的 YAML 键(sessionttl: 24h)或 mapstructure 标签。自行取舍:符合 Go 习惯的字段名加标签,或者不符合 Go 习惯的字段名但不加标签。此规范倾向后者,以尽量减少标签,但两种方式都说得通——在项目内保持一致即可。

    这同样适用于每一层嵌套——auth.session.ttl 中的每个部分都按相同方式解析。

  2. 尽量不要在 YAML 中使用 snake_case。 像 session_ttl 这样的 snake_case 键永远无法自动绑定到 SessionTTL——adder 会比较 sessionttl 和 session_ttl,发现不匹配,因此字段会悄无声息地保留为零值(例如 time.Duration(0),对于 TTL 来说意味着“立即过期”)。如果为了可读性必须使用 snake_case,则每个这样的字段都需要显式的 mapstructure 标签:

    // only if you insist on snake_case YAML
    SessionTTL time.Duration `mapstructure:"session_ttl"`
    

    优先采用规则 1——标签越少,忘记添加标签的可能性也越小。

  3. 将默认值放在 application.yml 中,而不是代码里。 application.yml 是默认值的规范来源。代码不应使用硬编码的回退值掩盖缺失的配置(例如 if cfg.SessionTTL == 0 { cfg.SessionTTL = 24*time.Hour })——这会掩盖真正的配置错误。如果某个值是必需的,应在启动时进行验证,并给出清晰的报错后 panic。

    列出每个字段,并提供本地开发用的值。 application.yml 是本地运行所需的配置,也是维护者查看所有可用配置的全局视图。为每个字段提供适用于本地开发的值(localhost、docker-compose 密码、10s);环境变量会在不同部署中覆盖这些值。只有没有安全开发值的密钥(第三方 API 密钥)才使用 ""。切勿提交真实凭据。如果非字符串字段确实没有默认值,请使用不带值的键(timeout:),不要用 ""——"" 无法解析为 time.Duration。

    保持 application.yml 中没有注释——列出每个字段后,逐字段添加注释只会淹没配置内容。

    auth:
      sessionTtl: 24h
      smtp:
        host: localhost
        port: 1025
        password: ""
        from: dev@localhost
    

    如果代码调用 godotenv.Overload(),请在 .env.example 中说明覆盖项。 列出部署时需要设置的环境变量——密钥以及任何在生产环境中开发默认值不适用的变量——并为每个变量添加注释说明。只能使用占位值。如果没有 godotenv.Overload(),则不要添加 .env 文件。

    # SMTP relay for outgoing auth emails
    AUTH_SMTP_HOST=smtp.example.com
    AUTH_SMTP_PORT=587
    # App password from the mail provider, not the account password
    AUTH_SMTP_PASSWORD=changeme
    [email protected]
    
  4. 标准环境变量模式依赖 AutomaticEnv()。 keyPath 是 adder 在内部使用的点分路径——结构体字段 Auth.SessionTtl 会变为 keyPath auth.sessionttl。使用 SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 和 AutomaticEnv() 后,adder 会将 strings.ToUpper(keyPath) 中的点替换为下划线,从而得出环境变量名。对于符合此模式的环境变量,不要添加 BindEnv 调用——这只是多余内容。

    keyPath: openrouter.apikey  →  env: OPENROUTER_APIKEY   (auto, no BindEnv needed)
    keyPath: db.url             →  env: DB_URL              (auto)
    
  5. 只有环境变量名与自动模式不一致时才使用 BindEnv。 常见原因是存在既定的外部环境变量名(例如 OPENROUTER_API_KEY,其中 API 和 KEY 之间有下划线),与自动推导出的 OPENROUTER_APIKEY 不匹配。

    // Only because the external name is OPENROUTER_API_KEY, not OPENROUTER_APIKEY
    _ = adder.BindEnv("openrouter.apikey", "OPENROUTER_API_KEY")
    
    // Don't write this — DB_URL is already automatic from keyPath db.url
    // _ = adder.BindEnv("db.url", "DB_URL")
    

    如果可以将环境变量名改为符合自动模式的形式,就这样做并删除 BindEnv。

  6. 通过环境变量中的索引访问切片元素。 切片字段的 keyPath 会增加元素索引:Clients []ClientConfig → clients.0.token → CLIENTS_0_TOKEN。如果索引超出 application.yml 中列表的长度,就会追加一个新元素,因此列表可以完全通过环境变量提供。追加会在遇到第一个缺失的索引时停止,不带索引的键(CLIENTS=...)会被忽略。不带索引的元素内字段键(CLIENTS_TOKEN=...)仍会应用于每个现有元素;两者都有值时,带索引的键优先。

    CLIENTS_1_TOKEN=t9      → clients[1].token    (override)
    CLIENTS_2_NAME=baz      → clients[2] appended
    CLIENTS_2_AUTH_ID=id-2  → clients[2].auth.id
    PATHS_0=/one            → paths[0], no yaml entry needed
    

    对于固定结构列表中的密钥,请使用 ${NAME} 占位符,而不是 ""——yaml 仍会记录列表结构,并且命名环境变量在列表重排后仍然有效,而带索引的环境变量则不会。仅将它们用于字符串密钥:未设置的变量会展开为空值,并且不支持 ${NAME:-default} 语法。如果项目有 .env.example,请在其中列出 ${NAME} 变量。仅在不同环境中的元素数量不固定时,才使用带索引的环境变量。

  7. 将配置结构体与其配置的包放在一起。 每个包管理自己的配置类型(internal/auth/config.go → auth.Config,internal/db/config.go → db.Config)。cmd 层的 Config 只负责组合。

    // cmd/myapp/config.go
    type Config struct {
        Auth auth.Config
        DB   db.Config
    }
    

    两种方式都可以绑定 Adder——这样安排是为了组织代码:包负责自己的字段、屏蔽标签,以及任何 Enabled()/Validate() 辅助函数,而 cmd/<app>/config.go 则保持简短。仅用于 cmd 装配的配置类型(例如 LogConfig)可以留在 cmd/<app>/ 中。

验证流程

添加或更改配置字段后(这些检查适用于每个嵌套层级——请验证最深层的字段,而不仅仅是顶层结构体):

  1. 小写匹配检查——strings.ToLower(fieldName) 是否与 YAML 键完全相同?如果不同,就需要添加 mapstructure 标签,或者重命名 YAML 键。
  2. 默认值检查——application.yml 中是否有该字段对应的键,且包含可正常使用的本地开发值?不设置环境变量覆盖项运行二进制程序,并确认字段的值符合预期。不要依赖记忆——请记录日志,例如 log.Printf("%+v", cfg)。
  3. 环境变量覆盖检查——如果该字段应支持环境变量覆盖,请设置自动派生的环境变量(UPPER_CASE_WITH_UNDERSCORES),并确认它覆盖了 YAML 默认值。同样,请记录加载后的值,不要凭假设判断。
  4. 零值陷阱——对于 time.Duration、int、float64 或 bool 字段,绑定失败看起来与“配置为 0/false”完全相同。务必测试非零默认值是否确实加载。

需要留意的常见错误

  • 添加多词字段时未设置 mapstructure 标签,却在 YAML 中使用 snake_case。 这是最常见的陷阱:字段会静默绑定为零值。SessionTTL + session_ttl: → 0,而不是 24h。
  • 为已符合自动模式的环境变量添加 BindEnv。 这会造成冗余,并让人难以看出哪些绑定确有必要。
  • 在 application.yml 中遗漏仅通过环境变量设置的字段。 它们仍可从环境变量绑定,但阅读 yaml 的维护者无法看到它们的存在。
  • 使用生产环境值或 "" 作为默认值。 application.yml 应开箱即用地支持本地运行;仅对没有安全开发值的密钥使用 ""。
  • 在 application.yml 中添加注释。 保持文件简洁;如果项目使用 godotenv.Overload(),请在 .env.example 中环境变量旁添加说明。
  • 用代码默认值掩盖配置错误。 if cfg.X == 0 { cfg.X = default } 会掩盖真正的问题(YAML 键没有绑定)。应修复绑定,而不是添加默认值。
  • 认为不带索引的环境变量可以替换整个列表。 CLIENTS=... 不起作用;切片覆盖必须带索引(CLIENTS_0_NAME)。
  • 误以为大小写不敏感就能识别 snake_case。 并非如此——caseInsensitiveLookup 只会统一字母大小写,不会处理分隔符。
  • 忘记 time.Duration 的底层类型是 int64。 缺失的时长会显示为 0s,而大多数代码会将其视为“无超时”或“已过期”——这几乎总是错误的。

评论

支持 Markdown