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

Merged
RO
3mo ago
+1479-867

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