Dagger TypeScript SDK 目录过滤指南:深入解析 DirectoryFilterOpts 与 Directory.filter()
2026/9/17 2:18:12 网站建设 项目流程

Dagger TypeScript SDK 目录过滤指南:深入解析 DirectoryFilterOpts 与 Directory.filter()

【免费下载链接】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 中,Directory是构建、测试与交付流水线的核心数据模型之一,而DirectoryFilterOpts是 TypeScript SDK 为Directory.filter()方法提供的选项类型,用于按 glob 模式或 .gitignore 规则生成目录快照的子集。本文以@dagger.io/daggerTypeScript SDK 在 v0.20 版本中导出的DirectoryFilterOpts类型别名为主体,结合该仓库的 TypeScript 客户端生成源码、Go 运行时与核心 schema 实现,系统讲解excludeincludegitignore三个选项的含义、底层实现与优先级语义,并给出可直接运行的实战示例,帮助你精确控制进入构建上下文的文件集合。

DirectoryFilterOpts:一次看懂类型定义

DirectoryFilterOpts是 Dagger TypeScript SDK 导出的类型别名,其完整定义位于sdk/typescript/src/api/client.gen.ts

export type DirectoryFilterOpts = { /** * If set, paths matching one of these glob patterns is excluded from the new snapshot. * Example: ["node_modules/", ".git*", ".env"] */ exclude?: string[] /** * If set, only paths matching one of these glob patterns is included in the new snapshot. * Example: (e.g., ["app/", "package.*"]). */ include?: string[] /** * If set, apply .gitignore rules when filtering the directory. */ gitignore?: boolean }

从类型结构看,它本质上是一个全部字段均可选(optional)的纯数据对象,被用作Directory.filter(opts?: DirectoryFilterOpts): Directory方法的唯一入参。三个字段的语义如下:

字段类型默认行为作用
excludestring[]不排除任何路径命中任一 glob 模式的路径从新快照中移除
includestring[]包含所有路径仅保留命中任一 glob 模式的路径
gitignorebooleanfalse过滤时应用.gitignore规则

对应地,Go 运行时中也有完全一致的结构体DirectoryFilterOpts(见 sdk/typescript/runtime/internal/dagger/dagger.gen.go),字段为Exclude []stringInclude []stringGitignore bool,其生成代码逐一通过querybuilder.IsZeroValue判断后作为可选参数附加到filter查询选择器上。这说明该类型别名是 SDK 各语言代码生成器统一产出的"选项对象"模式,TypeScript 与 Go 的调用方式一一对应。

Directory.filter():从哪里来、返回什么

DirectoryFilterOpts只服务于Directory类上的一个方法。在 sdk/typescript/src/api/client.gen.ts 中,其签名与 JSDoc 注释如下:

/** * Return a snapshot with some paths included or excluded * @param opts.exclude If set, paths matching one of these glob patterns is excluded from the new snapshot. Example: ["node_modules/", ".git*", ".env"] * @param opts.include If set, only paths matching one of these glob patterns is included in the new snapshot. Example: (e.g., ["app/", "package.*"]). * @param opts.gitignore If set, apply .gitignore rules when filtering the directory. */ filter = (opts?: DirectoryFilterOpts): Directory => { const ctx = this._ctx.select("filter", { ...opts }) return new Directory(ctx) }

关键特性:

  • 返回值仍是Directoryfilter()不会立即执行任何 I/O,而是把filter选择器追加到惰性求值的查询上下文中,返回一个新的Directory实例。因此它可以继续链式调用entries()file()dockerBuild()等方法,直到真正触发执行(如entries()contents()等异步方法)时引擎才落盘计算。
  • opts 可省略:不传任何选项时等价于"什么都不过滤",返回原目录快照。
  • 惰性语义:与 Dagger 整体设计一致,Directory是快照的抽象句柄,filter只是定义了一个新的快照变换,最终由引擎缓存并执行。

