Skip to content

ci: single arm64 debian-13 build host; ThinLTO everywhere; baseline-only x64; rust+link merge; sysroots; WebKit a36c188; rust 2026-07-20#34782

Merged
Jarred-Sumner merged 19 commits into
mainfrom
claude/farm/6ea6ece4/ci-pr-lto-off-main-tests
Jul 21, 2026
Merged

ci: single arm64 debian-13 build host; ThinLTO everywhere; baseline-only x64; rust+link merge; sysroots; WebKit a36c188; rust 2026-07-20#34782
Jarred-Sumner merged 19 commits into
mainfrom
claude/farm/6ea6ece4/ci-pr-lto-off-main-tests

Conversation

@robobun

@robobun robobun commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

One image bake covering a set of coupled CI/build changes. Commit subject carries [build images] so the v40 images bake on this PR.

What

Single arm64 debian-12 build host

Every build lane (all 13 targets: linux x64/aarch64 gnu/musl/android/asan, darwin x64/aarch64, freebsd x64/aarch64, windows x64/aarch64) now runs on one linux-aarch64-12-debian host image and cross-compiles to its target via --target + --sysroot. buildHostPlatform in .buildkite/ci.mjs is the single source of truth; getCppAgent() / getLinkBunAgent() always pick c8g.4xlarge / r8g.2xlarge. getBuildArgs() always emits --os/--arch/--abi so resolveConfig() never infers target from the host.

scripts/build/config.ts gains a Linux cross-arch/cross-abi block: when target arch or abi differs from the host's, crossTarget is set to <arch>-unknown-linux-<gnu|musl> and sysroot to the matching /opt/linux-sysroot-{glibc,musl}{,-arm64}. scripts/build/flags.ts adds the matching --target/--sysroot linkerFlags entry. scripts/build/shims.ts points the musl CRT -print-file-name probe at the sysroot when cross-compiling.

