Skip to content
Open
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
89 changes: 86 additions & 3 deletions bin/setup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bun
import { existsSync, statSync } from "node:fs";
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

Expand Down Expand Up @@ -40,8 +40,10 @@ const SUITES: SuiteSetup[] = [
id: "wpt-wintertc",
aliases: ["wpt", "wpt-wintertc"],
path: "suites/wpt",
sparse: ["resources", "url", "encoding", "fetch"],
required: ["resources", "url", "encoding", "fetch"],
// `tools` carries wptserve (`wpt serve`) + its vendored deps, needed to serve fetch/ tests
// against a real WPT server (see harness/src/adapters/wpt-server.ts).
sparse: ["resources", "url", "encoding", "fetch", "tools"],
required: ["resources", "url", "encoding", "fetch", "tools/serve/serve.py"],
filterBlobNone: true,
},
{
Expand Down Expand Up @@ -184,6 +186,9 @@ function assertSuiteReady(suite: SuiteSetup): void {
if (missing.length) {
throw new Error(`${suite.id}: missing required path(s): ${missing.join(", ")}`);
}
if (suite.id === "wpt-wintertc") {
assertWptServePatched(checkout);
}
}

async function prepareSuite(suite: SuiteSetup): Promise<void> {
Expand Down Expand Up @@ -216,9 +221,87 @@ async function prepareSuite(suite: SuiteSetup): Promise<void> {
], `populating sparse paths for ${suite.id}`);
}

if (suite.id === "wpt-wintertc") {
patchWptServe(resolve(ROOT, suite.path));
}

assertSuiteReady(suite);
}

/**
* Make the vendored `wpt serve` runnable offline inside the harness container. Two in-place edits
* to the (submodule-local, uncommitted) checkout, mirroring cloudflare/workerd's WPT patch:
* 1. `tools/serve/commands.json`: `virtualenv: false` — run against the system Python + WPT's
* vendored `tools/third_party/` deps, with no per-invocation venv/pip step (needs network).
* 2. `tools/wpt/paths`: drop the `docs/` line so the CLI's command loader does not require
* `docs/commands.json`, which the sparse checkout deliberately omits.
* Idempotent: re-running leaves an already-patched checkout unchanged (the paths edit normalizes to
* a single trailing newline so a second pass is a no-op). `assertWptServePatched` verifies the
* result, so a re-vendored submodule that reset these files is caught by `setup --check`.
*/
interface ServeCommands {
serve?: { virtualenv?: boolean; conditional_requirements?: unknown };
}

/** Parse `tools/serve/commands.json`, or return null if absent. Throws a clear error on bad JSON. */
function readServeCommands(commandsPath: string): ServeCommands | null {
if (!existsSync(commandsPath)) return null;
let parsed: unknown;
try {
parsed = JSON.parse(readFileSync(commandsPath, "utf8"));
} catch (err) {
const detail = err instanceof Error ? err.message : String(err);
throw new Error(`wpt serve patch: ${commandsPath} is not valid JSON (${detail})`);
}
if (typeof parsed !== "object" || parsed === null) {
throw new Error(`wpt serve patch: ${commandsPath} is not a JSON object`);
}
return parsed as ServeCommands;
}

function patchWptServe(suitePath: string): void {
const commandsPath = resolve(suitePath, "tools/serve/commands.json");
const commands = readServeCommands(commandsPath);
if (commands) {
const serve = commands.serve;
if (!serve) {
throw new Error(`wpt serve patch: ${commandsPath} has no "serve" command entry (WPT layout changed?)`);
}
if (serve.virtualenv !== false) {
serve.virtualenv = false;
delete serve.conditional_requirements;
writeFileSync(commandsPath, `${JSON.stringify(commands, null, 2)}\n`);
}
}
const pathsFile = resolve(suitePath, "tools/wpt/paths");
if (existsSync(pathsFile)) {
const original = readFileSync(pathsFile, "utf8");
const kept = original.split("\n").filter((l: string) => l.trim() !== "docs/");
const next = `${kept.join("\n").replace(/\n+$/, "")}\n`;
if (next !== original) writeFileSync(pathsFile, next);
}
}

/**
* Confirm the wpt serve checkout carries the offline patch (see {@link patchWptServe}). Content-based
* rather than a marker file, so a re-vendored/updated submodule that silently reset these files is
* caught here (by `setup --check`) instead of failing opaquely at server start; `prepareSuite`
* re-applies the patch unconditionally, so a plain `setup` self-heals.
*/
function assertWptServePatched(suitePath: string): void {
const commandsPath = resolve(suitePath, "tools/serve/commands.json");
const commands = readServeCommands(commandsPath);
if (commands && commands.serve?.virtualenv !== false) {
throw new Error(
`wpt-wintertc: ${commandsPath} not patched for offline serve (serve.virtualenv must be false) — re-run setup`,
);
}
const pathsFile = resolve(suitePath, "tools/wpt/paths");
if (existsSync(pathsFile) && readFileSync(pathsFile, "utf8").split("\n").some((l) => l.trim() === "docs/")) {
throw new Error(`wpt-wintertc: ${pathsFile} still lists docs/ (unpatched) — re-run setup`);
}
}

async function main(): Promise<number> {
process.chdir(ROOT);
const options = parseArgs(Bun.argv.slice(2));
Expand Down
Loading
Loading