Skip to content

feat: Make turbo watch Cargo-aware - #13280

Merged
anthonyshew merged 2 commits into
mainfrom
shew/cargo-watch
Jul 6, 2026
Merged

anthonyshew merged 2 commits into
mainfrom
shew/cargo-watch

Conversation

@anthonyshew

Copy link
Copy Markdown
Contributor

Why

Next in the Cargo workspace series. turbo watch in a Cargo-enabled repo has two failure modes today: the watcher's private package graph is JS-only (crate edits map to nothing), and nothing protects against the feedback loop where Cargo's continuous target/ writes re-trigger the very tasks that produced them. Per the series design, the fix is a trait surface — Toolchain::watch_spec — with both implementations stated honestly.

What

  • Toolchain::watch_spec: workspace-definition file names/paths (change → full rediscovery) and build-byproduct directory prefixes (events dropped). Plain-string data per the toolchain design rules
  • Cargo: any Cargo.toml or the root Cargo.lock triggers rediscovery (the crate set or its edges may have changed); manifests under target/ are exempt; all events under root target/ are dropped — the feedback loop must not depend on a .gitignore entry
  • JavaScript: declares nothing extra, documented as the real answer — JS workspace redefinition is already caught by the change mapper's conservative all-packages fallback
  • The watcher builds its package graph with the same toolchains a run registers (plumbed from the future flag in watch.rs and daemon.rs), so watch sees the run's package set
  • Known gap documented in ARCHITECTURE.md: hash-watcher content dedup is JS-glob-based, so a no-op crate save re-runs as a fast cache hit
  • Rider: one-line collapsible-if fix in turborepo-napi that currently breaks cargo lint on main (and therefore every contributor's pre-push hook) — flagged here rather than a separate PR since it blocks this branch's own push

How

Flag off: watcher behavior is unchanged (the merged spec is empty; classify short-circuits identically). Flag on, verified live under turbo watch on the cargo_monorepo fixture: dependency-crate source edit re-runs the entrypoint; JS edit re-runs only the JS package; writes under target/ (including a Cargo.toml cargo places there) trigger nothing; adding a new crate manifest triggers full rediscovery. Unit coverage: three classify tests (manifest → rediscovery, lockfile → rediscovery, target/ suppression) each asserting the flag-off behavior too.

Watch mode becomes toolchain-generic through a new trait surface:
Toolchain::watch_spec declares the workspace-definition files whose change
requires full rediscovery and the build-byproduct directories whose events
must be ignored. The watcher consumes the union of every registered
toolchains spec, and builds its private package graph with the same
toolchains a run would register — watch sees exactly the package set a run
sees.

Cargo declares: any Cargo.toml or the root Cargo.lock defines the crate set
and its edges, so their change triggers rediscovery (manifests under
target/ are build byproducts, not workspace definition, and are exempt);
events under the root target/ directory are dropped entirely — Cargo writes
there continuously during builds, and the feedback loop where build writes
re-trigger the tasks that produced them must not depend on a .gitignore
entry.

JavaScript declares nothing extra, stated as the real answer: workspace
redefinition (a new or removed package.json, a lockfile change) is caught
by the change mappers conservative all-packages fallback, and JS build
outputs land inside package directories where gitignore filtering already
applies.

Known gap, documented: the hash watchers content-hash dedup is
JS-glob-based, so a no-op save inside a crate re-runs its tasks as a fast
cache hit rather than being suppressed.

Includes an unrelated one-line collapsible-if fix in turborepo-napi that
currently breaks cargo lint on main and blocks every pre-push hook.

Verified live on the cargo_monorepo fixture under turbo watch: editing a
dependency crates source re-runs the entrypoint, editing the JS package
re-runs only it, writes under target/ (including a Cargo.toml cargo itself
places there) trigger nothing, and adding a new crate manifest triggers
full rediscovery.
@anthonyshew
anthonyshew requested a review from a team as a code owner July 6, 2026 17:02
@anthonyshew
anthonyshew requested review from tknickman and removed request for a team July 6, 2026 17:02
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples-basic-web Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
examples-designsystem-docs Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
examples-gatsby-web Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
examples-kitchensink-blog Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
examples-nonmonorepo Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
examples-svelte-web Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
examples-tailwind-web Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
examples-vite-web Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
turbo-site Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm
turborepo-eve-agent Ready Ready Preview, Comment, Open in v0 Jul 6, 2026 5:09pm

@anthonyshew
anthonyshew merged commit 39d623e into main Jul 6, 2026
42 checks passed
@anthonyshew
anthonyshew deleted the shew/cargo-watch branch July 6, 2026 17:19
anthonyshew pushed a commit that referenced this pull request Jul 6, 2026
## Release v2.10.4

> [!CAUTION]
> Versioned docs aliasing FAILED. [View
logs](https://github.com/vercel/turborepo/actions/runs/28819266360)

### Changes

- release(turborepo): 2.10.3 (#13222) (`02f4ed0`)
- ci: Retry Windows nextest aborts from transient 0xc0000142 spawn
failures (#13240) (`222ffff`)
- perf: Use mimalloc as the global allocator (#13237) (`c42a8a9`)
- perf: Parse yarn v1 lockfiles in a single pass (#13241) (`6207099`)
- ci: Fix cache outputs for Eve app (#13243) (`d8b556d`)
- perf: Stop materializing spans for the disabled daemon log layer
(#13244) (`623aadc`)
- fix: Keep ancestor-scoped bun dependencies resolvable after prune
renames (#13236) (`8658b66`)
- perf: Overlap external dependency hashing with package file hashing
(#13234) (`445b0c7`)
- perf: Build tracked repo index concurrently with package graph
(#13232) (`f4699c6`)
- perf: Release tokio runtime at exit so background DNS lookups never
stall the user (#13231) (`ae635bd`)
- perf: Overhaul pnpm lockfile parsing and dependency closure
computation (#13228) (`669676d`)
- feat: Add futureFlags.experimentalCargoWorkspaces flag (no-op)
(#13227) (`f546930`)
- feat: Introduce toolchain provider abstraction (#13235) (`f03addb`)
- perf: Parse Berry lockfiles in a single pass (#13242) (`6d36125`)
- fix: Apply input exclusion globs to the filesystem walk (#13224)
(`d3f9dd3`)
- fix: Stop root-directory packages from claiming every file in change
mapping (#13225) (`f80203a`)
- chore: Rename turborepo-repository napi package to @turbo/repository
(#13226) (`c1972b3`)
- perf: Hash git blobs with hardware-accelerated SHA-1 (#13245)
(`17ffa0c`)
- release(turborepo): 2.10.4-canary.1 (#13247) (`a31389e`)
- feat: Discover Cargo crates as packages (#13248) (`d813d12`)
- perf: Reuse per-package external dependency hashes in run summaries
(#13249) (`ef7ef06`)
- perf: Stat workspace turbo.json files concurrently during discovery
(#13251) (`57be8e5`)
- chore: Add tracing spans to workspace discovery and globwalk phases
(#13252) (`bd76946`)
- fix: Avoid non-reentrant libc calls in concurrent shutdown process
scans (#13256) (`bf2d827`)
- perf: Memoize resolved task definitions during engine construction
(#13257) (`9085289`)
- perf: Compute transitive closures and external dependency hashes
concurrently with run setup (#13250) (`b305fe2`)
- perf: Probe microfrontends configs in parallel (#13262) (`ea7c1f8`)
- feat: Execute Cargo crate tasks via cargo (#13261) (`ee81349`)
- perf: Pre-size engine task collections (#13265) (`17bc22e`)
- feat: Expose resolved experimentalCI task configuration in `turbo
query` (#13264) (`0f564e5`)
- feat: Derive input and output globs for Cargo tasks (#13263)
(`05b0e74`)
- perf: Parse large pnpm lockfile sections in parallel (#13266)
(`ac4155e`)
- feat: Hash Cargo external dependencies per-crate (#13267) (`71300aa`)
- ci: Install pnpm 10 in musl containers for Library Release (#13269)
(`5ba9f66`)
- ci: Force pnpm overwrite in Library Release musl containers (#13270)
(`560ee2f`)
- perf: Defer the untracked-scan barrier to first file-hash use (#13268)
(`96b0f5a`)
- ci: Fetch API-created commit before updating local ref (#13271)
(`4cbaf48`)
- release(library): 0.0.1-canary.22 (#13272) (`d2e0691`)
- perf: Remove lock and dispatch overhead from task hash precomputation
(#13273) (`dd6de07`)
- fix: Fall back to lockfile detection in @turbo/repository when package
manager is undeclared (#13275) (`8cf0e75`)
- test: Add end-to-end coverage for Cargo workspaces (#13274)
(`ce18f0a`)
- release(library): 0.0.1-canary.23 (#13276) (`8e3a59f`)
- release(turborepo): 2.10.4-canary.2 (#13278) (`7e02f94`)
- fix: Collapse nested package-manager fallback conditional (#13279)
(`af01fdf`)
- feat: Make turbo watch Cargo-aware (#13280) (`39d623e`)
- perf: Skip dependency-closure assembly for toolchains that derive
nothing (#13277) (`ff0d508`)
- feat: Make turbo prune Cargo-aware (#13281) (`ddc584d`)
- fix: Raise the open-file soft limit at startup (#13282) (`947b478`)
- fix: Stop flagging relative imports that resolve into node_modules in
boundaries (#13284) (`11a68c7`)
- perf: Evaluate simple include globs without wax compilation (#13285)
(`189897a`)
- chore: Dogfood native Cargo support in this repository (#13283)
(`42f067b`)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
anthonyshew pushed a commit that referenced this pull request Jul 7, 2026
## Release v2.10.5-canary.1

> [!CAUTION]
> Versioned docs aliasing FAILED. [View
logs](https://github.com/vercel/turborepo/actions/runs/28833246614)

### Changes

- perf: Remove lock and dispatch overhead from task hash precomputation
(#13273) (`dd6de07`)
- fix: Fall back to lockfile detection in @turbo/repository when package
manager is undeclared (#13275) (`8cf0e75`)
- test: Add end-to-end coverage for Cargo workspaces (#13274)
(`ce18f0a`)
- release(library): 0.0.1-canary.23 (#13276) (`8e3a59f`)
- release(turborepo): 2.10.4-canary.2 (#13278) (`7e02f94`)
- fix: Collapse nested package-manager fallback conditional (#13279)
(`af01fdf`)
- feat: Make turbo watch Cargo-aware (#13280) (`39d623e`)
- perf: Skip dependency-closure assembly for toolchains that derive
nothing (#13277) (`ff0d508`)
- feat: Make turbo prune Cargo-aware (#13281) (`ddc584d`)
- fix: Raise the open-file soft limit at startup (#13282) (`947b478`)
- fix: Stop flagging relative imports that resolve into node_modules in
boundaries (#13284) (`11a68c7`)
- 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`)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview – turborepo-eve-agent 611ac3b5 Deployed Jul 6, 2026 by vercel[bot]
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.

1 participant