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
5 changes: 4 additions & 1 deletion scripts/build/deps/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
// Windows ICU data table filtered + per-item zstd compressed, and Windows
// unwind info (RtlAddGrowableFunctionTable) registered for the fixed JIT
// pool (LLInt pending offlineasm .seh_* emission).
export const WEBKIT_VERSION = "a40d462206e1caf8388062120acde61e37a4ae7d";
// Preview of oven-sh/WebKit#328: RELEASE_AND_RETURN on the inspector
// injected-script prototype host functions so Runtime.evaluate survives
// validateExceptionChecks=1.
export const WEBKIT_VERSION = "autobuild-preview-pr-328-31913c3e";
Comment on lines +13 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 WEBKIT_VERSION is pinned to autobuild-preview-pr-328-31913c3e, an ephemeral preview tag that GitHub deletes as soon as oven-sh/WebKit#328 merges or closes — at which point every fresh build of bun main 404s downloading WebKit. Before this lands, swap the pin to the merged 40-hex oven-sh/WebKit main sha (and drop the "Preview of…" comment).

Extended reasoning...

What the bug is

scripts/build/deps/webkit.ts:16 sets WEBKIT_VERSION = "autobuild-preview-pr-328-31913c3e". This is an autobuild-preview-pr-* tag — a temporary GitHub release that oven-sh/WebKit's CI publishes while a WebKit PR is open, and deletes when that PR merges or closes. The previous pin was a permanent 40-hex commit sha (a40d462206e1caf8388062120acde61e37a4ae7d).

The code path that triggers it

