Skip to content

fix(turbo): cache-poisoning fixes, config cleanup, uniform typecheck coverage - #519

Open
goosewobbler wants to merge 5 commits into
mainfrom
fix/bundler-cache-poisoning
Open

fix(turbo): cache-poisoning fixes, config cleanup, uniform typecheck coverage#519
goosewobbler wants to merge 5 commits into
mainfrom
fix/bundler-cache-poisoning

Conversation

@goosewobbler

@goosewobbler goosewobbler commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Durable fix for #509 — the bundler dist/ can be self-inconsistent in the turbo remote cache and crash every downstream build. Scope grew during review to cover several adjacent turbo cache-correctness / config issues in the same area.

Changes

1. Stable chunk names (rollup.config.ts)
output.chunkFileNames: '[name].js' — the shared plugins chunk is emitted as plugins.js instead of plugins-<hash>.js. cli.js always imports ./plugins.js, so a cached dist/ can never have cli.js pointing at a hash that doesn't match the cached chunk (root cause of the Cannot destructure property 'injectDependencyPlugin' … undefined crash).

2. Toolchain in the cache key (packages/bundler/turbo.json, new)
Adds $TURBO_ROOT$/pnpm-lock.yaml to the bundler's build inputs so a build-tool bump via a lockfile-only change invalidates the bundler cache key. Belt-and-suspenders — Turbo already hashes each package's npm deps via hashOfExternalDependencies — but it also catches lockfile edits that don't move the resolved closure.

3. Package-config consolidation (packages/bundler/turbo.json + root) — from Greptile review
Moved the bundler build into a package-level turbo.json and deleted the redundant root @wdio/bundler#build override. With the override gone, $TURBO_EXTENDS$ correctly inherits the generic build inputs, so the input list is drift-proof rather than a hand-copied glob list.

4. Track shared tsconfig.base in globalDependencies (root) — found during review
Every package tsconfig extends ../../tsconfig.base.json, but Turbo does not follow tsconfig extends chains and the base files were tracked nowhere. So editing them (e.g. the recent TS6 baseUrl/rootDir migration) served stale build and typecheck caches, remote cache included. Unlike an npm bump, a tsconfig isn't a package, so hashOfExternalDependencies doesn't cover it. Added both shared base configs to globalDependencies.

