Skip to content

fix(ci): repair broken rustup shim chain on macos-latest#14040

Merged
hardfist merged 11 commits into
mainfrom
ci/fix-macos-rustup-init-symlink
May 14, 2026
Merged

fix(ci): repair broken rustup shim chain on macos-latest#14040
hardfist merged 11 commits into
mainfrom
ci/fix-macos-rustup-init-symlink

Conversation

@stormslowly
Copy link
Copy Markdown
Contributor

Summary

  • The macos-latest runner image ships Homebrew's rustup-init and populates ~/.cargo/bin/{cargo,rustc,rustup,...} as symlinks pointing back to it. rustup-init is the installer binary, not the multi-call rustup proxy.
  • The current command -v rustup probe sees the symlink and skips reinstall. rustup toolchain install still works via the rustup argv proxy, but cargo test invokes the installer's arg parser and dies with unexpected argument 'test' — surfaced on https://github.com/web-infra-dev/rspack/actions/runs/25848161725/job/75948085190.
  • Switch the probe to rustup show, which only succeeds against a real rustup. On failure, wipe ~/.cargo/bin (only the broken symlinks — ~/.rustup/toolchains is untouched, so the rustup-cache restore still hits) and install a real rustup via the official script.

Test plan

  • Re-run Run Rust Tests / Rust watcher test - "macos-latest" (the job that originally failed) and confirm cargo test -p rspack_watcher proceeds past arg-parsing.
  • Confirm rustup-cache restore still reports a hit on a subsequent run.
  • Sanity-check non-macOS jobs (runner.os != 'Windows' branch covers Linux too) are unaffected — rustup show succeeds on a healthy install so the new branch is a no-op there.

The macos-latest runner image ships Homebrew's rustup-init and symlinks
~/.cargo/bin/{cargo,rustc,rustup,...} to it. rustup-init is the
installer binary, not the multi-call rustup proxy, so `cargo test` ends
up invoking the installer's argument parser and fails with "unexpected
argument 'test'". The previous `command -v rustup` probe saw the
symlink and skipped reinstallation, leaving the broken chain in place.

Probe with `rustup show` to detect a genuinely working rustup. When it
fails, wipe ~/.cargo/bin and reinstall via the official script.
~/.rustup/toolchains is untouched so the rustup-cache restore still
hits.
Copilot AI review requested due to automatic review settings May 14, 2026 08:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Repairs CI on the macos-latest runner image, which now ships Homebrew's rustup-init and exposes it via ~/.cargo/bin/{cargo,rustc,rustup,...} symlinks. Because rustup-init is the installer (not the multi-call rustup proxy), cargo test invocations were failing with unexpected argument 'test'. The probe is changed from command -v rustup to rustup show, which only succeeds against a real rustup; on failure the broken shim directory is wiped and the official rustup installer is run with --no-modify-path (preserving the cached ~/.rustup/toolchains).

Changes:

  • Replace command -v rustup presence check with rustup show functional check.
  • On failure, rm -rf the cargo bin directory to remove broken Homebrew shims before reinstalling.
  • Pass --no-modify-path to the rustup install script and explicitly append the bin path to $GITHUB_PATH.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

📦 Binary Size-limit