三个选项的语义详解与实战

exclude:剔除不需要的路径

exclude接收一组 glob 模式,命中任一模式的路径会被从新快照中排除。文档中给出的典型示例是排除依赖目录、隐藏文件与环境变量文件:

import { connect } from "@dagger.io/dagger" const filtered = client.host().directory(".") .filter({ exclude: ["node_modules/", ".git*", ".env"], })

在上述示例中:

  • node_modules/:目录后缀带/,表示匹配该目录(及其整棵子树);
  • .git*:匹配.git.gitignore.gitattributes等以.git开头的隐藏条目;
  • .env:精确匹配环境变量文件本身。

从实现上看,exclude既可命中文件也可命中目录:仓库集成测试 core/integration/directory_test.go 中的TestDirectoryFilterIncludeExclude验证了Exclude: ["subdir"]可以整体剔除一个子目录,也验证了Exclude: ["*.rar"]可以按后缀过滤散落在各层级的文件。

include:白名单式保留

include接收一组 glob 模式,只有命中任一模式的路径会被保留,其余全部丢弃。文档示例:

const filtered = client.host().directory(".") .filter({ include: ["app/", "package.*"], })

该示例只保留app/目录与package.jsonpackage-lock.json这类以package.开头的文件,非常适合在打包场景下构造"最小构建上下文"。

测试同样验证了 include 的匹配行为:对一个包含a.txtb.txtc.txt.rarsubdir/的目录执行Include: ["*.rar"],最终entries()只返回["c.txt.rar"]

gitignore:复用仓库忽略规则

gitignore是一个布尔开关。置为true时,Dagger 会在过滤时应用目标目录内的.gitignore规则:

const filtered = client.host().directory(".") .filter({ gitignore: true })

测试用例构造了内容为b.txt\nsubdir/\n.gitignore文件后调用Filter(dagger.DirectoryFilterOpts{Gitignore: true}),结果中b.txtsubdir/被剔除,而.gitignore自身、a.txtc.txt.rar得以保留——即.gitignore不会"忽略自己"。

gitignore适合直接接住仓库既有的忽略配置,避免在流水线里重复维护一套 glob 白名单。它也可以与excludeinclude组合使用(组合优先级见下一节)。

include 与 exclude 的优先级语义(由测试确认)

includeexclude同时出现时,二者并非简单的"取并集"。仓库集成测试TestDirectoryFilterIncludeExclude明确验证了如下规则:

  1. exclude 优先于 includeInclude: ["*.txt"]Exclude: ["b.txt"]时,结果为["a.txt"];反过来Include: ["a.txt"]Exclude: ["*.txt"]时,结果为[]
  2. 即:先按 include 做白名单收缩,再按 exclude 做黑名单剔除;exclude 永远压过 include
  3. 过滤作用于目录的每一层:对subdir执行filter({ exclude: ["*.rar"] })时,其内部的d.txte.txt正常保留,f.txt.rar被剔除,说明模式匹配是递归、逐条目进行的。

这一语义与底层CopyFilter的实现一致(见下节),在编写流水线时请牢记:不要指望 include 能"救回"被 exclude 命中的路径

源码级实现:从 GraphQL 选择器到 CopyFilter

理解DirectoryFilterOpts的底层机制,可以沿两条路径追溯:

路径一:TypeScript → Go 运行时

filter = (opts?) => this._ctx.select("filter", { ...opts })生成一个名为filter的 GraphQL 选择器。在 Go 运行时 sdk/typescript/runtime/internal/dagger/dagger.gen.go 中,对应生成方法为:

