ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Dagger CurrentModule 深度解析:在 TypeScript 模块运行时反射模块自身 API

Dagger CurrentModule 深度解析:在 TypeScript 模块运行时反射模块自身 API Dagger CurrentModule 深度解析在 TypeScript 模块运行时反射模块自身 API【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/daggerCurrentModule 是 Dagger 暴露给模块函数运行时的反射式模块 API它让运行中的模块代码能够查询自身的名称与依赖、访问源码目录与生成代码、读写模块执行时的 scratch 工作目录。本文将基于 Dagger 0.20 版本 TypeScript SDK 的 API 参考文档结合仓库中的客户端生成源码与引擎实现逐一拆解 CurrentModule 的每个方法、参数语义与底层调用链帮助你写出真正了解我是谁、我在哪的自省型 Dagger 模块。一、CurrentModule 是什么在 TypeScript API 参考文档 中CurrentModule被定义为Reflective module API provided to functions at runtime.即在运行时提供给函数的反射式模块 API。它继承自BaseClient是一棵惰性求值的查询树根节点——调用其方法不会立即执行而是构建 Dagger GraphQL 查询直到最终await时才真正与引擎通信。从引擎侧看CurrentModule在核心 GraphQL Schema 中被定义为一个实现了Node接口的对象类型Reflective module API provided to functions at runtime. type CurrentModule implements Node { dependencies: [Module!]! generatedContextDirectory: Directory! generators(include: [String!]): GeneratorGroup! id: ID! name: String! source: Directory! workdir(path: String!, exclude: [String!] [], include: [String!] [], gitignore: Boolean false): Directory! workdirFile(path: String!): File! } scalar CurrentModuleID该定义完整记录在 core/schema/testdata/base_schema.graphqls 中是与版本化 API 文档一一对应的黄金事实源。构造函数仅供内部使用CurrentModule的构造函数签名为new CurrentModule(ctx?, _id?, _name?): CurrentModulectx?Context查询上下文_id?CurrentModuleID当前模块的唯一标识_name?string模块名称。文档与源码api/client.gen.ts都明确标注Constructor is used for internal usage only, do not create object from it.构造函数仅供内部使用请勿自行创建实例。在 SDK 生成的 Go 运行时客户端中同样如此见 dagger.gen.go。你不需要也不应该new CurrentModule()正确获取它的方式是调用查询根上的入口方法const cur client.currentModule()currentModule()在 api/client.gen.ts 中实现它只是简单地创建了一个绑定到currentModuleGraphQL 选择器的CurrentModule实例。引擎侧入口定义在 core/schema/module.go最终返回一个包着当前执行模块的*core.CurrentModule对象。二、身份查询id() 与 name()这两个方法回答模块运行时最基本的问题我是谁id()id(): PromiseCurrentModuleID返回当前模块在本次会话中的唯一标识符CurrentModuleID。在 TypeScript 客户端中id是惰性求值的id async (): PromiseID { if (this._id) { return this._id } const ctx this._ctx.select(id) const response: AwaitedID await ctx.execute() return response }注意其短路优化如果构造时已经注入了_id则直接返回而不再发起网络查询api/client.gen.ts。由于CurrentModule implements Node它同时支持通过loadCurrentModuleFromID(id: CurrentModuleID!): CurrentModule!从 ID 重新加载对象见 base_schema.graphqls。name()name(): Promisestring返回正在执行的模块的名称The name of the module being executed in。引擎侧实现非常直接——它直接读取模块对象的名称字段func (s *moduleSchema) currentModuleName(...) (string, error) { return curMod.Module.Self().NameField, nil }见 core/schema/module.go。也就是说name()返回的正是dagger.json中配置的模块名。与id()相同name()也带有_name短路缓存api/client.gen.ts。三、目录与文件访问source() 与 generatedContextDirectory()模块代码经常需要读取自身源码或生成代码这两组方法提供了完整的访问路径。source()source(): Directory返回加载进引擎的模块源码目录并叠加了可能已生成的代码The directory containing the modules source code loaded into the engine (plus any generated code that may have been created)。这是理解 Dagger 模块工作方式的关键source()不是原始的裸源码目录而是上下文目录 生成代码补丁的合成结果。引擎侧实现清晰地展示了这一叠加过程core/schema/module.go取出模块的Source.Value与SourceSubpath若为空则回退到SourceRootSubpath先选择generatedContextDirectory拿到生成内容的 diff再在ContextDirectory上执行withDirectory(path: /, source: generatedDiff)将生成代码叠加到根目录最后按srcSubpath定位到模块源码所在的子目录并返回。因此当你在模块函数里执行dag.currentModule().source()时得到的是能直接看到dagger.json、模块源码以及 SDK 生成文件的完整视图。generatedContextDirectory()generatedContextDirectory(): Directory返回在模块源码的上下文目录之上生成的文件与目录The generated files and directories made on top of the module sources context directory。它就是上一步叠加到源码上的那层生成补丁本身。引擎实现同样清晰core/schema/module.go直接在模块源对象上选择generatedContextDirectory字段。对比小结方法返回内容典型用途source()上下文目录 生成代码合成后读取模块整体源码、遍历文件generatedContextDirectory()仅生成的那一层目录查看 SDK 生成了什么、对比生成差异四、依赖查询dependencies()dependencies(): PromiseModule_[]返回当前模块的全部依赖模块每个元素是Module_对象数组。引擎侧遍历模块的依赖树core/schema/module.godepMods : make([]*core.Module, 0, len(mod.Module.Self().Deps.Mods())) for _, dep : range mod.Module.Self().Deps.Mods() { if depInst : dep.ModuleResult(); depInst.Self() ! nil { depMods append(depMods, depInst.Self()) continue } switch dep.(type) { case *CoreMod: // skip 核心内置模块 default: return nil, fmt.Errorf(unexpected mod dependency type %T, dep) } }从源码可以看到两个实现细节跳过CoreModDagger 核心内置模块如core依赖不会出现在结果里返回的是用户声明的模块依赖类型校验遇到无法识别的依赖类型会直接报错保证返回结构的一致性。TypeScript 客户端收到结果后会用selectNode把每个 ID 重新绑定为Module_对象api/client.gen.ts因此你可以继续对每个依赖模块做进一步调用。五、工作目录访问workdir() 与 workdirFile()这是CurrentModule中最动态的能力读取模块执行期间对 scratch 工作目录所做的修改。workdir(path, opts?)workdir(path: string, opts?: CurrentModuleWorkdirOpts): DirectoryLoad a directory from the modules scratch working directory, including any changes that may have been made to it during module function execution.从模块的 scratch 工作目录加载一个目录包括模块函数执行期间对其做出的任何修改。path参数是相对位置例如.表示工作目录根。CurrentModuleWorkdirOpts的三个可选参数定义于 api/client.gen.ts参数类型默认值说明excludestring[][]排除匹配指定模式的文件/目录如[node_modules/, .git*]includestring[][]仅包含匹配指定模式的文件/目录如[app/, package.*]gitignorebooleanfalse是否在目录内应用.gitignore过滤规则这些过滤模式exclude/include/gitignore由引擎透传给host.directory选择器执行core/schema/module.go与Directory对象上的过滤语义一致可以放心复用你熟悉的 glob 模式知识。引擎侧有一个值得注意的安全校验if !filepath.IsLocal(args.Path) { return inst, fmt.Errorf(workdir path %q escapes workdir, args.Path) } args.Path filepath.Join(sdk.RuntimeWorkdirPath, args.Path)见 core/schema/module.go。它使用filepath.IsLocal拒绝一切可能逃逸出工作目录的路径如../、绝对路径然后把合法路径拼接到 SDK 运行时的工作目录前缀下。这意味着workdir()只允许访问工作目录内部从引擎层面杜绝了路径穿越。workdirFile(path)workdirFile(path: string): FileLoad a file from the modules scratch working directory, including any changes that may have been made to it during module function execution.加载工作目录中的单个文件同样包含执行期间的修改。path示例README.md。其实现与workdir()共享同一套路径校验逻辑core/schema/module.go安全性等价。典型场景模块函数在容器内生成产物如构建出的二进制、渲染出的文档随后用workdirFile(output.json)或workdir(dist)把结果读取出来交给调用方。六、实验性能力generators()generators(opts?: CurrentModuleGeneratorsOpts): GeneratorGroupReturn all generators defined by the module返回模块定义的全部生成器。该方法在文档与源码中都被标记为Experimental引擎侧注解为 This API is highly experimental and may be removed or replaced entirely.见 core/schema/module.go 与 base_schema.graphqls因此在正式生产代码中应谨慎使用API 可能随时变动。其唯一参数CurrentModuleGeneratorsOptsexport type CurrentModuleGeneratorsOpts { /** * Only include generators matching the specified patterns */ include?: string[] }include用于只返回匹配指定模式的生成器例如按名称模式筛选。方法返回一个GeneratorGroup对象api/client.gen.ts可以继续对其调用进行更细粒度的查询。七、实战组合使用 CurrentModule下面把各方法组合起来展示一个典型的自省型 TypeScript Dagger 模块函数import { dag, Directory, Container } from dagger.io/dagger /** * 自省模块自身打印名称与依赖并把源码生成代码与 * 运行期间工作目录中的产物一起返回给调用方。 */ export function introspectSelf(workdir: Directory): Container { const cur dag.currentModule() // 1) 身份模块名惰性求值await 时才真正查询 const moduleName await cur.name() // 2) 依赖获取模块声明的全部依赖核心内置模块会被跳过 const deps await cur.dependencies() console.log(module${moduleName}, deps${deps.length}) // 3) 源码视图上下文目录 生成代码的合成结果 const sourceWithGenerated cur.source() // 4) 生成层只查看 SDK 生成的那一层 const generatedLayer cur.generatedContextDirectory() // 5) 工作目录读取函数执行期间产生的文件支持过滤 const buildOutput cur.workdir(out, { exclude: [node_modules/, .git*], gitignore: true, }) const manifest cur.workdirFile(out/manifest.json) // ... 继续用 sourceWithGenerated / buildOutput / manifest 组装容器等 return dag.container() }要点回顾name()与dependencies()返回 Promise需要awaitsource()、workdir()等返回惰性对象可先构建查询再统一执行workdir()只允许工作目录内部路径../或绝对路径会被引擎拒绝需要同时拿到源码与生成代码时直接使用source()只想检查生成内容时用generatedContextDirectory()。八、结语CurrentModule是 Dagger 模块运行时自省的入口name()/id()回答我是谁dependencies()回答我依赖谁source()/generatedContextDirectory()回答我的代码在哪workdir()/workdirFile()回答我运行时改了什么。通过文档、TypeScript 客户端源码api/client.gen.ts与引擎实现core/schema/module.go三者的对照你可以准确掌握每个方法的语义与边界写出更健壮、更动态的 Dagger 模块。【免费下载链接】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),仅供参考
返回列表