feat: Run the Rust workspace tests through nextest via command override - #13316
Merged
Merged
Conversation
The synthetic workspace package previously took a magic reserved name
(cargo, then rust). Magic names collide with real packages (a crate
named rust was silently skipped), confuse (rust#test names a package
nobody defined), and overload the toolchain id.
Using Turborepo with Rust now requires naming the workspace in the
root Cargo.toml:
[workspace.metadata]
name = "my-workspace"
The key is deliberately un-namespaced: a workspace's name is a
property of the workspace with universally shared semantics, not tool
configuration. No Rust tool names workspaces today, so this mints an
agnostic convention; if cargo ever grows native workspace names, we
read those and deprecate this.
- Missing name (with members present) is a hard, actionable error
showing the exact TOML to add. Memberless manifests need no name.
- A name colliding with a crate is a discovery error naming both
parties; the reserved-name skip-with-warning hack is deleted.
- Names follow crate-name shape rules (they become task keys and
filter arguments); rust/javascript warn as confusing choices.
- This repository names its workspace turborepo-crates.
Also commits PLAN.md: the task command override design this enables,
replacing the completed devEngines plan.
The task command override design doc is working material, not a repository artifact. Restores the file to its state on main.
PR 1 of the task command override series: the schema, flag, and validation layer. Nothing consumes the field yet - resolution, toolchain plumbing, and hashing follow in the next PR. The command field on task definitions declares the argv a task runs, replacing the toolchain's own resolution (package.json scripts, Cargo verb tables). Three JSON shapes, dispatched by a Deserializable visitor so null and absent stay distinguishable: - an argv array, executed directly - no shell - null or [] - an explicit opt-out, valid only in package-scoped positions (it cancels a broader default for one package) - a per-toolchain map keyed by toolchain id, valid only on unscoped root tasks (scoped positions already determine their toolchain) Gated behind futureFlags.experimentalTaskCommand as a hard error, not a silent strip: ignoring the field would change what executes. Processing canonicalizes toolchain keys (typescript aliases javascript; alias + canonical in one map is a conflict error), rejects unknown keys with a did-you-meant (cargo points at rust), requires experimentalCargoWorkspaces for the rust key, rejects empty arguments and $TURBO_EXTENDS$ (a command is atomic - it also merges atomically across the extends chain), and warns on $VAR-shaped arguments, which are passed literally because there is no shell.
PR 2 of the task command override series: the five-level resolver, toolchain placement, executor plumbing, and hashing. The field parsed and validated in the previous PR now changes what runs. Resolution lives in one function in the engine builder, consulted with the chain entries tagged by position (root pkg#task keys and Package Configuration entries are package-scoped; bare root keys are unscoped defaults). Precedence, highest to lowest: scoped command, package-authored script (the new Toolchain::authors_task, which distinguishes authored definitions from synthesized verb tables), unscoped default (per-toolchain maps fan out to matching packages), toolchain resolution. The resolved override is authoritative in both directions - an argv executes even where the toolchain defines nothing (library crates included), an opt-out never executes even where it does - and that rule is applied uniformly at the three surfaces that ask 'does this task run': global-deps hashing, the TUI task list, and the executor. Toolchains place the override in their frame: argv[0] is the program, nothing prepended, cwd is the package directory, pass-through args append verbatim. Cargo keeps its serial group when the override still invokes cargo (the group exists because of cargo's build-directory lock, a property of the binary). JS overrides bypass the package manager entirely. The argv and opt-out flag join TaskHashable (capnp schema change: the documented one-time hash bust, pinned-hash tests updated), so editing a command invalidates exactly the affected tasks. Run summaries and dry-runs display the literal argv.
…ask-command-resolver
- resolve_command_override precedence unit test with a stub toolchain (the one knob the resolver consults: authors_task) - Cargo frame: overrides work on library crates, cwd is the crate dir, serial group kept iff argv[0] is cargo, dropped otherwise - JS frame: override bypasses the package manager, argv[0] is the program, pass-through args append verbatim - e2e (basic_monorepo): override replaces script, command-only tasks, opt-out no-op, script shadows unscoped default, per-toolchain map fan-out, flag-off hard error, command edit invalidates cache - e2e (cargo_monorepo): rust map key grants tasks to crates, scoped override replaces the verb table - Dry-run snapshot and pinned-hash updates from the capnp schema change (the documented one-time hash bust) - ARCHITECTURE.md: engine-builder section documents the resolver
PR 3 of the task command override series: the dogfood that motivated the whole design. turborepo-crates#test now declares its command in turbo.json - cargo nextest run with the same excludes CI uses - via futureFlags.experimentalTaskCommand. Turbo itself knows nothing about nextest; this repo's configuration does. The Vercel canary pins move to the first canary that can parse the new flag and field (placeholder version; adjust to the actual canary cut after the series merges).
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
tknickman
approved these changes
Jul 9, 2026
cargo test stops at the first failing test binary by default, and the known-flaky graceful_shutdown_test sorts before run_caching, run_summary, and unnamed_packages_test - so the earlier snapshot pass never reached them. Caught by CI; regenerated with --no-fail-fast.
The commandOverride list was initialized unconditionally, and an explicitly initialized empty list is a non-null pointer in capnp - encoded differently from the default null pointer, which defeated canonical form's trailing-default truncation and shifted every task hash for every user on upgrade. Guard both new fields the way external_deps_hash already is: only written when set. Canonicalization then truncates them away and every existing task hash is byte-identical to before - only tasks that actually use a command override hash differently. Reverts the pinned hashes and snapshots the unguarded encoding forced; they now pass at their original values, which is the proof of stability.
anthonyshew
marked this pull request as ready for review
July 9, 2026 17:11
The nextest invocation and its excludes now live in turbo.json as the turborepo-crates#test command, so the three rust_test jobs invoke turbo run test --filter=turborepo-crates instead of duplicating the command - CI and local runs are now identical by construction. turbo.json joins the rust change-detection pattern: it defines what the Rust test task runs, so editing it must re-run the Rust tests.
anthonyshew
added a commit
that referenced
this pull request
Jul 9, 2026
The rust_test jobs run through turbo now (#13316) but had no Remote Cache credentials, so neither task caching nor the compile cache could engage. Wire in the OIDC exchange on push events (matching check-lockfiles and js_native_packages) and set TURBO_CACHE, then flip futureFlags.experimentalCargoSccache: with credentials present, turbo serves the Remote Cache as sccache's storage backend and routes rustc invocations through the embedded wrapper - proven at 3.4x on the bench runs, with the fail-soft layers (runtime status gate, local-compile fallback) making credential or storage trouble degrade to a plain build instead of failing it. Job timeouts padded for compile-cache populate runs, which are slower than plain builds.
anthonyshew
added a commit
that referenced
this pull request
Jul 9, 2026
…13292) ## Why The Rust CI jobs run through turbo now (#13316) — but with no Remote Cache credentials, every run is a cold `cache miss` and the sccache compile cache (whose 3.4× speedup was proven on the [bench runs](https://github.com/vercel/turborepo/actions/runs/28874070404): 407/407 compile-unit hits) sits dormant behind its `RemoteCacheStatus::Enabled` gate. This PR — originally just the flag flip, now adapted to the post-OIDC, post-task-command world — turns both caches on. ## What - **`futureFlags.experimentalCargoSccache: true`** — with credentials present, turbo serves the Remote Cache as sccache's webdav backend and routes rustc invocations through the embedded wrapper. CI-only by design; local builds keep cargo's incremental compilation. - **Rust jobs get Remote Cache credentials**: the OIDC exchange on push events + `TURBO_CACHE` (push: `remote:rw`, PRs: `local:rw`) — the exact pattern `check-lockfiles` and `js_native_packages` already use. This also gives `turborepo-crates#test` remote **task** caching on main. - **Timeouts padded** (15→25 job / 20 step): compile-cache populate runs are slower than plain builds (artifact writes). ## How Safety posture, all previously landed: the proxy only starts when the runtime remote-cache status is actually `Enabled` (bad credentials → no proxy, plain build); `SCCACHE_IGNORE_SERVER_IO_ERROR=1` + the fork's local-compile fallback make mid-run storage trouble degrade instead of fail; ambient `CARGO_INCREMENTAL=0` from `setup-environment` composes with the injection. The wrapper is the released turbo binary itself (`2.10.5-canary.4`) — nothing to install. **Verification is a push-to-main property**: PR runs use the local cache (fork PRs can't mint OIDC tokens), so this PR's own CI proves only that the jobs still pass. After merge, the first main push with Rust changes populates; the second shows `turborepo-crates#test` either as a remote task-cache hit or with compile-unit hits in the cargo output.
anthonyshew
pushed a commit
that referenced
this pull request
Jul 10, 2026
## Release v2.10.5-canary.5 > [!CAUTION] > Versioned docs aliasing FAILED. [View logs](https://github.com/vercel/turborepo/actions/runs/29092745941) ### Changes - release(turborepo): 2.10.5-canary.4 (#13325) (`6626261`) - feat: Run the Rust workspace tests through nextest via command override (#13316) (`6711bbc`) - feat: Engage the Remote Cache and sccache compile cache for Rust CI (#13292) (`1ae2065`) - ci: Pin GitHub Actions to full commit SHAs (#13143) (`ad614d6`) - fix: Make npm prune rehoisting deterministic and complete (#13323) (`2dac737`) - feat: Report incremental cache reuse in the run summary (#13327) (`9f3d24a`) - chore: Remove planning docs (#13329) (`5cd1d02`) - docs: Migrate docs site to @vercel/geistdocs package (#13320) (`5517bb2`) - ci: Update Remote Cache action (#13330) (`4a39887`) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
anthonyshew
pushed a commit
that referenced
this pull request
Jul 13, 2026
## Release v2.10.5 > [!CAUTION] > Versioned docs aliasing FAILED. [View logs](https://github.com/vercel/turborepo/actions/runs/29267191097) ### Changes - perf: Evaluate simple include globs without wax compilation (#13285) (`189897a`) - chore: Dogfood native Cargo support in this repository (#13283) (`42f067b`) - release(turborepo): 2.10.4 (#13286) (`f2fce38`) - ci: Remove dead sccache configuration (#13289) (`558df3f`) - feat: Serve the Remote Cache as an sccache backend for Cargo tasks (#13288) (`0d9803f`) - fix: Reject output path traversal (#13290) (`733ccca`) - fix: Disable the sccache proxy when remote cache use is off (#13291) (`3249e22`) - feat: Embed sccache so the Cargo compile cache needs no installation (#13293) (`b6d0035`) - release(turborepo): 2.10.5-canary.1 (#13294) (`e6cd498`) - fix: Pin a flag-aware turbo canary for the eve-agent deployment (#13295) (`e60a4bd`) - fix: Make the sccache compile cache actually cache (#13296) (`2ac099c`) - release(turborepo): 2.10.5-canary.2 (#13297) (`e6e6fb6`) - fix: Stop ambient CARGO_INCREMENTAL from suppressing compile cache injection (#13298) (`784af75`) - fix: Never let compile cache storage failures fail the build (#13299) (`a6fc6c5`) - test: Add outputs path-traversal negative-case regression tests (#13300) (`2ff6df9`) - release(turborepo): 2.10.5-canary.3 (#13302) (`7e44a29`) - ci: Authenticate to Remote Cache via OIDC token exchange (#13303) (`a86839c`) - fix: Keep pnpm patches with version range keys during prune (#13307) (`3c27a89`) - test: Scrub ambient turbo configuration from integration test children (#13306) (`b03d0d6`) - fix: Show toolchain tasks in the TUI and never run in silence (#13308) (`d7622b6`) - ci: Fix change detection on push events (#13304) (`eec3d61`) - refactor: Rename the Cargo toolchain id to rust (#13311) (`a548b02`) - fix: Remove extraneous bun.lock entries during prune (#13317) (`382e9f5`) - feat: Require a user-declared name for the Cargo workspace package (#13312) (`5f01746`) - feat: Parse and validate the task command field (#13313) (`389ea49`) - chore: Harden turbo-vsc to invoke turbo without a shell (#13319) (`4a19b6e`) - feat: Resolve and execute task command overrides (#13315) (`a549baa`) - release(turborepo): 2.10.5-canary.4 (#13325) (`6626261`) - feat: Run the Rust workspace tests through nextest via command override (#13316) (`6711bbc`) - feat: Engage the Remote Cache and sccache compile cache for Rust CI (#13292) (`1ae2065`) - ci: Pin GitHub Actions to full commit SHAs (#13143) (`ad614d6`) - fix: Make npm prune rehoisting deterministic and complete (#13323) (`2dac737`) - feat: Report incremental cache reuse in the run summary (#13327) (`9f3d24a`) - chore: Remove planning docs (#13329) (`5cd1d02`) - docs: Migrate docs site to @vercel/geistdocs package (#13320) (`5517bb2`) - ci: Update Remote Cache action (#13330) (`4a39887`) - release(turborepo): 2.10.5-canary.5 (#13331) (`f699719`) - ci: Remove path-based workflow scheduling (#13332) (`84f2b2c`) - fix: Resolve EADDRINUSE in kitchen-sink api dev script (#13328) (`5886c71`) - ci: Disable telemetry messages in workflows (#13334) (`2218dea`) - fix: Prevent Cargo run tasks from being cached (#13335) (`68f449b`) - fix: Isolate Cargo cache by host platform (#13337) (`c71f5cd`) - fix: Continue TUI text selection beyond viewport (#13338) (`9cbfd90`) - ci: Dogfood Cargo target restoration (#13336) (`8ad90a7`) - fix: Require current Cargo lockfiles for caching (#13339) (`3a3d381`) - fix: Reject unsupported Cargo local packages (#13340) (`708d656`) - fix: Allow outputs outside package roots (#13342) (`de6a0d3`) - fix: Resolve Cargo lock dependencies by source (#13343) (`2223a33`) - docs: Update Cargo workspace support (#13349) (`241ada8`) - fix: Build Ghostty for baseline CPUs (#13352) (`a2a04cc`) - fix: Preserve watch rerun semantics for task inputs (#13351) (`6ed5eab`) - fix: Isolate Command Overrides From Toolchain Cache I/O (#13354) (`e76f0b4`) - release(turborepo): 2.10.5-canary.6 (#13355) (`f82e2c7`) - fix: Include patched Ghostty crate in Cargo workspace (#13357) (`b3cd7a1`) - ci: Shard Rust tests across runners (#13356) (`ef122d9`) - fix: Hash Cargo build environment inputs (#13348) (`d533266`) - fix: Synchronize Cargo prune Docker lockfile (#13350) (`ed17249`) - chore: Update agents app Eve dependency (#13364) (`38aa7d2`) - fix: Preserve Yarn package extension ranges when pruning (#13363) (`6ed2fb4`) - refactor: Add `Toolchain` output availability (#13360) (`82bcfb6`) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 3 of the task
commandoverride series — the dogfood that motivated the entire design.Why
The question that started all of this was "run
cargo nextest runinstead ofcargo test, without teaching turbo about nextest." This is that, in 15 lines of configuration:Turbo knows nothing about nextest. The excludes mirror today's CI invocations exactly, and
.config/nextest.tomlis already a hashed workspace input (cargo'shash_input_globs), so nextest config edits invalidate cached test results.What
futureFlags.experimentalTaskCommandenabledturborepo-crates#testcommand override (scoped — level 2 — replacing the verb table'scargo test --workspace)2.10.5-canary.4, the real published version)Verification
Locally with a branch-built turbo:
turbo run test --filter=turborepo-crates --dry=jsonresolves the command to the literal argv and hashes it. This also unblocks the parked CI-simplification step 4 (turbo run test --filter=turborepo-cratesin the rust CI jobs, replacing the raw nextest invocations).