Bump the workflow image pin to the 2026.06.14 tag that bakes in
wasm-tools. And keep the wasm-tools download/caching steps for the macOS
and Windows jobs — but skip those steps on Linux. So, the Linux jobs use
the wasm-tools copy the image installs.
Follow-up to https://github.com/LadybirdBrowser/ladybird/pull/9972
Cache the wasm-tools binary rather than doing (trying) a re-fetch of it
on every build job. And when we ever do need to re-download it (after a
cache miss), then do it with some sensible retry-with-backoff-and-wait.
Otherwise, a single network hiccup when trying to download wasm-tools
was failing multiple CI jobs at once — before their builds even started.
Otherwise, without this change, the Lint Code job intermittently fails —
for the same cause described in 61d0fca133: rustup tries to auto-install
1.96.0 the first time a proxied cargo runs — and depending on timing,
that can lead to a crate ending up seeing rustc, but not cargo.
Problem: CI intermittently fails when a required-file download hiccups;
e.g., the WASM spec test suite fetch failed on both its primary URL and
the Web Archive fallback on the same run — breaking configure under CI.
Cause: download_file_multisource was trying each source only once — so a
single transient HTTP error could break the whole build. The WASM spec
test tarball was also being fetched fresh every run — rather than from
the shared download cache — so each build was exposed to that flakiness.
Fix: Retry each fetch a few times across all sources — for every config-
time download: the WASM spec tests, HSTS preload list, and public-suffix
list. Cache the WASM spec test tarball across CI runs — keyed by commit,
so it’s (re)fetched only once per commit bump.
Problem: The libjs-test262 job was failing — often — with “cargo is not
installed for the toolchain 1.96.0”, while building the Rust crates.
Cause: The job ran in an older CI image lacking 1.96.0 — making runtime
rustup try to auto-install 1.96.0 the first time a proxied cargo ran.
And because the build compiles multiple Rust crates in parallel, several
cargo invocations hit the missing toolchain at once — and each started a
rustup install of the same toolchain. And rustup apparently isn’t safe
against concurrent installs: Its in-place component swap shuffles files
out to a temp directory, and a crate that reads the toolchain mid-swap
sees rustc — but not cargo. Whether it corrupts depends on timing — so
the failure is intermittent, and a re-run usually “fixes” it.
Fix: Update the job to the current ladybird-ci:2026.06.02 image — which
already has 1.96.0 (with cargo) baked in — so no runtime install happens
and there’s nothing to race. As an additional guard, also install the
pinned toolchain *explicitly*, before the parallel build: so any install
happens exactly once — and serially — rather than being raced.
Let's not depend on a nightly pre-release, especially not just for a
linting option. We can enable the linter option once it lands in the
stable release.
Problem: A new upstream nightly Rust release made all CI lint code
checks fail in the Set Up Environment step
Cause: CI runs “rustup toolchain install nightly --component rustfmt”.
The runner container ships nightly with rustfmt and clippy preinstalled.
But “rustup toolchain install” treats an existing channel as an upgrade
target, and tries to refresh it whenever a newer nightly is available.
The refresh removes the old clippy component first, renaming
/opt/rustup/toolchains/.../share/doc/clippy into /opt/rustup/tmp/, and
those two directories live on different overlayfs mounts in the
container — so the rename fails with EXDEV.
Fix: Replace “rustup toolchain install nightly --component rustfmt” with
“rustup component add rustfmt --toolchain nightly”. That adds the
component to the existing toolchain without checking for or applying a
channel update — so the cross-mount-rename path is never reached.
Because the container already provisions rustfmt + clippy at build time,
the new command is effectively a no-op on healthy containers, and fails
loudly only if a future container loses the nightly toolchain.
The Linux CI workflows were still pinned to ladybird-ci:2026.05.4, even
though the CI image had since been rebuilt as 2026.05.25 after the Rust
toolchain switched to nightly.
This left CI running in an older container where cargo was not installed
for the nightly toolchain selected by the checked-out tree, causing Rust
crate builds to fail during the fuzzer host-tools build.
Update the workflow image references to the current published CI image.
By default, `rustfmt` persists the import granularity. In practice, most
Rust code has import granularity "Module" due to LSP's actions.
"Item" gets rid of import groupings and achieves cleaner diffs and
better conflict resolution. Better greppability is a positive side
effect.
Note: it's an unstable rustfmt feature. `cargo +nightly fmt` must be
used instead of `cargo fmt`.
Keep GCC compiler warning coverage on pull requests without running the
GCC sanitizer configuration that has been timing out in CI. Leave the
Clang sanitizer jobs in place for sanitizer coverage.
We were forgetting to pass CMake options in the Fuzzers build.
These CMake options contain -DENABLE_CI_BASELINE_CPU=ON, which sets
a baseline architecture to properly allow ccache sharing between
different runners. Without it, it defaults to the native architecture,
which is then cached and can be used on a runner that doesn't have the
same instruction set.
This is targetted at fixing the "Illegal instruction" crash we've been
seeing with the bindings generator.
CTest may run tests from build subdirectories. That means sanitizer logs
can be written below Build instead of the workspace root.
Search Build recursively when printing sanitizer output, and include
matching logs in the LibWeb test artifact.
Keep sanitizer log_path values relative so they work on Windows.
Absolute paths with drive letters contain colons there. That conflicts
with the ASAN_OPTIONS separator.
This means running the build inside a Docker container based on a (more)
stable build image.
* The setup action is now largely simplified for Linux.
* We hardcode llvm@21 for macOS since it allows us to get rid of
specifying llvm_version as an input variable. Since we no longer
support switching the LLVM version on Linux via that variable, this
seems like the best course of action.
We use a trick to optionally introduce a container image: `fromJSON()`
allows `null` as a return value, which causes the job to execute on the
runner directly. We need this for macOS and Windows jobs.
The goal is to have a stable, reproducible build environment for Linux
amd64 and arm64 that we can simply pull in for CI build jobs. We've seen
that apt repositories can be temporarily unreachable or build dependency
versions are silently updated causing builds to fail. A nice benefit of
this is that we can skip most of the environment setup, which takes a
couple of minutes each run.
The idea is that scripts directly under Meta are meant to be run by
people. Scripts that are only imported or run by other scripts are
moved to a subdirectory.
Note that the IPC lint step can be removed from the CI workflow because
we no longer need a build in order to perform the lint, and IPC linting
is now covered by lint-ci.sh.
The vcpkg cache should rarely change - only when new dependencies are
added or a version changes. Both of these events involve modifying the
vcpkg.json file. So instead of uploading duplicated vcpkg caches in each
workflow, let's only upload changed caches.