For a prebuilt WebKit (the default, cfg.webkit === "prebuilt"), webkit.source() calls prebuiltUrl(), which turns cfg.webkitVersion into a release-download URL. autobuild-* prefixes are handled (const tag = version.startsWith("autobuild-") ? version : \autobuild-${version}`), so the pin *works today* — the URL resolves to https://github.com/oven-sh/WebKit/releases/download/autobuild-preview-pr-328-31913c3e/bun-webkit--.tar.gz. At build time, ninja invokes fetch-cli.tsdownload.ts::downloadWithRetry()to fetch that tarball intocfg.cacheDir`.

Why existing code doesn't prevent it

The build system explicitly supports preview pins so a WebKit PR can be tested inside a bun PR — that's why prebuiltUrl(), prebuiltDestDir(), and depVersionsHeader.ts all special-case the autobuild- prefix. But the same codebase also documents that preview pins must not persist. scripts/build/download.ts:276-295 has a dedicated prebuiltDownloadError() branch for exactly this failure:

autobuild-preview-pr-* WebKit tags are the sharp edge: GitHub deletes the preview release when the PR merges or closes, so every build 404s at once.

and the remediation it prints is:

Set WEBKIT_VERSION in scripts/build/deps/webkit.ts to the merged main sha

So the repo's own error-handling treats a preview pin on main as a bad pin, not a supported state. Nothing at configure time rejects the preview tag — the build only fails once the release is gone, which is precisely when it's most disruptive (post-merge, on other people's machines).

Impact

If this PR merges to main as-is and oven-sh/WebKit#328 subsequently merges (which is the intended outcome — this bun PR exists to ship that WebKit fix), the preview release is deleted. From that moment:

  • Every fresh clone of bun main fails bun bd / bun run build at the WebKit download step with HTTP 404.
  • Every CI lane without a warm $BUN_INSTALL/build-cache/webkit-preview-pr-328-31913c3e* extraction fails the same way.
  • Developers with a cached extraction keep working, masking the break for whoever lands it.

The PR description already acknowledges this is a preview build ("the first CI run here will fail … until the preview build … finishes publishing"), and the added code comment says "Preview of oven-sh/WebKit#328" — so the author is aware. This finding is the merge-gate reminder, not an oversight report.

Step-by-step proof

  1. This PR merges to main with WEBKIT_VERSION = "autobuild-preview-pr-328-31913c3e".
  2. inspector: release throw scope before tail-calling impl in injected-script prototype host functions WebKit#328 merges. oven-sh/WebKit's release automation deletes the autobuild-preview-pr-328-31913c3e release (per the doc comment at download.ts:278-279).
  3. A contributor clones bun main fresh and runs bun bd.
  4. Configure emits a dep_fetch edge for WebKit; ninja runs fetch-cli.ts dep WebKit … https://github.com/oven-sh/WebKit/releases/download/autobuild-preview-pr-328-31913c3e/bun-webkit-linux-amd64-debug.tar.gz.
  5. GitHub returns HTTP 404. downloadWithRetry() throws; prebuiltDownloadError() matches name === "WebKit" && missing && url.includes("/autobuild-preview-pr-") and raises BuildError: WebKit preview release is gone: … HTTP 404.
  6. The build fails. Every fresh build of main is broken until someone lands a follow-up commit changing WEBKIT_VERSION.

How to fix

Before merging this PR: merge oven-sh/WebKit#328 first, then update WEBKIT_VERSION here to the resulting 40-hex commit sha on oven-sh/WebKit main (and replace the "Preview of oven-sh/WebKit#328" comment with the permanent description, matching the style of the surrounding lines). The PR description's own workflow — "re-run once autobuild-preview-pr-328-31913c3e appears" — is fine for iterating on the branch, but the final merged commit must carry the permanent sha.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional for now: the preview pin lets CI exercise the fix before oven-sh/WebKit#328 merges. I will swap this to the merged 40-hex main sha (and drop the preview comment) before this PR is marked ready to merge. Noted in the PR body under Notes as well.

Comment on lines +13 to +16

@coderabbitai coderabbitai Bot Jul 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== scripts/build/deps/webkit.ts ==\n'
cat -n scripts/build/deps/webkit.ts | sed -n '1,120p'

printf '\n== references to WEBKIT_VERSION ==\n'
rg -n "WEBKIT_VERSION|autobuild-preview-pr-328-31913c3e|webkit" scripts packages -g '!**/node_modules/**' | sed -n '1,200p'

printf '\n== dependency docs or notes ==\n'
rg -n "preview-pr|WebKit#328|validateExceptionChecks|autobuild-preview" . -g '!**/node_modules/**' | sed -n '1,200p'

Repository: oven-sh/bun

Length of output: 21931


🌐 Web query:

WebKit autobuild-preview-pr-328-31913c3e release preview pr 328 validateExceptionChecks=1

💡 Result:

The term "validateExceptionChecks" refers to a specific diagnostic option in WebKit's JavaScriptCore (JSC) engine [1][2]. When set to true (or 1), this option enables verification that necessary exception checks are performed within the engine [2], which helps identify and debug missing exception handling in the code [3][4]. The identifier "autobuild-preview-pr-328-31913c3e" appears to be a specific build artifact or container image tag, likely generated by a CI/CD pipeline for a pull request (PR). While "pr-328" may refer to a specific pull request in a repository, the exact combination provided does not correspond to a singular, globally recognized WebKit release or public document. The structure is consistent with automated PR preview environments where unique tags (like 31913c3e) are used to track specific builds [5][6][7]. In summary: - validateExceptionChecks: A JSC engine configuration option used for validating exception handling [1][2]. - autobuild-preview-pr-328-31913c3e: An internal identifier for a transient build environment, typical of automated CI systems [5][6][7].

Citations:


Replace the preview WebKit pin before release. scripts/build/deps/webkit.ts:16 points WEBKIT_VERSION at an autobuild-preview-pr-* tag, and GitHub can remove those releases when the PR merges or closes. Pin the merged WebKit commit instead so default downloads keep working.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build/deps/webkit.ts` around lines 13 - 16, Update WEBKIT_VERSION to
reference the merged WebKit commit’s stable release or commit-based artifact
instead of the temporary autobuild-preview-pr-328-31913c3e tag, ensuring default
downloads remain available after the upstream PR lifecycle ends.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged: the pin stays on the preview tag while iterating on this branch, and afe1ec4 adds a source-lint test that fails on any autobuild-preview-* value so CI itself blocks the merge until the pin is swapped to the merged main sha. Leaving this thread open as the merge-gate reminder alongside the earlier one.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== referenced commit =='
git show --no-ext-diff --format=fuller --stat afe1ec4

printf '%s\n' '== referenced commit patch =='
git show --no-ext-diff --format= --find-renames afe1ec4 -- \
  scripts/build/deps/webkit.ts test scripts | sed -n '1,280p'

printf '%s\n' '== preview-pin guard candidates on PR head =='
rg -n -C 4 'autobuild-preview|WEBKIT_VERSION' \
  scripts test .github -g '!**/node_modules/**' -g '!**/.git/**' | sed -n '1,280p'

Length of output: 19755


@robobun, verified. afe1ec4 adds test/internal/source-lints/webkit-prebuilt-url.test.ts, whose new assertion rejects WEBKIT_VERSION values beginning with autobuild-preview-. Since the current pin still uses that prefix, CI will intentionally fail until it is swapped for the merged WebKit SHA.

That fully addresses the risk during iteration while preserving the required pre-merge action. Keeping this thread open as the merge-gate reminder is appropriate.


✏️ Learnings added
Learnt from: robobun
Repo: oven-sh/bun PR: 35333
File: scripts/build/deps/webkit.ts:13-16
Timestamp: 2026-07-24T00:19:05.827Z
Learning: For oven-sh/bun PR `#35333`, `test/internal/source-lints/webkit-prebuilt-url.test.ts` intentionally rejects `WEBKIT_VERSION` values starting with `autobuild-preview-`. A preview WebKit pin may be used while iterating on the branch, but it must be replaced with the merged WebKit main SHA before merge; the source-lint test is the CI merge gate.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: robobun
Repo: oven-sh/bun PR: 32761
File: scripts/build/deps/ghostty-vt.ts:1-18
Timestamp: 2026-06-26T13:57:16.037Z
Learning: For dependency definition files in scripts/build/deps/*.ts, keep any top-of-file header comment limited to dependency-specific context (e.g., why a particular commit/version is pinned and how related package hashes or hashes-to-pins are connected). Follow the existing convention used by the other dependency files. For build flags in these dep files that have a non-obvious invariant or safety rationale (e.g., ODR concerns, sanitizer/runtime requirements, or other correctness constraints), prefer a short comment placed next to that specific flag rather than a long file-level “playbook” comment.


/**
* WebKit (JavaScriptCore) — the JS engine.
Expand Down
69 changes: 68 additions & 1 deletion test/cli/inspect/inspect.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Subprocess, spawn } from "bun";
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
import fs from "fs";
import { bunEnv, bunExe, isPosix, randomPort, tempDirWithFiles } from "harness";
import { bunEnv, bunExe, isASAN, isDebug, isPosix, randomPort, tempDirWithFiles } from "harness";
import { join } from "node:path";
import stripAnsi from "strip-ansi";
import { WebSocket } from "ws";
Expand Down Expand Up @@ -300,6 +300,73 @@ describe("websocket", () => {
});
});

// JSInjectedScriptHostPrototype / JSJavaScriptCallFramePrototype host functions declare a
// ThrowScope and then tail-call into an impl that declares its own; without a
// RELEASE_AND_RETURN the first Runtime.evaluate aborts under exception-check validation with
// "Unchecked JS exception ... jsInjectedScriptHostPrototypeFunctionEvaluateWithScopeExtension".
// ENABLE_EXCEPTION_SCOPE_VERIFICATION is (ASSERT_ENABLED || ASAN_ENABLED), so this runs on
// debug and asan builds; plain release compiles it out.
test.skipIf(!isDebug && !isASAN)("Runtime.evaluate does not trip exception-check validation", async () => {
await using child = spawn({
cwd: import.meta.dir,
cmd: [bunExe(), "--inspect-wait=127.0.0.1:0", "inspectee.js"],
env: {
...bunEnv,
BUN_JSC_validateExceptionChecks: "1",
BUN_JSC_dumpSimulatedThrows: "1",
},
stdout: "ignore",
stderr: "pipe",
});

let stderr = "";
const decoder = new TextDecoder();
const { promise: urlPromise, resolve: resolveUrl, reject: rejectUrl } = Promise.withResolvers<URL>();
const drained = (async () => {
for await (const chunk of child.stderr) {
stderr += decoder.decode(chunk);
for (const line of stderr.split("\n")) {
try {
const u = new URL(line.trim());
if (u.protocol.includes("ws")) resolveUrl(u);
} catch {}
}
}
rejectUrl(new Error("inspectee exited before printing inspector URL:\n" + stderr));
})();

const url = await urlPromise;
const ws = new WebSocket(url);
let reply: unknown;
try {
await new Promise<void>((resolve, reject) => {
ws.addEventListener("open", () => resolve());
ws.addEventListener("error", cause => reject(new Error("WebSocket error", { cause })));
});

ws.send(JSON.stringify({ id: 1, method: "Runtime.evaluate", params: { expression: "1 + 1" } }));
reply = await new Promise<unknown>((resolve, reject) => {
ws.addEventListener("message", ({ data }) => resolve(JSON.parse(String(data))));
ws.addEventListener("close", ({ code, reason }) => resolve({ closed: { code, reason } }));
ws.addEventListener("error", cause => reject(new Error("WebSocket error", { cause })));
});
} finally {
ws.close();
child.kill();
}

await Promise.all([child.exited, drained]);
// Without the WebKit-side fix the inspectee SIGABRTs ("Unchecked JS exception")
// before replying, so the socket closes 1006 and reply is { closed: { code: 1006, ... } }.
if (child.signalCode === "SIGABRT") {
throw new Error(`inspectee aborted under validateExceptionChecks (reply=${JSON.stringify(reply)}):\n${stderr}`);
}
Comment thread
robobun marked this conversation as resolved.
expect(reply).toMatchObject({
id: 1,
result: { result: { type: "number", value: 2 } },
});
});

describe("http metadata endpoint", () => {
let metadataInspectee: Subprocess | undefined;

Expand Down
1 change: 0 additions & 1 deletion test/expectations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ test/js/bun/spawn/spawn-maxbuf.test.ts [ FLAKY ]

# Tests timed out due to ASAN
[ ASAN ] test/js/bun/spawn/spawn.test.ts [ TIMEOUT ]
[ ASAN ] test/cli/inspect/inspect.test.ts [ TIMEOUT ]

# Tests failed due to memory leaks
[ ASAN ] test/js/node/url/pathToFileURL.test.ts [ LEAK ] # pathToFileURL doesn't leak memory
Expand Down
7 changes: 7 additions & 0 deletions test/internal/source-lints/webkit-prebuilt-url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,11 @@
test("WEBKIT_VERSION is either a 40-hex sha or an autobuild-* tag", () => {
expect(/^[0-9a-f]{40}$/.test(WEBKIT_VERSION) || WEBKIT_VERSION.startsWith("autobuild-")).toBe(true);
});

// autobuild-preview-pr-* releases are deleted when the oven-sh/WebKit PR
// merges or closes, which would 404 every fresh build of main. Preview pins
// are fine on a branch while iterating; this test is the merge gate.
test("WEBKIT_VERSION is not an autobuild-preview-* tag (preview releases are deleted on upstream merge)", () => {
expect(WEBKIT_VERSION.startsWith("autobuild-preview-")).toBe(false);

Check failure on line 131 in test/internal/source-lints/webkit-prebuilt-url.test.ts

View workflow job for this annotation

GitHub Actions / Source lints

error: expect(received).toBe(expected)

Expected: false Received: true at <anonymous> (/home/runner/work/bun/bun/test/internal/source-lints/webkit-prebuilt-url.test.ts:131:61)
});
});
Loading