Dagger TypeScript SDK 中的 Stat 类:文件/目录状态查询 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/dagger
导读
Stat是 Dagger TypeScript SDK(@dagger.io/dagger)中用于描述文件或目录状态的核心数据对象。通过Container、Directory、File对象的stat入口,你可以在不把文件拉取到本机的情况下,直接查询容器或目录中任意路径的文件类型、文件名、权限位(permission bits)与字节大小,常用于构建前的结构校验、变更集类型断言、挂载点内容检查等场景。读完本文,你将掌握Stat类的全部公开 API(fileType()、id()、name()、permissions()、size())、配套的FileType枚举取值,以及这些接口在 Dagger 引擎中的底层实现原理与可复现的集成测试用例。
本文对应的原始 API 文档为 Stat 类参考,配套类型请参阅 FileType 枚举 与 StatID 类型别名。
Stat 是什么:一个文件或目录状态对象
按照官方 API 文档的定义,Stat是“A file or directory status object”(文件或目录状态对象)。它并不携带文件内容,只携带描述文件元信息的四个字段。这一设计在 Dagger 的 GraphQL 核心 Schema 中有完整对应——core/schema/testdata/base_schema.graphqls中的type Stat implements Node定义了以下字段(schema 定义):
"""A file or directory status object.""" type Stat implements Node { """file type""" fileType: FileType """A unique identifier for this Stat.""" id: ID! """file name""" name: String! """permission bits""" permissions: Int! """file size""" size: Int! }TypeScript SDK 中,Stat类继承自BaseClient(所有 Dagger API 对象的公共基类),并在构造时持有_id、_fileType、_name、_permissions、_size五个内部字段(类定义):
export class Stat extends BaseClient { private readonly _id?: ID = undefined private readonly _fileType?: FileType = undefined private readonly _name?: string = undefined private readonly _permissions?: number = undefined private readonly _size?: number = undefined constructor( ctx?: Context, _id?: ID, _fileType?: FileType, _name?: string, _permissions?: number, _size?: number, ) { ... } }需要特别注意:Stat的构造函数是仅供 SDK 内部使用的(文档原话 “Constructor is used for internal usage only, do not create object from it”)。你永远不应该、也不需要new Stat()来手动创建实例——Stat对象只能通过引擎在执行查询后返回给你。
获取 Stat 的三种入口
Stat对象不会凭空出现,它由以下三个 GraphQL 查询入口返回:
1.Container.stat(path)—— 查询容器内任意路径
在 TypeScript SDK 中,Container对象暴露了stat(path)方法,返回Promise<Stat | null>。它在客户端实现上先选择"stat"字段再选择"id",拿到 ID 后用selectNode重建一个可继续查询的Stat实例(实现代码):
stat = async (): Promise<Stat | null> => { const ctx = this._ctx.select("stat").select("id") const response: Awaited<string | null> = await ctx.execute() if (response === null) { return null } return new Stat(ctx.copy().selectNode(response, "Stat")) }2.Directory.stat(path, opts?)—— 查询目录中的路径
Directory的stat接受路径参数,并可通过DirectoryStatOpts传入doNotFollowSymlinks布尔选项(类型定义):
export type DirectoryStatOpts = { /** * If specified, do not follow symlinks. */ doNotFollowSymlinks?: boolean }3.File.stat()—— 查询单个文件
File对象上的stat无参数,用于获取该文件自身的状态。
路径不存在时的行为
当目标路径不存在时,查询会失败并返回路径错误。在 Go 核心实现中,Directory.Stat对空路径、快照为空、或文件系统返回fs.ErrNotExist的情况,统一返回&os.PathError{Op: "stat", Path: targetPath, Err: syscall.ENOENT}(core/directory.go),即标准的“文件不存在”(ENOENT)语义。
Stat 类的五个公开方法详解
以下五个方法构成Stat类的完整公开 API。它们的共同点是都返回Promise,即每次调用都会向 Dagger 引擎发起一次求值(除非字段已在构造时被注入)。
fileType()—— 文件类型
签名:fileType(): Promise<FileType>
返回该路径条目的文件类型,取值来自FileType枚举(见下一节)。底层 GraphQL 字段fileType: FileType在 Schema 中声明为可空(未加!),因此某些特殊环境下可能返回null,但引擎在正常情况下会返回上述四种取值之一。
客户端拿到引擎返回的字符串(如"REGULAR")后,会调用工具函数FileTypeNameToValue将其转换为枚举值(实现代码):
fileType = async (): Promise<FileType> => { if (this._fileType) { return this._fileType } const ctx = this._ctx.select("fileType") const response: Awaited<FileType> = await ctx.execute() return FileTypeNameToValue(response) }name()—— 文件名
签名:name(): Promise<string>
返回os.FileInfo.Name()语义下的文件名——即路径的最后一段(basename),而不是完整路径。例如对路径/sub/subdir/data调用stat,name()返回"data"。
permissions()—— 权限位
签名:permissions(): Promise<number>
返回 POSIX 风格的权限位(permission bits),对应 Go 中fileInfo.Mode().Perm()的结果,即传统 Unix 的rwxr-xr-x之类的 9 位权限数值(八进制 0–0o777)。它不包含setuid、setgid、sticky 等特殊位。
size()—— 文件大小
签名:size(): Promise<number>
返回文件大小,单位是字节。对于目录,此值通常是文件系统报告的逻辑大小(在 Go 实现中同样取自os.FileInfo.Size())。
id()—— Stat 的唯一标识符
签名:id(): Promise<StatID>
返回该Stat实例的全局唯一标识。其类型为StatID,定义是string & object(带__StatID品牌标记的字符串字面量类型,见 StatID 文档)。Stat实现了 GraphQL 的Node接口,因此id可用于在后续查询中直接引用该对象(例如通过loadStatFromID加载)。
FileType 枚举:四种文件类型
fileType()的返回值类型FileType是 Dagger 公开枚举,包含 4 个逻辑取值,其中每个取值都提供了一对等价成员(Xxx与XxxType指向同一字符串值),方便不同风格的代码引用(枚举定义):
| 枚举成员 | 底层字符串值 | 含义 |
|---|---|---|
FileType.Directory/FileType.DirectoryType | "DIRECTORY" | 目录 |
FileType.Regular/FileType.RegularType | "REGULAR" | 普通文件 |
FileType.Symlink/FileType.SymlinkType | "SYMLINK" | 符号链接 |
FileType.Unknown | "UNKNOWN" | 未知类型(如设备文件、FIFO、socket) |
在 TypeScript SDK 源码 中,枚举按字符串值注册,并提供了双向转换工具函数FileTypeValueToName与FileTypeNameToValue(转换函数),前者用于把枚举值转回字符串以便传给引擎暴露的函数,后者用于把引擎返回的字符串解析为枚举。
在 Go 核心实现中,这四种取值由 dagql 枚举视图注册,描述文本与 SDK 一一对应(注册代码):
FileTypeRegular = FileTypes.RegisterView("REGULAR", enumView, "regular file type") FileTypeDirectory = FileTypes.RegisterView("DIRECTORY", enumView, "directory file type") FileTypeSymlink = FileTypes.RegisterView("SYMLINK", enumView, "symlink file type") FileTypeUnknown = FileTypes.RegisterView("UNKNOWN", enumView, "unknown file type")底层原理:Stat 在引擎中如何被计算出来
Stat不是凭空捏造的元数据——它由 Dagger 引擎对容器快照(container snapshot)执行真实的文件系统stat系统调用获得。
核心数据结构
Go 侧的Stat结构体定义在 core/directory.go:
type Stat struct { Size int `field:"true" doc:"file size"` Name string `field:"true" doc:"file name"` FileType FileType `field:"true" doc:"file type"` Permissions int `field:"true" doc:"permission bits"` }它实现了dagql.PersistedObject与dagql.PersistedObjectDecoder接口,支持将Stat序列化/反序列化,从而可以被缓存并在多轮查询间复用(编解码实现)。
目录 Stat 的实现
Directory.Stat的核心逻辑(core/directory.go)依次是:
- 求值目录快照,若快照为空则返回 ENOENT;
- 在
doNotFollowSymlinks为 true 时,把os.Stat换成os.Lstat(不跟随符号链接),并把路径解析函数换成RootPathWithoutFinalSymlink,这是“symlink testing 必须用 Lstat”的关键所在; - 在挂载的快照根目录下解析目标路径并执行
stat; - 从
os.FileInfo提取Size、Name、Mode().Perm(); - 通过模式位判断文件类型:目录 →
FileTypeDirectory,普通文件 →FileTypeRegular,符号链接 →FileTypeSymlink,其余(如设备、FIFO)→FileTypeUnknown:
m := fileInfo.Mode() stat := &Stat{ Size: int(fileInfo.Size()), Name: fileInfo.Name(), Permissions: int(fileInfo.Mode().Perm()), } if m.IsDir() { stat.FileType = FileTypeDirectory } else if m.IsRegular() { stat.FileType = FileTypeRegular } else if m&fs.ModeSymlink != 0 { stat.FileType = FileTypeSymlink } else { stat.FileType = FileTypeUnknown }容器 Stat 的实现
Container.Stat(core/container.go)会先通过locatePath判断目标路径位于容器 rootfs、目录挂载点还是文件挂载点,然后分别委派给对应的Directory.stat或File.stat查询:
- rootfs 内路径:先选择容器的
rootfs(一个Directory),再对其调用stat; - 目录挂载点(
mnt.DirectorySource):选择挂载目录对应的directory对象后调用stat; - 文件挂载点(
mnt.FileSource):若子路径不为空则直接返回 ENOENT,否则对挂载的File调用stat。
复用 Stat 的辅助判断
Stat结构体还提供了IsDir()便捷方法(core/directory.go),并被引擎内部的Exists检查、git 主机目录检测(git_hostdir.go中对.git目录类型与普通文件类型的区分)、以及文件内容提取(core/file.go中判断“Stat 为目录时拒绝读取内容”)等逻辑复用,可见Stat是 Dagger 内部大量路径判断的基础设施。
实战示例:用 Stat 校验构建产物
下面是一段完整的 TypeScript SDK 用法,演示如何查询容器内文件状态并逐项断言:
import { connect, FileType } from "@dagger.io/dagger" connect(async (client) => { const ctr = client .container() .from("alpine:latest") .withWorkdir("/sub") .withNewFile("subdir/data", "contents") // 1. 查询容器内路径的状态 const stat = await ctr.stat("subdir/data") if (!stat) { throw new Error("stat 返回 null") } // 2. 断言文件类型为普通文件 const fileType = await stat.fileType() console.log("file type:", fileType) // FileType.Regular // 3. 断言文件大小(字节) const size = await stat.size() console.log("size:", size) // 8 // 4. 读取文件名(basename)与权限位 const name = await stat.name() // "data" const perms = await stat.permissions() // 例如 0o644 console.log(`${name} perms=${perms.toString(8)}`) // 5. 目录的状态(fileType 返回 FileType.Directory) const dirStat = await ctr.stat("/sub") console.log(await dirStat?.fileType()) })上面示例中size()返回8的事实,在仓库集成测试中有精确对应:core/integration/container_test.go的TestStat用例中,withNewFile("subdir/data", "contents")写入 8 字节内容,随后断言FileType为FileTypeRegularType、Size为8(测试用例)。同文件中的TestStatWithMountedDir与TestStatWithMountedFile则分别验证了目录挂载与文件挂载场景下stat的查询结果。
注意:符号链接与 doNotFollowSymlinks
Stat的一个关键语义是它默认报告的是被解析后的路径状态,还是符号链接自身的状态:
- 默认情况下(
doNotFollowSymlinks: false),引擎使用os.Stat,跟随符号链接——对一个指向普通文件的 symlink 调用stat,fileType()会返回REGULAR而非SYMLINK; - 传入
doNotFollowSymlinks: true时(仅Directory.stat支持此选项),引擎改用os.Lstat,fileType()返回SYMLINK,从而能识别出“链接指向的类型”。
这一点在core/integration/changeset_test.go的符号链接变更测试中被反复验证:retarget、new-link、dangling-link等路径在DoNotFollowSymlinks: true下全部断言为FileTypeSymlinkType,而把链接替换成普通文件后的link-to-file则断言为FileTypeRegularType(测试用例)。如果你需要精确区分“目录被文件替换”“链接被重定向”这类结构变更,务必开启此选项。
小结
Stat是 Dagger 中描述文件/目录状态的统一对象,四个字段(类型、名称、权限位、大小)分别对应fileType()、name()、permissions()、size()四个方法,另有id()提供唯一标识;- 它只能通过
Container.stat(path)、Directory.stat(path, opts)、File.stat()获取,不可手动构造; FileType枚举覆盖DIRECTORY、REGULAR、SYMLINK、UNKNOWN四种取值,并通过doNotFollowSymlinks选项控制是否跟随符号链接;- 其底层实现基于容器快照的真实文件系统
stat/lstat系统调用,相关逻辑与测试分别位于 core/directory.go、core/container.go 与 core/integration/container_test.go,可以作为你实现 Dagger 模块或 CI 流水线时校验文件状态的可靠依据。
【免费下载链接】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),仅供参考