news 2026/9/14 5:21:20

Dagger GeneratedCode 类详解:掌握 TypeScript SDK Codegen 结果对象与版本控制元数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dagger GeneratedCode 类详解:掌握 TypeScript SDK Codegen 结果对象与版本控制元数据

Dagger GeneratedCode 类详解:掌握 TypeScript SDK Codegen 结果对象与版本控制元数据

【免费下载链接】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

导读

GeneratedCode是 Dagger TypeScript SDK(@dagger.io/dagger)中代表「一次 SDK codegen 运行结果」的核心类:它既携带最终生成的代码目录(Directory),又携带如何将生成产物接入版本控制的元数据(.gitattributes.gitignore路径列表)。本文将以 Dagger v0.19 的官方 API 参考文档为主体,结合引擎端core/codegen.gocore/schema/modulesource.go等源码实现,完整讲解该类的每个方法、底层工作原理,以及 Go / TypeScript SDK 如何实际生产与消费这一对象,帮助你编写可复用、可维护的自定义 SDK 与代码生成模块。

GeneratedCode 是什么

根据 GeneratedCode.md 中的类描述:

The result of running an SDK's codegen.

即:运行一个 SDK 的 codegen 之后返回的结果对象。它由三部分信息组成:

  1. 生成代码目录:即 codegen 产出的文件树(如 Go 的dagger.gen.go、TypeScript 的sdk/目录),以 Dagger 的Directory类型承载,可继续参与管线计算、导出或写入。
  2. VCS 生成路径(vcsGeneratedPaths):应被标记为「生成文件」的路径列表,引擎会将其写入.gitattributes并附带linguist-generated标记,避免仓库统计把生成代码计入语言占比。
  3. VCS 忽略路径(vcsIgnoredPaths):应被版本控制忽略的路径列表,引擎会将其写入.gitignore

这三个字段在引擎端 core/codegen.go 中由结构体字段直接定义:

type GeneratedCode struct { Code dagql.ObjectResult[*Directory] `field:"true" doc:"The directory containing the generated code."` VCSGeneratedPaths []string `field:"true" name:"vcsGeneratedPaths" doc:"List of paths to mark generated in version control (i.e. .gitattributes)."` VCSIgnoredPaths []string `field:"true" name:"vcsIgnoredPaths" doc:"List of paths to ignore in version control (i.e. .gitignore)."` }

TypeScript 客户端类与之一一对应:code()vcsGeneratedPaths()vcsIgnoredPaths()是只读查询,withVCSGeneratedPaths()withVCSIgnoredPaths()是返回新对象的链式设置方法。

类结构与构造函数

在 TypeScript 端,GeneratedCode继承自BaseClient,这是所有 Dagger API 客户端类的共同基类,负责持有 GraphQL 查询上下文(Context)并执行选择器。