Comparing a486f47 to feat: inline const with module declarations (#14032) by Gengkun

🙈 Size remains the same at 61.95MB

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

Rsdoctor Bundle Diff Analysis

Found 6 projects in monorepo, 6 projects with changes.

📊 Quick Summary
Project Total Size Change
popular-libs 1.7 MB -
react-10k 5.7 MB -
react-1k 826.3 KB -
react-5k 2.7 MB -
rome 1.6 MB -
ui-components 4.8 MB -
📋 Detailed Reports (Click to expand)

📁 popular-libs

Path: ../build-tools-performance/cases/popular-libs/dist/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 1.7 MB - -
📄 JavaScript 1.7 MB - -
🎨 CSS 0 B - -
🌐 HTML 289.0 B - -
📁 Other Assets 0 B - -

📁 react-10k

Path: ../build-tools-performance/cases/react-10k/dist/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 5.7 MB - -
📄 JavaScript 5.7 MB - -
🎨 CSS 21.0 B - -
🌐 HTML 328.0 B - -
📁 Other Assets 0 B - -

📁 react-1k

Path: ../build-tools-performance/cases/react-1k/dist/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 826.3 KB - -
📄 JavaScript 826.0 KB - -
🎨 CSS 0 B - -
🌐 HTML 328.0 B - -
📁 Other Assets 0 B - -

📁 react-5k

Path: ../build-tools-performance/cases/react-5k/dist/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 2.7 MB - -
📄 JavaScript 2.7 MB - -
🎨 CSS 21.0 B - -
🌐 HTML 328.0 B - -
📁 Other Assets 0 B - -

📁 rome

Path: ../build-tools-performance/cases/rome/dist/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 1.6 MB - -
📄 JavaScript 1.6 MB - -
🎨 CSS 0 B - -
🌐 HTML 0 B - -
📁 Other Assets 0 B - -

📁 ui-components

Path: ../build-tools-performance/cases/ui-components/dist/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 4.8 MB - -
📄 JavaScript 4.7 MB - -
🎨 CSS 107.0 KB - -
🌐 HTML 328.0 B - -
📁 Other Assets 0 B - -

Generated by Rsdoctor GitHub Action

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 14, 2026

Merging this PR will not alter performance

✅ 34 untouched benchmarks
⏩ 25 skipped benchmarks1


Comparing ci/fix-macos-rustup-init-symlink (a486f47) with main (1f4032a)

Open in CodSpeed

Footnotes

  1. 25 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

The previous probe `rustup show` failed too eagerly on Linux runners
that had a usable rustup but no default toolchain, which then wiped
~/.cargo/bin and reinstalled rustup. The cache restore later put the
nightly toolchain back into ~/.rustup/toolchains/, but the fresh
rustup metadata didn't know rust-src was already there. Any later
toolchain resolve (e.g. `rustc -vV` inside Swatinem/rust-cache) tried
to install rust-src per rust-toolchain.toml and crashed on the file
conflict.

Narrow the detector: only wipe when ~/.cargo/bin/rustup actually
resolves to a `rustup-init` binary (the macos-latest Homebrew case).
Healthy installs on Linux/macOS are left alone.
The previous detector spent a multi-line case statement on what is
essentially a single comparison: does `rustup` resolve to `rustup-init`?
`readlink -f` already follows the whole symlink chain and returns an
empty string when the target is missing, so a single `[[ ... == ... ]]`
short-circuit handles all three cases (missing, healthy, broken).
BSD `readlink` on macOS does not reliably support `-f`. With the error
swallowed by `2>/dev/null`, the substitution returned an empty string,
the comparison never matched, and the broken symlink chain was left in
place. `realpath` is available natively on both macOS (BSD) and Linux
and resolves symlink chains, so it gives a portable canonical path
to test against.
Two rounds of trying to sniff out the broken state at runtime
(readlink -f, then realpath) silently no-op'd on the macos-latest
runner — the symlink check returned something that didn't match
`*/rustup-init`, so the wipe never ran and `cargo test` kept hitting
the installer's arg parser.

macos-latest's pre-installed rustup is always the broken Homebrew
symlink chain; there's no useful state to preserve in ~/.cargo/bin
there. Stop guessing — wipe unconditionally on macOS in a separate
step and let the existing installer lay down real rustup proxies.
~/.rustup/toolchains is untouched, so the rustup-cache restore still
hits.
macos-latest ships TWO rustup-related binaries from Homebrew:
- /opt/homebrew/bin/rustup-init (the installer)
- /opt/homebrew/bin/rustup (a working rustup proxy for rustup-only
  subcommands)

After the wipe step removes the broken ~/.cargo/bin/ shim chain, the
existing `command -v rustup` probe still found Homebrew's real rustup
on PATH and skipped the installer. `rustup toolchain install` worked
via Homebrew's rustup, but cargo/rustc proxies were never recreated
in ~/.cargo/bin, so `cargo test` ended with "cargo: command not
found".

Switch the probe to `command -v cargo`. Homebrew doesn't ship a
standalone cargo, so the probe accurately reflects whether the proxy
shims exist. On Linux this is equivalent — rustup always installs
cargo alongside.
I've shipped four iterations of the rustup shim fix without nailing
down the actual PATH / shim state on macos-latest. Print the path
resolution and inventory of ~/.cargo/bin, /opt/homebrew, ~/.rustup
right before `cargo test`, and open an SSH tmate session on failure
so we can inspect the runner live instead of guessing from logs.

Both the diagnostics step and the tmate session are gated on the
macOS matrix entry — Linux/Windows runs are unaffected.

Revert this once the macOS shim issue is resolved.
The previous wipe step at the top of the composite action was getting
undone: Swatinem/rust-cache (the Cargo cache step) defaults to
cache-bin: true and captures ~/.cargo/bin. On restore it puts back the
runner image's original Apr-21 Homebrew rustup-init symlink chain,
overwriting the fresh proxies we'd just installed.

Move the wipe + curl install to AFTER the Cargo cache step. The cargo
cache restore happens first (potentially with broken symlinks), then
we lay down real proxies. Post-action cache save then captures the
fixed state going forward.

Diagnostic from tmate session: ls -la ~/.cargo/bin showed Apr 21 12:12
timestamps on cargo/rustc/rustup → confirmed symlinks were never
deleted because Swatinem put them back.
Detect the homebrew symlink chain with plain POSIX readlink (no -f,
which BSD readlink on macOS doesn't reliably handle) and run
rustup-init -y to lay down a real rustup with cargo/rustc proxies.
Runs after the Cargo cache step so the Swatinem restore can't undo
the repair.
@stormslowly stormslowly requested a review from hardfist May 14, 2026 17:52
@hardfist hardfist merged commit 5b4782a into main May 14, 2026
35 checks passed
@hardfist hardfist deleted the ci/fix-macos-rustup-init-symlink branch May 14, 2026 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants