turbo: Updating a dependency is not invalidating other application's cache

What version of Turborepo are you using?

1.2.4

What package manager are you using / does the bug impact?

Yarn v1

What operating system are you using?

Mac

Describe the Bug

When updating a shared library, it’s not invalidating the cache of projects that depend on that library.

I am updating a component called Button.js, and when running the build scripts, the projects that depend on the shared library’s Button.js are replaying the cached output rather than running build again.

Expected Behavior

When updating Button.js inside of the shared library, it should invalidate the project’s cache that depends on shared.

To Reproduce

Repository: https://github.com/mcrowder65/turbo-bug Run:

yarn
yarn build

Update shared/src/button.js to something else.

yarn build

Note that the cache is invalidated, and it rebuilds the all projects.

Now if you checkout the branch called broken

Update shared/src/button.js to something else.

yarn build

Note that the cache is NOT invalidated, and it replays the previous build

A workaround for this with 1.2.4 is to add "shared/**/*" to "globalDependencies" inside of turbo.json

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 3
  • Comments: 17 (9 by maintainers)

Most upvoted comments

Totally agree this is a bit confusing and some clarification is needed. @avaragado and yes, the ^lint:code dependsOn has a undesirable side effect of triggering unnecessary tasks just to get the consuming package to fire its task. However I may have found a reasonable workaround to this problem such that the correct package will get built/lint/etc… WITHOUT having the downstream packages having to fire as well.

  1. Create a “fake” task in turbo with empty inputs and outputs.
 "fake": {
      "inputs": [],
      "outputs": []
    },
  1. Now, instead of…
"lint:code": {
      "dependsOn": ["^lint:code"],
      "outputs": []
    },

Have your task depend on ^fake.

"lint:code": {
      "dependsOn": ["^fake"],
      "outputs": []
    },

Follow a similar approach to other tasks that need this behavior.

The ^build had confused me too. My understanding of ^build task is that all dependencies must complete build task first – and that’s it. In fact, it seems it has also another extremely important effect – it computes task hash from dependencies files too. I’m not saying it’s wrong, just confusing.

That does seem to be the reality, yes. And I’d argue that it’s wrong as well as confusing.

See https://turborepo.org/docs/core-concepts/caching#hashing - particularly the bullet “The hashes of all internal dependencies”. I may be wrong, but I interpret that to mean “The hashes of all workspaces in the monorepo that the package depends on”. If I’m wrong, I’d like to know what it actually means.

@jaredpalmer I think this is a high severity blocking issue. We have a large mono repo with both public and private packages. The public packages have dependencies on private packages. If a private package changes, only it is getting build, but at noted above, all its consumers replay cached builds.

I don’t think the workaround is a scalable solution. Is there any way to get this prioritized soon?

I’m seeing a similar issue - with linting, in this case. This is with npm 8.5.3.

I have a monorepo with workspaces under apps, libs, and tools directories. Each workspace’s name is npm-scoped with the relevant directory name. Workspaces under tools relate to infrastructure, such as linting configuration. So workspaces under libs, eg @libs/beacons, have devDependencies on @tools/eslint, @tools/tsconfig, and so on.

In turbo.json:

    "lint:code": {
      "dependsOn": [],
      "outputs": []
    },

And here’s what happens when I change the eslint configuration between linting runs:

❯ npm run lint:code -- --filter=@libs/beacons

> monorepo@0.0.0 lint:code
> cross-env FORCE_COLOR=1 turbo run lint:code "--filter=@libs/beacons"

• Packages in scope: @libs/beacons
• Running lint:code in 1 packages
@libs/beacons:lint:code: cache miss, executing e8f8367e4e3173f1
@libs/beacons:lint:code:
@libs/beacons:lint:code: > @libs/beacons@0.0.0 lint:code
@libs/beacons:lint:code: > eslint . --fix || true
@libs/beacons:lint:code:

 Tasks:    1 successful, 1 total
Cached:    0 cached, 1 total
  Time:    8.257s


~/monorepo
❯ echo '// asdf' >> tools/eslint/index.js

~/monorepo
❯ npm run lint:code -- --filter=@libs/beacons

> monorepo@0.0.0 lint:code
> cross-env FORCE_COLOR=1 turbo run lint:code "--filter=@libs/beacons"

• Packages in scope: @libs/beacons
• Running lint:code in 1 packages
@libs/beacons:lint:code: cache hit, replaying output e8f8367e4e3173f1
@libs/beacons:lint:code:
@libs/beacons:lint:code: > @libs/beacons@0.0.0 lint:code
@libs/beacons:lint:code: > eslint . --fix || true
@libs/beacons:lint:code:

 Tasks:    1 successful, 1 total
Cached:    1 cached, 1 total
  Time:    100ms >>> FULL TURBO

As @libs/beacons dev-depends on @tools/eslint, I would expect that changing @tools/eslint would invalidate any cached output from @libs/beacons. From the above transcript, it isn’t.

I can make turbo.json look like this:

    "lint:code": {
      "dependsOn": ["^lint:code"],
      "outputs": []
    },

Which does correctly invalidate the cache when I change @tools/eslint. But ^lint:code causes all the other internal dependencies of @libs/beacons that have a lint:code task to run that task: in my case, 5 other workspaces. This just clutters up the output and burns some CPU pointlessly.