bootstrap.sh v40

  • install_linux_glibc_sysroot(): extracts glibc 2.17 headers/libs + devtoolset libstdc++ from quay.io/pypa/manylinux2014_<arch>:latest via skopeo copy dir: + manifest-ordered layer unpack into /opt/linux-sysroot-glibc{,-arm64}. Best-effort during bake (prints a warning, does not abort); the build throws a clear BuildError if missing when cross-compiling.
  • install_linux_musl_sysroot(): extracts <triple>/ + lib/gcc/ from musl.cc/<arch>-linux-musl-cross.tgz into /opt/linux-sysroot-musl{,-arm64}.
  • install_macos_sdk(): fetches scripts/build/xmac.mjs at BUN_BOOTSTRAP_REPO_REF and splats the pinned SDK into /opt/macos-sdk, so the per-job Apple CDN fetch in configure goes away.
  • install_cross_compiler_rt(): best-effort dpkg --add-architecture amd64 + libclang-rt-<v>-dev:amd64 on arm64 debian so x64 asan cross can link. If apt.llvm.org lacks the amd64 packages on arm64 repos, the asan lane is the one candidate for an x64-host exception.
  • install_rust(): adds {x86_64,aarch64}-unknown-linux-{gnu,musl} rustup targets.
  • clean_system(): apt-get clean / dnf clean all / apk cache purge, rm -rf /tmp/*, and fstrim -av before snapshot to shrink the EBS snapshot.
  • Dead install_osxcross path and --osxcross flag removed; lsb-release and qemu-user added to apt installs.

rust-and-link merged step

getBuildRustStep() and getLinkBunStep() collapse into one getBuildBunStep() (key *-build-bun, timeout_in_minutes: 60) that runs ci-rust-and-link: ninja bun-rust, poll buildkite-agent step get outcome --step <target>-build-cpp (60m deadline), download the cpp archive, link, package. build-cpp runs in parallel with no depends_on between them. test/release/verify-baseline/binary-size depends_on stay on *-build-bun.

PR builds skip LTO

getBuildArgs() passes --lto=off on non-main branches. [lto] in the commit subject opts back in. binary-size is annotate-only on non-LTO PRs. main keeps LTO.

Tests on main, canary gated on green

The !isMainBranch() guard around test steps is dropped (asan stays PR-only). getReleaseStep() depends_on every *-test-bun key.

Test shards in their own groups

Test groups are keyed on getPlatformKey/getPlatformLabel (distro + release) instead of getTargetKey/getTargetLabel, so they no longer merge into the build groups by label. Build groups depend only on the buildHostPlatform image; test groups depend on their own native image.

verify-baseline stays arch-matched

getVerifyBaselineHost() picks a per-target-arch native host (debian-13 / windows-2019) so the pinned qemu/SDE binaries keep working. scripts/verify-baseline.ts accepts --arch instead of inferring from process.arch.

x64 is baseline-only

scripts/build/config.ts defaults baseline = true on x64, scripts/build/flags.ts drops -march=haswell. Non-baseline x64 lanes removed from ci.mjs. upload-release.sh gains alias_baseline_artifact() + rezip_as() which rezips each x64 artifact under its old non-baseline name so existing download URLs keep resolving. ENABLE_SIMD and BASELINE env constants removed from src/bun_core/env.rs; bun_warn_avx_missing removed from c-bindings.cpp and lib.rs.

Rust nightly-2026-07-20

Folds #34452. #[allow(suspicious_runtime_symbol_definitions)] on the safe fn libc extern blocks in src/bun_core/Global.rs and src/sys/lib.rs for the new lint.

scripts/packer/build-image.pkr.hcl

New Packer template with docker + amazon-ebs sources both running scripts/bootstrap.sh, so local docker run and CI AMIs share one provisioner. Not yet wired into machine.mjs.

Verification

Generated .buildkite/ci.yml under [build images] / [publish images] / [build linux images] / plain-PR: all YAML-valid, 0 dangling depends_on, 0 duplicate keys. Bake set is 9 images (1 build host + 8 test-platform). sh -n/bash -n on bootstrap.sh and upload-release.sh. bunx tsc --noEmit -p scripts/build/tsconfig.json introduces no new errors. cargo check --workspace --target {x86_64,aarch64}-unknown-linux-{gnu,musl} and {aarch64-apple-darwin, x86_64-unknown-freebsd, aarch64-pc-windows-msvc} all pass. bun bd builds; bun bd test on the four config/flags/shims-touching internal tests passes.

The actual cross-link of x64/musl binaries from an arm64 host is not exercisable locally (needs the baked sysroots); the [build images] run on this PR is the first end-to-end exercise.


no test proof · iteration 32 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/run/require-cache.test.ts test/internal/macos-cross-config.test.ts test/internal/source-lints/windows-cross-config.test.ts test/js/bun/perf/linker-order.test.ts test/napi/napi.test.ts

@robobun

robobun commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 3:05 AM PT - Jul 21st, 2026

@robobun, your commit 2c1bffe is building: #76764

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Changes

This PR consolidates x64 baseline handling, updates CI release gating and artifact aliases, adds Linux glibc and macOS SDK bootstrap support, pins Rust nightly toolchains, removes AVX2-based platform selection, and applies Rust fixed-size chunk and expression-based refactors.

Possibly related PRs

  • oven-sh/bun#34561: Both changes update baseline-related CI and release artifact aliasing.
  • oven-sh/bun#34207: Both changes modify baseline handling in CI and WebKit prebuilt selection.
  • oven-sh/bun#34009: Both changes update WebKit prebuilt suffix selection.

Suggested reviewers: jarred-sumner, alii

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is verbose, but it clearly reflects the PR’s main CI/build, x64 baseline, sysroot, and Rust updates.
Description check ✅ Passed The description covers both required sections with detailed change notes and verification results.

Comment @coderabbitai help to get the list of available commands.

@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from f72bd40 to 710f3fb Compare July 20, 2026 06:49
@robobun robobun changed the title ci: skip LTO on PR builds, run tests on main, gate canary on green tests ci: no-LTO PR builds, tests on main, baseline-only x64, image sysroots, rust 2026-07-20 Jul 20, 2026
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from 710f3fb to 84ca491 Compare July 20, 2026 06:52
@robobun

robobun commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Added unit tests in test/internal/macos-cross-config.test.ts for the two behavioural configure-time changes (x64 baseline default + -march=nehalem flag emission, and detectLinuxGlibcSysroot / cfg.linuxSysroot wiring). They pass on this branch and fail to load against main's scripts/build/config.ts.

Note on the fail-before gate: the src/**.rs changes in this PR are toolchain-compat refactors for the nightly-2026-07-20 lints (chunks_exact -> as_chunks, uninitialized-let -> let-if-else, isolate_lowest_one(), #[allow(suspicious_runtime_symbol_definitions)]) with zero runtime behavioural delta; there is no src/-only fail-before to construct. The configure-time tests above exercise the actual behavioural change, which lives in scripts/.

@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from 10907c1 to d4d4c5e Compare July 20, 2026 06:55

@claude claude Bot left a comment

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.

Additional findings (outside current diff — PR may have been updated during review):

  • 🟡 .buildkite/ci.mjs:1626-1633 — The comment says "enforce only on main or [lto] PRs", but the code is sizeRecordOnly = isMainBranch() || !options.forceLto — on main, recordOnly is true so --no-fail is passed and the size threshold is not enforced (it only records the baseline, matching pre-PR behavior and your own verification table). Enforcement happens only on [lto] PRs; consider rewording to something like "enforce only on [lto] PRs; main records the baseline; non-LTO PRs annotate only."

    Extended reasoning...

    What the comment says vs. what the code does

    The newly-added comment at .buildkite/ci.mjs:1626-1628 reads:

    Binary-size tracking. Non-LTO PR binaries are larger than the LTO main canary they compare against, so enforce only on main or [lto] PRs; other PRs still annotate.

    The code immediately below it is:

    const sizeRecordOnly = isMainBranch() || !options.forceLto;
    steps.push(getBinarySizeStep(strippedPlatforms, options, { recordOnly: sizeRecordOnly }));

    getBinarySizeStep() translates recordOnly: true into the --no-fail flag, which makes scripts/binary-size.ts annotate without failing the build. So "enforce" ↔ recordOnly === false, and "annotate only" ↔ recordOnly === true.

    Step-by-step proof

    Scenario isMainBranch() options.forceLto sizeRecordOnly --no-fail passed? Enforced?
    main true (irrelevant — short-circuit) true yes no
    PR without [lto] false false true yes no
    PR with [lto] false true (truthy string) false no yes

    So enforcement happens only on [lto] PRs. Main is record-only — it never enforces the threshold.

    The code is intentional; the comment is wrong

    Three independent sources confirm main is meant to be record-only:

    1. Pre-PR behavior: the old line was recordOnly: isMainBranch() — main was already record-only before this PR touched it.
    2. getBinarySizeStep()'s own docstring (unchanged in this PR): "Runs on PR builds (comparison) and main (record-only, to produce the baseline artifact)."
    3. The PR description's verification table: the main row shows binary-size --no-fail count = 1, i.e. main gets --no-fail.

    So the logic is deliberate and correct; only the comment's phrase "enforce only on main or [lto] PRs" contradicts it.

    Impact

    No runtime effect — this is purely a documentation inaccuracy. But per REVIEW.md, comments must "carry only durable non-obvious content" and be accurate. A future reader of this comment would reasonably conclude that main hard-fails on binary-size regressions, when in fact main only records the baseline artifact and never fails on size. That could lead to confusion when debugging why a size regression on main didn't block the canary, or when someone later revisits this gating.

    Suggested fix

    Reword the comment to match the code, e.g.:

    // Binary-size tracking. Non-LTO PR binaries are larger than the LTO main
    // canary they compare against, so enforce only on [lto] PRs; main records
    // the baseline; non-LTO PRs annotate only.

@robobun

robobun commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Good catch on the binary-size comment wording: sizeRecordOnly = isMainBranch() || !options.forceLto means only [lto] PRs enforce; main records the baseline (unchanged from before this PR). Reworded the comment to match. Holding that one-line push until the in-flight v40 image bake (build 76092) reports back so it doesn't get cancelled; will squash it into the next push.

@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from d4d4c5e to 00ea037 Compare July 20, 2026 07:17

@claude claude Bot left a comment

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.

Additional findings (outside current diff — PR may have been updated during review):

  • 🔴 .buildkite/scripts/upload-release.sh:340-349alias_baseline_artifact() copies the zip verbatim (cp "$artifact" "$alias"), so the inner directory is still bun-linux-x64-baseline/. On AVX2-capable machines install.sh computes target=linux-x64 (no -baseline suffix), downloads the aliased bun-linux-x64.zip, and then mv "$bin_dir/bun-linux-x64/bun" ... fails with "Failed to move extracted bun to destination" — same for install.ps1 (line 191/200 → "Download is corrupt or intercepted Antivirus?") and bun upgrade from every already-installed haswell x64 build (EXE_SUBPATH uses Version::FOLDER_NAME with Environment::BASELINE = false). The alias needs to repack: unzip → rename the inner directory to the aliased stem → re-zip.

    Extended reasoning...

    What the bug is

    upload-release.sh now aliases each *-baseline*.zip to its historical non-baseline name so "existing download URLs keep resolving" (per the code comment and the PR description). But the alias is created with a bare run_command cp "$artifact" "$alias", which renames only the zip file — the top-level directory inside the zip is unchanged.

    The zip layout is ${bunTriplet}.zip → ${bunTriplet}/bun[.exe] where bunTriplet = bun-${os}-${arch}[-musl][-baseline] (scripts/build/ci.ts:330–339, computeBunTriplet() / makeZip()). So bun-linux-x64-baseline.zip contains bun-linux-x64-baseline/bun, and after the cp the aliased bun-linux-x64.zip also contains bun-linux-x64-baseline/bun.

    Every consumer of these zips relies on the invariant that the inner directory name matches the zip filename stem — that convention is exactly what the alias exists to preserve.

    Code paths that break

    src/runtime/cli/install.sh (the curl -fsSL https://bun.sh/install | bash path):

    • Line 83: Linux x86_64target=linux-x64
    • Line 117: appends -baseline only when grep avx2 /proc/cpuinfo is empty — on the vast majority of modern x64 machines AVX2 is present, so target stays linux-x64 (or linux-x64-musl on Alpine)
    • Line 132: downloads .../bun-linux-x64.zip → gets the alias
    • Line 152: unzip -oqd "$bin_dir" "$exe.zip" → creates $bin_dir/bun-linux-x64-baseline/
    • Line 155: mv "$bin_dir/bun-linux-x64/bun" "$exe"No such file or directoryerror 'Failed to move extracted bun to destination'

    src/runtime/cli/install.ps1:

    • Line 143: $Target = "bun-windows-$BunArch" (no -baseline unless $IsBaseline, which is CPU-feature-gated)
    • Line 148: downloads .../bun-windows-x64.zip → gets the alias
    • Line 191: Test-Path "${BunBin}\bun-windows-x64\bun.exe"false (actual path is ${BunBin}\bun-windows-x64-baseline\bun.exe)
    • Line 192: throws "Download is corrupt or intercepted Antivirus?"

    src/runtime/cli/upgrade_command.rs (bun upgrade):

    • EXE_SUBPATH = Version::FOLDER_NAME + "/bun" (line 503–508), where FOLDER_NAME includes -baseline only if Environment::BASELINE is true (line 116–123)
    • Every x64 binary already installed on user machines was built as haswell (non-baseline), so Environment::BASELINE = false in those binaries → they download bun-linux-x64.zip / bun-windows-x64.zip (the alias) and look for bun-linux-x64/bun inside → extraction fails

    Why nothing prevents it

    The PR description says "the install scripts keep resolving." The URLs resolve — GitHub/S3 serve the aliased asset — but resolving the URL is only half the contract. The install scripts, install.ps1, and bun upgrade all hard-code the inner-directory-matches-zip-stem convention, and cp doesn't rewrite zip contents.

    Impact

    On the first canary shipped from main after this merges:

    • curl -fsSL https://bun.sh/install | bash fails for essentially every Linux x64 glibc and musl user with a post-2013 CPU.
    • irm bun.sh/install.ps1 | iex fails for essentially every Windows x64 user with an AVX2-capable CPU.
    • bun upgrade fails for every existing x64 install.

    Users on non-AVX2 CPUs (who get target=…-baseline and download the real -baseline.zip) are unaffected, as are aarch64 users.

    Step-by-step proof

    1. CI builds bun-linux-x64-baseline.zip containing bun-linux-x64-baseline/bun (ci.ts makeZip(cfg, bunTriplet, [...]) stages files under resolve(buildDir, bunTriplet)).
    2. upload-release.sh runs alias_baseline_artifact "bun-linux-x64-baseline.zip" → echoes bun-linux-x64.zip.
    3. run_command cp "bun-linux-x64-baseline.zip" "bun-linux-x64.zip" — byte-identical copy, inner dir still bun-linux-x64-baseline/.
    4. Both are uploaded to S3 + the GitHub release.
    5. A user on an AVX2-capable Ubuntu box runs curl -fsSL https://bun.sh/install | bash.
    6. target=linux-x64 (line 83); /proc/cpuinfo contains avx2 so line 117 does not append -baseline.
    7. bun_uri=.../releases/latest/download/bun-linux-x64.zip — downloads the alias.
    8. unzip -oqd "$HOME/.bun/bin" bun.zip → creates $HOME/.bun/bin/bun-linux-x64-baseline/bun.
    9. mv "$HOME/.bun/bin/bun-linux-x64/bun" "$HOME/.bun/bin/bun"mv: cannot stat …: No such file or directory.
    10. Script exits with error: Failed to move extracted bun to destination.

    Fix

    Repack the alias so the inner directory matches the aliased stem, e.g.:

    local alias="$(alias_baseline_artifact "$artifact")"
    if [ -n "$alias" ]; then
      local src_dir="${artifact%.zip}"
      local dst_dir="${alias%.zip}"
      rm -rf "$src_dir" "$dst_dir"
      run_command unzip -q "$artifact"
      run_command mv "$src_dir" "$dst_dir"
      run_command zip -qr "$alias" "$dst_dir"
      rm -rf "$dst_dir"
      upload_one "$alias"
    fi

    (unzip/zip are already available on the release agent — install_aws_linux installs unzip, and Amazon Linux 2023 ships zip; add a command -v zip || package_manager_install zip guard if desired.)

Comment thread scripts/build/config.ts Outdated
Comment thread scripts/build/flags.ts
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from 00ea037 to a903927 Compare July 20, 2026 07:24
Comment thread .buildkite/scripts/upload-release.sh
Comment thread scripts/build/deps/webkit.ts Outdated
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from a903927 to 0b859d1 Compare July 20, 2026 07:52

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/cli/install.ps1 (1)

133-135: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add one hermetic regression suite for baseline-free artifact resolution. The installer and upgrader now share unsuffixed x64 artifact naming, but the supplied tests only exercise build configuration.

  • src/runtime/cli/install.ps1#L133-L135: test plain Windows x64 archive selection for current and legacy requested versions.
  • src/runtime/cli/install.sh#L108-L110: test Unix x64 resolution through fixture-backed redirect behavior, without public-network access.
  • src/runtime/cli/upgrade_command.rs#L116-L124: test normal and profile ZIP/folder names selected by upgrade metadata parsing.

As per coding guidelines, every behavioral change must include an automated regression test in the same change.

🤖 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 `@src/runtime/cli/install.ps1` around lines 133 - 135, Add a hermetic
regression suite covering baseline-free artifact resolution: in
src/runtime/cli/install.ps1 lines 133-135, test plain Windows x64 archive
selection for both current and legacy requested versions; in
src/runtime/cli/install.sh lines 108-110, test Unix x64 resolution using
fixture-backed redirects without network access; and in
src/runtime/cli/upgrade_command.rs lines 116-124, test normal and profile
ZIP/folder names produced by upgrade metadata parsing. Use the existing test
conventions and keep all cases automated within this change.

Source: Coding guidelines

🤖 Prompt for all review comments with 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.

Inline comments:
In @.buildkite/ci.mjs:
- Around line 158-162: Condense the Windows LTO rationale comment in
.buildkite/ci.mjs lines 158-162 to three lines or fewer while retaining the key
constraint and reference to ltoDefault. Also condense the baseline-alias
compatibility rationale comment in .buildkite/scripts/upload-release.sh lines
317-320 to three lines or fewer, preserving its essential meaning.

In @.buildkite/scripts/upload-release.sh:
- Around line 340-346: Update the archive repacking flow around src_zip and
dst_zip to derive absolute workspace paths for all rm, unzip, mv, and zip file
operations, while passing relative paths to zip as needed to preserve archive
entry names. Ensure cleanup and repacking remain anchored to the intended
workspace regardless of the caller’s current directory.

In `@scripts/bootstrap.sh`:
- Around line 1408-1474: Require checksum verification in
install_linux_glibc_sysroot before any tar extraction by populating
linux_sysroot_sha256_x86_64 and linux_sysroot_sha256_aarch64 with the matching
published digests and removing the empty-checksum fallback. Ensure each
architecture uses download_and_verify_file and aborts rather than extracting
when its checksum is missing or invalid.

In `@scripts/build/config.ts`:
- Around line 564-583: Update detectLinuxGlibcSysroot so the aarch64 candidates
cannot include the generic /opt/linux-sysroot-glibc path used for x86_64; retain
only the arm64-specific path for aarch64 and preserve the existing x86_64
candidate behavior. Keep the environment override and undefined result
unchanged.

In `@src/js_parser/parse/parse_typescript.rs`:
- Around line 637-638: Update the comment in the TypeScript parsing code to
remove the temporary reference to duplicated slice8 implementations and retain
only the durable invariant that to_utf8_e_string guarantees is_utf16 == false,
explaining why accessing .data directly is safe.

In `@src/js_printer/lib.rs`:
- Around line 7926-7932: Compress the comment immediately above module_scope
into no more than three lines, preserving both points: Scope is not Copy, and
compute_reserved_names_for_scope only traverses members/generated/children, so
referencing the in-place tree.module_scope preserves the required lifetime.

In `@test/internal/macos-cross-config.test.ts`:
- Line 76: Remove the redundant “explicit override still wins” comment from the
regression test, leaving the adjacent assertion unchanged; retain only an issue
URL comment if one is required by the project guidelines.

---

Outside diff comments:
In `@src/runtime/cli/install.ps1`:
- Around line 133-135: Add a hermetic regression suite covering baseline-free
artifact resolution: in src/runtime/cli/install.ps1 lines 133-135, test plain
Windows x64 archive selection for both current and legacy requested versions; in
src/runtime/cli/install.sh lines 108-110, test Unix x64 resolution using
fixture-backed redirects without network access; and in
src/runtime/cli/upgrade_command.rs lines 116-124, test normal and profile
ZIP/folder names produced by upgrade metadata parsing. Use the existing test
conventions and keep all cases automated within this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2ec5289a-54b3-44cf-a26a-9793a1f92fd9

📥 Commits

Reviewing files that changed from the base of the PR and between 86fc072 and 0b859d1.

📒 Files selected for processing (48)
  • .buildkite/ci.mjs
  • .buildkite/scripts/upload-release.sh
  • .github/workflows/clippy.yml
  • .github/workflows/format.yml
  • .github/workflows/miri.yml
  • rust-toolchain.toml
  • scripts/bootstrap.sh
  • scripts/build/buildOptionsRs.ts
  • scripts/build/ci.ts
  • scripts/build/config.ts
  • scripts/build/deps/webkit.ts
  • scripts/build/flags.ts
  • scripts/build/rust.ts
  • scripts/build/source.ts
  • scripts/build/tools.ts
  • src/ast/char_freq.rs
  • src/boringssl/lib.rs
  • src/bun_alloc/lib.rs
  • src/bun_bin/lib.rs
  • src/bun_core/Global.rs
  • src/bun_core/build.rs
  • src/bun_core/env.rs
  • src/bun_core/lib.rs
  • src/collections/bit_set.rs
  • src/collections/multi_array_list.rs
  • src/crash_handler/lib.rs
  • src/css/values/color.rs
  • src/http_jsc/websocket_client/WebSocketUpgradeClient.rs
  • src/install/PackageManager/CommandLineArguments.rs
  • src/install/resolvers/folder_resolver.rs
  • src/js_parser/parse/parse_typescript.rs
  • src/js_printer/lib.rs
  • src/jsc/bindings/ANSIHelpers.h
  • src/jsc/bindings/c-bindings.cpp
  • src/jsc/bindings/xxhash3.cpp
  • src/options_types/compile_target.rs
  • src/react_compiler/ssa/enter_ssa.rs
  • src/router/lib.rs
  • src/runtime/cli/install.ps1
  • src/runtime/cli/install.sh
  • src/runtime/cli/upgrade_command.rs
  • src/runtime/image/codecs.rs
  • src/runtime/node/path.rs
  • src/runtime/socket/udp_socket.rs
  • src/runtime/valkey_jsc/valkey.rs
  • src/runtime/webcore/encoding.rs
  • src/sql_jsc/mysql/MySQLValue.rs
  • test/internal/macos-cross-config.test.ts
💤 Files with no reviewable changes (2)
  • scripts/build/buildOptionsRs.ts
  • src/jsc/bindings/c-bindings.cpp

Comment thread .buildkite/ci.mjs Outdated
Comment thread .buildkite/scripts/upload-release.sh Outdated
Comment thread scripts/bootstrap.sh Outdated
Comment thread scripts/build/config.ts
Comment thread src/js_parser/parse/parse_typescript.rs
Comment thread src/js_printer/lib.rs
Comment thread test/internal/macos-cross-config.test.ts Outdated
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from 0b859d1 to 40bcd4d Compare July 20, 2026 08:08
Comment thread src/options_types/compile_target.rs
Comment thread .buildkite/scripts/upload-release.sh Outdated
Comment thread src/bun_core/env.rs Outdated
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch 2 times, most recently from 9045f7b to 2039218 Compare July 20, 2026 08:23
Comment thread scripts/build/config.ts Outdated
Comment thread src/runtime/cli/install.sh Outdated
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from 2039218 to ec0ca0f Compare July 20, 2026 08:53

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🤖 Prompt for all review comments with 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.

Inline comments:
In `@scripts/bootstrap.sh`:
- Around line 1604-1640: Harden install_macos_sdk by eliminating execution of an
implicitly mutable xmac.mjs ref: default BUN_BOOTSTRAP_REPO_REF to the pinned
commit or tag associated with this bootstrap.sh, and add checksum verification
for the downloaded xmac_mjs before invoking it via bun. Reuse the existing
trusted-reference or sha256 validation patterns in the script, and abort before
execution when validation fails.

In `@src/jsc/bindings/ANSIHelpers.h`:
- Around line 50-53: Condense the comments at ANSIHelpers.h lines 50-53 and
77-82, and xxhash3.cpp lines 4-8, to no more than three lines each while
preserving their explanations of Highway runtime dispatch, the long-scan fast
path, and file-level dispatch rationale.

In `@src/runtime/cli/install.sh`:
- Around line 108-112: Restore the Darwin x64 branch in the target-selection
case handling so pinned Haswell releases on non-AVX2 macOS hosts probe AVX2 and
fall back to the baseline artifact. Keep the current behavior for newer releases
unchanged, and condense the associated comment to no more than three lines so it
accurately describes the preserved fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1a52bebf-9a65-4677-967c-66b842860c72

📥 Commits

Reviewing files that changed from the base of the PR and between 0b859d1 and ec0ca0f.

📒 Files selected for processing (50)
  • .buildkite/ci.mjs
  • .buildkite/scripts/upload-release.sh
  • .github/workflows/clippy.yml
  • .github/workflows/format.yml
  • .github/workflows/miri.yml
  • packages/bun-release/src/platform.ts
  • rust-toolchain.toml
  • scripts/bootstrap.sh
  • scripts/build/buildOptionsRs.ts
  • scripts/build/ci.ts
  • scripts/build/config.ts
  • scripts/build/deps/webkit.ts
  • scripts/build/flags.ts
  • scripts/build/rust.ts
  • scripts/build/source.ts
  • scripts/build/tools.ts
  • src/ast/char_freq.rs
  • src/boringssl/lib.rs
  • src/bun_alloc/lib.rs
  • src/bun_bin/lib.rs
  • src/bun_core/Global.rs
  • src/bun_core/build.rs
  • src/bun_core/env.rs
  • src/bun_core/lib.rs
  • src/bun_core/string/immutable/unicode.rs
  • src/collections/bit_set.rs
  • src/collections/multi_array_list.rs
  • src/crash_handler/lib.rs
  • src/css/values/color.rs
  • src/http_jsc/websocket_client/WebSocketUpgradeClient.rs
  • src/install/PackageManager/CommandLineArguments.rs
  • src/install/resolvers/folder_resolver.rs
  • src/js_parser/parse/parse_typescript.rs
  • src/js_printer/lib.rs
  • src/jsc/bindings/ANSIHelpers.h
  • src/jsc/bindings/c-bindings.cpp
  • src/jsc/bindings/xxhash3.cpp
  • src/options_types/compile_target.rs
  • src/react_compiler/ssa/enter_ssa.rs
  • src/router/lib.rs
  • src/runtime/cli/install.ps1
  • src/runtime/cli/install.sh
  • src/runtime/cli/upgrade_command.rs
  • src/runtime/image/codecs.rs
  • src/runtime/node/path.rs
  • src/runtime/socket/udp_socket.rs
  • src/runtime/valkey_jsc/valkey.rs
  • src/runtime/webcore/encoding.rs
  • src/sql_jsc/mysql/MySQLValue.rs
  • test/internal/source-lints/windows-cross-config.test.ts
💤 Files with no reviewable changes (3)
  • scripts/build/buildOptionsRs.ts
  • src/jsc/bindings/c-bindings.cpp
  • src/bun_core/env.rs

Comment thread scripts/bootstrap.sh
Comment thread src/jsc/bindings/ANSIHelpers.h
Comment thread src/runtime/cli/install.sh Outdated
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from ec0ca0f to 890c0cd Compare July 20, 2026 09:07
Comment thread scripts/build/deps/webkit.ts Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with 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.

Inline comments:
In @.buildkite/ci.mjs:
- Around line 1592-1613: Remove the unused forceTests destructuring and simplify
the test-step condition in the relevant pipeline setup to rely only on
skipTests, since getPipelineOptions does not populate forceTests. Preserve the
existing behavior of running tests when tests are not skipped and omitting them
when skipTests is enabled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2fbfdef8-8008-438e-bfaf-99bb9668f498

📥 Commits

Reviewing files that changed from the base of the PR and between ec0ca0f and 890c0cd.

📒 Files selected for processing (51)
  • .buildkite/ci.mjs
  • .buildkite/scripts/upload-release.sh
  • .github/workflows/clippy.yml
  • .github/workflows/format.yml
  • .github/workflows/miri.yml
  • packages/bun-release/src/platform.ts
  • rust-toolchain.toml
  • scripts/bootstrap.sh
  • scripts/build/buildOptionsRs.ts
  • scripts/build/ci.ts
  • scripts/build/config.ts
  • scripts/build/deps/webkit.ts
  • scripts/build/flags.ts
  • scripts/build/rust.ts
  • scripts/build/source.ts
  • scripts/build/tools.ts
  • src/ast/char_freq.rs
  • src/boringssl/lib.rs
  • src/bun_alloc/lib.rs
  • src/bun_bin/lib.rs
  • src/bun_core/Global.rs
  • src/bun_core/build.rs
  • src/bun_core/env.rs
  • src/bun_core/lib.rs
  • src/bun_core/string/immutable/unicode.rs
  • src/collections/bit_set.rs
  • src/collections/multi_array_list.rs
  • src/crash_handler/lib.rs
  • src/css/values/color.rs
  • src/http_jsc/websocket_client/WebSocketUpgradeClient.rs
  • src/install/PackageManager/CommandLineArguments.rs
  • src/install/resolvers/folder_resolver.rs
  • src/js_parser/parse/parse_typescript.rs
  • src/js_printer/lib.rs
  • src/jsc/bindings/ANSIHelpers.h
  • src/jsc/bindings/c-bindings.cpp
  • src/jsc/bindings/xxhash3.cpp
  • src/options_types/compile_target.rs
  • src/react_compiler/ssa/enter_ssa.rs
  • src/router/lib.rs
  • src/runtime/cli/install.ps1
  • src/runtime/cli/install.sh
  • src/runtime/cli/upgrade_command.rs
  • src/runtime/image/codecs.rs
  • src/runtime/node/path.rs
  • src/runtime/socket/udp_socket.rs
  • src/runtime/valkey_jsc/valkey.rs
  • src/runtime/webcore/encoding.rs
  • src/sql_jsc/mysql/MySQLValue.rs
  • test/internal/source-lints/webkit-prebuilt-url.test.ts
  • test/internal/source-lints/windows-cross-config.test.ts
💤 Files with no reviewable changes (3)
  • src/jsc/bindings/c-bindings.cpp
  • src/bun_core/env.rs
  • scripts/build/buildOptionsRs.ts

Comment thread .buildkite/ci.mjs
Comment thread scripts/build/deps/webkit.ts Outdated
Comment thread scripts/build/flags.ts
Comment thread scripts/bootstrap.sh
@robobun
robobun force-pushed the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch from 890c0cd to bcae883 Compare July 20, 2026 10:02

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🤖 Prompt for all review comments with 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.

Inline comments:
In @.buildkite/ci.mjs:
- Around line 1592-1621: Add automated pipeline-generation regression tests
covering the relevantTestPlatforms filtering for main without ASAN, release test
dependency wiring through testStepKeys, and getBinarySizeStep behavior for main
versus non-LTO and LTO builds. Keep the assertions focused on generated steps
and dependencies, and place the coverage alongside the existing
.buildkite/ci.mjs pipeline-generation tests.
- Line 1643: Filter ASAN platforms out of the buildPlatforms list before passing
it to getReleaseStep in the steps.push release-step construction, while
preserving all other platform dependencies and existing release options.

In `@packages/bun-release/src/platform.ts`:
- Around line 133-136: Add automated postinstall platform-selection coverage for
supportedPlatforms, using the x64 Linux-musl scenario to assert that the plain
package is selected, the compatibility alias is excluded, and the Linux-musl
match remains included. Place the regression test alongside the existing
platform-selection tests and preserve the current filtering behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: edb90c0a-02db-498d-9986-c2a654e275ac

📥 Commits

Reviewing files that changed from the base of the PR and between 890c0cd and bcae883.

📒 Files selected for processing (52)
  • .buildkite/ci.mjs
  • .buildkite/scripts/upload-release.sh
  • .github/workflows/clippy.yml
  • .github/workflows/format.yml
  • .github/workflows/miri.yml
  • packages/bun-release/src/platform.ts
  • rust-toolchain.toml
  • scripts/bootstrap.sh
  • scripts/build/buildOptionsRs.ts
  • scripts/build/ci.ts
  • scripts/build/config.ts
  • scripts/build/deps/brotli.ts
  • scripts/build/deps/webkit.ts
  • scripts/build/flags.ts
  • scripts/build/rust.ts
  • scripts/build/source.ts
  • scripts/build/tools.ts
  • src/ast/char_freq.rs
  • src/boringssl/lib.rs
  • src/bun_alloc/lib.rs
  • src/bun_bin/lib.rs
  • src/bun_core/Global.rs
  • src/bun_core/build.rs
  • src/bun_core/env.rs
  • src/bun_core/lib.rs
  • src/bun_core/string/immutable/unicode.rs
  • src/collections/bit_set.rs
  • src/collections/multi_array_list.rs
  • src/crash_handler/lib.rs
  • src/css/values/color.rs
  • src/http_jsc/websocket_client/WebSocketUpgradeClient.rs
  • src/install/PackageManager/CommandLineArguments.rs
  • src/install/resolvers/folder_resolver.rs
  • src/js_parser/parse/parse_typescript.rs
  • src/js_printer/lib.rs
  • src/jsc/bindings/ANSIHelpers.h
  • src/jsc/bindings/c-bindings.cpp
  • src/jsc/bindings/xxhash3.cpp
  • src/options_types/compile_target.rs
  • src/react_compiler/ssa/enter_ssa.rs
  • src/router/lib.rs
  • src/runtime/cli/install.ps1
  • src/runtime/cli/install.sh
  • src/runtime/cli/upgrade_command.rs
  • src/runtime/image/codecs.rs
  • src/runtime/node/path.rs
  • src/runtime/socket/udp_socket.rs
  • src/runtime/valkey_jsc/valkey.rs
  • src/runtime/webcore/encoding.rs
  • src/sql_jsc/mysql/MySQLValue.rs
  • test/internal/source-lints/webkit-prebuilt-url.test.ts
  • test/internal/source-lints/windows-cross-config.test.ts
💤 Files with no reviewable changes (4)
  • scripts/build/deps/brotli.ts
  • scripts/build/buildOptionsRs.ts
  • src/jsc/bindings/c-bindings.cpp
  • src/bun_core/env.rs

Comment thread .buildkite/ci.mjs Outdated
Comment thread .buildkite/ci.mjs
Comment thread packages/bun-release/src/platform.ts
Comment thread scripts/build/rust.ts Outdated
robobun and others added 3 commits July 21, 2026 04:30
The Rust side is #[host_fn(export = ...)] which emits extern "sysv64" on
win-x64, but NodeTLS.cpp declared them as plain extern "C" (Win64 ABI).
Without LTO this worked by accident: the sysv_abi wrapper getDefaultCiphers
receives args in RDI/RSI, clang moves them to RCX/RDX for the (misdeclared
as Win64) call, and Rust reads RDI/RSI which still hold the original values.
ThinLTO reorders the registers and the coincidence breaks, so every ESM
import of node:tls (which enumerates exports and hits the DEFAULT_CIPHERS
getter) crashes with STATUS_BREAKPOINT.

Scan of the other 31 #[host_fn(export = ...)] symbols found no other
plain-extern-"C" declarations.

Also:
- allowlist-x64-windows: widen rsaz_1024_gather5_avx2 ceiling to cover the
  PDB range bleed into _aesni_ctr32_ghash_6x (both OPENSSL_ia32cap_P-gated)
- rust.ts: collapse the stale ELF-full-LTO bullet above
  CARGO_PROFILE_RELEASE_LTO into the ThinLTO one-liner
CI runs tests under bun-profile, which under ThinLTO carries ~2x the DWARF
of the full-LTO build (333 MB vs 175 MB zip; ~700 MB uncompressed on
linux-x64). bun build --compile copies, reads, and rewrites the whole
executable to /tmp, which on debian/ubuntu is disk-backed gp3 (tmp.mount
is masked), so the I/O alone exceeds the old 10s budget. alpine passes
because /tmp is tmpfs there.

[skip size check]
rust-lld is built without LLVM_ENABLE_ZLIB, so the crosslang-LTO swap to
rust-lld (rustc's LLVM 22 is ahead of the CI clang 21 after the nightly
bump) drops -Wl,--compress-debug-sections=zlib and bun-profile came out
895 MB instead of the ~380 MB it is with compressed DWARF.

The test lanes run against bun-profile, and `bun build --compile` copies
the running executable, so every --compile in the suite was writing
~900 MB. That is what took type-export.test.ts (dozens of compiles) past
the file timeout and pushed napi.test.ts's --compile case (10 s cap, its
compile step alone hit 5.5 s) into `code 1` on all four linux lanes.

Compress after the link with llvm-objcopy instead — the same
`&& tool $out` postlink pattern the darwin-cross fixup already uses, and
the same llvm-objcopy the musl CRT shim already relies on. Measured on the
build-76603 artifact: 895 MB -> 383 MB, --compile 2.4 s -> 1.1 s locally.

No-Verification-Needed: build-script-only change; the LTO+rust-lld link path is not reachable from a local debug drive
Comment thread scripts/build/tools.ts
Comment thread scripts/build/profiles.ts
Comment thread packages/bun-release/src/platform.ts
Jarred-Sumner and others added 5 commits July 21, 2026 05:44
- Pass --lto-whole-program-visibility to lld on ELF (was Darwin-only via
  -mllvm). The WebKit -lto prebuilts carry !vcall_visibility metadata, so
  this lets whole-program devirtualization fire on JSC/WTF's exported
  classes, not just our -fvisibility=hidden ones.
- -Zshare-generics=y for the Rust build: reuse an upstream crate's
  monomorphization instead of re-instantiating it per crate. Cross-language
  ThinLTO still imports+inlines small callees, so this only dedups the
  large bodies (parse_selector x3, printer/lexer x2, ...).
… alias filter; doc

- leaksan.supp: HTTPClient::clone_metadata (daemon http thread holds the last
  response at exit) and napi_internal_enqueue_finalizer (global_exit GC sweep
  enqueues finalizer tasks the already-exiting loop never drains). Both
  pre-existing; -Zshare-generics=y routes Box/Vec allocs through the shared
  alloc-crate instantiation so the old stack no longer matches whatever
  suppression covered them.
- upload-npm.ts: drop alias platforms from the root package optionalDependencies
  so npm on x64 does not download both @oven/bun-*-x64 and the byte-identical
  -baseline alias. Aliases are still published for back-compat.
- scripts/build/CLAUDE.md: add rust-and-link to the split-CI-modes enumeration.

[skip size check]
…entry [skip size check]

Routing Box/Vec allocs through the shared alloc-crate instantiation moves
their frames enough that LSAN loses some at-exit allocations it previously
found reachable (bun-info, bun-audit, issue 30205). ASAN builds do not
benefit from the binary-size savings anyway, so keep share-generics for
release and skip it under ASAN rather than suppressing clone_metadata
(which would also hide a real header-cloning leak if one ever lands).

The napi_internal_enqueue_finalizer suppression stays: it is
unambiguously at-exit (global_exit GC sweep enqueues finalizer tasks into
a loop that is already exiting) and already matched.
-fno-threadsafe-statics (unix) / /Zc:threadSafeInit- /Zc:tlsGuards-
(windows) for C++ TUs. WTF's convention is that multi-thread-reachable
statics are main-thread-first-touched or std::once-guarded, so the
compiler-emitted guard around every function-local static is dead weight.
Comment thread scripts/build/flags.ts Outdated
robobun added 2 commits July 21, 2026 08:27
… check]

This reverts commit 0b3359f.

globalFlags reaches bun's own bindings and every vendored dep, not just
WebKit (which is prebuilt and already has this flag upstream). At least
src/jsc/bindings/image_coregraphics_shim.cpp:146 explicitly relies on
C++11 magic-static guards for WorkPool thread safety, and
test-http2-reset-flood.js started hitting an ExceptionScope::assertNoException()
SIGABRT on asan with this flag. Binary sizes were byte-identical between
76721 and 76728, so the guard removal was zero measured benefit anyway.
…kip size check]

RSS reports ~280 MB on alpine-aarch64 under ThinLTO + -Zshare-generics for
the same 400-iteration require+delete workload that measures under 64 MB on
every other lane. Intermittent (passed on retry in build 76728, red on
76690/76721/76756). Already todo'd on macOS CI for the same RSS-accounting
sensitivity; extending the condition rather than masking a potential
regression with a threshold bump. Worth a closer look once the rest of the
pipeline is green.
Comment on lines +60 to 66
* release workflow naming in oven-sh/WebKit's CI. There is no -baseline
* variant: every x64 WebKit is built at the nehalem floor.
*/
function prebuiltSuffix(cfg: Config): string {
let s = "";
if (cfg.linux && cfg.abi === "musl") s += "-musl";
if (cfg.linux && cfg.abi === "android") s += "-android";

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.

🟡 nit: This PR removed the -baseline branch from prebuiltSuffix() here and moved every build lane onto one arm64 host, but the sibling consumer scripts/prefetch-deps.ts (invoked by prefetch_build_deps() in bootstrap.sh at image-bake time) wasn't updated for either change: (1) prefetch-deps.ts:21/:45 still list baseline as a URL-affecting dimension and :56 still iterates for (const baseline of baseCfg.x64 ? [false, true] : [false]) — with the -baseline suffix gone the two iterations produce identical URLs (harmlessly deduped at :80). Same class as the baseline sweep this PR already did in brotli.ts/rust.ts/buildOptionsRs.ts/env.rs; per REVIEW.md "Delete dead code in the same PR that makes it dead", drop the loop and reword the comments. (2) prefetch-deps.ts:50's base has no os/arch (host-detected) and only varies asan/lto/baseline/abi, so the single debian-13 arm64 build host's baked cache only holds bun-webkit-linux-arm64* — the linux-x64 gnu/musl/asan lanes (which pre-PR had their own x64 images with matching prefetch) now cache-miss and re-download WebKit in every build-cpp job. Not a correctness bug (download fallback works; the file's own doc says partial hits are tolerated), but same class as the is_ci_build_host() gate this PR added at bootstrap.sh:1155-1163 for sysroots — either enumerate os/arch on the build host, or gate prefetch_build_deps() on is_ci_build_host() too (test images never build, so their WebKit prefetch is dead weight in the baked image).

