在全新 sandbox 中运行仓库的测试。本例中,Durable Object 会克隆仓库、安装依赖、运行测试脚本、返回结果,然后停止实例。
- 带有 Durable Object 的 Worker,该 Durable Object 使用 Durable Object 调度策略 启动 container。要创建一个,请参阅 运行 Linux 命令。
当您运行 wrangler deploy 时,必须在本地运行 Docker。对于大多数人来说,安装 Docker 的最佳方式是按照安装 Docker Desktop 的文档 ↗进行操作。其他工具(如 Colima ↗)也可能有效。
您可以通过在终端中运行 docker info 命令来检查 Docker 是否正常运行。如果 Docker 正在运行,该命令将成功执行。如果 Docker 未运行,docker info 命令将挂起或返回包含 "Cannot connect to the Docker daemon" 消息的错误。
-
在项目根目录创建
Dockerfile,包含 Git 以及测试所需的运行时。这个镜像会在带 Node.js 24 的 Debian Trixie 中添加 Git:Dockerfiledockerfile FROM node:24-trixie-slim RUN apt-get update \ && apt-get install --yes --no-install-recommends ca-certificates git \ && rm -rf /var/lib/apt/lists/* CMD ["sleep", "infinity"]sleep infinity会让 container 保持运行,以便接受更多exec()调用。 -
在
wrangler.jsonc中,将Dockerfile构建为containers条目中的具名镜像:{ "containers": [ { "class_name": "MyContainer", "scheduling_policy": "durable_object", "images": { "tests": { "dockerfile": "./Dockerfile", }, }, }, ], }[[containers]] class_name = "MyContainer" scheduling_policy = "durable_object" [containers.images.tests] dockerfile = "./Dockerfile" -
向 Durable Object 添加方法,用于克隆仓库、安装依赖并运行测试:
src/index.tsts export class MyContainer extends DurableObject<Env> { // ... async runTests(repository: string, revision: string) { const container = this.ctx.container; if (!container) { throw new Error("No container is configured"); } container.start({ image: container.images.tests, // `git` and `npm` need the Internet. Do not pass credentials into this sandbox enableInternet: true, }); const decoder = new TextDecoder(); const run = async (step: string, argv: string[], cwd?: string) => { // Limit each step to 10 minutes. const process = await container.exec( ["timeout", "--kill-after=5", "600", ...argv], { cwd }, ); const output = await process.output(); return { step, stdout: decoder.decode(output.stdout), stderr: decoder.decode(output.stderr), exitCode: output.exitCode, }; }; try { const clone = await run("clone", [ "git", "clone", "--depth=1", `--branch=${revision}`, "--", repository, "/workspace", ]); if (clone.exitCode !== 0) { return clone; } const install = await run( "install", ["npm", "ci", "--no-audit", "--no-fund"], "/workspace", ); if (install.exitCode !== 0) { return install; } return await run("test", ["npm", "test", "--", "--silent"], "/workspace"); } finally { if (container.running) { await container.destroy(); } } } }--会阻止git将以-开头的仓库值当作选项读取。运行时间超过 10 分钟的步骤会返回退出代码
124;如果timeout也发送了SIGKILL,则返回137。timeout会停止该步骤及其启动且仍处于同一进程组中的进程。更多信息请参阅停止命令启动的进程。finally代码块会在运行后停止实例。output()会将步骤的全部输出保存在 Durable Object 的内存中。对于输出很多的测试套件,请改为 流式输出。enableInternet: true允许git和npm下载仓库及其软件包。它也允许仓库的安装和测试脚本访问互联网,因此不要将凭据传入此 sandbox。更多信息请参阅 Sandbox 安全。 -
向 Worker 添加路由,在新 sandbox 中运行测试:
src/index.jsjs export default { async fetch(request, env) { if (request.method !== "POST") { return new Response("Not found", { status: 404 }); } const sandbox = env.MY_CONTAINER.getByName(crypto.randomUUID()); const result = await sandbox.runTests( "https://github.com/cloudflare/workers-oauth-provider.git", "v0.0.6", ); return Response.json(result, { status: result.exitCode === 0 ? 200 : 500, }); }, };src/index.tsts export default { async fetch(request: Request, env: Env): Promise<Response> { if (request.method !== "POST") { return new Response("Not found", { status: 404 }); } const sandbox = env.MY_CONTAINER.getByName(crypto.randomUUID()); const result = await sandbox.runTests( "https://github.com/cloudflare/workers-oauth-provider.git", "v0.0.6", ); return Response.json(result, { status: result.exitCode === 0 ? 200 : 500, }); }, } satisfies ExportedHandler<Env>;随机 sandbox 名称会为每次运行提供新实例,因此一次运行看不到另一次运行的文件。
本例中,Worker 运行公开
workers-oauth-provider↗ 仓库版本v0.0.6的测试。请用你自己的仓库、修订版本和npm命令替换它们。exec()不会启动 shell,因此请将每个参数放在独立的数组项中。 -
部署你的 Worker:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy -
向 Wrangler 打印的
workers.devURL 发送POST请求:curl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev --request POST响应包含测试输出和退出码:
{ "step": "test", "stdout": "...\n Test Files 1 passed (1)\n Tests 37 passed (37)\n...", "stderr": "", "exitCode": 0 }当某个步骤失败时,Worker 会以
500响应,step会标出失败的步骤。
- 流式命令输出:在测试运行时显示测试输出。
- 使用 snapshot 保存并恢复 sandbox:在各次运行之间保留已安装依赖。
- 处理出站流量:仅允许 Git server 和软件包仓库,而不是整个互联网。
- 克隆私有仓库:使用留在 Worker 中的 token 进行克隆。