use anyhow::{Context, Result};
use serde::Serialize;
use std::path::{Path, PathBuf};
use crate::state::primitives::atomic_write;
use crate::ui::context::OutputContext;
use crate::ui::theme::StatusIndicator;
const SHIM_VERSION: &str = "6";
const SHIM_MARKER: &str = "# rch-shim-version:";
fn shim_dir() -> Result<PathBuf> {
dirs::home_dir()
.map(|h| h.join(".rch").join("shims"))
.context("Could not determine home directory")
}
fn cargo_shim_path() -> Result<PathBuf> {
Ok(shim_dir()?.join("cargo"))
}
fn cargo_clippy_shim_path() -> Result<PathBuf> {
Ok(shim_dir()?.join("cargo-clippy"))
}
fn cargo_clippy_shim_body(require_remote: bool) -> String {
let offload = if require_remote {
"exec env RCH_REQUIRE_REMOTE=1 RCH_QUEUE_WHEN_BUSY=1 rch exec -- cargo clippy \"$@\""
} else {
"exec rch exec -- cargo clippy \"$@\""
};
format!(
r##"#!/bin/sh
# rch cargo-clippy shim — MANAGED FILE, edit via `rch shim install`.
{marker} {version}
#
# Catches DIRECT `cargo-clippy` invocations (scripts, Makefiles, lint gates)
# that bypass the `cargo` shim entirely by never going through cargo.
REAL="${{RCH_SHIM_REAL_CARGO_CLIPPY:-$HOME/.cargo/bin/cargo-clippy}}"
# Cap parallelism on every LOCAL fallback. Local builds never reach rch's slot
# accounting, so nothing else bounds them — and a repo's own .cargo/config.toml
# outranks ~/.cargo/config.toml, so a committed `jobs = -1` (= nproc-1) wins over
# any global default. CARGO_BUILD_JOBS outranks every config file, so setting it
# here is the only reliable cap. An explicitly-set CARGO_BUILD_JOBS always wins;
# tune the default with RCH_LOCAL_MAX_JOBS.
exec_local() {{
if [ -z "${{CARGO_BUILD_JOBS:-}}" ]; then
CARGO_BUILD_JOBS="${{RCH_LOCAL_MAX_JOBS:-8}}"
export CARGO_BUILD_JOBS
fi
exec "$REAL" "$@"
}}
# Loop-break + fail-open, identical contract to the cargo shim.
if [ "${{RCH_CARGO_WRAPPER_BYPASS:-}}" = "1" ] || [ "${{RCH_SHIM_LOCAL_IDE:-}}" = "1" ] \
|| ! command -v rch >/dev/null 2>&1 || [ ! -x "$REAL" ]; then
exec_local "$@"
fi
# Invoked BY cargo as a subcommand (`cargo-clippy clippy ...`): the outer cargo
# already made the offload decision, so never re-enter rch here.
if [ "${{1:-}}" = "clippy" ]; then
exec_local "$@"
fi
# Direct invocation — normalize to `cargo clippy` and offload.
{offload}
"##,
marker = SHIM_MARKER,
version = SHIM_VERSION,
offload = offload,
)
}
fn cargo_shim_body(require_remote: bool) -> String {
let offload = if require_remote {
"exec env RCH_REQUIRE_REMOTE=1 RCH_QUEUE_WHEN_BUSY=1 rch exec -- cargo \"$@\""
} else {
"exec rch exec -- cargo \"$@\""
};
format!(
r##"#!/bin/sh
# rch cargo shim — MANAGED FILE, edit via `rch shim install`.
{marker} {version}
#
# Harness-agnostic build offload: any agent (codex/scripts/CI, not only Claude
# Code) that runs `cargo build|test|check|...` is routed through `rch exec` to
# the worker fleet instead of compiling locally on this box. See `rch shim`.
# Every LOCAL path (RCH_SHIM_LOCAL_IDE=1, wrapper bypass, IDE diagnostics, the
# catch-all arm) must exec a WORKING real cargo. On hosts where the active
# toolchain's cargo was renamed to `cargo-rch-real` (rch toolchain wrapping, or
# a rustup update over it), the rustup proxy at ~/.cargo/bin/cargo can no
# longer dispatch — rustup answers "the 'cargo' binary ... is not applicable
# to the '<toolchain>' toolchain" — so a single hardcoded fallback dies exactly
# when the escape hatch is needed most. The real cargo is resolved in tiers,
# first executable wins:
# 1. $RCH_REAL_CARGO explicit operator override;
# 2. $RCH_SHIM_REAL_CARGO set by the rch toolchain wrapper (preserves the
# caller's toolchain identity through the handoff);
# 3. <active-toolchain>/bin/cargo-rch-real, the active toolchain discovered
# via `rustc --print sysroot` (honors RUSTUP_TOOLCHAIN
# and rust-toolchain.toml, i.e. the toolchain this
# build would actually use);
# 4. $HOME/.cargo/bin/cargo — the stock rustup proxy, correct wherever rch
# never renamed anything.
resolve_real() {{
if [ -n "${{RCH_REAL_CARGO:-}}" ] && [ -x "$RCH_REAL_CARGO" ]; then
REAL="$RCH_REAL_CARGO"
return 0
fi
if [ -n "${{RCH_SHIM_REAL_CARGO:-}}" ] && [ -x "$RCH_SHIM_REAL_CARGO" ]; then
REAL="$RCH_SHIM_REAL_CARGO"
return 0
fi
if command -v rustc >/dev/null 2>&1; then
sysroot=$(rustc --print sysroot 2>/dev/null)
if [ -n "$sysroot" ] && [ -x "$sysroot/bin/cargo-rch-real" ]; then
REAL="$sysroot/bin/cargo-rch-real"
return 0
fi
fi
if [ -x "$HOME/.cargo/bin/cargo" ]; then
REAL="$HOME/.cargo/bin/cargo"
return 0
fi
echo "rch shim: no working real cargo found for local execution" >&2
echo " tried: \$RCH_REAL_CARGO, \$RCH_SHIM_REAL_CARGO, <active toolchain>/bin/cargo-rch-real, \$HOME/.cargo/bin/cargo" >&2
echo " fix: set RCH_REAL_CARGO=/path/to/real/cargo (e.g. \$HOME/.rustup/toolchains/<tc>/bin/cargo-rch-real)" >&2
echo " or restore the stock layout with: rch shim uninstall" >&2
exit 127
}}
# Cap parallelism on every LOCAL fallback. Local builds never reach rch's slot
# accounting, so nothing else bounds them — and a repo's own .cargo/config.toml
# outranks ~/.cargo/config.toml, so a committed `jobs = -1` (= nproc-1) wins over
# any global default. CARGO_BUILD_JOBS outranks every config file, so setting it
# here is the only reliable cap. An explicitly-set CARGO_BUILD_JOBS always wins;
# tune the default with RCH_LOCAL_MAX_JOBS.
exec_local() {{
if [ -z "${{CARGO_BUILD_JOBS:-}}" ]; then
CARGO_BUILD_JOBS="${{RCH_LOCAL_MAX_JOBS:-8}}"
export CARGO_BUILD_JOBS
fi
# +toolchain is rustup syntax, not a real Cargo subcommand. The command-line
# selection outranks the ambient toolchain and any inherited handoff path.
# Re-enter rustup with the selected installation and an explicit loop break:
# its cargo may itself be an RCH toolchain wrapper.
case "${{1:-}}" in
+*)
selected_toolchain="${{1#+}}"
shift
if [ -z "$selected_toolchain" ]; then
echo "rch shim: an empty +toolchain selector cannot select Cargo" >&2
exit 2
fi
if ! command -v rustup >/dev/null 2>&1; then
echo "rch shim: rustup is required for the selected +$selected_toolchain; refusing to use another Cargo" >&2
exit 127
fi
RCH_CARGO_WRAPPER_BYPASS=1
export RCH_CARGO_WRAPPER_BYPASS
exec rustup run "$selected_toolchain" cargo "$@" ;;
esac
resolve_real
# A renamed toolchain cargo runs with its own bin dir FIRST on PATH, or the
# build's rustc/clippy-driver resolve through the rustup proxy's default
# toolchain instead of the one being built with (breaks -Z flags in a repo's
# .cargo/config.toml when default != requested toolchain).
case "$REAL" in
*/cargo-rch-real)
PATH="${{REAL%/*}}:$PATH"
export PATH
;;
esac
exec "$REAL" "$@"
}}
# Loop-break: rch sets RCH_CARGO_WRAPPER_BYPASS=1 on its own local-fallback exec.
# Also fail open if rch is unavailable — never block a build.
if [ "${{RCH_CARGO_WRAPPER_BYPASS:-}}" = "1" ] || ! command -v rch >/dev/null 2>&1; then
exec_local "$@"
fi
# Explicit operator escape hatch: force this one build local.
if [ "${{RCH_SHIM_LOCAL_IDE:-}}" = "1" ]; then
exec_local "$@"
fi
# `cargo clippy` runs as: set RUSTC_WORKSPACE_WRAPPER=clippy-driver, then exec an
# inner `cargo check`. Offloading that INNER cargo would drop the wrapper and
# silently return plain check results instead of lints, so it stays local — the
# OUTER `cargo clippy` is the invocation that offloads. Same for any other
# caller-chosen rustc wrapper.
if [ -n "${{RUSTC_WORKSPACE_WRAPPER:-}}" ]; then
exec_local "$@"
fi
# rust-analyzer must stay local (streaming JSON + local state). It is identified
# by its distinctive rendered-ansi/short diagnostic formats. A PLAIN
# `--message-format=json` is what scripts, CI and build tooling pass, and those
# must still offload — treating every --message-format as an IDE was the bug
# that silently kept script-driven builds on the dev box.
format_value=0
for a in "$@"; do
if [ "$format_value" = "1" ]; then
case "$a" in
json-diagnostic-rendered-ansi*|json-diagnostic-short*) exec_local "$@" ;;
esac
format_value=0
continue
fi
case "$a" in
--) break ;;
--message-format) format_value=1 ;;
--message-format=json-diagnostic-rendered-ansi*|--message-format=json-diagnostic-short*)
exec_local "$@" ;;
esac
done
# Inspect a function-local copy of argv. Never shift the caller's arguments or
# reconstruct a shell command: option values can contain spaces, quotes, and
# names such as "build". RCH still owns eligibility, topology and placement.
# Only the first argument can be rustup's +toolchain selector. Unknown options
# stay local rather than guessing. Recognizing -C does not change cwd here:
# RCH's source-closure preflight must still approve or refuse that request.
find_subcommand() {{
rch_subcommand=
case "${{1:-}}" in +?*) shift ;; esac
while [ "$#" -gt 0 ]; do
case "$1" in
-v|-vv|-vvv|--verbose|-q|--quiet|--locked|--offline|--frozen) shift ;;
--color|--config|-Z|-C)
[ "$#" -ge 2 ] || return 0
shift 2 ;;
--color=?*|--config=?*|-Z?*|-C?*) shift ;;
--) shift; break ;;
-*) return 0 ;;
*) break ;;
esac
done
rch_subcommand="${{1:-}}"
}}
find_subcommand "$@"
case "$rch_subcommand" in
build|b|test|t|check|c|clippy|bench|doc|nextest|run|r|zigbuild|xwin)
{offload} ;;
*)
exec_local "$@" ;;
esac
"##,
marker = SHIM_MARKER,
version = SHIM_VERSION,
offload = offload,
)
}
fn installed_shim_version(path: &Path) -> Option<String> {
let content = std::fs::read_to_string(path).ok()?;
content.lines().find_map(|line| {
line.trim()
.strip_prefix(SHIM_MARKER)
.map(|v| v.trim().to_string())
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
enum Interception {
Direct,
Delegated,
None,
}
impl Interception {
fn intercepts(self) -> bool {
matches!(self, Self::Direct | Self::Delegated)
}
}
fn delegates_to_shim(path: &Path, target_executable: bool) -> bool {
let Ok(meta) = std::fs::metadata(path) else {
return false;
};
if meta.len() > 8 * 1024 {
return false;
}
let Ok(content) = std::fs::read_to_string(path) else {
return false;
};
(target_executable && content.contains(".rch/shims/cargo")) || content.contains(SHIM_MARKER)
}
fn cargo_interception(shim: &Path) -> Interception {
let Some(path) = std::env::var_os("PATH") else {
return Interception::None;
};
let Ok(cwd) = std::env::current_dir() else {
return Interception::None;
};
cargo_interception_in(&path, shim, &cwd)
}
fn cargo_interception_in(path: &std::ffi::OsStr, shim: &Path, cwd: &Path) -> Interception {
if !cwd.is_absolute() {
return Interception::None;
}
let Ok(shim) = cwd.join(shim).canonicalize() else {
return Interception::None;
};
let shim_executable = which::which(&shim).is_ok();
for entry in std::env::split_paths(path) {
let candidate = cwd.join(entry).join("cargo");
let Ok(candidate) = which::which(candidate) else {
continue;
};
if candidate.canonicalize().ok().as_ref() == Some(&shim) {
return Interception::Direct;
}
if delegates_to_shim(&candidate, shim_executable) {
return Interception::Delegated;
}
return Interception::None;
}
Interception::None
}
const TOOLCHAIN_WRAP_VERSION: &str = "3";
const TOOLCHAIN_MARKER: &str = "# rch-toolchain-wrap-version:";
const REAL_CARGO_NAME: &str = "cargo-rch-real";
fn rustup_toolchains_dir() -> Option<PathBuf> {
if let Some(home) = std::env::var_os("RUSTUP_HOME") {
return Some(PathBuf::from(home).join("toolchains"));
}
dirs::home_dir().map(|h| h.join(".rustup").join("toolchains"))
}
fn toolchain_cargos() -> Vec<PathBuf> {
let Some(root) = rustup_toolchains_dir() else {
return Vec::new();
};
let Ok(entries) = std::fs::read_dir(&root) else {
return Vec::new();
};
let mut out: Vec<PathBuf> = entries
.flatten()
.map(|e| e.path().join("bin").join("cargo"))
.filter(|p| p.is_file())
.collect();
out.sort();
out
}
fn toolchain_wrap_body() -> String {
format!(
r##"#!/bin/sh
# rch toolchain cargo wrapper — MANAGED FILE, edit via `rch shim install`.
{marker} {version}
#
# Routes ABSOLUTE-path toolchain cargo calls through the canonical rch shim.
# Scripts/Makefiles/agents invoking ~/.rustup/toolchains/<tc>/bin/cargo directly
# bypass PATH; this is the one layer they cannot dodge.
SELF_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
REAL="$SELF_DIR/{real}"
SHIM="$HOME/.rch/shims/cargo"
# Same local-parallelism cap as the cargo shim: this path runs the real toolchain
# cargo directly, so it must not inherit an unbounded `-j` from a repo config.
exec_local() {{
if [ -z "${{CARGO_BUILD_JOBS:-}}" ]; then
CARGO_BUILD_JOBS="${{RCH_LOCAL_MAX_JOBS:-8}}"
export CARGO_BUILD_JOBS
fi
# The real cargo is a toolchain binary: its children (rustc, clippy-driver,
# rust-lld) must resolve from THIS toolchain's bin, not from whatever the
# rustup proxy default happens to be — same rule as the shim's local fallback.
PATH="$SELF_DIR:$PATH"
export PATH
exec "$REAL" "$@"
}}
# Loop-break + fail-open: rch sets RCH_CARGO_WRAPPER_BYPASS=1 on its own local
# fallback exec. Never block a build if the shim or rch is missing.
if [ "${{RCH_CARGO_WRAPPER_BYPASS:-}}" = "1" ] || [ ! -x "$SHIM" ] || ! command -v rch >/dev/null 2>&1; then
exec_local "$@"
fi
# Preserve toolchain identity through any local fallback.
RCH_SHIM_REAL_CARGO="$REAL"
export RCH_SHIM_REAL_CARGO
exec "$SHIM" "$@"
"##,
marker = TOOLCHAIN_MARKER,
version = TOOLCHAIN_WRAP_VERSION,
real = REAL_CARGO_NAME,
)
}
fn toolchain_wrap_version(path: &Path) -> Option<String> {
let meta = std::fs::metadata(path).ok()?;
if meta.len() > 8 * 1024 {
return None;
}
let content = std::fs::read_to_string(path).ok()?;
content.lines().find_map(|line| {
line.trim()
.strip_prefix(TOOLCHAIN_MARKER)
.map(|v| v.trim().to_string())
})
}
fn is_toolchain_wrapped(path: &Path) -> bool {
toolchain_wrap_version(path).is_some()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum WrapOutcome {
Wrapped,
Refreshed,
AlreadyCurrent,
}
#[cfg(unix)]
fn wrap_toolchain_cargo(cargo: &Path) -> Result<WrapOutcome> {
use std::os::unix::fs::PermissionsExt;
let dir = cargo
.parent()
.context("toolchain cargo has no parent directory")?;
let real = dir.join(REAL_CARGO_NAME);
if let Some(v) = toolchain_wrap_version(cargo) {
if v == TOOLCHAIN_WRAP_VERSION && real.exists() {
return Ok(WrapOutcome::AlreadyCurrent);
}
if !real.exists() {
anyhow::bail!(
"{} is an rch wrapper but {} is missing — cannot recover the real cargo; \
reinstall this toolchain with `rustup toolchain install --force`",
cargo.display(),
real.display()
);
}
atomic_write(cargo, toolchain_wrap_body().as_bytes())
.with_context(|| format!("Failed to refresh wrapper at {}", cargo.display()))?;
let mut perms = std::fs::metadata(cargo)?.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(cargo, perms)?;
return Ok(WrapOutcome::Refreshed);
}
if real.exists() {
std::fs::remove_file(&real)
.with_context(|| format!("Failed to remove stale real cargo at {}", real.display()))?;
}
std::fs::hard_link(cargo, &real).with_context(|| {
format!(
"Failed to hardlink {} -> {}",
cargo.display(),
real.display()
)
})?;
if is_toolchain_wrapped(&real) {
let _ = std::fs::remove_file(&real);
anyhow::bail!(
"concurrent wrap detected for {}: refusing to preserve a wrapper as the real cargo",
cargo.display()
);
}
if let Err(e) = atomic_write(cargo, toolchain_wrap_body().as_bytes()) {
let _ = std::fs::remove_file(&real);
return Err(e).with_context(|| format!("Failed to write wrapper at {}", cargo.display()));
}
let mut perms = std::fs::metadata(cargo)?.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(cargo, perms)?;
Ok(WrapOutcome::Wrapped)
}
#[cfg(not(unix))]
fn wrap_toolchain_cargo(_cargo: &Path) -> Result<WrapOutcome> {
anyhow::bail!("toolchain wrapping is only supported on Unix")
}
fn unwrap_toolchain_cargo(cargo: &Path) -> Result<bool> {
if !is_toolchain_wrapped(cargo) {
return Ok(false);
}
let dir = cargo
.parent()
.context("toolchain cargo has no parent directory")?;
let real = dir.join(REAL_CARGO_NAME);
if !real.exists() {
anyhow::bail!(
"refusing to remove wrapper at {}: {} is missing (no real cargo to restore)",
cargo.display(),
real.display()
);
}
std::fs::rename(&real, cargo)
.with_context(|| format!("Failed to restore real cargo at {}", cargo.display()))?;
Ok(true)
}
fn toolchain_wrap_counts() -> (usize, usize) {
let cargos = toolchain_cargos();
let wrapped = cargos.iter().filter(|c| is_toolchain_wrapped(c)).count();
(wrapped, cargos.len())
}
fn toolchain_wrappers_current() -> bool {
toolchain_cargos().iter().all(|cargo| {
toolchain_wrap_version(cargo).as_deref() == Some(TOOLCHAIN_WRAP_VERSION)
&& which::which(cargo).is_ok()
&& which::which(cargo.with_file_name(REAL_CARGO_NAME)).is_ok()
})
}
#[cfg(unix)]
fn local_build_process_count() -> Option<usize> {
let out = std::process::Command::new("pgrep")
.args(["-x", "rustc"])
.output()
.ok()?;
if !out.status.success() && out.stdout.is_empty() {
return Some(0);
}
Some(
out.stdout
.split(|b| *b == b'\n')
.filter(|l| !l.is_empty())
.count(),
)
}
#[cfg(not(unix))]
fn local_build_process_count() -> Option<usize> {
None
}
#[derive(Debug, Serialize)]
struct ShimStatus {
installed: bool,
clippy_shim_installed: bool,
path: String,
version: Option<String>,
embedded_version: String,
up_to_date: bool,
on_path_ahead_of_cargo: bool,
interception: Interception,
local_builds_running: Option<usize>,
toolchains_wrapped: usize,
toolchains_total: usize,
#[serde(skip_serializing_if = "Vec::is_empty")]
toolchain_errors: Vec<String>,
}
pub fn shim_install(
require_remote: bool,
wrap_toolchains: bool,
ctx: &OutputContext,
) -> Result<()> {
if let Ok(config) = crate::config::load_config()
&& config.general.role == rch_common::BoxRole::Worker
{
anyhow::bail!(
"refusing to install the cargo shim: general.role = \"worker\" — this box IS \
the compute. Set role = \"dispatcher\" or \"hybrid\" in config.toml if this \
machine should offload builds."
);
}
let style = ctx.theme();
let path = cargo_shim_path()?;
let body = cargo_shim_body(require_remote);
let existed = path.exists();
atomic_write(&path, body.as_bytes())
.with_context(|| format!("Failed to write cargo shim to {}", path.display()))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&path)?.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&path, perms)?;
}
let clippy_path = cargo_clippy_shim_path()?;
atomic_write(
&clippy_path,
cargo_clippy_shim_body(require_remote).as_bytes(),
)
.with_context(|| {
format!(
"Failed to write cargo-clippy shim to {}",
clippy_path.display()
)
})?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&clippy_path)?.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&clippy_path, perms)?;
}
let mut wrapped_now = 0usize;
let mut refreshed = 0usize;
let mut already = 0usize;
let mut wrap_errors: Vec<String> = Vec::new();
if wrap_toolchains {
for cargo in toolchain_cargos() {
match wrap_toolchain_cargo(&cargo) {
Ok(WrapOutcome::Wrapped) => wrapped_now += 1,
Ok(WrapOutcome::Refreshed) => refreshed += 1,
Ok(WrapOutcome::AlreadyCurrent) => already += 1,
Err(e) => wrap_errors.push(format!("{}: {e:#}", cargo.display())),
}
}
}
let interception = cargo_interception(&path);
let ahead = interception.intercepts();
let (tc_wrapped, tc_total) = toolchain_wrap_counts();
if ctx.is_json() {
let status = ShimStatus {
installed: true,
clippy_shim_installed: cargo_clippy_shim_path()
.map(|p| p.exists())
.unwrap_or(false),
path: path.display().to_string(),
version: Some(SHIM_VERSION.to_string()),
embedded_version: SHIM_VERSION.to_string(),
up_to_date: true,
on_path_ahead_of_cargo: ahead,
interception,
local_builds_running: local_build_process_count(),
toolchains_wrapped: tc_wrapped,
toolchains_total: tc_total,
toolchain_errors: wrap_errors.clone(),
};
let _ = ctx.json(&status);
return Ok(());
}
println!(
"{} {} cargo shim at {}",
StatusIndicator::Success.display(style),
if existed { "Updated" } else { "Installed" },
style.highlight(&path.display().to_string())
);
println!(
" policy: {}",
if require_remote {
"fail-closed (queue for a worker; never build locally)"
} else {
"fail-open (offload, but fall back to local under load)"
}
);
println!(
" local fallback resolves real cargo: $RCH_REAL_CARGO → $RCH_SHIM_REAL_CARGO → <active toolchain>/bin/cargo-rch-real → ~/.cargo/bin/cargo"
);
if wrap_toolchains {
if tc_total == 0 {
println!(
" {} no rustup toolchains found to wrap",
StatusIndicator::Info.display(style)
);
} else {
println!(
" {} toolchains: {tc_wrapped}/{tc_total} wrapped ({wrapped_now} new, {refreshed} refreshed, {already} already current)",
StatusIndicator::Success.display(style)
);
}
for err in &wrap_errors {
println!(" {} {err}", StatusIndicator::Warning.display(style));
}
}
match interception {
Interception::None => {
let dir = shim_dir()?;
println!(
"\n{} The shim is not yet ahead of ~/.cargo/bin on PATH. Add this to your shell rc:",
StatusIndicator::Warning.display(style)
);
println!(" export PATH=\"{}:$PATH\"", dir.display());
println!(" then restart your shells (or `hash -r`).");
if tc_wrapped > 0 {
println!(
" {} toolchain wrappers still catch absolute-path cargo calls meanwhile.",
StatusIndicator::Info.display(style)
);
}
}
Interception::Direct => println!(
" {} PATH already resolves this shim ahead of ~/.cargo/bin.",
StatusIndicator::Info.display(style)
),
Interception::Delegated => println!(
" {} PATH resolves a wrapper that delegates to this shim — cargo is intercepted.",
StatusIndicator::Info.display(style)
),
}
Ok(())
}
pub(crate) fn dispatcher_shim_problems() -> Result<Vec<String>> {
let mut problems = Vec::new();
let cargo = cargo_shim_path()?;
let clippy = cargo_clippy_shim_path()?;
for (name, path) in [("cargo", &cargo), ("cargo-clippy", &clippy)] {
if which::which(path).is_err() {
problems.push(format!("{name} shim is missing or not executable"));
} else if installed_shim_version(path).as_deref() != Some(SHIM_VERSION) {
problems.push(format!("{name} shim is stale or unmanaged"));
}
}
if !cargo_interception(&cargo).intercepts() {
problems.push("PATH does not resolve Cargo through the RCH shim".to_string());
}
let (wrapped, total) = toolchain_wrap_counts();
if wrapped != total {
problems.push(format!(
"only {wrapped}/{total} toolchain Cargo binaries are wrapped"
));
} else if !toolchain_wrappers_current() {
problems.push("toolchain Cargo wrappers are stale or not executable".to_string());
}
Ok(problems)
}
pub fn shim_status(ctx: &OutputContext) -> Result<()> {
let style = ctx.theme();
let path = cargo_shim_path()?;
let installed = path.exists();
let version = if installed {
installed_shim_version(&path)
} else {
None
};
let up_to_date = version.as_deref() == Some(SHIM_VERSION)
&& which::which(&path).is_ok()
&& cargo_clippy_shim_path().is_ok_and(|clippy| {
installed_shim_version(&clippy).as_deref() == Some(SHIM_VERSION)
&& which::which(clippy).is_ok()
})
&& toolchain_wrappers_current();
let interception = if installed {
cargo_interception(&path)
} else {
Interception::None
};
let ahead = interception.intercepts();
let local_builds = local_build_process_count();
let (tc_wrapped, tc_total) = toolchain_wrap_counts();
let clippy_installed = cargo_clippy_shim_path()
.map(|p| p.exists())
.unwrap_or(false);
if ctx.is_json() {
let status = ShimStatus {
installed,
clippy_shim_installed: clippy_installed,
path: path.display().to_string(),
version,
embedded_version: SHIM_VERSION.to_string(),
up_to_date,
on_path_ahead_of_cargo: ahead,
interception,
local_builds_running: local_builds,
toolchains_wrapped: tc_wrapped,
toolchains_total: tc_total,
toolchain_errors: Vec::new(),
};
let _ = ctx.json(&status);
return Ok(());
}
let clippy_note = if clippy_installed {
None
} else {
Some(format!(
"{} cargo-clippy shim missing — direct `cargo-clippy` calls (lint \
gates, CI scripts) will build LOCALLY. Run `rch shim install`.",
StatusIndicator::Warning.display(style)
))
};
if !installed {
if let Some(note) = &clippy_note {
println!(" {note}");
}
println!(
"{} cargo shim not installed. Run `rch shim install`.",
StatusIndicator::Warning.display(style)
);
return Ok(());
}
println!(
"{} cargo shim installed at {}",
StatusIndicator::Success.display(style),
style.highlight(&path.display().to_string())
);
if up_to_date {
println!(" version: {SHIM_VERSION} (current)");
} else {
println!(
" {} version: {} (embedded {SHIM_VERSION}) — run `rch shim install` to refresh",
StatusIndicator::Warning.display(style),
version.as_deref().unwrap_or("unknown")
);
}
match &clippy_note {
Some(note) => println!(" {note}"),
None => println!(
" {} cargo-clippy shim installed — direct clippy calls offload too",
StatusIndicator::Success.display(style)
),
}
match interception {
Interception::Direct => println!(
" {} PATH resolves the shim ahead of ~/.cargo/bin",
StatusIndicator::Info.display(style)
),
Interception::Delegated => println!(
" {} PATH resolves a wrapper that delegates to this shim — cargo IS intercepted",
StatusIndicator::Info.display(style)
),
Interception::None => println!(
" {} PATH does NOT resolve the shim first — cargo is not being intercepted",
StatusIndicator::Warning.display(style)
),
}
if tc_total == 0 {
println!(
" {} no rustup toolchains installed",
StatusIndicator::Info.display(style)
);
} else if tc_wrapped == tc_total {
println!(
" {} toolchains: {tc_wrapped}/{tc_total} wrapped (absolute-path cargo is intercepted)",
StatusIndicator::Info.display(style)
);
} else {
println!(
" {} toolchains: {tc_wrapped}/{tc_total} wrapped — {} unwrapped toolchain(s) can still build locally via absolute path; run `rch shim install`",
StatusIndicator::Warning.display(style),
tc_total - tc_wrapped
);
}
if let Some(n) = local_builds {
if n == 0 {
println!(
" {} no local rustc running",
StatusIndicator::Info.display(style)
);
} else {
println!(
" {} {n} local rustc running — builds are compiling on this box (check PATH order / unwrapped toolchains above)",
StatusIndicator::Warning.display(style)
);
}
}
Ok(())
}
pub fn shim_uninstall(ctx: &OutputContext) -> Result<()> {
let style = ctx.theme();
let path = cargo_shim_path()?;
let mut restored = 0usize;
let mut unwrap_errors: Vec<String> = Vec::new();
for cargo in toolchain_cargos() {
match unwrap_toolchain_cargo(&cargo) {
Ok(true) => restored += 1,
Ok(false) => {}
Err(e) => unwrap_errors.push(format!("{}: {e:#}", cargo.display())),
}
}
let had_shim = path.exists();
if had_shim {
std::fs::remove_file(&path)
.with_context(|| format!("Failed to remove cargo shim at {}", path.display()))?;
}
let clippy_path = cargo_clippy_shim_path()?;
if clippy_path.exists() {
std::fs::remove_file(&clippy_path).with_context(|| {
format!(
"Failed to remove cargo-clippy shim at {}",
clippy_path.display()
)
})?;
}
if ctx.is_json() {
let (tc_wrapped, tc_total) = toolchain_wrap_counts();
let status = ShimStatus {
installed: false,
clippy_shim_installed: false,
path: path.display().to_string(),
version: None,
embedded_version: SHIM_VERSION.to_string(),
up_to_date: false,
on_path_ahead_of_cargo: false,
interception: Interception::None,
local_builds_running: local_build_process_count(),
toolchains_wrapped: tc_wrapped,
toolchains_total: tc_total,
toolchain_errors: unwrap_errors.clone(),
};
let _ = ctx.json(&status);
return Ok(());
}
if had_shim {
println!(
"{} Removed cargo shim at {}",
StatusIndicator::Success.display(style),
style.highlight(&path.display().to_string())
);
} else {
println!(
"{} cargo shim not installed; nothing to remove.",
StatusIndicator::Info.display(style)
);
}
if restored > 0 {
println!(
" {} restored {restored} toolchain cargo binaries",
StatusIndicator::Success.display(style)
);
}
for err in &unwrap_errors {
println!(" {} {err}", StatusIndicator::Warning.display(style));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shim_body_is_loop_safe_and_fail_open() {
let body = cargo_shim_body(true);
assert!(body.contains("RCH_CARGO_WRAPPER_BYPASS"));
assert!(body.contains("command -v rch"));
assert!(body.contains("--message-format"));
assert!(body.contains("build|b|test|t|check|c|clippy|bench|doc|nextest"));
assert_eq!(
installed_shim_version_from_str(&body).as_deref(),
Some(SHIM_VERSION)
);
}
#[test]
fn require_remote_toggles_fail_closed() {
let closed = cargo_shim_body(true);
let open = cargo_shim_body(false);
assert!(closed.contains("RCH_REQUIRE_REMOTE=1"));
assert!(closed.contains("RCH_QUEUE_WHEN_BUSY=1"));
assert!(!open.contains("RCH_REQUIRE_REMOTE=1"));
assert!(open.contains("rch exec -- cargo"));
}
#[test]
fn shim_only_offloads_build_subcommands() {
let body = cargo_shim_body(true);
assert!(body.contains("*)\n exec_local \"$@\" ;;"));
}
#[cfg(unix)]
fn write_exe(p: &Path, contents: &str) {
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
let mut f = std::fs::File::create(p).unwrap();
f.write_all(contents.as_bytes()).unwrap();
let mut perms = std::fs::metadata(p).unwrap().permissions();
perms.set_mode(0o755);
std::fs::set_permissions(p, perms).unwrap();
}
#[cfg(unix)]
fn output_retrying_text_busy(cmd: &mut std::process::Command) -> std::process::Output {
let mut attempts = 0;
loop {
match cmd.output() {
Err(e) if e.kind() == std::io::ErrorKind::ExecutableFileBusy && attempts < 50 => {
attempts += 1;
std::thread::sleep(std::time::Duration::from_millis(10));
}
Ok(output)
if output.status.code() == Some(126)
&& String::from_utf8_lossy(&output.stderr).contains("Text file busy")
&& attempts < 50 =>
{
attempts += 1;
std::thread::sleep(std::time::Duration::from_millis(10));
}
result => return result.expect("spawn sandboxed shim"),
}
}
}
#[cfg(unix)]
fn shim_sandbox(body: &str, name: &str, args: &[&str]) -> PathBuf {
static SEQ: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
let seq = SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let dir = std::env::temp_dir().join(format!(
"rch-shim-test-{}-{}-{}-{}",
name,
std::process::id(),
args.join("_").replace(['/', '=', '-', ' '], ""),
seq
));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
write_exe(&dir.join(name), body);
write_exe(
&dir.join("rch"),
"#!/bin/sh\necho OFFLOAD \"$@\" jobs=${CARGO_BUILD_JOBS:-unset}\n",
);
write_exe(
&dir.join("real-cargo"),
"#!/bin/sh\necho LOCAL \"$@\" jobs=${CARGO_BUILD_JOBS:-unset}\n",
);
dir
}
#[cfg(unix)]
fn exec_shim(
dir: &Path,
name: &str,
args: &[&str],
env: &[(&str, &str)],
home: Option<&Path>,
) -> std::process::Output {
let mut cmd = std::process::Command::new(dir.join(name));
cmd.args(args)
.env("PATH", format!("{}:/usr/bin:/bin", dir.display()))
.env_remove("RCH_CARGO_WRAPPER_BYPASS")
.env_remove("RCH_SHIM_LOCAL_IDE")
.env_remove("RUSTC_WORKSPACE_WRAPPER")
.env_remove("RCH_REAL_CARGO")
.env_remove("RCH_SHIM_REAL_CARGO")
.env_remove("RCH_SHIM_REAL_CARGO_CLIPPY")
.env_remove("CARGO_BUILD_JOBS")
.env_remove("RCH_LOCAL_MAX_JOBS");
if let Some(h) = home {
cmd.env("HOME", h);
}
for (k, v) in env {
cmd.env(k, v);
}
output_retrying_text_busy(&mut cmd)
}
#[cfg(unix)]
fn run_shim(body: &str, name: &str, args: &[&str], env: &[(&str, &str)]) -> String {
let dir = shim_sandbox(body, name, args);
let real = dir.join("real-cargo");
let mut full: Vec<(&str, &str)> = vec![
("RCH_SHIM_REAL_CARGO", real.to_str().unwrap()),
("RCH_SHIM_REAL_CARGO_CLIPPY", real.to_str().unwrap()),
];
full.extend_from_slice(env);
let out = exec_shim(&dir, name, args, &full, None);
let _ = std::fs::remove_dir_all(&dir);
assert!(
out.status.success(),
"shim exited {:?}; stderr: {}",
out.status.code(),
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
#[cfg(unix)]
#[test]
fn plain_message_format_json_still_offloads() {
let body = cargo_shim_body(true);
let out = run_shim(&body, "cargo", &["check", "--message-format=json"], &[]);
assert!(out.starts_with("OFFLOAD"), "expected offload, got: {out}");
}
#[cfg(unix)]
#[test]
fn rust_analyzer_diagnostic_formats_stay_local() {
let body = cargo_shim_body(true);
for fmt in [
"--message-format=json-diagnostic-rendered-ansi",
"--message-format=json-diagnostic-short",
] {
let out = run_shim(&body, "cargo", &["check", fmt], &[]);
assert!(out.starts_with("LOCAL"), "{fmt} should stay local: {out}");
}
}
#[cfg(unix)]
#[test]
fn workspace_wrapper_forces_local_so_clippy_lints_survive() {
let body = cargo_shim_body(true);
let out = run_shim(
&body,
"cargo",
&["check"],
&[("RUSTC_WORKSPACE_WRAPPER", "/usr/bin/clippy-driver")],
);
assert!(out.starts_with("LOCAL"), "expected local, got: {out}");
}
#[cfg(unix)]
#[test]
fn local_fallbacks_cap_parallelism() {
let body = cargo_shim_body(true);
for (args, env) in [
(vec!["build"], vec![("RCH_CARGO_WRAPPER_BYPASS", "1")]),
(vec!["build"], vec![("RCH_SHIM_LOCAL_IDE", "1")]),
(
vec!["check"],
vec![("RUSTC_WORKSPACE_WRAPPER", "/usr/bin/clippy-driver")],
),
(
vec!["check", "--message-format=json-diagnostic-short"],
vec![],
),
(vec!["metadata"], vec![]),
] {
let out = run_shim(&body, "cargo", &args, &env);
assert!(out.starts_with("LOCAL"), "{args:?} should be local: {out}");
assert!(
out.ends_with("jobs=8"),
"{args:?} must cap local parallelism, got: {out}"
);
}
}
#[cfg(unix)]
#[test]
fn offload_path_is_not_capped() {
let body = cargo_shim_body(true);
let out = run_shim(&body, "cargo", &["build"], &[]);
assert!(out.starts_with("OFFLOAD"), "expected offload, got: {out}");
assert!(
out.ends_with("jobs=unset"),
"offload must not inherit the local cap, got: {out}"
);
}
#[cfg(unix)]
#[test]
fn explicit_job_settings_win_over_the_default_cap() {
let body = cargo_shim_body(true);
let out = run_shim(
&body,
"cargo",
&["build"],
&[
("RCH_CARGO_WRAPPER_BYPASS", "1"),
("CARGO_BUILD_JOBS", "64"),
],
);
assert!(out.ends_with("jobs=64"), "explicit value must win: {out}");
let out = run_shim(
&body,
"cargo",
&["build"],
&[
("RCH_CARGO_WRAPPER_BYPASS", "1"),
("RCH_LOCAL_MAX_JOBS", "16"),
],
);
assert!(out.ends_with("jobs=16"), "override must apply: {out}");
}
#[cfg(unix)]
#[test]
fn clippy_shim_local_paths_cap_parallelism() {
let body = cargo_clippy_shim_body(true);
for (args, env) in [
(vec!["--version"], vec![("RCH_CARGO_WRAPPER_BYPASS", "1")]),
(vec!["clippy", "--workspace"], vec![]),
] {
let out = run_shim(&body, "cargo-clippy", &args, &env);
assert!(out.starts_with("LOCAL"), "{args:?} should be local: {out}");
assert!(out.ends_with("jobs=8"), "{args:?} must cap: {out}");
}
}
#[cfg(unix)]
#[test]
fn local_ide_escape_hatch_forces_local() {
let body = cargo_shim_body(true);
let out = run_shim(&body, "cargo", &["build"], &[("RCH_SHIM_LOCAL_IDE", "1")]);
assert!(out.starts_with("LOCAL"), "expected local, got: {out}");
}
#[cfg(unix)]
fn selected_local_output(args: &[&str], extras: &[(&str, &str)]) -> std::process::Output {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &[]);
write_exe(
&dir.join("rustup"),
"#!/bin/sh\nprintf '%s\\000' \"${RCH_CARGO_WRAPPER_BYPASS:-unset}\" \"${CARGO_BUILD_JOBS:-unset}\" \"$@\"\nexit \"${RUSTUP_STATUS:-0}\"\n",
);
let wrong = dir.join("real-cargo");
let mut env = vec![
("RCH_REAL_CARGO", wrong.to_str().unwrap()),
("RCH_SHIM_REAL_CARGO", wrong.to_str().unwrap()),
("RUSTUP_TOOLCHAIN", "wrong-ambient"),
];
env.extend_from_slice(extras);
exec_shim(&dir, "cargo", args, &env, Some(&dir))
}
#[cfg(unix)]
#[test]
fn selected_toolchain_survives_every_explicit_local_escape() {
let args = [
"+nightly",
"--offline",
"test",
"--",
"",
"a\nb",
"$(false)",
];
for cause in [
("RCH_CARGO_WRAPPER_BYPASS", "1"),
("RCH_SHIM_LOCAL_IDE", "1"),
("RUSTC_WORKSPACE_WRAPPER", "/clippy-driver"),
] {
for (setting, cap) in [
(("RCH_LOCAL_MAX_JOBS", "8"), "8"),
(("RCH_LOCAL_MAX_JOBS", "3"), "3"),
(("CARGO_BUILD_JOBS", "19"), "19"),
] {
let out = selected_local_output(&args, &[cause, setting]);
assert!(out.status.success(), "{cause:?}: {:?}", out.stderr);
let mut expected = vec!["1", cap, "run", "nightly", "cargo"];
expected.extend_from_slice(&args[1..]);
expected.push("");
assert_eq!(out.stdout, expected.join("\0").as_bytes());
}
}
}
#[cfg(unix)]
#[test]
fn selected_toolchain_handles_local_queries_and_preserves_rustup_failure() {
for tail in [
vec!["--version"],
vec!["check", "--message-format=json-diagnostic-short"],
] {
let mut args = vec!["+custom"];
args.extend_from_slice(&tail);
let out = selected_local_output(&args, &[]);
assert!(out.status.success());
let mut expected = vec!["1", "8", "run", "custom", "cargo"];
expected.extend(tail);
expected.push("");
assert_eq!(out.stdout, expected.join("\0").as_bytes());
}
let out = selected_local_output(
&["+absent", "build"],
&[("RCH_SHIM_LOCAL_IDE", "1"), ("RUSTUP_STATUS", "67")],
);
assert_eq!(out.status.code(), Some(67));
assert_eq!(out.stdout, b"1\x008\x00run\x00absent\x00cargo\x00build\x00");
}
#[cfg(unix)]
#[test]
fn unavailable_rustup_or_empty_selector_cannot_use_the_default_compiler() {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &[]);
for (selector, expected_code) in [("+nightly", 127), ("+", 2)] {
let out = std::process::Command::new("/bin/sh")
.arg(dir.join("cargo"))
.args([selector, "build"])
.env_clear()
.env("PATH", &dir)
.env("HOME", &dir)
.env("RCH_SHIM_LOCAL_IDE", "1")
.env("RCH_REAL_CARGO", dir.join("real-cargo"))
.output()
.unwrap();
assert_eq!(out.status.code(), Some(expected_code));
assert!(
out.stdout.is_empty(),
"an unrelated real Cargo was executed"
);
assert!(!out.stderr.is_empty());
}
}
#[cfg(unix)]
#[test]
fn selected_local_toolchain_reentry_uses_the_real_wrapped_toolchain() {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &[]);
let toolchain = dir.join(".rustup/toolchains/selected/bin");
let canonical = dir.join(".rch/shims");
std::fs::create_dir_all(&toolchain).unwrap();
std::fs::create_dir_all(&canonical).unwrap();
write_exe(&canonical.join("cargo"), &cargo_shim_body(true));
write_exe(&toolchain.join("cargo"), &toolchain_wrap_body());
write_exe(
&toolchain.join("cargo-rch-real"),
"#!/bin/sh\nexec rustc \"$@\"\n",
);
write_exe(
&toolchain.join("rustc"),
"#!/bin/sh\nprintf '%s\\000' SELECTED \"${RCH_CARGO_WRAPPER_BYPASS:-unset}\" \"${CARGO_BUILD_JOBS:-unset}\" \"$@\"\n",
);
write_exe(
&dir.join("rustc"),
"#!/bin/sh\necho WRONG-COMPILER\nexit 91\n",
);
write_exe(
&dir.join("rustup"),
r#"#!/bin/sh
[ "$1" = run ] && [ "$2" = selected ] && [ "$3" = cargo ] || exit 92
shift 3
exec "$HOME/.rustup/toolchains/selected/bin/cargo" "$@"
"#,
);
let out = exec_shim(
&dir,
"cargo",
&["+selected", "build", "--locked"],
&[
("RCH_SHIM_LOCAL_IDE", "1"),
("RUSTUP_TOOLCHAIN", "wrong-ambient"),
],
Some(&dir),
);
assert!(out.status.success(), "{:?}", out.stderr);
assert_eq!(out.stdout, b"SELECTED\x001\x008\x00build\x00--locked\x00");
assert!(out.stderr.is_empty());
}
#[cfg(unix)]
fn record_shim(
body: &str,
args: &[std::ffi::OsString],
env: &[(&str, &str)],
) -> std::process::Output {
let directory = tempfile::tempdir().unwrap();
let root = directory.path();
write_exe(&root.join("shim"), body);
write_exe(
&root.join("rch"),
concat!(
"#!/bin/sh\n",
"printf '%s\\0' RCH \"${RCH_REQUIRE_REMOTE:-unset}\" ",
"\"${RCH_QUEUE_WHEN_BUSY:-unset}\" \"${CARGO_BUILD_JOBS:-unset}\"\n",
"printf '%s\\0' \"$@\"\n",
"exit \"${STUB_RCH_EXIT:-0}\"\n",
),
);
write_exe(
&root.join("real-cargo"),
"#!/bin/sh\nprintf '%s\\0' LOCAL \"${CARGO_BUILD_JOBS:-unset}\"\nif [ \"$#\" -gt 0 ]; then printf '%s\\0' \"$@\"; fi\n",
);
write_exe(
&root.join("rustup"),
"#!/bin/sh\n[ \"$1\" = run ] && [ \"$3\" = cargo ] || exit 92\nRUSTUP_TOOLCHAIN=$2\nexport RUSTUP_TOOLCHAIN\nshift 3\nexec \"$HOME/real-cargo\" \"$@\"\n",
);
let mut command = std::process::Command::new("/bin/sh");
command
.arg(root.join("shim"))
.args(args)
.env_clear()
.env("PATH", format!("{}:/usr/bin:/bin", root.display()))
.env("HOME", root)
.env("RCH_SHIM_REAL_CARGO", root.join("real-cargo"))
.env("RCH_SHIM_REAL_CARGO_CLIPPY", root.join("real-cargo"));
for (name, value) in env {
command.env(name, value);
}
output_retrying_text_busy(&mut command)
}
#[cfg(unix)]
fn argv_bytes(args: &[&str]) -> Vec<std::ffi::OsString> {
args.iter()
.map(|arg| std::ffi::OsString::from(*arg))
.collect()
}
#[cfg(unix)]
fn recorded_fields(output: &std::process::Output) -> Vec<&[u8]> {
let mut fields: Vec<_> = output.stdout.split(|byte| *byte == 0).collect();
assert_eq!(fields.pop(), Some(&b""[..]), "unterminated argument record");
fields
}
#[cfg(unix)]
#[test]
fn global_options_and_toolchain_selectors_reach_rch_with_original_arguments() {
let cases: &[&[&str]] = &[
&["+nightly", "build", "--release"],
&["+1.90.0", "--offline", "test", "--", "--nocapture"],
&["--color", "never", "--locked", "check"],
&["--config", "build.jobs=2", "build"],
&["--config=build.jobs=2", "-vvv", "clippy"],
&["+nightly", "-Z", "unstable-options", "build"],
&[
"+nightly",
"-Zunstable-options",
"-C",
"/selected/project",
"build",
],
&["--", "build"],
];
for args in cases {
let command = shell_words::join(std::iter::once("cargo").chain(args.iter().copied()));
assert!(
rch_common::classify_command(&command).is_compilation,
"{command}"
);
let output = record_shim(&cargo_shim_body(true), &argv_bytes(args), &[]);
assert!(output.status.success(), "{:?}: {:?}", args, output);
let fields = recorded_fields(&output);
assert_eq!(
&fields[..7],
&[
b"RCH".as_slice(),
b"1",
b"1",
b"unset",
b"exec",
b"--",
b"cargo"
]
);
assert_eq!(
&fields[7..],
args.iter().map(|arg| arg.as_bytes()).collect::<Vec<_>>()
);
}
}
#[cfg(unix)]
#[test]
fn opaque_global_values_and_local_aliases_are_not_build_commands() {
let cases: &[&[&str]] = &[
&["--config", "build", "metadata"],
&["--color", "test", "--version"],
&["--config", "alias.ltest=\"test --lib\"", "ltest"],
&["+nightly", "fmt", "--check"],
&["+nightly", "--help", "build"],
&["--unknown", "build"],
&["--config"],
&["--color", "build"],
&["-Z", "build"],
&["+nightly"],
];
for args in cases {
let output = record_shim(&cargo_shim_body(true), &argv_bytes(args), &[]);
assert!(output.status.success(), "{:?}: {:?}", args, output);
let fields = recorded_fields(&output);
assert_eq!(&fields[..2], &[b"LOCAL".as_slice(), b"8"]);
let expected: &[&str] = if args.first().is_some_and(|arg| arg.starts_with('+')) {
&args[1..]
} else {
args
};
assert_eq!(
&fields[2..],
expected
.iter()
.map(|arg| arg.as_bytes())
.collect::<Vec<_>>()
);
}
}
#[cfg(unix)]
#[test]
fn additional_supported_build_entrypoints_no_longer_bypass_rch() {
for args in [
vec!["run"],
vec!["r"],
vec!["zigbuild"],
vec!["xwin", "build"],
] {
let command = shell_words::join(std::iter::once("cargo").chain(args.iter().copied()));
assert!(
rch_common::classify_command(&command).is_compilation,
"{command}"
);
let output = record_shim(&cargo_shim_body(true), &argv_bytes(&args), &[]);
assert!(output.status.success());
assert_eq!(recorded_fields(&output)[0], b"RCH");
}
}
#[cfg(unix)]
#[test]
fn separate_ide_format_is_local_but_payload_format_is_not_a_routing_option() {
for format in ["json-diagnostic-rendered-ansi", "json-diagnostic-short"] {
let args = argv_bytes(&["check", "--message-format", format]);
let output = record_shim(&cargo_shim_body(true), &args, &[]);
assert_eq!(recorded_fields(&output)[0], b"LOCAL");
for args in [
vec![
"test".to_owned(),
"--".to_owned(),
format!("--message-format={format}"),
],
vec![
"test".to_owned(),
"--".to_owned(),
"--message-format".to_owned(),
format.to_owned(),
],
] {
let args: Vec<_> = args.into_iter().map(std::ffi::OsString::from).collect();
let output = record_shim(&cargo_shim_body(true), &args, &[]);
assert_eq!(recorded_fields(&output)[0], b"RCH");
}
}
let output = record_shim(
&cargo_shim_body(true),
&argv_bytes(&["check", "--message-format", "json"]),
&[],
);
assert_eq!(recorded_fields(&output)[0], b"RCH");
}
#[cfg(unix)]
#[test]
fn shim_handoff_preserves_literal_and_non_utf8_arguments() {
use std::os::unix::ffi::{OsStrExt, OsStringExt};
let mut args = argv_bytes(&[
"+nightly",
"--config",
"build.rustflags=['--cfg', 'feature=\"a b\"']",
"test",
"--",
"",
"with space",
"literal'quote",
"$(not-executed)",
"*.rs",
"line\nbreak",
"--message-format=json-diagnostic-short",
]);
args.push(std::ffi::OsString::from_vec(b"raw-\xff\xfe".to_vec()));
let output = record_shim(&cargo_shim_body(true), &args, &[]);
assert!(output.status.success());
let fields = recorded_fields(&output);
assert_eq!(fields[0], b"RCH");
assert_eq!(
&fields[7..],
args.iter().map(|arg| arg.as_bytes()).collect::<Vec<_>>()
);
}
#[cfg(unix)]
#[test]
fn cargo_and_direct_clippy_share_install_policy_without_a_local_job_cap() {
for strict in [false, true] {
for (body, args, clippy) in [
(
cargo_shim_body(strict),
vec!["--locked", "clippy", "--all-targets"],
false,
),
(
cargo_clippy_shim_body(strict),
vec!["--all-targets", "--", "-D", "warnings"],
true,
),
] {
let output = record_shim(&body, &argv_bytes(&args), &[]);
assert!(output.status.success());
let fields = recorded_fields(&output);
let policy = if strict {
b"1".as_slice()
} else {
b"unset".as_slice()
};
assert_eq!(&fields[..4], &[b"RCH".as_slice(), policy, policy, b"unset"]);
let mut expected = vec![b"exec".as_slice(), b"--", b"cargo"];
if clippy {
expected.push(b"clippy");
}
expected.extend(args.iter().map(|arg| arg.as_bytes()));
assert_eq!(&fields[4..], expected);
}
}
}
#[cfg(unix)]
#[test]
fn routing_failure_preserves_exit_and_never_runs_the_local_stub() {
for (body, args) in [
(
cargo_shim_body(true),
vec!["+nightly", "--locked", "clippy"],
),
(cargo_clippy_shim_body(true), vec!["--all-targets"]),
] {
for exit in ["1", "101", "103", "137"] {
let output = record_shim(&body, &argv_bytes(&args), &[("STUB_RCH_EXIT", exit)]);
assert_eq!(output.status.code(), Some(exit.parse().unwrap()));
assert_eq!(recorded_fields(&output)[0], b"RCH");
}
}
}
#[cfg(unix)]
#[test]
fn explicit_local_controls_still_bypass_prefixed_builds() {
for control in [
"RCH_CARGO_WRAPPER_BYPASS",
"RCH_SHIM_LOCAL_IDE",
"RUSTC_WORKSPACE_WRAPPER",
] {
let output = record_shim(
&cargo_shim_body(true),
&argv_bytes(&["+nightly", "--offline", "build"]),
&[(control, "1")],
);
assert!(output.status.success());
assert_eq!(&recorded_fields(&output)[..2], &[b"LOCAL".as_slice(), b"8"]);
}
}
const V3_SHIM_BODY: &str = r#"#!/bin/sh
# rch cargo shim — MANAGED FILE, edit via `rch shim install`.
# rch-shim-version: 3
#
# Harness-agnostic build offload: any agent (codex/scripts/CI, not only Claude
# Code) that runs `cargo build|test|check|...` is routed through `rch exec` to
# the worker fleet instead of compiling locally on this box. See `rch shim`.
REAL="${RCH_SHIM_REAL_CARGO:-$HOME/.cargo/bin/cargo}"
# Cap parallelism on every LOCAL fallback. Local builds never reach rch's slot
# accounting, so nothing else bounds them — and a repo's own .cargo/config.toml
# outranks ~/.cargo/config.toml, so a committed `jobs = -1` (= nproc-1) wins over
# any global default. CARGO_BUILD_JOBS outranks every config file, so setting it
# here is the only reliable cap. An explicitly-set CARGO_BUILD_JOBS always wins;
# tune the default with RCH_LOCAL_MAX_JOBS.
exec_local() {
if [ -z "${CARGO_BUILD_JOBS:-}" ]; then
CARGO_BUILD_JOBS="${RCH_LOCAL_MAX_JOBS:-8}"
export CARGO_BUILD_JOBS
fi
exec "$REAL" "$@"
}
# Loop-break: rch sets RCH_CARGO_WRAPPER_BYPASS=1 on its own local-fallback exec.
# Also fail open if rch is unavailable — never block a build.
if [ "${RCH_CARGO_WRAPPER_BYPASS:-}" = "1" ] || ! command -v rch >/dev/null 2>&1; then
exec_local "$@"
fi
# Explicit operator escape hatch: force this one build local.
if [ "${RCH_SHIM_LOCAL_IDE:-}" = "1" ]; then
exec_local "$@"
fi
# `cargo clippy` runs as: set RUSTC_WORKSPACE_WRAPPER=clippy-driver, then exec an
# inner `cargo check`. Offloading that INNER cargo would drop the wrapper and
# silently return plain check results instead of lints, so it stays local — the
# OUTER `cargo clippy` is the invocation that offloads. Same for any other
# caller-chosen rustc wrapper.
if [ -n "${RUSTC_WORKSPACE_WRAPPER:-}" ]; then
exec_local "$@"
fi
# rust-analyzer must stay local (streaming JSON + local state). It is identified
# by its distinctive rendered-ansi/short diagnostic formats. A PLAIN
# `--message-format=json` is what scripts, CI and build tooling pass, and those
# must still offload — treating every --message-format as an IDE was the bug
# that silently kept script-driven builds on the dev box.
for a in "$@"; do
case "$a" in
--message-format=json-diagnostic-rendered-ansi*|--message-format=json-diagnostic-short*)
exec_local "$@" ;;
esac
done
case "${1:-}" in
build|b|test|t|check|c|clippy|bench|doc|nextest)
exec env RCH_REQUIRE_REMOTE=1 RCH_QUEUE_WHEN_BUSY=1 rch exec -- cargo "$@" ;;
*)
exec_local "$@" ;;
esac
"#;
#[test]
fn shim_body_documents_tiered_real_cargo_resolution() {
let body = cargo_shim_body(true);
for needle in [
"resolve_real",
"RCH_REAL_CARGO",
"RCH_SHIM_REAL_CARGO",
"cargo-rch-real",
"rustc --print sysroot",
"rch shim uninstall",
] {
assert!(body.contains(needle), "shim body must carry {needle}");
}
}
#[cfg(unix)]
#[test]
fn real_cargo_tier1_explicit_env_override_wins() {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &["build"]);
let over = dir.join("override-cargo");
write_exe(
&over,
"#!/bin/sh\necho OVERRIDE \"$@\" jobs=${CARGO_BUILD_JOBS:-unset}\n",
);
let out = exec_shim(
&dir,
"cargo",
&["build"],
&[
("RCH_SHIM_LOCAL_IDE", "1"),
("RCH_REAL_CARGO", over.to_str().unwrap()),
(
"RCH_SHIM_REAL_CARGO",
dir.join("real-cargo").to_str().unwrap(),
),
],
None,
);
let _ = std::fs::remove_dir_all(&dir);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.starts_with("OVERRIDE"), "tier 1 must win: {stdout}");
}
#[cfg(unix)]
#[test]
fn real_cargo_tier2_wrapper_handoff_beats_plain_proxy() {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &["build"]);
let home = dir.join("home");
std::fs::create_dir_all(home.join(".cargo").join("bin")).unwrap();
write_exe(
&home.join(".cargo").join("bin").join("cargo"),
"#!/bin/sh\necho PROXY \"$@\"\n",
);
let out = exec_shim(
&dir,
"cargo",
&["build"],
&[
("RCH_SHIM_LOCAL_IDE", "1"),
(
"RCH_SHIM_REAL_CARGO",
dir.join("real-cargo").to_str().unwrap(),
),
],
Some(&home),
);
let _ = std::fs::remove_dir_all(&dir);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.starts_with("LOCAL"),
"tier 2 must beat the plain proxy: {stdout}"
);
}
#[cfg(unix)]
#[test]
fn real_cargo_tier3_sysroot_discovery_and_path_prepend() {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &["build"]);
let tc = dir.join("fake-tc");
std::fs::create_dir_all(tc.join("bin")).unwrap();
write_exe(
&tc.join("bin").join("cargo-rch-real"),
"#!/bin/sh\necho TC-REAL \"$@\" jobs=${CARGO_BUILD_JOBS:-unset} first=${PATH%%:*}\n",
);
write_exe(
&dir.join("rustc"),
&format!("#!/bin/sh\necho {}\n", tc.display()),
);
let home = dir.join("home");
std::fs::create_dir_all(home.join(".cargo").join("bin")).unwrap();
write_exe(
&home.join(".cargo").join("bin").join("cargo"),
"#!/bin/sh\necho PROXY \"$@\"\n",
);
let out = exec_shim(
&dir,
"cargo",
&["build"],
&[("RCH_SHIM_LOCAL_IDE", "1"), ("RCH_SHIM_REAL_CARGO", "")],
Some(&home),
);
let _ = std::fs::remove_dir_all(&dir);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.starts_with("TC-REAL"),
"tier 3 must resolve the renamed real cargo: {stdout}"
);
assert!(
stdout.contains(&format!("first={}", tc.join("bin").display())),
"tier 3 must prepend the toolchain bin to PATH: {stdout}"
);
}
#[cfg(unix)]
#[test]
fn real_cargo_tier4_plain_proxy_when_nothing_renamed() {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &["build"]);
let home = dir.join("home");
std::fs::create_dir_all(home.join(".cargo").join("bin")).unwrap();
write_exe(
&home.join(".cargo").join("bin").join("cargo"),
"#!/bin/sh\necho PROXY \"$@\" jobs=${CARGO_BUILD_JOBS:-unset}\n",
);
write_exe(
&dir.join("rustc"),
&format!("#!/bin/sh\necho {}\n", dir.join("no-tc").display()),
);
let out = exec_shim(
&dir,
"cargo",
&["build"],
&[("RCH_SHIM_LOCAL_IDE", "1"), ("RCH_SHIM_REAL_CARGO", "")],
Some(&home),
);
let _ = std::fs::remove_dir_all(&dir);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
assert!(
stdout.starts_with("PROXY"),
"tier 4 must use the plain proxy: {stdout}"
);
assert!(
stdout.ends_with("jobs=8"),
"tier 4 keeps the local cap: {stdout}"
);
}
#[cfg(unix)]
#[test]
fn real_cargo_resolution_failure_names_the_fix() {
let dir = shim_sandbox(&cargo_shim_body(true), "cargo", &["build"]);
let home = dir.join("home"); std::fs::create_dir_all(&home).unwrap();
write_exe(
&dir.join("rustc"),
&format!("#!/bin/sh\necho {}\n", dir.join("no-tc").display()),
);
let out = exec_shim(
&dir,
"cargo",
&["build"],
&[("RCH_SHIM_LOCAL_IDE", "1"), ("RCH_SHIM_REAL_CARGO", "")],
Some(&home),
);
let _ = std::fs::remove_dir_all(&dir);
assert_eq!(out.status.code(), Some(127));
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("RCH_REAL_CARGO"),
"must name the env override: {stderr}"
);
assert!(
stderr.contains("rch shim uninstall"),
"must name the recovery path: {stderr}"
);
}
#[cfg(unix)]
#[test]
fn nightly_renamed_cargo_local_ide_v3_broke_v4_works() {
for (label, body) in [("v3", V3_SHIM_BODY), ("v4", cargo_shim_body(true).as_str())] {
let dir = shim_sandbox(body, "cargo", &["build"]);
let tc = dir.join("nightly-tc");
std::fs::create_dir_all(tc.join("bin")).unwrap();
write_exe(
&tc.join("bin").join("cargo-rch-real"),
"#!/bin/sh\necho TC-REAL \"$@\" first=${PATH%%:*}\n",
);
write_exe(
&dir.join("rustc"),
&format!("#!/bin/sh\necho {}\n", tc.display()),
);
let home = dir.join("home");
std::fs::create_dir_all(home.join(".cargo").join("bin")).unwrap();
write_exe(
&home.join(".cargo").join("bin").join("cargo"),
concat!(
"#!/bin/sh\n",
"echo \"error: the 'cargo' binary, normally provided by the 'cargo' component, is not applicable to the 'nightly' toolchain\" >&2\n",
"exit 1\n"
),
);
let out = exec_shim(
&dir,
"cargo",
&["build"],
&[("RCH_SHIM_LOCAL_IDE", "1"), ("RCH_SHIM_REAL_CARGO", "")],
Some(&home),
);
let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
let _ = std::fs::remove_dir_all(&dir);
if label == "v3" {
assert_eq!(
out.status.code(),
Some(1),
"v3 must die with the proxy's error, stderr: {stderr}"
);
assert!(
stderr.contains("not applicable"),
"v3 proxies straight into the broken rustup dispatch: {stderr}"
);
} else {
assert!(
out.status.success(),
"v4 must build locally via cargo-rch-real, stderr: {stderr}"
);
assert!(
stdout.starts_with("TC-REAL"),
"v4 must resolve the renamed real cargo: {stdout}"
);
assert!(
stdout.contains(&format!("first={}", tc.join("bin").display())),
"v4 must prepend the toolchain bin so children get nightly rustc: {stdout}"
);
}
}
}
#[cfg(unix)]
#[test]
fn bypass_still_breaks_the_loop() {
let body = cargo_shim_body(true);
let out = run_shim(
&body,
"cargo",
&["build"],
&[("RCH_CARGO_WRAPPER_BYPASS", "1")],
);
assert!(out.starts_with("LOCAL"), "expected local, got: {out}");
}
#[cfg(unix)]
#[test]
fn direct_cargo_clippy_offloads() {
let body = cargo_clippy_shim_body(true);
let out = run_shim(&body, "cargo-clippy", &["--all-targets"], &[]);
assert!(out.starts_with("OFFLOAD"), "expected offload, got: {out}");
assert!(
out.contains("cargo clippy"),
"should normalize to `cargo clippy`: {out}"
);
}
#[cfg(unix)]
#[test]
fn cargo_clippy_as_subcommand_passes_through() {
let body = cargo_clippy_shim_body(true);
let out = run_shim(&body, "cargo-clippy", &["clippy", "--all-targets"], &[]);
assert!(out.starts_with("LOCAL"), "expected passthrough, got: {out}");
}
#[test]
fn clippy_shim_carries_the_version_marker() {
let body = cargo_clippy_shim_body(true);
assert_eq!(
installed_shim_version_from_str(&body).as_deref(),
Some(SHIM_VERSION)
);
}
fn installed_shim_version_from_str(body: &str) -> Option<String> {
body.lines().find_map(|line| {
line.trim()
.strip_prefix(SHIM_MARKER)
.map(|v| v.trim().to_string())
})
}
#[test]
fn toolchain_wrapper_is_loop_safe_and_fail_open() {
let body = toolchain_wrap_body();
assert!(body.contains("RCH_CARGO_WRAPPER_BYPASS"));
assert!(body.contains(r#"[ ! -x "$SHIM" ]"#));
assert!(body.contains("command -v rch"));
assert!(body.contains("RCH_SHIM_REAL_CARGO=\"$REAL\""));
assert!(body.contains("export RCH_SHIM_REAL_CARGO"));
assert!(body.contains(r#"SELF_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"#));
assert!(body.contains(REAL_CARGO_NAME));
assert!(body.contains(r#"PATH="$SELF_DIR:$PATH""#));
}
#[cfg(unix)]
#[test]
fn toolchain_wrapper_local_fallback_prepends_its_bin() {
let dir = tempfile::tempdir().unwrap();
let bin = dir.path().join("bin");
std::fs::create_dir_all(&bin).unwrap();
write_exe(&bin.join("cargo"), &toolchain_wrap_body());
write_exe(
&bin.join(REAL_CARGO_NAME),
"#!/bin/sh\necho TC-REAL \"$@\" jobs=${CARGO_BUILD_JOBS:-unset} first=${PATH%%:*}\n",
);
let mut cmd = std::process::Command::new(bin.join("cargo"));
cmd.arg("build")
.env("PATH", format!("{}:/usr/bin:/bin", dir.path().display()))
.env("RCH_CARGO_WRAPPER_BYPASS", "1")
.env_remove("CARGO_BUILD_JOBS")
.env_remove("RCH_LOCAL_MAX_JOBS");
let out = output_retrying_text_busy(&mut cmd);
assert!(
out.status.success(),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
assert!(
stdout.starts_with("TC-REAL"),
"must exec the real cargo: {stdout}"
);
assert!(
stdout.contains(&format!("first={}", bin.display())),
"toolchain bin must lead PATH for child rustc: {stdout}"
);
assert!(stdout.contains("jobs=8"), "local cap must hold: {stdout}");
}
#[test]
fn toolchain_wrapper_version_is_detectable() {
let body = toolchain_wrap_body();
let dir = tempfile::tempdir().unwrap();
let p = dir.path().join("cargo");
std::fs::write(&p, &body).unwrap();
assert_eq!(
toolchain_wrap_version(&p).as_deref(),
Some(TOOLCHAIN_WRAP_VERSION)
);
assert!(is_toolchain_wrapped(&p));
}
#[test]
fn a_real_cargo_binary_is_not_mistaken_for_a_wrapper() {
let dir = tempfile::tempdir().unwrap();
let p = dir.path().join("cargo");
std::fs::write(&p, vec![0u8; 16 * 1024]).unwrap();
assert!(!is_toolchain_wrapped(&p));
assert!(!delegates_to_shim(&p, true));
}
#[cfg(unix)]
#[test]
fn wrap_then_unwrap_restores_the_original_bytes() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().unwrap();
let bin = dir.path().join("bin");
std::fs::create_dir_all(&bin).unwrap();
let cargo = bin.join("cargo");
let original = b"#!/bin/sh\necho i-am-the-real-cargo\n";
std::fs::write(&cargo, original).unwrap();
std::fs::set_permissions(&cargo, std::fs::Permissions::from_mode(0o755)).unwrap();
assert_eq!(wrap_toolchain_cargo(&cargo).unwrap(), WrapOutcome::Wrapped);
assert!(is_toolchain_wrapped(&cargo));
let real = bin.join(REAL_CARGO_NAME);
assert_eq!(std::fs::read(&real).unwrap(), original);
assert_eq!(
std::fs::metadata(&cargo).unwrap().permissions().mode() & 0o111,
0o111
);
assert_eq!(
wrap_toolchain_cargo(&cargo).unwrap(),
WrapOutcome::AlreadyCurrent
);
assert_eq!(std::fs::read(&real).unwrap(), original);
assert!(unwrap_toolchain_cargo(&cargo).unwrap());
assert_eq!(std::fs::read(&cargo).unwrap(), original);
assert!(!real.exists());
assert!(!unwrap_toolchain_cargo(&cargo).unwrap());
}
#[cfg(unix)]
#[test]
fn rustup_replacing_a_toolchain_rewraps_the_new_binary() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().unwrap();
let bin = dir.path().join("bin");
std::fs::create_dir_all(&bin).unwrap();
let cargo = bin.join("cargo");
std::fs::write(&cargo, b"old-cargo").unwrap();
std::fs::set_permissions(&cargo, std::fs::Permissions::from_mode(0o755)).unwrap();
wrap_toolchain_cargo(&cargo).unwrap();
std::fs::write(&cargo, b"new-cargo-from-rustup").unwrap();
assert_eq!(wrap_toolchain_cargo(&cargo).unwrap(), WrapOutcome::Wrapped);
assert_eq!(
std::fs::read(bin.join(REAL_CARGO_NAME)).unwrap(),
b"new-cargo-from-rustup"
);
assert!(unwrap_toolchain_cargo(&cargo).unwrap());
assert_eq!(std::fs::read(&cargo).unwrap(), b"new-cargo-from-rustup");
}
#[cfg(unix)]
#[test]
fn unwrap_refuses_when_the_real_binary_is_missing() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().unwrap();
let bin = dir.path().join("bin");
std::fs::create_dir_all(&bin).unwrap();
let cargo = bin.join("cargo");
std::fs::write(&cargo, b"real").unwrap();
std::fs::set_permissions(&cargo, std::fs::Permissions::from_mode(0o755)).unwrap();
wrap_toolchain_cargo(&cargo).unwrap();
std::fs::remove_file(bin.join(REAL_CARGO_NAME)).unwrap();
assert!(unwrap_toolchain_cargo(&cargo).is_err());
assert!(cargo.exists());
}
#[cfg(unix)]
#[test]
fn concurrent_wrap_never_preserves_a_wrapper_as_the_real_cargo() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().unwrap();
let bin = dir.path().join("bin");
std::fs::create_dir_all(&bin).unwrap();
let cargo = bin.join("cargo");
std::fs::write(&cargo, toolchain_wrap_body()).unwrap();
std::fs::set_permissions(&cargo, std::fs::Permissions::from_mode(0o755)).unwrap();
assert!(wrap_toolchain_cargo(&cargo).is_err());
assert!(is_toolchain_wrapped(&cargo));
assert!(!bin.join(REAL_CARGO_NAME).exists());
}
fn write_path_candidate(path: &Path, contents: &[u8], executable: bool) {
std::fs::write(path, contents).unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mode = if executable { 0o755 } else { 0o644 };
std::fs::set_permissions(path, std::fs::Permissions::from_mode(mode)).unwrap();
}
#[cfg(not(unix))]
let _ = executable;
}
#[cfg(unix)]
fn shell_cargo_output(path: &std::ffi::OsStr, cwd: &Path) -> String {
let output = output_retrying_text_busy(
std::process::Command::new("/bin/sh")
.args(["-c", "cargo"])
.env_clear()
.env("PATH", path)
.env("HOME", cwd)
.current_dir(cwd),
);
assert!(
output.status.success(),
"shell lookup failed: stdout={} stderr={}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).unwrap().trim().to_owned()
}
#[cfg(unix)]
#[test]
fn empty_path_entries_resolve_cargo_from_cwd_in_order() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join("shims");
std::fs::create_dir_all(&shim_dir).unwrap();
let shim = shim_dir.join("cargo");
write_path_candidate(&shim, b"#!/bin/sh\nprintf SHIM\n", true);
write_path_candidate(
&dir.path().join("cargo"),
b"#!/bin/sh\nprintf LOCAL\n",
true,
);
let with_empty =
std::env::join_paths([std::path::PathBuf::new(), shim_dir.clone()]).unwrap();
assert_eq!(
cargo_interception_in(&with_empty, &shim, dir.path()),
Interception::None
);
let trailing_empty = std::env::join_paths([shim_dir, std::path::PathBuf::new()]).unwrap();
assert_eq!(
cargo_interception_in(&trailing_empty, &shim, dir.path()),
Interception::Direct
);
assert_eq!(shell_cargo_output(&with_empty, dir.path()), "LOCAL");
assert_eq!(shell_cargo_output(&trailing_empty, dir.path()), "SHIM");
assert_eq!(shell_cargo_output("".as_ref(), dir.path()), "LOCAL");
assert_eq!(
cargo_interception_in("".as_ref(), &shim, dir.path()),
Interception::None
);
}
#[cfg(unix)]
#[test]
fn first_cargo_on_path_decides_and_a_real_binary_reports_none() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join("shims");
let real_dir = dir.path().join("cargo-bin");
std::fs::create_dir_all(&shim_dir).unwrap();
std::fs::create_dir_all(&real_dir).unwrap();
let shim = shim_dir.join("cargo");
write_path_candidate(&shim, cargo_shim_body(true).as_bytes(), true);
write_path_candidate(&real_dir.join("cargo"), &vec![0u8; 16 * 1024], true);
let real_first = std::env::join_paths([real_dir.clone(), shim_dir.clone()]).unwrap();
assert_eq!(
cargo_interception_in(&real_first, &shim, dir.path()),
Interception::None
);
let shim_first = std::env::join_paths([shim_dir, real_dir]).unwrap();
assert_eq!(
cargo_interception_in(&shim_first, &shim, dir.path()),
Interception::Direct
);
}
#[cfg(unix)]
#[test]
fn delegating_wrapper_earlier_on_path_is_still_interception() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join("shims");
let deleg_dir = dir.path().join("local-bin");
std::fs::create_dir_all(&shim_dir).unwrap();
std::fs::create_dir_all(&deleg_dir).unwrap();
let shim = shim_dir.join("cargo");
write_path_candidate(&shim, cargo_shim_body(true).as_bytes(), true);
write_path_candidate(
&deleg_dir.join("cargo"),
b"#!/bin/sh\nSHIM=\"$HOME/.rch/shims/cargo\"\nexec \"$SHIM\" \"$@\"\n",
true,
);
let deleg_first = std::env::join_paths([deleg_dir, shim_dir]).unwrap();
assert_eq!(
cargo_interception_in(&deleg_first, &shim, dir.path()),
Interception::Delegated
);
}
#[test]
fn delegating_wrapper_counts_as_interception() {
let dir = tempfile::tempdir().unwrap();
let p = dir.path().join("cargo");
std::fs::write(
&p,
b"#!/bin/sh\nSHIM=\"$HOME/.rch/shims/cargo\"\nexec \"$SHIM\" \"$@\"\n",
)
.unwrap();
assert!(delegates_to_shim(&p, true));
assert!(!delegates_to_shim(&p, false));
}
#[cfg(windows)]
#[test]
fn windows_path_eligibility_rejects_shebang_files_and_resolves_real_pe_images() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join("shims");
let earlier_dir = dir.path().join("earlier");
std::fs::create_dir_all(&shim_dir).unwrap();
std::fs::create_dir_all(&earlier_dir).unwrap();
let shim = shim_dir.join("cargo");
let earlier = earlier_dir.join("cargo");
let executable = std::env::current_exe().unwrap();
std::fs::copy(&executable, &shim).unwrap();
write_path_candidate(
&earlier,
b"#!/bin/sh\nexec \"$HOME/.rch/shims/cargo\" \"$@\"\n",
true,
);
let earlier_first = std::env::join_paths([&earlier_dir, &shim_dir]).unwrap();
assert!(
which::which(&earlier).is_err(),
"a shebang is not a native PE image"
);
assert_eq!(
cargo_interception_in(&earlier_first, &shim, dir.path()),
Interception::Direct
);
std::fs::copy(&executable, &earlier).unwrap();
assert_eq!(
cargo_interception_in(&earlier_first, &shim, dir.path()),
Interception::None
);
let shim_first = std::env::join_paths([&shim_dir, &earlier_dir]).unwrap();
assert_eq!(
cargo_interception_in(&shim_first, &shim, dir.path()),
Interception::Direct
);
write_path_candidate(&shim, cargo_shim_body(true).as_bytes(), true);
assert_eq!(
cargo_interception_in(&shim_first, &shim, dir.path()),
Interception::None,
"an extensionless POSIX shim cannot authorize native interception"
);
}
#[cfg(windows)]
#[test]
fn windows_relative_candidates_use_explicit_cwd_with_real_pe_images() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join("shims");
std::fs::create_dir_all(&shim_dir).unwrap();
let shim = shim_dir.join("cargo");
let executable = std::env::current_exe().unwrap();
std::fs::copy(&executable, &shim).unwrap();
std::fs::copy(executable, dir.path().join("cargo")).unwrap();
let cwd_first = std::env::join_paths([Path::new("."), Path::new("shims")]).unwrap();
let shim_first = std::env::join_paths([Path::new("shims"), Path::new(".")]).unwrap();
assert_eq!(
cargo_interception_in(&cwd_first, &shim, dir.path()),
Interception::None
);
assert_eq!(
cargo_interception_in(&shim_first, &shim, dir.path()),
Interception::Direct
);
}
#[cfg(unix)]
#[test]
fn path_lookup_skips_nonexecutables_and_legacy_target_must_execute() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join(".rch/shims");
let real_dir = dir.path().join(".cargo/bin");
let wrapper_dir = dir.path().join("local-bin");
for path in [&shim_dir, &real_dir, &wrapper_dir] {
std::fs::create_dir_all(path).unwrap();
}
let shim = shim_dir.join("cargo");
let real = real_dir.join("cargo");
let wrapper = wrapper_dir.join("cargo");
write_path_candidate(&shim, b"#!/bin/sh\nprintf SHIM\n", true);
write_path_candidate(&real, b"#!/bin/sh\nprintf LOCAL\n", false);
let real_first = std::env::join_paths([&real_dir, &shim_dir]).unwrap();
assert_eq!(
cargo_interception_in(&real_first, &shim, dir.path()),
Interception::Direct
);
assert_eq!(shell_cargo_output(&real_first, dir.path()), "SHIM");
let legacy = b"#!/bin/sh\nSHIM=\"$HOME/.rch/shims/cargo\"\nif [ -x \"$SHIM\" ]; then exec \"$SHIM\" \"$@\"; fi\nexec \"$HOME/.cargo/bin/cargo\" \"$@\"\n";
write_path_candidate(&wrapper, legacy, false);
write_path_candidate(&real, b"#!/bin/sh\nprintf LOCAL\n", true);
let wrapper_first = std::env::join_paths([&wrapper_dir, &real_dir, &shim_dir]).unwrap();
assert_eq!(
cargo_interception_in(&wrapper_first, &shim, dir.path()),
Interception::None
);
assert_eq!(shell_cargo_output(&wrapper_first, dir.path()), "LOCAL");
write_path_candidate(&wrapper, legacy, true);
assert_eq!(
cargo_interception_in(&wrapper_first, &shim, dir.path()),
Interception::Delegated
);
assert_eq!(shell_cargo_output(&wrapper_first, dir.path()), "SHIM");
write_path_candidate(&shim, b"#!/bin/sh\nprintf SHIM\n", false);
assert_eq!(
cargo_interception_in(&wrapper_first, &shim, dir.path()),
Interception::None
);
assert_eq!(shell_cargo_output(&wrapper_first, dir.path()), "LOCAL");
let shim_first = std::env::join_paths([&shim_dir, &real_dir]).unwrap();
assert_eq!(
cargo_interception_in(&shim_first, &shim, dir.path()),
Interception::None
);
assert_eq!(shell_cargo_output(&shim_first, dir.path()), "LOCAL");
}
#[cfg(unix)]
#[test]
fn relative_path_and_literal_tilde_follow_cwd_and_symlink_identity() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join("shims");
let real_dir = dir.path().join("bin");
let literal_tilde = dir.path().join("~/bin");
for path in [&shim_dir, &real_dir, &literal_tilde] {
std::fs::create_dir_all(path).unwrap();
}
let shim = shim_dir.join("cargo");
write_path_candidate(&shim, b"#!/bin/sh\nprintf SHIM\n", true);
write_path_candidate(&real_dir.join("cargo"), b"#!/bin/sh\nprintf LOCAL\n", true);
std::os::unix::fs::symlink(&shim, literal_tilde.join("cargo")).unwrap();
for (path, expected, output) in [
("bin:./shims", Interception::None, "LOCAL"),
("./shims:bin", Interception::Direct, "SHIM"),
("~/bin:bin", Interception::Direct, "SHIM"),
] {
assert_eq!(
cargo_interception_in(path.as_ref(), &shim, dir.path()),
expected,
"{path}"
);
assert_eq!(
shell_cargo_output(path.as_ref(), dir.path()),
output,
"{path}"
);
}
assert_eq!(
cargo_interception_in("shims".as_ref(), &shim, Path::new("relative-cwd")),
Interception::None
);
}
#[cfg(unix)]
#[test]
fn standalone_marked_copy_does_not_need_executable_canonical_target() {
let dir = tempfile::tempdir().unwrap();
let shim_dir = dir.path().join("shims");
let copy_dir = dir.path().join("copy");
std::fs::create_dir_all(&shim_dir).unwrap();
std::fs::create_dir_all(©_dir).unwrap();
let shim = shim_dir.join("cargo");
write_path_candidate(&shim, cargo_shim_body(true).as_bytes(), false);
let marked = format!("#!/bin/sh\n{SHIM_MARKER}{SHIM_VERSION}\nprintf STANDALONE\n");
write_path_candidate(©_dir.join("cargo"), marked.as_bytes(), true);
let path = std::env::join_paths([©_dir, &shim_dir]).unwrap();
assert_eq!(
cargo_interception_in(&path, &shim, dir.path()),
Interception::Delegated
);
assert_eq!(shell_cargo_output(&path, dir.path()), "STANDALONE");
}
#[test]
fn interception_intercepts_predicate() {
assert!(Interception::Direct.intercepts());
assert!(Interception::Delegated.intercepts());
assert!(!Interception::None.intercepts());
}
}