Extended reasoning...

What's stale / regressed

scripts/prefetch-deps.ts runs at CI-image bake time (via prefetch_build_deps() in scripts/bootstrap.sh:2139, called unconditionally at :2248 gated only on $ci=1) to warm a read-only download cache for WebKit and other dep tarballs. Two of this PR's changes leave it out of sync:

(1) Dead baseline dimension. This PR removed if (cfg.baseline && cfg.x64) s += "-baseline" from prebuiltSuffix() in scripts/build/deps/webkit.ts ("There is no -baseline variant: every x64 WebKit is built at the nehalem floor"). But scripts/prefetch-deps.ts still treats baseline as URL-affecting:

  • :20-21 — comment: "Enumerates variants on the dimensions that affect download URLs (asan, lto, baseline, musl)"
  • :45 — comment: "WebKit prebuilt URL varies by (musl, baseline, debug|lto, asan)"
  • :56 — for (const baseline of baseCfg.x64 ? [false, true] : [false])

With the -baseline suffix gone, cfg.baseline no longer affects any URL prebuiltSuffix() produces, so on an x64 host the two loop iterations produce identical URLs — harmlessly deduped by items.has(item.url) at :80.

(2) Prefetch coverage after the single-host move. prefetch-deps.ts:50 has const base: PartialConfig = { buildType: "Release", ci: true, webkit: "prebuilt" } with no os/arch, so resolveConfig detects the host. The variants loop at :54-58 varies only asan/lto/baseline/abi — never os or arch. Pre-PR each build platform had its own image (amazonlinux-x64, amazonlinux-aarch64, alpine-x64, alpine-aarch64) and each prefetched its own target's WebKit. Post-PR one debian-13 arm64 image (buildHostPlatform in ci.mjs) serves all 13 build lanes, but its baked prefetch only contains bun-webkit-linux-arm64{,-musl}{,-lto,-debug,-asan}.tar.gz.

