Dagger TypeScript SDK connect() 函数详解:GraphQL 客户端连接机制与实战
2026/9/16 20:34:57 网站建设 项目流程

Dagger TypeScript SDK connect() 函数详解:GraphQL 客户端连接机制与实战

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

connect()是 Dagger TypeScript SDK 中建立引擎连接的入口函数,它负责启动 GraphQL 服务端会话、初始化 GraphQL 客户端,并通过回调函数把可用的Client交给调用方执行查询。本文以 docs/versioned_docs/version-0.20/reference/typescript/connect/functions/connect.md 为主线,结合 sdk/typescript/src/connect.ts 等源码,深入讲解其签名、配置项、底层连接机制,以及它与connection()的区别,帮助你写出正确、可运行的 Dagger TypeScript 程序。

一、函数签名与基本语义

connect()的完整类型签名为:

connect(cb: CallbackFct, config?: ConnectOpts): Promise<void>

根据 connect.md 的定义,它的语义是:

connect runs GraphQL server and initializes a GraphQL client to execute query on it through its callback. This implementation is based on the existing Go SDK.

即:connect运行 GraphQL 服务端,并初始化一个 GraphQL 客户端,通过回调函数在其上执行查询。官方注释明确指出,这一实现思路源自已有的 Go SDK。

在源码 sdk/typescript/src/connect.ts 中,实现完全对应上述语义:

