Dagger v0.18.6 发布解析:Secret 缓存键机制变更、GitRepository.branches 与顶层 File 字段
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
Dagger v0.18.6(2025-05-06 发布)的核心是一次面向缓存正确性的破坏性变更:URI 形式的 Secret 从此按明文的安全哈希而非 URI 字符串来计算缓存键,并新增可选的cacheKey参数以兼容"明文经常轮换但不应破坏缓存"的场景。围绕这一版本,本篇结合仓库源码讲解 Secret 缓存键的底层实现(argon2 派生、会话资源句柄绑定)、新增的GitRepository.branches与顶层File字段,以及本版本修复的五类缺陷,帮助你在升级 v0.18.6 时理解行为差异并正确使用新 API。
破坏性变更:Secret 缓存键从 URI 改为明文哈希
旧行为为什么会有问题
在 v0.18.6 之前,URI 形式的 Secret(例如env://FOO、file:///some/path)在引擎中的"缓存键"就是 URI 字符串本身。这意味着任何包含该 Secret 的操作(典型如把 Secret 作为环境变量注入Container.withEnvVariable后再withExec)都会按 URI 字符串命中缓存。
问题在于:URI 相同并不意味着明文相同。来自不同客户端的两个 Secret 可能使用完全相同的 URI,但指向完全不同的明文值——此时它们共享同一份缓存,含密操作的输出就会被错误地复用到另一客户端的执行中,产生"意料之外"的结果。这在多客户端并发、跨会话复用引擎缓存等场景下尤为隐蔽。
新行为:按明文的安全哈希做缓存键
v0.18.6 起,URI 形式的 Secret 按其明文值的安全哈希来缓存。两个 URI 相同但明文不同的 Secret 会分别缓存,包含它们的操作不再互相共享缓存。
从源码实现看,这一机制落在 core/secret.go 的两个关键函数上:
SecretHandleFromPlaintext(core/secret.go):默认路径下,引擎用 argon2 密钥派生函数(timeCost=10、memory=2048、threads=1、keySize=32)对明文加盐派生出 32 字节密钥,base64 编码后拼成argon2:前缀的 digest,作为 Secret 的会话资源句柄(SessionResourceHandle)。使用 KDF 而非简单哈希,既保证缓存键与明文一一对应,又避免缓存键本身能反推明文,这就是发布说明中"secure hashes"的工程含义。SecretHandleFromCacheKey(core/secret.go):显式提供cacheKey时的路径,直接对自定义字符串做哈希生成句柄。
在 GraphQL 层的 resolver 中(core/schema/secret.go),构造Secret时的选择逻辑非常清晰:
- 若
args.CacheKey有值,调用core.SecretHandleFromCacheKey生成句柄; - 否则回落到默认路径——先解析明文,再用
core.SecretHandleFromPlaintext(配合父节点的SecretSalt()加盐)生成句柄;若明文解析失败,则记录 warn 日志并回退到 32 字节随机数作为缓存键(保证 Secret 仍然可构造,但不会意外命中任何缓存)。
随后引擎把"具体 Secret 对象"(含 URI 与来源客户端 ID)通过cache.BindSessionResource绑定到该句柄上(core/schema/secret.go),供后续含密操作在需要时按句柄取回真实明文。这也解释了为什么旧行为会跨客户端串缓存:旧版句柄只由 URI 推导,不含客户端维度与明文维度;新版句柄要么内嵌明文派生值,要么内嵌用户显式声明的缓存键,语义都收敛到"明文等价才共享缓存"。
新参数cacheKey:为轮换密文保留共享缓存的通道
发布说明同时指出,有些用户的 Secret 明文会频繁轮换但语义上并不不同(例如定时刷新的 Token),希望它们共享缓存、不因轮换而打破缓存。为此Secret构造新增了一个可选的cacheKey参数。
cacheKey的官方文档描述(定义于 core/schema/secret.go)值得完整引用:
若设置,则给定字符串将作为该 Secret 的缓存键。这意味着任何缓存键相同的 Secret,在缓存查找时将被视为等价,即使它们的 URI 或明文值不同。 例如,两个具有相同缓存键的 Secret 作为环境变量传入两个"其他方面等价"的 Container 时,两者的
withExec会互相命中缓存。 若不设置,Secret 的缓存键将在构造 Secret 时按其查得的明文值推导。
各入口的使用方式:
SDK:各语言 SDK 将cacheKey作为Secret构造器的可选参数暴露。
dagger shell:
dagger shell -c "some-function --secret-arg $(secret env://FOO --cache-key my-cache-key)"dagger call(支持通过 URI 查询参数设置缓存键的特殊语法):
dagger call some-function --secret-arg "env://FOO?cacheKey=my-cache-key"升级提示:如果你升级前依赖的是"同 URI 共享缓存"的行为(例如同一env://FOO在不同流水线中总是相同明文,且你希望复用缓存),升级后行为不变——相同明文仍然命中相同缓存键。真正受影响的是同 URI 但明文不同的场景,它们从"错误地共享缓存"变为"正确地区分开";如果你反而希望它们继续共享,请显式加上cacheKey。
新增:GitRepository.branches API
本版本新增GitRepository.branches字段,返回与给定 glob 模式匹配的分支列表,与既有tags字段形成对称。
schema 定义见 core/schema/git.go:
dagql.Func("tags", s.tags). Doc(`tags that match any of the given glob patterns.`). Args( dagql.Arg("patterns").Doc(`Glob patterns (e.g., "refs/tags/v*").`), ), dagql.Func("branches", s.branches). Doc(`branches that match any of the given glob patterns.`). Args( dagql.Arg("patterns").Doc(`Glob patterns (e.g., "refs/tags/v*").`), ),实现上,branches的 resolver(core/schema/git.go)接受可选的patterns参数(dagql.Optional[dagql.ArrayInput[dagql.String]]),通过parent.LoadRemote(ctx)加载仓库远端引用,再执行remote.Filter(patterns).Branches().ShortNames()返回短名列表。也就是说:
patterns是 glob 模式数组,不传则返回全部分支;- 返回值是短名(不含
refs/heads/前缀),与tags返回的短名风格一致; - 典型用法是在模块中枚举符合规则(如
release/*)的分支并逐个触发构建。
新增:顶层 File 字段
v0.18.6 在Query上直接暴露了file字段,用于更便捷地创建File对象,无需再经由"空目录 +withNewFile+ 取文件"的绕路方式。
字段定义与参数说明见 core/schema/file.go:
| 参数 | 说明 | 示例 |
|---|---|---|
name | 新文件名,不允许包含目录部分 | "foo.txt" |
contents | 文件内容 | "Hello world!" |
permissions | 文件权限 | 0600 |
resolver(core/schema/file.go)的校验与实现逻辑:
- 若
name中包含目录部分,直接报错file name %q must not contain a directory; - 再经
core.ValidateFileName做文件名合法性校验; - 内部实现是一个选择链:
directory → withNewFile(path, contents, permissions) → file(path),即复用 Directory 的子文件能力构造出目标File。参数默认权限为0644(见newFileArgs,core/schema/file.go)。
这使得"在流水线中临时生成一个配置文件"这类操作变得直接,例如 `query.file(name: "config.toml", contents: ...)。
缺陷修复(Fixed)
本版本还包含五项修复,均对应源码中可定位的行为:
GitRepository.tags的patterns参数现在对本地 git 仓库生效:此前本地仓库场景下 glob 过滤未生效;与新增的branches同属一次 git 引用枚举能力的完善(core/schema/git.go 展示了过滤的统一入口remote.Filter(patterns))。dagger call中函数参数与持久化(persistent)flag 冲突时返回错误:参数名冲突不再被静默处理,而是在解析阶段显式报错,避免用户误以为参数已生效。- 并发执行同名函数且一个客户端取消请求时的报错修复:修复了"failed to return error"和"failed to emit telemetry"这类二次错误——此前两个相同函数并发执行、其一客户端取消时,错误回传与遥测发射路径会互相干扰并产生误导性的二次错误。
- vault Secret provider panic 修复:当 vault 中路径(path)存在但目标 secret 不存在时,旧代码会 panic;现在会返回正常错误。从当前仓库的 provider 实现(如 engine/client/secretprovider/vault.go 中的缓存与字段解析逻辑)可以看到,provider 对"缓存未命中"与"字段缺失"的分支处理正是这类健壮性修复的落点。
Container.build使用FROM scratchDockerfile 时的 panic 修复:FROM scratch是最基础的构建场景,修复后该路径不再触发引擎内部 panic,构建会以正常错误流程处理或成功完成。
升级建议小结
- 检查你的流水线中是否存在同 URI、不同明文的 Secret 跨客户端复用缓存的依赖——升级后这类缓存将被正确隔离,若你希望保持共享,请改用显式
cacheKey。 - 对明文频繁轮换但语义恒定的 Secret(轮换 Token、定期重签的凭据),显式传入
cacheKey(SDK 构造参数 /dagger shell的--cache-key/dagger call的?cacheKey=查询参数)可保持缓存稳定性。 - 新的
GitRepository.branches与顶层File字段均为纯增量 API,可直接在模块与 GraphQL 查询中启用。 - 若你此前遇到过
FROM scratch构建 panic 或 vault secret 缺失时的 panic,直接升级到 v0.18.6 即可获得修复。
适用前提:本文所有源码行为描述均基于当前仓库 HEAD 的实现(core/secret.go、core/schema/secret.go、core/schema/git.go、core/schema/file.go),与 v0.18.6 发布说明对应;后续版本的 schema 可能在此基础上继续演进,以实际使用的引擎版本 introspection 结果为准。
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考