Step-by-step proof

Take the linux-x64 build lane on a [build images] run of this PR:

  1. Image bake. bootstrap.sh runs on the debian-13 aarch64 build-host VM. At :2248 it calls prefetch_build_deps() (gated only on $ci=1 at :2139-2145, not on is_ci_build_host()).
  2. prefetch-deps.ts resolves the host. base at :50 has no os/arch, so resolveConfig(base, toolchain) returns {os: "linux", arch: "aarch64", abi: "gnu", ...}. baseCfg.x64 is false, so the :56 loop iterates [false] only (part (1) is arm64-moot on the actual build host, but the dead code and stale comments remain).
  3. Variants enumerate arm64 only. The loop varies asan × lto × abi on the fixed host arch. prebuiltSuffix() produces suffixes like ``, -musl, `-lto`, `-musl-lto`, `-asan`, etc.; `prebuiltUrl()` (webkit.ts) prepends `bun-webkit-linux-arm64`. The baked `by-url/` cache holds only linux-arm64 tarballs.
  4. linux-x64 build-cpp runs. getBuildArgs() passes --os=linux --arch=x64 --abi=gnu; webkit.source(cfg) computes bun-webkit-linux-amd64-lto.tar.gz. downloadWithRetry computes prefetchPathForUrl(url, BUN_BUILD_PREFETCH_DIR) → key = sha256 of the amd64 URL → not in by-url/ → cache miss → downloads ~200 MB from GitHub.
  5. Pre-PR contrast. The same lane ran on an amazonlinux-x64 image whose bake-time prefetch-deps.ts had baseCfg.x64 === true and cached bun-webkit-linux-amd64* — cache hit.

