将 0.12 命令调用替换为容器上的 exec()。在 Sandbox SDK 0.12 中,exec() 在从 /workspace 启动的长期 bash 会话中运行每条命令字符串。在 1.0 中,每次调用启动一个进程,一次调用不会把任何内容带到下一次。
你的 1.0 类已替换 0.12 Sandbox 类,并具有 替换 Sandbox 类 中的 container getter、ensureRunning() 方法和 ENV 常量。在每个运行命令的方法开头调用 ensureRunning()。
本页替换 exec()、execStream()、createSession()、getSession()、deleteSession(),以及 timeout 和 commandTimeoutMs 选项。有关文件调用,请参阅 更改文件调用。有关 startProcess(),请参阅 迁移后台进程。
将命令作为参数数组传入,并带上 0.12 提供给它的工作目录和变量:
const result = await sandbox.exec("npm test");
if (!result.success) {
console.error(result.stderr);
}const process = await this.container.exec(["npm", "test"], {
cwd: "/workspace",
env: ENV,
});
const result = await process.output();
if (result.exitCode !== 0) {
console.error(new TextDecoder().decode(result.stderr));
}exec() 从 / 启动,即使镜像设置了 WORKDIR。它不会继承 start() 设置的变量,PATH 除外。output() 等待进程退出,并以字节形式返回其输出。与 0.12 一样,非零退出码不会抛出错误。
exec() 在不使用 shell 的情况下运行命令。对于管道、重定向和变量,请用 bash 运行 0.12 字符串:
const process = await this.container.exec(
["bash", "-c", "npm test 2>&1 | tee /tmp/test.log"],
{ cwd: "/workspace", env: ENV },
);当可执行文件不存在时,exec() 会抛出类似 Command `npm` was not found in the container 的错误。0.12 的 bash 则返回退出码 127。通过 bash -c 运行的命令仍会返回 127。
要替换 execStream(),或带有 stream: true 和 onOutput 的 exec(),请将进程的 stdout 作为响应正文返回:
const stream = await sandbox.execStream("npm test");
return new Response(stream, {
headers: { "Content-Type": "text/event-stream" },
});const process = await this.container.exec(["npm", "test"], {
cwd: "/workspace",
env: ENV,
stderr: "combined",
});
return new Response(process.stdout, {
headers: { "Content-Type": "text/event-stream" },
});stderr: "combined" 会在同一流中发送标准错误。当命令几乎同时写入两个流时,行可能会乱序到达。要保持顺序,请改为在 shell 中重定向,例如 ["sh", "-c", "npm test 2>&1"]。保持 Content-Type: text/event-stream。使用其他内容类型时,响应可能在进程退出时一次性到达。
0.12 发送由 parseSSEStream() 解码的 JSON 事件。1.0 正文保存命令的输出。请改用 fetch() 和 stream reader 读取,而不是 EventSource。要将每行作为 server-sent event 发送,请参阅 流式命令输出。
0.12 会话会在调用之间保留工作目录和变量。将它们保存在对象中,并传给每次调用:
const build = await sandbox.createSession({
cwd: "/workspace/app",
env: { CI: "1" },
});
await build.exec("npm ci");
await build.exec("npm test");const build = { cwd: "/workspace/app", env: { ...ENV, CI: "1" } };
const install = await this.container.exec(["npm", "ci"], build);
await install.exitCode;
const test = await this.container.exec(["npm", "test"], build);
const result = await test.output();exec() 在进程启动时 resolve,因此在下一步之前请等待 exitCode。
在 0.12 中,一次 exec() 调用中的 cd 或 export 会带到下一次调用,包括默认会话。在 1.0 中,它只持续到该命令退出。将相互依赖的步骤放在一条命令中运行,例如 ["bash", "-c", "cd app && npm ci && npm test"]。
ExecutionSession 方法与顶层方法的对应关系相同。删除 deleteSession(),因为没有需要删除的内容。0.12 接受但忽略 isolation 选项,因此同一 sandbox 中的会话始终共享进程和文件。若要隔离两个会话的工作,请为它们分别指定 sandbox 名称。
使用 GNU coreutils timeout 启动命令;它会停止命令及其启动的进程,这些进程必须仍位于其进程组中:
const result = await sandbox.exec("npm test", { timeout: 60_000 });const process = await this.container.exec(
["timeout", "--kill-after=5", "60", "bash", "-c", "npm test"],
{ cwd: "/workspace", env: ENV },
);
const result = await process.output();
// 124 or 137 means the command ran longer than 60 seconds
const timedOut = result.exitCode === 124 || result.exitCode === 137;0.12 会抛出消息为 Command timeout after 60000ms 的 CommandError,而命令会继续运行。在 1.0 中,超时的命令会停止,因此出错后无法再更改文件。
要从代码中取消命令,请传入 AbortSignal。如何清除其计时器,请参阅 exec()。
从类上的方法运行带选项和不带选项的命令:
const command = [
"sh",
"-c",
'echo "pwd=$(pwd) NODE_ENV=${NODE_ENV:-unset}"',
];
const bare = await this.container.exec(command);
const withOptions = await this.container.exec(command, {
cwd: "/workspace",
env: ENV,
});bare 的输出显示默认值,withOptions 的输出显示 0.12 提供给每条命令的内容:
pwd=/ NODE_ENV=unset
pwd=/workspace NODE_ENV=test