Dagger TypeScript SDK 的 CurrentModuleGeneratorsOpts 详解:用 include 精确筛选模块生成器
【免费下载链接】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
导读
CurrentModuleGeneratorsOpts是 Dagger TypeScript SDK(@dagger.io/dagger)在api/client.gen模块中定义的一个类型别名,它是CurrentModule.generators()方法的可选参数对象。本文从该类型在 SDK 中的定义出发,结合仓库内 TypeScript 生成代码与 Go 侧 GraphQL Schema 实现,深入讲解其唯一字段include的模式匹配规则、底层执行链路,并给出在模块函数内按需筛选生成器的实战写法。读完本文,你将能够在 Dagger 模块中精确控制"本次只运行哪些代码生成器",从而在 CI、本地开发或工作流编排中按需触发代码生成。
一、类型定义:一个可选的 include 模式数组
在 Dagger v0.19 的 TypeScript API 参考文档中,CurrentModuleGeneratorsOpts的定义极为简洁:
CurrentModuleGeneratorsOpts = object它只包含一个可选属性:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
include? | string[] | 否 | Only include generators matching the specified patterns(仅包含与指定模式匹配的生成器) |
这份文档对应的真实 TypeScript 源码位于仓库 sdk/typescript/src/api/client.gen.ts,由 Dagger 的代码生成器根据引擎 GraphQL Schema 自动产出,其内容与文档完全一致:
export type CurrentModuleGeneratorsOpts = { /** * Only include generators matching the specified patterns */ include?: string[] }从类型结构可以确认两点事实:
- 整体可选:
include字段是可选的,整个opts参数在调用时也可省略,省略时等价于"返回模块定义的所有生成器"; - 数组语义:传入多个模式时按"任一匹配即命中"(OR 语义)处理,这一点可以从底层实现中确认(详见第三节)。
同目录下还有一个结构完全相同的兄弟类型ModuleGeneratorsOpts(见 ModuleGeneratorsOpts.md 与 client.gen.ts 附近定义),它服务于Module.generators()方法,区别仅在于调用主体不同。
二、方法签名:CurrentModule.generators(opts?) 与返回类型
CurrentModuleGeneratorsOpts只被一个方法消费,即CurrentModule类上的generators方法。SDK 源码 sdk/typescript/src/api/client.gen.ts 中给出了带注释的完整签名:
/** * Return all generators defined by the module * @param opts.include Only include generators matching the specified patterns * @experimental */ generators = (opts?: CurrentModuleGeneratorsOpts): GeneratorGroup => { const ctx = this._ctx.select("generators", { ...opts }) return new GeneratorGroup(ctx) }需要注意的要点:
- 返回类型是
GeneratorGroup,而不是单个生成器。GeneratorGroup封装了一个或多个生成器的集合,其 API 同样定义在 client.gen.ts,包括:list():列出组内每个生成器及其详情;changes(opts?):上一次运行合并后的 changeset(文件差异);isEmpty():上一次运行产生的 changeset 是否为空;loadFailures():收集生成器时被容忍的加载失败信息。
- 该方法被标记为
@experimental:在 GraphQL Schema 中同样标注为 "highly experimental and may be removed or replaced entirely"(见 core/schema/module.go),因此生产环境使用时需注意 API 后续可能演进。 CurrentModule代表"正在其中执行的模块",与通用的Module不同:CurrentModule.generators()让模块代码在运行期读取自身定义的生成器集合,是实现自省式生成管线的入口。
三、include 的底层实现:从 TypeScript 到 Go 引擎的完整链路
要理解include的确切行为,需要沿调用链深入 Go 引擎侧。
3.1 GraphQL 字段定义
在引擎 Schema 中,currentModule.generators字段定义于 core/schema/module.go:
dagql.Func("generators", s.currentModuleGenerators). Experimental("This API is highly experimental and may be removed or replaced entirely."). Doc(`Return all generators defined by the module`). Args( dagql.Arg("include").Doc("Only include generators matching the specified patterns"), ),模块级别的module.generators与之对称,同样接收include参数(core/schema/module.go)。
3.2 参数解析与分组构建
对应的 Go 解析函数currentModuleGenerators位于 core/schema/module.go:
func (s *moduleSchema) currentModuleGenerators( ctx context.Context, mod *core.CurrentModule, args struct { Include dagql.Optional[dagql.ArrayInput[dagql.String]] }, ) (*core.GeneratorGroup, error) { var include []string if args.Include.Valid { for _, pattern := range args.Include.Value { include = append(include, pattern.String()) } } return core.NewGeneratorGroup(ctx, mod.Module, include) }这段代码清楚展示了:
include在 GraphQL 层是[String](字符串数组),可为空;- 省略时
include为空切片,此时NewGeneratorGroup不做任何过滤; - 传入时逐个取出字符串,原样交给
core.NewGeneratorGroup。
3.3 模式匹配的核心:RollupGenerator 与 Glob
NewGeneratorGroup(core/generators.go)先把模块挂载为模块树,再调用RollupGenerator:
func NewGeneratorGroup(ctx context.Context, mod dagql.ObjectResult[*Module], include []string) (*GeneratorGroup, error) { rootNode, err := NewModTree(ctx, mod) ... generatorNodes, err := rootNode.RollupGenerator(ctx, include, nil) ... }RollupGenerator(core/modtree.go)在模块树中遍历,只保留IsGenerator为 true 的节点,并把include数组传给通用的RollupNodes过滤逻辑。
模式匹配的最终语义由ModTreePath.Glob与Match定义(core/modtree.go):
func (p ModTreePath) Glob(ctx context.Context, pattern string) (bool, error) { // Normalize both pattern and path to CLI case (kebab-case) for consistent matching slashPattern := strings.Join(NewModTreePath(pattern).CliCase(), "/") slashPath := strings.Join(p.CliCase(), "/") if match, err := doublestar.PathMatch(slashPattern, slashPath); err != nil { return false, err } else if match { return true, nil } ... } func (node *ModTreeNode) Match(ctx context.Context, patterns []string) (bool, error) { ... if len(patterns) == 0 { return true, nil } for _, pattern := range patterns { if match, err := node.Path().Glob(ctx, pattern); err != nil { return false, err } else if match { return true, nil } patternAsPath := NewModTreePath(pattern) if patternAsPath.Contains(ctx, node.Path()) { return true, nil } } return false, nil }由此可以得出include模式的四条确定规则:
- 路径即身份:模式匹配的是生成器在模块树中的路径,而不是文件名或函数名本身;
- kebab-case 归一化:模式与路径都会统一转换为 CLI 风格(
strcase.ToKebab)后再匹配,因此changelog:generate与changelog:generate这类写法是稳定的; - 支持 doublestar 通配:匹配使用
doublestar.PathMatch,支持*、**、?等 glob 通配符,与dagger generateCLI 的选择器语义一致(例如protobuf:*表示某模块下所有以protobuf开头的生成器); - 前缀包含也命中:除了 glob 精确匹配,只要模式是节点路径的祖先前缀(
patternAsPath.Contains(...)),该节点同样被包含——这意味着指定一个模块名即可选中该模块下的全部生成器。
四、实战示例:在模块函数内按模式筛选生成器
CurrentModuleGeneratorsOpts的典型使用场景是:模块内部需要按需触发自身的部分代码生成任务。例如只运行changelog模块中名为generate的生成器:
import { dag, CurrentModule } from "@dagger.io/dagger"; // 在模块函数内部访问 CurrentModule async function runOnlyChangelog(mod: CurrentModule): Promise<boolean> { const generators = mod.generators({ include: ["changelog:generate"], }); // 运行选中的生成器(GeneratorGroup 上没有显式 run 时的等价做法, // 可在 SDK 中通过 list() 逐个获取 Generator 再调用) const list = await generators.list(); for (const g of list) { await g.run(); } // 判断本次生成是否产生了差异 return generators.isEmpty(); }需要说明的是:SDK 中GeneratorGroup与Generator类的run/changes/isEmpty方法均对应引擎侧 GraphQL 字段(见 core/schema/generators.go 中list、run、changes、isEmpty的定义),模块作者也可以直接基于GeneratorGroup的changes()获取合并后的 changeset 再做处理,而不是逐个执行。
筛选更多生成器时只需追加模式:
mod.generators({ include: ["protobuf:*", "changelog:generate"], });省略include则返回模块定义的全部生成器:
const all = mod.generators();五、与 dagger generate CLI 的对应关系
CurrentModuleGeneratorsOpts.include并不是孤立设计,它和 Dagger CLI 的dagger generate命令共享同一套"选择器"语义,方便用户在交互式命令行与编程式 API 之间无缝切换:
| 使用方式 | 筛选语法 | 含义 |
|---|---|---|
| CLI(见 docs/current_docs/using/generating.mdx) | dagger generate protobuf:* | 运行某模块全部匹配生成器 |
| CLI | dagger generate changelog:generate | 运行单个生成器 |
| CLI | dagger generate -l | 列出所有可用生成器 |
| SDK | mod.generators({ include: ["protobuf:*"] }) | 编程式等价筛选 |
| SDK | mod.generators({ include: ["changelog:generate"] }) | 编程式等价筛选 |
两者的模式匹配都由引擎同一套模块树路径逻辑支撑(见上文ModTreePath.Glob),因此在 CLI 中验证过的选择器写法可以直接迁移到 SDK 调用中。
另一个相关事实:dagger check --generate会把生成器作为只读检查运行,若生成的产物与已提交内容不一致则失败(docs/current_docs/using/generating.mdx)。这意味着编程式使用CurrentModuleGeneratorsOpts筛选出的生成器集合,也可以被编排进"先生成、再验证"的流水线中,而GeneratorGroup.isEmpty()正好可用于判断生成结果是否有漂移。
六、注意事项与边界
- 实验性 API:
generators及CurrentModuleGeneratorsOpts在 Schema 中被明确标注为高度实验性(Experimental,见 core/schema/module.go),在升级 Dagger 版本时需留意签名变动。 - include 只筛选、不改写:
include只决定"返回哪些生成器",不会改变生成器本身的运行行为或输出;多个生成器结果合并时的冲突策略由GeneratorGroup.changes(opts.onConflict)另行控制(见 client.gen.ts 与 core/schema/generators.go 中默认FAIL_EARLY的合并策略)。 - 空数组行为:
include: []与省略include等价,均返回全部生成器;若想确认某个模块下到底有哪些生成器可用于筛选,可先调用generators().list()查看路径清单。 - 模式基于路径而非函数名:筛选模式对应生成器在模块树中的命令路径(kebab-case),编写模式前建议先用
dagger generate -l或list()确认实际路径。
总结
CurrentModuleGeneratorsOpts是 Dagger TypeScript SDK 为CurrentModule.generators()提供的唯一可选参数,其include: string[]字段依托引擎侧模块树路径 + doublestar glob 的匹配机制,实现了与dagger generateCLI 完全一致的生成器筛选语义。掌握它,你就可以在 Dagger 模块中按模块名、通配模式或精确路径精准控制代码生成任务的范围,为 CI 中的定向生成、增量生成与漂移校验提供编程式基础。更多相关类型与类可继续阅读 api/client.gen 索引,以及GeneratorGroup、Generator的 SDK 参考文档。
【免费下载链接】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),仅供参考