Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 43 additions & 8 deletions apps/hub/src/builds/posix-sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,12 @@ export async function createPosixBuildExecutor(
`command_build_failed:${name} ${built.logs.join("; ")}`,
);
const shim = join(shimDirectory, name);
await writeFile(shim, `#!/bin/sh\nexec ${quote(binary)} "$@"\n`);
// srt points TMPDIR at a folder it does not make inside the sandbox's own /tmp, and Bun's
// WebView cannot start Chrome without a temp folder.
await writeFile(
shim,
`#!/bin/sh\n[ -z "$TMPDIR" ] || mkdir -p -- "$TMPDIR"\nexec ${quote(binary)} "$@"\n`,
);
await chmod(shim, 0o755);
}
const pageWatcher = join(shimDirectory, "watch-page.ts");
Expand Down Expand Up @@ -327,8 +332,30 @@ export async function createPosixBuildExecutor(
: undefined,
);
initialized = true;
// On Linux srt relays the sandbox's proxy ports to the host over Unix sockets in the host's temp
// folder, which the default-deny read policy hides; srt binds them before that deny lands, so
// every connection through the proxy was reset. Named in allowRead, they are bound back over it.
if (process.platform === "linux") {
await SandboxManager.waitForNetworkInitialization();
const bridges = new Set(
[
SandboxManager.getLinuxHttpSocketPath(),
SandboxManager.getLinuxSocksSocketPath(),
].filter((path): path is string => path !== undefined),
);
if (bridges.size)
SandboxManager.updateConfig({
...policy,
filesystem: {
...policy.filesystem,
allowRead: [...(policy.filesystem.allowRead ?? []), ...bridges],
},
});
}
stage = "wrap";
const childCommand = `exec ${[executable, "--no-env-file", layout.childPath, cwd].map(quote).join(" ")}`;
// The child resolves the workspace packages from source like the hub does;
// the vendored packages ship no built dist.
const childCommand = `exec ${[executable, "--no-env-file", "--conditions=intx-src", layout.childPath, cwd].map(quote).join(" ")}`;
const wrapped = await SandboxManager.wrapWithSandbox(
childCommand,
"/bin/sh",
Expand Down Expand Up @@ -779,7 +806,7 @@ export async function createPosixBuildExecutor(
env: childEnv,
stdin: "ignore",
stdout: "pipe",
stderr: "ignore",
stderr: "pipe",
detached: process.platform !== "linux",
},
);
Expand All @@ -788,11 +815,19 @@ export async function createPosixBuildExecutor(
const timer = setTimeout(stop, limits.commandTimeoutMs);
signal.addEventListener("abort", stop, { once: true });
try {
const output = await new Response(child.stdout).text();
return {
exitCode: await child.exited,
output: output.replaceAll(cwd + sep, ""),
};
const [output, errors] = await Promise.all([
new Response(child.stdout).text(),
new Response(child.stderr).text(),
]);
const exitCode = await child.exited;
// A command that died before printing its checklist would otherwise fail with nothing said.
const text =
exitCode === 0
? output
: [output, errors.slice(-2000)]
.filter((part) => part.trim())
.join("\n");
return { exitCode, output: text.replaceAll(cwd + sep, "") };
} finally {
clearTimeout(timer);
signal.removeEventListener("abort", stop);
Expand Down
5 changes: 3 additions & 2 deletions apps/hub/src/builds/starter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const CHROME_VERSION = "156.0.8078.4";
const CHROME_PLATFORMS: Record<string, string> = {
"darwin-arm64": "mac-arm64",
"darwin-x64": "mac-x64",
"linux-arm64": "linux-arm64",
"linux-x64": "linux64",
};
const CHROME_PLATFORM = CHROME_PLATFORMS[`${process.platform}-${process.arch}`];
Expand All @@ -55,8 +56,8 @@ const exists = (path: string) =>
);

// One Chrome for every sandbox, unpacked once beside the hub's other caches and only ever read by
// the sandboxes. Chrome for Testing has no build for some hosts (linux-arm64); there no browser is
// installed and check-app reports it missing.
// the sandboxes. On a host Chrome for Testing has no build for, no browser is installed and
// check-app reports it missing.
const BROWSER_CACHE = join(
homedir(),
".cache",
Expand Down
3 changes: 2 additions & 1 deletion workflows/coder/check/check-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,12 +572,13 @@ if (staticRoot) {

// The sandbox setup unpacks Chrome's headless shell under $HOME/browsers. A sandboxed process may not
// register Mach services, so Chrome runs as one process with its own sandbox off.
// With no browser installed the folder is not there at all, which is the same finding.
const [chrome] = await Array.fromAsync(
new Bun.Glob("chrome-headless-shell-*/chrome-headless-shell").scan({
cwd: join(homedir(), "browsers"),
absolute: true,
}),
);
).catch(() => [] as string[]);
if (!chrome) {
stop();
fail("browser", "nothing under $HOME/browsers");
Expand Down