The genuinely new misses are the linux-x64 gnu/musl/asan lanes (~3 lanes). darwin/windows/freebsd/android were already cache-misses pre-PR (also cross-compiled from a linux host that only prefetched linux WebKit), so "defeats prefetch for most lanes" describes the post-PR state accurately but the regression is narrower.

Why this is in-scope for this PR

Per REVIEW.md "Fix the whole class in the same PR (same-class sites are ONE concern, not scope creep)" and "Delete dead code in the same PR that makes it dead":

  • Part (1) is exactly the same class as the baseline sweep this PR already did in scripts/build/deps/brotli.ts (removed the !cfg.baseline -fno-lto block), scripts/build/rust.ts (removed the baseline ? "nehalem" : "haswell" ternary), buildOptionsRs.ts, env.rs, crash_handler/lib.rs, upgrade_command.rs — one more sibling of the same edit.
  • Part (2) is exactly the same class as the is_ci_build_host() gate this PR added at bootstrap.sh:1155-1163 for the same single-host reason ("Cross-compile sysroots + runtimes are only needed on the single build host … test images never cross-compile, so skip the ~3GB of NDK/SDK/sysroot downloads there"). prefetch_build_deps() at :2248 is the sibling that wasn't gated: test-only images (ubuntu, debian-x64, alpine, windows) still bake ~several hundred MB of WebKit tarballs they never read.