export class GeneratedCode extends BaseClient { private readonly _id?: ID = undefined constructor(ctx?: Context, _id?: ID) { super(ctx) this._id = _id } // ... }

参见 sdk/typescript/src/api/client.gen.ts。

构造函数仅供内部使用:官方文档明确说明 "Constructor is used for internal usage only, do not create object from it",因此你不应直接new GeneratedCode(...)。正确获取方式是通过Query.generatedCode()顶层字段,或从模块的codegen解析链中获得,例如:

const query = client // dagger.Connection() 返回的 client const dir = query.host().directory(".") const gen = query.generatedCode(dir) // 由 Directory 构造 GeneratedCode

在引擎端,这条路径对应 core/schema/modulesource.go 中的generatedCodeGraphQL 解析函数:它接收一个DirectoryID,加载目录后调用core.NewGeneratedCode(dir)构造对象,并在 GraphQL Schema 中暴露为顶层字段(见 core/schema/testdata/base_schema.graphqls):

generatedCode(code: ID! @expectedType(name: "Directory")): GeneratedCode!

方法详解

code():获取生成代码目录

code = (): Directory => { const ctx = this._ctx.select("code") return new Directory(ctx) }

返回包含生成代码的Directory。该方法返回的是惰性(lazy)的Directory对象而非立即执行的结果,只有在后续链式操作或调用id()、导出等方法时才会真正触发计算。

Directory类的完整方法集可参考 classes/Directory.md。典型用途是将生成的代码目录写回宿主文件系统:

await gen.code().export("/path/to/module")

从源码看,GeneratedCode.Code还被引擎通过AttachDependencyResults(见 core/codegen.go)接入 DAG 缓存存活图:GeneratedCode -> Code的依赖关系让 Code 目录的惰性执行失败(例如 Python codegen 期间uv lock失败)能够被归因到返回该GeneratedCode的 API span 上,便于遥测与调试。

id():获取唯一标识

id = async (): Promise<ID> => { if (this._id) return this._id const ctx = this._ctx.select("id") return await ctx.execute() }

返回该GeneratedCode的唯一标识符,类型为 GeneratedCodeID:

GeneratedCodeID=string&object,是GeneratedCode类型对象的标识标量。

实现上,core/codegen.go 通过EncodePersistedObject/DecodePersistedObject将对象序列化为持久化 payload(包含CodeResultIDVCSGeneratedPathsVCSIgnoredPaths三个字段)再编码为 ID。因此id()序列化了完整的生成代码目录引用与 VCS 元数据,可用于跨请求复用该对象。TypeScript 端亦提供loadGeneratedCodeFromID()用于从 ID 反查对象(见 sdk/typescript/runtime/internal/dagger/dagger.gen.go)。

vcsGeneratedPaths():查询生成路径列表

vcsGeneratedPaths = async (): Promise<string[]> => { const ctx = this._ctx.select("vcsGeneratedPaths") return await ctx.execute() }

返回应被标记为「生成文件」的路径列表(即写入.gitattributes的路径),例如 Go SDK 返回的dagger.gen.gointernal/dagger/**等。

vcsIgnoredPaths():查询忽略路径列表

vcsIgnoredPaths = async (): Promise<string[]> => { const ctx = this._ctx.select("vcsIgnoredPaths") return await ctx.execute() }

返回应被版本控制忽略的路径列表(即写入.gitignore的路径),例如node_modulesinternal/dagger等。

with():链式复用辅助方法

with = (arg: (param: GeneratedCode) => GeneratedCode) => { return arg(this) }

调用传入的函数处理当前GeneratedCode并返回其结果。其价值在于不破坏调用链,同时把一段可复用的变换逻辑抽成独立函数。官方文档描述为:

Call the provided function with current GeneratedCode. This is useful for reusability and readability by not breaking the calling chain.

典型用法:

const applyVCS = (g: GeneratedCode): GeneratedCode => g.withVCSGeneratedPaths(["sdk/**"]).withVCSIgnoredPaths(["node_modules"]) const finalGen = applyVCS(gen).with(applyVCS) // 复用同一套 VCS 规则

注意:with是同步方法,传入的回调返回新的GeneratedCode;若回调需要异步操作,可先在外部用await计算,再传入纯同步变换。

withVCSGeneratedPaths():设置生成路径

withVCSGeneratedPaths = (paths: string[]): GeneratedCode => { const ctx = this._ctx.select("withVCSGeneratedPaths", { paths }) return new GeneratedCode(ctx) }

设置要标记为生成的路径列表,返回新的GeneratedCode(不可变风格)。引擎端对应 core/schema/modulesource.go:

func (s *moduleSourceSchema) generatedCodeWithVCSGeneratedPaths(ctx context.Context, code *core.GeneratedCode, args struct { Paths []string }) (*core.GeneratedCode, error) { return code.WithVCSGeneratedPaths(args.Paths), nil }

其底层实现WithVCSGeneratedPaths(core/codegen.go)通过Clone()复制对象后替换VCSGeneratedPaths字段,保证原对象不被修改。

withVCSIgnoredPaths():设置忽略路径

withVCSIgnoredPaths = (paths: string[]): GeneratedCode => { const ctx = this._ctx.select("withVCSIgnoredPaths", { paths }) return new GeneratedCode(ctx) }

设置要忽略的路径列表,返回新的GeneratedCode。其引擎端实现有一个值得注意的细节(core/codegen.go):

func (code *GeneratedCode) WithVCSIgnoredPaths(paths []string) *GeneratedCode { code = code.Clone() code.VCSIgnoredPaths = paths // if the paths does not have a .env file we need to add it if !slices.Contains(code.VCSIgnoredPaths, ".env") { code.VCSIgnoredPaths = append(code.VCSIgnoredPaths, ".env") } return code }

无论调用方传入什么路径,.env都会被强制追加到忽略列表中,确保包含敏感环境变量的文件绝不会被提交进版本控制。这是一个安全兜底行为:即使 SDK 忘记声明.env,引擎也会自动忽略它。

引擎端如何消费这些元数据

仅仅设置路径并不会生效,引擎在runCodegen流程(core/schema/modulesource.go)中会真正把这些元数据落地为文件:

  1. 先运行 SDK codegenrunSDKCodegen(core/schema/modulesource.go)加载依赖模块后调用 SDK 实现的Codegen接口,得到*core.GeneratedCode
  2. 更新.gitattributes:若VCSGeneratedPaths非空,读取模块上下文目录中已存在的.gitattributes(若无则创建),对每个路径追加一行/<path> linguist-generated(权限0600)。若该路径已有配置则跳过,避免重复追加。
  3. 更新.gitignore:若VCSIgnoredPaths非空,同样追加/<path>形式的忽略规则;此处受模块配置automaticGitignore控制,可通过 core/modules/config.go 中的CodegenConfig.AutomaticGitignore开关关闭。
  4. 特殊场景处理:对于 toml 模块(dagger.json之外以 toml 配置的模块),由于代码生成文件本身被提交,引擎会通过ignoresGeneratedPath(core/schema/modulesource.go)过滤掉「同时落在生成路径内的忽略条目」,避免把生成文件从本地模块上下文中排除。

这意味着:只要你的自定义 SDK 正确填充VCSGeneratedPaths/VCSIgnoredPaths,引擎就会自动为你维护好.gitattributes.gitignore,无需在生成脚本里手动处理。

SDK 实际生产该对象的示例

Go SDK

core/sdk/go_sdk.go 中 Go SDK 的Codegen返回如下:

return &core.GeneratedCode{ Code: modifiedSrcDir, VCSGeneratedPaths: []string{ "dagger.gen.go", "internal/dagger/**", "internal/telemetry/**", }, VCSIgnoredPaths: []string{ "dagger.gen.go", "internal/dagger", "internal/telemetry", ".env", // this is here because the Go SDK does not use WithVCSIgnoredPaths on core/codegen/GeneratedCode }, }, nil

注意注释:由于 Go SDK 直接构造结构体而非调用WithVCSIgnoredPaths,因此它手动在忽略列表里补上了.env——这正好印证了上文中「.env兜底逻辑只在WithVCSIgnoredPaths路径生效」的行为。

TypeScript SDK

sdk/typescript/runtime/main.go 中 TypeScript SDK 的 codegen 返回则采用链式调用风格:

return dag.GeneratedCode( dag.Directory().WithDirectory(cfg.subPath, codegen), ). WithVCSGeneratedPaths([]string{ GenDir + "/**", EntrypointExecutableFile, }). WithVCSIgnoredPaths([]string{ EntrypointExecutableFile, GenDir, "**/node_modules/**", "**/.pnpm-store/**", }), nil

TypeScript SDK 将生成客户端目录(sdk/,含client.gen.ts)与入口文件标记为linguist-generated,并将入口文件、生成目录以及node_modules.pnpm-store标记为忽略。由于这里走的是WithVCSIgnoredPaths.env会被引擎自动追加,无需手工声明。

在 TypeScript 生成的客户端中,GeneratedCode还实现了Node接口(AsNode(),见 sdk/typescript/runtime/internal/dagger/dagger.gen.go),说明它可以作为 DAG 节点参与统一的对象图管理。

完整调用示例

将上述 API 组合起来,一个典型的「生成代码 → 标注 VCS 元数据 → 导出」流程如下:

import { connect } from "@dagger.io/dagger" connect(async (client) => { const src = client.host().directory(".") // 方式一:由 Directory 直接构造 GeneratedCode const gen = client.generatedCode(src).withVCSGeneratedPaths([ "sdk/**", "dagger.gen.go", ]).withVCSIgnoredPaths([ "node_modules", ]) // 方式二:读取 SDK codegen 管线产出的 GeneratedCode // const gen = await someModuleCodegen(...) // 读取生成代码目录并导出 const dir = gen.code() await dir.export("/tmp/generated") // 查询 VCS 元数据 const generated = await gen.vcsGeneratedPaths() const ignored = await gen.vcsIgnoredPaths() console.log("linguist-generated:", generated) console.log("gitignored:", ignored) // 获取唯一标识(可序列化、跨请求复用) const id = await gen.id() })

其中client.generatedCode()对应 TypeScript 客户端 sdk/typescript/src/api/client.gen.ts 中由Directory构造GeneratedCode的顶层字段。

总结

GeneratedCode是 Dagger 模块体系中连接「代码生成」与「版本控制」的关键对象:

能力方法底层机制
获取生成代码目录code()惰性Directory,接入缓存存活图
唯一标识id()持久化编码(CodeResultID + VCS 路径)
查询生成路径vcsGeneratedPaths()引擎写入.gitattributeslinguist-generated
查询忽略路径vcsIgnoredPaths()引擎写入.gitignore
链式复用with()不破坏调用链的变换注入
设置生成路径withVCSGeneratedPaths()不可变克隆式更新
设置忽略路径withVCSIgnoredPaths()自动兜底追加.env

无论你是使用既有 SDK 触发 codegen,还是编写自定义 SDK(实现Codegen接口并返回GeneratedCode),理解这套「代码目录 + VCS 元数据」双通道契约,都能让你的生成产物在仓库中保持整洁、可审计且不易被误提交。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 5:19:56

蓝牙模块量产选型必须索要的6类射频与功耗证据

1. 这不是买蓝牙模块&#xff0c;是买一份可量产的射频交付物“蓝牙模块选型”这六个字&#xff0c;听起来像在电子市场挑一块板子——查查型号、比比价格、看看尺寸、测测通电。但如果你正负责一款要出货5万台的智能体脂秤、一款要过CE/FCC认证的工业手持终端&#xff0c;或者…

作者头像 李华
网站建设 2026/9/14 5:19:17

用 Obsidian 打造你的 LLM 知识库:大模型学习与个人 Wiki 实践指南

llm_wiki&#xff1a;用 Obsidian 建立你的第一座大模型知识库 入行做 NLP 这几年&#xff0c;我电脑里的资料比头发掉得还快。PDF 论文、微信公众号文章、GitHub 上的教程、飞书文档链接&#xff0c;散落在各个文件夹里&#xff0c;真到用的时候什么都找不到。后来我花了一个…

作者头像 李华
网站建设 2026/9/14 5:17:55

STM32环境监测系统实战:从选型到调试的完整工程闭环

1. 项目概述&#xff1a;一个能真正落地的环境质量监测系统长什么样&#xff1f;“STM32项目开源&#xff1a;环境质量监测系统&#xff08;代码原理图仿真&#xff09;”——这个标题在嵌入式学习圈里出现频率极高&#xff0c;但真正打开下载包后能跑通、能看懂、能改、能用的…

作者头像 李华
网站建设 2026/9/14 5:17:38

grep、sed、awk三剑客实战:从日志分析到文本处理的完整方案

开头想直接扔一段"grep、sed、awk三兄弟"的废话&#xff1f;不行。我见过太多次这样的场景了&#xff1a;开发同事线上排查问题&#xff0c;日志文件几百兆&#xff0c;他打开编辑器从头翻到尾&#xff0c;小半天过去了还在骂骂咧咧找某个异常栈&#xff1b;或者是报…

作者头像 李华