func (r *Directory) Filter(opts ...DirectoryFilterOpts) *Directory { q := r.query.Select("filter") // `exclude` 可选参数 if !querybuilder.IsZeroValue(opts[i].Exclude) { q = q.Arg("exclude", opts[i].Exclude) } // `include`、`gitignore` 同理 return &Directory{query: q} }

未设置的选项通过零值判断被省略,不会出现在 GraphQL 请求中。

路径二:引擎侧 schema 实现

在 core/schema/directory.go 中,filter选择器对应的参数类型FilterArgs直接内嵌core.CopyFilter,其解析函数把三个参数原样转发给withDirectory选择器:

type FilterArgs struct { core.CopyFilter } func (s *directorySchema) filter(ctx context.Context, parent dagql.ObjectResult[*core.Directory], args FilterArgs) (inst dagql.ObjectResult[*core.Directory], err error) { // 内部等价于:在 scratch 目录上执行 // withDirectory(path="/", source=parent, exclude=..., include=..., gitignore=..., owner="") ... }

也就是说,Directory.filter在引擎内部被降级实现为"从空目录(scratch)复制父目录内容,并应用复制过滤规则"。而CopyFilter正是这套规则的数据结构,定义于 core/directory.go:

type CopyFilter struct { Exclude []string `default:"[]"` Include []string `default:"[]"` Gitignore bool `default:"false"` } func (cf *CopyFilter) IsEmpty() bool { return len(cf.Exclude) == 0 && len(cf.Include) == 0 && !cf.Gitignore }

它同时被withDirectorywithDirectoryDockerfileCompat等目录复制操作的持久化状态复用(见 core/directory.go 中persistedDirectoryWithDirectoryLazy等结构体的Filter CopyFilter字段),并在 schema 的copyFilterInputs中被序列化为 GraphQL 输入。由此可以推断:

  • 过滤规则随目录快照一起被持久化、参与内容寻址与缓存;
  • 相同的exclude/include/gitignore组合在多个流水线节点中可被缓存命中;
  • IsEmpty()用于短路优化——没有任何过滤规则时跳过额外处理。

完整实战示例:为构建上下文瘦身

下面是一个把"过滤 + 构建"串起来的端到端 TypeScript 示例,可用于在 CI 中把仓库目录裁剪后再交给dockerBuild

import { connect } from "@dagger.io/dagger" connect(async (client) => { // 1. 读取仓库目录 const repo = client.host().directory(".") // 2. 过滤:先按 .gitignore 剔除,再补一刀排除大目录 const buildCtx = repo.filter({ gitignore: true, exclude: ["dist/", "coverage/", "*.tar.gz"], }) // 3. 白名单收紧(可选,与 exclude 组合时 exclude 优先) const minimalCtx = repo.filter({ include: ["src/", "Dockerfile", "package.json", "yarn.lock"], }) // 4. 用过滤后的快照执行构建 const built = buildCtx.dockerBuild().stdout() console.log(await built) })

组合建议:

  • 只想"少带一点":优先gitignore: true加少量exclude,与仓库日常忽略规则保持一致;
  • 需要"严格白名单":用include精确枚举,同时留意exclude会压过include
  • 过滤后再挂载filter()返回的Directory也可作为withMountedDirectorywithDirectory的 source 参数,用于把裁剪后的目录挂进容器,从而减小上下文传输与缓存体积。

小结与注意事项

  • DirectoryFilterOptsDirectory.filter()的选项对象,包含excludeincludegitignore三个可选字段,其类型定义与 JSDoc 见 sdk/typescript/src/api/client.gen.ts,Go 侧对应结构体见 sdk/typescript/runtime/internal/dagger/dagger.gen.go。
  • filter返回新的惰性Directory,不会立即触发 IO;它内部复用withDirectory+CopyFilter的复制过滤机制(core/schema/directory.go、core/directory.go),过滤规则参与快照持久化与缓存。
  • 优先级语义:exclude 覆盖 include;模式递归作用于各层目录,由集成测试 core/integration/directory_test.go 的TestDirectoryFilterIncludeExclude逐条验证。
  • 实际匹配行为(目录后缀/、通配符、.gitignore不忽略自身)可直接参考上述测试用例,在自定义 glob 前先跑一遍测试数据构造,避免踩中模式语义的坑。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询