5. Uniform typecheck coverage in CI + drop redundant build gates (root + _ci-build.reusable.yml) — found during review
The build already fails on type errors, but only for files reachable from the rollup entry points; tsc --noEmit additionally covers all of src/** with the full strict config. CI never ran turbo run typecheck, so that broader check only happened for the packages whose #build gated on typecheck — uneven coverage. Added a Typecheck All Packages CI step (turbo run typecheck --filter=./packages/*, reuses the just-built dist/), which makes the per-build typecheck gates redundant, and removed them. The gate also supplied ^build transitively (typecheck → ^build), and every package builds via @wdio/bundler, so naive removal would drop that ordering; instead the gate-only overrides are deleted (they fall back to the generic build = dependsOn: ["^build"]) and tauri-service's gate is replaced with an explicit ^build.

6. Converge electron/electrobun builds onto ^build (root)
Their #build overrides only pinned @wdio/native-cdp-bridge#build + outputs: ["dist/**"], both already given by the generic build. Deleted them so both services depend on their full workspace-dep set directly (bundler, native-core/spy/types/utils, cdp-bridge) instead of reaching bundler transitively through the cdp-bridge chain.

Testing

  • @wdio/bundler unit + integration tests pass; emitted chunk name and cli.js import verified stable.
  • Bundler turbo hash reacts to pnpm-lock.yaml; a tsconfig.base.json edit now invalidates build + typecheck across packages.
  • --dry-run graph diff confirms removing the gates / overrides loses no build-ordering edge (every affected package still builds @wdio/bundler first; electron/electrobun keep the cdp-bridge edge); forced builds and full turbo run typecheck (21 package tasks) stay green.

Closes #509

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Standing release PR: #456 · 15 packages queued · open 242h 13m · ✅ ready to merge

Release Preview — 17 packages

Note: Labels on this PR are advisory in standing-pr mode. Bumps come from conventional commits in the standing PR; override by editing labels on the standing PR itself. Add release:immediate to bypass the standing PR and release this PR directly.

These changes will be added to the release PR (#456) when merged:

Changelog

@wdio/dioxus-bridge 1.0.0-next.3 → 1.0.0-next.4

Changed

  • Update version to 1.0.0-next.4
@wdio/dioxus-service 1.0.0-next.3 → 1.0.0-next.4

Changed

  • Update version to 1.0.0-next.4
wdio-dioxus-driver 1.0.0-next.3 → 1.0.0-next.4

Changed

  • Update version to 1.0.0-next.4
wdio-dioxus-embedded-driver 1.0.0-next.3 → 1.0.0-next.4

Changed

  • Update version to 1.0.0-next.4
@wdio/tauri-plugin 1.2.0 → 1.2.1

Changed

  • Update version to 1.2.1
@wdio/tauri-service 1.2.0 → 1.2.1

Changed

  • Update version to 1.2.1
tauri-plugin-wdio-webdriver 1.2.0 → 1.2.1

Changed

  • Update version to 1.2.1
@wdio/flutter-service 1.0.0-next.1 → 1.0.0-next.2

Changed

  • Update version to 1.0.0-next.2
wdio_flutter N/A → 0.1.1

Changed

  • Update version to 0.1.1
@wdio/electrobun-service 0.1.0 → 0.1.1

Changed

  • Update version to 0.1.1
@wdio/electron-service 10.1.0 → 10.1.1

Changed

  • Update version to 10.1.1
@wdio/native-cdp-bridge 1.0.0 → 1.0.1

Changed

  • Update version to 1.0.1
@wdio/native-core 1.0.0 → 1.0.1

Changed

  • Update version to 1.0.1
@wdio/native-mobile-core 1.0.0 → 1.0.1

Changed

  • Update version to 1.0.1
@wdio/native-spy 1.1.0 → 1.1.1

Changed

  • Update version to 1.1.1
@wdio/native-types 2.4.0 → 2.4.1

Changed

  • Update version to 2.4.1
@wdio/native-utils 2.5.0 → 2.5.1

Changed

  • Update version to 2.5.1
@wdio/react-native-service 1.0.0-next.0 → 1.0.0-next.1

Changed

  • Update version to 1.0.0-next.1

After merge — predicted release

No version escalation — this PR's changes will be included in the queued release without affecting the projected versions.

Package Standing PR This PR After merge
@wdio/dioxus-bridge 1.0.0-next.4 1.0.0-next.4 1.0.0-next.4
@wdio/dioxus-service 1.0.0-next.4 1.0.0-next.4 1.0.0-next.4
@wdio/electrobun-service 0.2.0 0.1.1 0.2.0
@wdio/electron-service 10.2.0 10.1.1 10.2.0
@wdio/native-cdp-bridge 1.1.0 1.0.1 1.1.0
@wdio/native-core 1.1.0 1.0.1 1.1.0
@wdio/native-spy 1.2.0 1.1.1 1.2.0
@wdio/native-types 2.5.0 2.4.1 2.5.0
@wdio/native-utils 2.6.0 2.5.1 2.6.0
@wdio/tauri-plugin 1.3.0 1.2.1 1.3.0
@wdio/tauri-service 1.3.0 1.2.1 1.3.0
tauri-plugin-wdio-webdriver 1.3.0 1.2.1 1.3.0
wdio_flutter 0.2.0 0.1.1 0.2.0
wdio-dioxus-driver 1.0.0-next.4 1.0.0-next.4 1.0.0-next.4
wdio-dioxus-embedded-driver 1.0.0-next.4 1.0.0-next.4 1.0.0-next.4

Updated automatically by ReleaseKit

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a Turbo remote-cache poisoning bug where cli.js could be paired with a hashed plugins-<hash>.js chunk that no longer matched the cached dist/, crashing every consumer build. It also tightens the cache-key inputs for the bundler package, adds the shared tsconfig bases to globalDependencies, removes six now-redundant per-package task overrides, and adds a uniform turbo run typecheck step to CI.

  • rollup.config.ts: chunkFileNames: '[name].js' makes shared chunk names stable and hash-free, eliminating the mismatched-import crash at its root.
  • packages/bundler/turbo.json: New package-level config uses $TURBO_EXTENDS$ to inherit root inputs and appends $TURBO_ROOT$/pnpm-lock.yaml, ensuring lockfile-only toolchain bumps invalidate the bundler cache key.
  • Root turbo.json: Adds tsconfig.base.json and tsconfig.base.cjs.json to globalDependencies (Turbo doesn't follow extends chains, so these were previously invisible to the hasher); removes six per-package #build overrides that existed solely for typecheck gates now covered by the new CI step.
  • CI workflow: Inserts a Typecheck All Packages step between build and artifact upload — all 21 package typecheck tasks run using Turbo cache hits from the preceding build, adding minimal wall-clock time while closing the coverage gap.

Confidence Score: 5/5

Safe to merge — all changes are additive or deletion of verified-redundant config, the root crash fix is narrow and well-justified, and the author confirmed build-graph correctness with a dry-run diff.

The rollup fix (chunkFileNames: '[name].js') directly addresses the crash with no correctness tradeoffs for a Node.js CLI tool. The Turbo config changes (new packages/bundler/turbo.json, globalDependencies, deleted overrides) were cross-checked: @wdio/native-utils is a declared workspace dependency of @wdio/tauri-service, so ^build preserves all ordering edges. The CI typecheck step is correctly positioned before artifact upload. No logic paths were changed, only build-tooling wiring.

No files require special attention.

Important Files Changed

Filename Overview
packages/bundler/rollup.config.ts Adds chunkFileNames: '[name].js' to produce stable, non-hashed shared chunk names, eliminating the Turbo remote-cache poisoning root cause.
packages/bundler/turbo.json New package-level turbo.json; uses $TURBO_EXTENDS$ to inherit root build inputs and appends pnpm-lock.yaml as an additional cache-key input — drift-proof and correct.
turbo.json Adds tsconfig.base.json/tsconfig.base.cjs.json to globalDependencies; removes six now-redundant per-package overrides (verified via --dry-run graph diff that no build-ordering edges are lost); @wdio/tauri-service#build switches explicit @wdio/native-utils#build dep to the broader ^build, which correctly covers all workspace deps including @wdio/native-utils.
.github/workflows/_ci-build.reusable.yml Inserts a turbo run typecheck --filter=./packages/* step after build and before artifact upload, ensuring full src/** type coverage in CI on every run; Turbo cache hits for already-built packages make this low-cost.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["👷 Checkout + Setup"] --> B["🏗️ Build All Packages\nturbo run build\n--filter=./packages/* --filter=./e2e"]
    B --> C["🔎 Typecheck All Packages\nturbo run typecheck\n--filter=./packages/*\n(NEW — full src/** coverage)"]
    C --> D["📦 Upload Artifacts\n(Linux only)"]

    subgraph "Bundler Cache Key"
        E["$TURBO_EXTENDS$ (inherits root build inputs)\n+ $TURBO_ROOT$/pnpm-lock.yaml"]
    end

    subgraph "Root globalDependencies (NEW)"
        F["tsconfig.base.json\ntsconfig.base.cjs.json"]
    end

    subgraph "rollup.config.ts fix"
        G["chunkFileNames: '[name].js'\n(stable, non-hashed chunk names)"]
        H["cli.js always imports plugins.js\nnever plugins-HASH.js"]
        G --> H
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["👷 Checkout + Setup"] --> B["🏗️ Build All Packages\nturbo run build\n--filter=./packages/* --filter=./e2e"]
    B --> C["🔎 Typecheck All Packages\nturbo run typecheck\n--filter=./packages/*\n(NEW — full src/** coverage)"]
    C --> D["📦 Upload Artifacts\n(Linux only)"]

    subgraph "Bundler Cache Key"
        E["$TURBO_EXTENDS$ (inherits root build inputs)\n+ $TURBO_ROOT$/pnpm-lock.yaml"]
    end

    subgraph "Root globalDependencies (NEW)"
        F["tsconfig.base.json\ntsconfig.base.cjs.json"]
    end

    subgraph "rollup.config.ts fix"
        G["chunkFileNames: '[name].js'\n(stable, non-hashed chunk names)"]
        H["cli.js always imports plugins.js\nnever plugins-HASH.js"]
        G --> H
    end
Loading

Reviews (6): Last reviewed commit: "refactor(turbo): converge electron/elect..." | Re-trigger Greptile

Comment thread packages/bundler/turbo.json
Comment thread packages/bundler/turbo.json
… input)

Two durable fixes for the bundler dist cache-poisoning in #509:

- rollup `output.chunkFileNames = '[name].js'` — the shared plugins chunk gets a
  stable, non-hashed name, so a cached dist/ can never have cli.js referencing a
  plugins-<hash>.js that doesn't match it (the `injectDependencyPlugin`-undefined
  crash). Content changes still trigger a rebuild.
- `packages/bundler/turbo.json` adds `$TURBO_ROOT$/pnpm-lock.yaml` to the
  bundler's build inputs, so a build-toolchain bump (e.g. rollup) via a
  lockfile-only change invalidates the bundler cache key. Consumers rebuild via
  `^build`, so non-bundler packages are unaffected.

Closes #509

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013sth6SZDQT3D5oWa8aPNSZ
@goosewobbler
goosewobbler force-pushed the fix/bundler-cache-poisoning branch from 74e2441 to aaa1e36 Compare July 3, 2026 11:53
goosewobbler and others added 2 commits July 3, 2026 13:20
…#519)

Move the bundler's `dependsOn: ["typecheck"]` gate into the package-level
turbo.json and drop the now-redundant root `@wdio/bundler#build` override.
With the override gone, `$TURBO_EXTENDS$` correctly inherits the generic
`build` inputs, so the input list is drift-proof (no hand-copied globs) while
still hashing the same 17 source files plus `pnpm-lock.yaml`. Verified via
`turbo run build --dry-run`: typecheck still gates the build and the resolved
input set is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1H8Jx9MwZedzN3VSb1tRY
Every package tsconfig extends ../../tsconfig.base.json, but Turbo does not
follow tsconfig `extends` chains and the base files were in no task's inputs,
no `globalDependencies`, and not in the global file set — so editing them
(e.g. the TS6 baseUrl/rootDir migration) served stale `build` AND `typecheck`
caches, including from the remote cache. Unlike npm dependency bumps (already
covered by Turbo's per-package hashOfExternalDependencies), a tsconfig is not
a package, so nothing invalidated on a change.

Add both shared base configs to `globalDependencies`. Verified with
`turbo run --dry-run`: a base-tsconfig edit now changes the build and
typecheck hashes for every package.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1H8Jx9MwZedzN3VSb1tRY
@goosewobbler goosewobbler changed the title fix(bundler): prevent turbo cache poisoning (stable chunks + lockfile input) fix(turbo): prevent cache poisoning — stable bundler chunks, config cleanup, track shared tsconfig Jul 3, 2026
…gates

The build itself fails on type errors (rollup @rollup/plugin-typescript), so
CI's `turbo run build` already type-checks the bundled import graph of every
package. But `tsc --noEmit` (the `typecheck` task) additionally covers all of
src/** with the full strict config, and CI never ran it — so that broader
check only happened for the handful of packages whose `#build` gated on
`typecheck`. Coverage was uneven.

Add a "Typecheck All Packages" step to the reusable build workflow
(`turbo run typecheck --filter=./packages/*`) so every package gets the strict
tsc pass uniformly, with a distinct signal, reusing the dist/ just built.

With that in place the per-build `dependsOn: ["typecheck"]` gates are
redundant, so remove them. The gates also supplied `^build` transitively (via
`typecheck`'s own `dependsOn: ["^build"]`), so the overrides that existed only
for the gate are deleted (they fall back to the generic `build`, which is
`dependsOn: ["^build"]`) and tauri-service's gate is replaced with an explicit
`^build`. Verified via `--dry-run` that no build-ordering edge is lost — every
affected package still (correctly) builds `@wdio/bundler` first — and that a
forced build + full `turbo run typecheck` stay green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1H8Jx9MwZedzN3VSb1tRY
@goosewobbler goosewobbler changed the title fix(turbo): prevent cache poisoning — stable bundler chunks, config cleanup, track shared tsconfig fix(turbo): cache-poisoning fixes, config cleanup, uniform typecheck coverage Jul 3, 2026
Their `#build` overrides only pinned `@wdio/native-cdp-bridge#build` and set
`outputs: ["dist/**"]` — both already provided by the generic `build` task
(`dependsOn: ["^build"]`, same outputs). Deleting them makes the two services
inherit the generic task, so their build now depends on the full set of their
workspace dependencies directly (bundler, native-core/spy/types/utils,
cdp-bridge) instead of reaching bundler transitively through the cdp-bridge
chain. Verified via `--dry-run`: the native-cdp-bridge edge is preserved, the
dep set is a superset of before, and forced builds of both stay green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1H8Jx9MwZedzN3VSb1tRY
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.

Bundler turbo build cache can be silently poisoned (hashed chunks + inputs miss the toolchain)

1 participant