export async function connect( cb: CallbackFct, config: ConnectOpts = {}, ): Promise<void> { await withGQLClient(config, async (gqlClient: GraphQLClient) => { const connection = new Connection(gqlClient) const ctx = new Context([], connection) const client = new Client(ctx) // Warning shall be throw if versions are not compatible try { await client.version() } catch (e) { console.error("failed to check version compatibility:", e) } return await cb(client) }) }

从中可以梳理出connect()内部的关键流程:

  1. 调用withGQLClient建立并获取底层的GraphQLClient(来自graphql-request库);
  2. 用该客户端构造Connection对象;
  3. Connection构造Context,进而构造 SDK 暴露给用户的Client
  4. 调用client.version()做一次版本兼容性探测,失败时仅打印警告而不中断;
  5. Client传入用户回调cb并等待其完成。

参数说明

参数类型默认值说明
cbCallbackFct无(必填)接收已初始化Client的异步回调,回调内编写所有 Dagger API 调用
config?ConnectOpts{}连接配置对象,可控制工作目录、工作区模块加载与日志输出

回调类型 CallbackFct

cb的类型是CallbackFct,定义于 CallbackFct.md,同时也在源码中显式导出:

export type CallbackFct = (client: Client) => Promise<void>

它接收一个Client参数(自动生成的类型化 Dagger API 客户端),返回Promise<void>。这意味着回调必须是异步函数,所有 Dagger 操作(如container()from()withExec())都要在回调内完成。

返回值

connect()返回Promise<void>,即它不向调用方返回业务数据。所有结果都在回调内部通过Client的方法获取,或通过await client.xxx()的返回值在回调内处理。

二、连接配置 ConnectOpts 详解

config的类型是ConnectOpts,完整定义在 sdk/typescript/src/connectOpts.ts:

export interface ConnectOpts { /** * Use to overwrite Dagger workdir * @defaultValue process.cwd() */ Workdir?: string /** * Opt into loading workspace modules for this connection. * By default, only the core API is exposed. */ LoadWorkspaceModules?: boolean /** * Enable logs output */ LogOutput?: Writable }

三个配置项的含义与使用场景如下:

1.Workdir:覆盖 Dagger 工作目录

  • 类型:string
  • 默认值:process.cwd()
  • 用途:覆盖本次连接使用的 Dagger 工作目录,影响client.host().workdir()等相对路径解析。
  • 适用场景:当你的脚本从与目标项目不同的目录启动时,显式指定项目根目录,确保模块发现、路径挂载等行为符合预期。

2.LoadWorkspaceModules:加载工作区模块

  • 类型:boolean
  • 默认值:false
  • 用途:是否为本连接加载工作区模块。默认情况下只暴露核心 API(core API);开启后,工作区中定义的模块(如自定义函数、SDK 模块)也会在连接中可用。
  • 适用场景:在含有自定义 Dagger 模块(dagger.toml/dagger.json配置的模块)的项目中运行脚本时,需要开启该选项才能调用模块暴露的自定义 API。

3.LogOutput:启用日志输出

  • 类型:node:streamWritable(如process.stdoutprocess.stderr
  • 默认值:未设置(无日志输出)
  • 用途:将引擎会话的运行日志输出到指定流,便于排查执行过程。
  • 适用场景:调试 CI 中不透明的执行失败,或希望把引擎日志并入统一的日志采集。

ConnectOpts的 JSDoc 示例给出了与LogOutput组合的用法:

connect(async (client: Client) => { const source = await client.host().workdir().id() // ... 其他 Dagger 调用 }, { LogOutput: process.stdout })

三、connect() 与 connection() 的区别

在 connect 模块 下,除了connect(),还有一个语义相近但定位不同的函数connection()(见 connection.md):

connection(fct: () => Promise<void>, cfg?: ConnectOpts): Promise<void>

两者的核心差异在于回调参数

函数回调签名回调中使用的客户端适用场景
connect(cb, config?)(client: Client) => Promise<void>显式传入的Client需要精确控制客户端实例、在隔离上下文中执行的场景
connection(fct, cfg?)() => Promise<void>全局单例dag直接使用 SDK 导出的全局dag对象,代码更简洁

connection()的文档示例(同时出现在 connection.md 与源码注释中):

await connection( async () => { await dag .container() .from("alpine") .withExec(["apk", "add", "curl"]) .withExec(["curl", "https://dagger.io/"]) .sync() }, { LogOutput: process.stderr }, )

从源码 sdk/typescript/src/connect.ts 看,connection()的实现比connect()多做了几件事:

  1. telemetry.initialize()初始化 OpenTelemetry 遥测,并把连接放入 telemetry 的 context 中以实现上下文传播;
  2. 通过globalConnection.setGQLClient(gqlClient)把 GraphQL 客户端注入全局单例连接(供dag对象使用);
  3. 无论回调成功与否,finally中都会调用globalConnection.resetClient()复位全局连接;
  4. 最后telemetry.close()关闭遥测。

全局连接与惰性求值

globalConnection定义于 sdk/typescript/src/common/graphql/connection.ts,它包装了可变的 GraphQL 客户端引用:

export class Connection { constructor(private _gqlClient?: GraphQLClient) {} resetClient() { this._gqlClient = undefined } setGQLClient(gqlClient: GraphQLClient) { this._gqlClient = gqlClient } getGQLClient(): GraphQLClient { if (!this._gqlClient) { throw new Error("GraphQL client is not set") } return this._gqlClient } } export const globalConnection = new Connection()

这解释了为何connection()的测试(sdk/typescript/src/test/connect.spec.ts)中会反复断言dag["_ctx"]["_connection"]["_gqlClient"]在调用前为undefined、调用中非空、调用结束后又复位为undefined——全局dag对象采用惰性求值,只有在connection()(或connect()内部创建并注入的)连接生效期间才可执行查询,从而保证连接资源的生命周期可控。

四、底层连接机制:会话优先,自动供给兜底

connect()并不直接连接引擎,而是委托给withGQLClient(sdk/typescript/src/common/graphql/connect.ts)。它的核心逻辑是两种连接方式按优先级切换

export async function withGQLClient<T>( connectOpts: ConnectOpts, cb: (gqlClient: GraphQLClient) => Promise<T>, ): Promise<T> { if (process.env["DAGGER_SESSION_PORT"]) { const port = process.env["DAGGER_SESSION_PORT"] if (!process.env["DAGGER_SESSION_TOKEN"]) { throw new Error( "DAGGER_SESSION_TOKEN must be set if DAGGER_SESSION_PORT is set", ) } const token = process.env["DAGGER_SESSION_TOKEN"] return await cb(createGQLClient(Number(port), token)) } try { const provisioning = await import("../../provisioning/index.js") return await provisioning.withEngineSession(connectOpts, cb) } catch (e) { throw new Error( `failed to execute function with automatic provisioning: ${e}`, { cause: e }, ) } }

模式一:复用既有会话(DAGGER_SESSION_PORT)

当环境变量DAGGER_SESSION_PORT存在时,SDK 认为已存在一个由外部(如 Dagger CLI、其他 SDK)建立好的引擎会话,直接连向http://127.0.0.1:<port>/query,并用DAGGER_SESSION_TOKEN作为鉴权凭据。

关键约束(从源码可见):

  • DAGGER_SESSION_TOKEN必须同时设置,否则抛出"DAGGER_SESSION_TOKEN must be set if DAGGER_SESSION_PORT is set"
  • 该模式下跳过自动供给,不再尝试下载 CLI 或启动引擎。

对应测试见 connect.spec.ts:测试设置了DAGGER_SESSION_TOKEN=fooDAGGER_SESSION_PORT=1234,随后断言客户端 URL 为http://127.0.0.1:1234/query,且请求头为{"Authorization":"Basic Zm9vOg=="}(即 token 经 Base64 编码后作为 Basic Auth 传递)。

模式二:自动供给引擎(Automatic Provisioning)

当未设置DAGGER_SESSION_PORT时,SDK 动态加载provisioning模块,调用withEngineSession(connectOpts, cb)自动完成:下载并解压 Dagger CLI 二进制(必要时启动未缓存引擎镜像)、建立会话、在回调结束后自动清理。若供给失败,会抛出带 cause 的包装错误"failed to execute function with automatic provisioning: ..."

自动供给的端到端行为在 connect.spec.ts 中通过模拟下载服务器验证:它生成一个包含 CLI 二进制的 tar.gz 归档、按dagger_v<版本>_<OS>_<ARCH>.tar.gz命名并计算 sha256 校验和,然后用 Mock HTTP 服务器提供归档与 checksums 文件,最终调用connect()验证client.defaultPlatform()可正常执行。这证实了“无本地引擎时 SDK 也能自动拉起环境”的能力,也是connect()在本地开发、CI 和云端皆可运行的基础。

五、版本兼容性探测

connect()Client交给回调之前,会先执行一次版本检查:

// Warning shall be throw if versions are not compatible try { await client.version() } catch (e) { console.error("failed to check version compatibility:", e) }

从源码注释看,其意图是:若 SDK 与引擎版本不兼容应抛出警告。当前实现采取“软失败”策略——版本探测失败只向console.error输出"failed to check version compatibility:",不会中断connect(),用户回调仍会被执行。如果你在集成时遇到此类警告输出,可据此判断是引擎与 SDK 版本不匹配所致。

六、完整实战示例

示例 1:显式 Client 模式(connect)

import { connect } from "@dagger.io/dagger" await connect( async (client) => { const out = await client .container() .from("alpine") .withExec(["echo", "hello", "world"]) .stdout() console.log(out) // hello world\n }, { LogOutput: process.stderr }, )

示例 2:指定工作目录并加载工作区模块

import { connect } from "@dagger.io/dagger" await connect( async (client) => { // 相对路径基于 Workdir 解析 const source = client.host().directory(".") // ... 基于 source 构建镜像、运行测试等 }, { Workdir: "/path/to/project", LoadWorkspaceModules: true, LogOutput: process.stdout, }, )

示例 3:直接使用 GraphQL 客户端(connection 模式)

connection()回调内不仅可以用类型安全的dagAPI,还可以通过dag.getGQLClient()获取底层 GraphQL 客户端执行原生查询。这正是 connect.spec.ts 验证的场景:

import { connection, dag } from "@dagger.io/dagger" await connection(async () => { const result = await dag.getGQLClient().request(` query { container { from(address: "alpine") { withExec(args: ["echo", "hello", "world"]) { stdout } } } } `) console.log(result.container.from.withExec.stdout) // hello world\n })

示例 4:连接自动关闭验证

无论回调成功或抛错,connect()/connection()都会通过withGQLClient的收尾逻辑与globalConnection.resetClient()确保连接被释放。下面的模式可在脚本中安全复用:

import { connect } from "@dagger.io/dagger" async function runPipeline() { await connect( async (client) => { // 连接期内安全使用 client await client.container().from("alpine:3.16.2") .withExec(["echo", "pipeline", "ok"]).sync() }, { LogOutput: process.stderr }, ) // 退出回调后连接已自动关闭 } await runPipeline()

七、使用要点与注意事项

  1. 回调必须可 awaitCallbackFct返回Promise<void>,回调体内不要遗漏await,否则 Dagger 查询不会被真正执行。
  2. 不要在回调外使用Clientdag:连接生命周期与回调绑定,回调结束后连接即被复位(resetClient),在外部使用全局dag会触发"GraphQL client is not set"错误(见 connection.ts)。
  3. 会话复用优先:在 Dagger CLI 或 CI 已注入DAGGER_SESSION_PORT/DAGGER_SESSION_TOKEN的环境中,SDK 直接复用会话,不会重复供给引擎,这既快又省资源;二者必须成对出现,否则报错。
  4. 无引擎环境自动供给:本地无 CLI、无引擎时,SDK 会自动下载对应平台(normalizedOS/normalizedArch)的 CLI 二进制并启动会话,因此connect()在全新 CI 环境中也能开箱即用。
  5. 版本不一致只会告警:引擎与 SDK 版本不兼容时输出"failed to check version compatibility"告警,不会阻断执行;生产环境建议固定 SDK 与引擎版本以消除潜在行为差异。
  6. connection()connect()二选一:在同一个进程内混用两种模式时注意全局dag的状态是共享的;推荐一个程序统一使用其中一种,保持连接生命周期清晰。

八、总结

connect()是 Dagger TypeScript SDK 所有程序运行的起点:它基于 Go SDK 的设计思想,通过withGQLClient完成“复用既有会话”与“自动供给引擎”的双通道连接,构建出类型安全的Client,并在回调结束后自动释放连接资源。理解ConnectOpts三个配置项(WorkdirLoadWorkspaceModulesLogOutput)与connect/connection的差异,即可在本地开发、CI 与云端场景中稳定地编写和运行 Dagger 自动化流水线。

相关源码与文档索引:

  • 主文档:docs/versioned_docs/version-0.20/reference/typescript/connect/functions/connect.md
  • 回调类型:docs/versioned_docs/version-0.20/reference/typescript/connect/type-aliases/CallbackFct.md
  • 姊妹函数:docs/versioned_docs/version-0.20/reference/typescript/connect/functions/connection.md
  • 核心实现:sdk/typescript/src/connect.ts、sdk/typescript/src/connectOpts.ts、sdk/typescript/src/common/graphql/connect.ts、sdk/typescript/src/common/graphql/connection.ts
  • 行为测试:sdk/typescript/src/test/connect.spec.ts

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

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

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

立即咨询