Why nit (not a regression that blocks)

Nothing breaks. Part (1) is dead code + stale comments, harmlessly deduped. Part (2) is CI-cost only: the file's own doc at :14-15 explicitly says "the baked cache just becomes a partial hit until the next image refresh", and downloadWithRetry in download.ts falls through cleanly on a cache miss. Every build lane still produces correct artifacts.

Fix

  • Part (1): delete the baseline loop at :56 (unwrap to a single iteration), drop baseline from the comments at :20-21 and :45.
  • Part (2): either (a) on the build host, enumerate arch (and optionally os) in the variants loop — e.g. for (const arch of ["x64", "aarch64"]) — so the arm64 image also caches bun-webkit-linux-amd64*; or (b) gate prefetch_build_deps() at bootstrap.sh:2248 on is_ci_build_host() (matching the sysroot gate at :1155-1163) so test images stop baking dead-weight WebKit; ideally both.

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.

Valid on both counts. Part (1) is a trivial dead-loop cleanup; part (2) is CI-cost (cache miss falls through to download, no correctness impact). Will batch into the next push if one is needed; not pushing for a non-blocking optimization while 2c1bffe is nearly green.

@Jarred-Sumner
Jarred-Sumner merged commit e550f2c into main Jul 21, 2026
52 of 54 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the claude/farm/6ea6ece4/ci-pr-lto-off-main-tests branch July 21, 2026 09:35
robobun added a commit that referenced this pull request Jul 21, 2026
- scripts/bootstrap.{sh,ps1}: Version 41->42 / 23->24 (main caught up to
  41/22 via #34782).
- scripts/bootstrap.sh: alpine_sysroot_version 3.23 -> 3.24.
- scripts/build/deps/webkit.ts: pin to autobuild-preview-pr-296-6144f510
  (oven-sh/WebKit#296 rebased onto main c9296e353e).
- src/jsc/bindings/highway_{json,sourcemap}.cpp: disable scalable
  HWY_SVE/HWY_SVE2. clang >= 22 stops marking them HWY_BROKEN in
  detect_targets.h, so foreach_target compiles for them, but BitsFromMask
  only exists for the fixed-size SVE_256/SVE2_128 variants.
@robobun

robobun commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Heads up: this pushed ASAN --test-worker startup over the 200ms budget the JEST_WORKER_ID assertion in test/cli/test/parallel.test.ts was relying on, so worker 0 now work-steals worker 1's file before it boots on the debian-13 x64-asan lane. Deflaked in #34992.

@robobun

robobun commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

The debian-13 x64-asan lane surfaced a latent 200ms guard-timer flake in socket.io-connection-state-recovery.test.ts (the work takes ~220ms there); fixed in #35132.

Jarred-Sumner pushed a commit that referenced this pull request Jul 22, 2026
…#35132)

Fixes
`test/js/third_party/socket.io/socket.io-connection-state-recovery.test.ts`
going red on the debian-13 x64-asan lane.

## Failure

```
error: timeout
      at <anonymous> (test/js/third_party/socket.io/socket.io-connection-state-recovery.test.ts:46:26)
✗ connection state recovery > should restore session and missed packets [219.96ms]
```

Seen on builds [77055](https://buildkite.com/bun/bun/builds/77055)
(219.84ms) and [77789](https://buildkite.com/bun/bun/builds/77789)
(219.96ms), both debian-13 x64-asan.

## Cause

Each test in this file carries a `setTimeout(() => fail(...), 200)`
guard that was added when the suite was ported from upstream socket.io
in fe74c94. The [upstream
test](https://github.com/socketio/socket.io/blob/main/packages/socket.io/test/connection-state-recovery.ts)
has no such timers.

These tests drive the Engine.IO long-polling transport by hand via
supertest: the first test performs 8 sequential HTTP round trips. On
release that is ~50ms; on release+ASAN it is ~220ms. #34782 moved the
x64-asan build lane from amazonlinux-2023 to debian-13 on Jul 21, and
the failures started immediately after, with the work landing just past
the 200ms guard every time.

The guard is not a behavioral assertion. It races real work against a
wall clock and turns a slow-but-correct run into a failure. The test
runner already applies its own per-test timeout (90s, tripled under ASAN
in CI).

## Fix

Drop the guard timers and align with the upstream structure: plain
`async` tests that await the protocol events, with `io.close()` in a
`finally` block so the server is always released. Every assertion is
preserved; the same code paths are exercised.

## Verification

```
# before, bun bd (debug+asan), 5 runs: 0 pass / 7 fail every time
# after, bun bd (debug+asan), 5 runs:
7 pass / 0 fail (×5)

# release bun, 3 runs:
7 pass / 0 fail (×3)
```

<!-- robobun:evidence:begin -->

---

**[stamp-90s]** gate passed · iteration 0 · 1 files touched

<details><summary>passes on PR (with fix)</summary>

```console
Test-only change.

Debug/ASAN (expected pass):
$ bun bd test 'test/js/third_party/socket.io/socket.io-connection-state-recovery.test.ts'
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test test/js/third_party/socket.io/socket.io-connection-state-recovery.test.ts
bun test v1.4.0 (70a1c05)

test/js/third_party/socket.io/socket.io-connection-state-recovery.test.ts:
(pass) connection state recovery > should restore session and missed packets [2559.28ms]
(pass) connection state recovery > should restore rooms and data attributes [1087.52ms]
(pass) connection state recovery > should not run middlewares upon recovery by default [1125.18ms]
(pass) connection state recovery > should run middlewares even upon recovery [1000.73ms]
(pass) connection state recovery > should fail to restore an unknown session [421.80ms]
(pass) connection state recovery > should be disabled by default [434.45ms]
(pass) connection state recovery > should not call adapter#persistSession or adapter#restoreSession if disabled [405.80ms]

 7 pass
 0 fail
 43 expect() calls
Ran 7 tests across 1 file. [14.50s]
Exit: 0
```

</details>

<details><summary>diff hotspot</summary>

```
.../socket.io-connection-state-recovery.test.ts    | 318 +++++++++------------
 1 file changed, 134 insertions(+), 184 deletions(-)
```

</details>

**gate history** · 1 passed · 0 rejected · iteration 0

<details><summary>evidence per changed file</summary>

```
file                                                      reads  edits  tests
…y/socket.io/socket.io-connection-state-recovery.test.ts      2      3      0
```

</details>

**self-review** · no surviving concerns

25 concerns were raised and did not survive verification.

**root cause** · written by the author bot

The test wrapped its async work in a hardcoded 200ms setTimeout guard
that was not a behavioral assertion, and on slow ASAN builds the real
protocol steps outran that window, so the guard fired and failed the
test even though the code under test behaved correctly. The fix removes
the wall-clock guards entirely and converts each test to a plain async
function that awaits the protocol steps directly, with server cleanup
moved into a try/finally block. Hangs are now bounded by the test
runner's per-test timeout rather than an arbitrary 200ms race, and all
original assertions are preserved.

<!-- robobun:evidence:end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants