turbo icon indicating copy to clipboard operation
turbo copied to clipboard

Cache is extracted to wrong destination

Open markbrockhoff opened this issue 1 year ago • 3 comments

Verify canary release

  • [X] I verified that the issue exists in the latest Turborepo canary release.

Link to code that reproduces this issue

https://github.com/markbrockhoff/turbo-repo-bug-repro

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

pnpm

What operating system are you using?

Mac

Which canary version will you have in your reproduction?

2.0.4-canary.3

Describe the Bug

Whenever I execute a build using turborepo for the first time everything is generated correctly and cached. But when running the same build again the existing cache is restored to the wrong directory causing build files to end up in the src directory.

Expected Behavior

I'd expect turborepo to restore the build cache in the exact same location where it came from.

To Reproduce

I created a minimal reproduction using the latest version of turborepo containing only one package which is the default starter for creating a Nuxt module (That's where I encountered the issue)

To reproduce the issue do the following steps:

  1. Clone the repro: git clone https://github.com/markbrockhoff/turbo-repo-bug-repro
  2. Install the dependencies pnpm i
  3. Run a first build: pnpm build
  4. Run a second build: pnpm build

After the second build you'll see that two new files were created within /packages/nuxt-test/src/runtime. These files should be placed inside the dist folder but somehow seem to be restored into the src folder. I think the issue has something to do with the fact that the build task depends on the dev:prepare task and both habe the dist folder as output, however it's still very strange to me that the files end up in a totally different place.

Additional context

I'm using pnpm v9.3.0 and node 20.11.0, any help with this issue would be appreciated. Thanks

markbrockhoff avatar Jun 13 '24 13:06 markbrockhoff

I can confirm that this is due to the overlapping outputs interacting in a very weird way. It appears that something in dev:prepare is creating a symlink from dist/runtime -> src/runtime:

[0 olszewski@chriss-mbp] /tmp/turbo-repo-bug-repro/packages/nuxt-test $ pnpm dev:prepare

> [email protected] dev:prepare /private/tmp/turbo-repo-bug-repro/packages/nuxt-test
> nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground

ℹ Stubbing my-module                                                                                                                         11:30:47 AM
ℹ Cleaning dist directory: ./dist                                                                                                            11:30:47 AM
✔ Types generated in .nuxt                                                                                                                   11:30:49 AM
✔ Types generated in playground/.nuxt                                                                                                        11:30:50 AM
[0 olszewski@chriss-mbp] /tmp/turbo-repo-bug-repro/packages/nuxt-test $ ls -lh dist 
total 48
-rw-r--r--  1 olszewski  wheel   227B Jun 13 11:30 module.cjs
-rw-r--r--  1 olszewski  wheel   171B Jun 13 11:30 module.d.ts
-rw-r--r--  1 olszewski  wheel   156B Jun 13 11:30 module.json
-rw-r--r--  1 olszewski  wheel   477B Jun 13 11:30 module.mjs
lrwxr-xr-x  1 olszewski  wheel    64B Jun 13 11:30 runtime -> /private/tmp/turbo-repo-bug-repro/packages/nuxt-test/src/runtime
-rw-r--r--  1 olszewski  wheel   918B Jun 13 11:30 types.d.mts
-rw-r--r--  1 olszewski  wheel   912B Jun 13 11:30 types.d.ts

This leads to the issue when we there's a cache hit for my-module#build and we attempt to restore dist/runtime/plugin.js and dist/runtime/plugin.d.ts to dist/runtime which now points to src/runtime due to the symlink created by dev:prepare

[0 olszewski@chriss-mbp] /tmp/turbo-repo-bug-repro $ tar --use-compress-program=unzstd -tvf .turbo/cache/72cffa046a69ae97.tar.zst 
-rw-r--r--  0 0      0        1257 Dec 31  1969 packages/nuxt-test/.turbo/turbo-build.log
drwxr-xr-x  0 0      0           0 Dec 31  1969 packages/nuxt-test/dist/
-rw-r--r--  0 0      0         227 Dec 31  1969 packages/nuxt-test/dist/module.cjs
-rw-r--r--  0 0      0         192 Dec 31  1969 packages/nuxt-test/dist/module.d.mts
-rw-r--r--  0 0      0         192 Dec 31  1969 packages/nuxt-test/dist/module.d.ts
-rw-r--r--  0 0      0         156 Dec 31  1969 packages/nuxt-test/dist/module.json
-rw-r--r--  0 0      0         415 Dec 31  1969 packages/nuxt-test/dist/module.mjs
drwxr-xr-x  0 0      0           0 Dec 31  1969 packages/nuxt-test/dist/runtime/
-rw-r--r--  0 0      0         152 Dec 31  1969 packages/nuxt-test/dist/runtime/plugin.d.ts
-rw-r--r--  0 0      0         141 Dec 31  1969 packages/nuxt-test/dist/runtime/plugin.js
drwxr-xr-x  0 0      0           0 Dec 31  1969 packages/nuxt-test/dist/runtime/server/
-rw-r--r--  0 0      0          56 Dec 31  1969 packages/nuxt-test/dist/runtime/server/tsconfig.json
-rw-r--r--  0 0      0         425 Dec 31  1969 packages/nuxt-test/dist/types.d.mts
-rw-r--r--  0 0      0         419 Dec 31  1969 packages/nuxt-test/dist/types.d.ts

chris-olszewski avatar Jun 13 '24 18:06 chris-olszewski

Ah, that makes sense that the symlik is causing the issue. Thanks for the explanation. I was able to fix the issue inside my project by avoiding the duplicate caching of the dist folder. However I still wanted to report the strange behavior as it might also affect other people.

markbrockhoff avatar Jun 14 '24 06:06 markbrockhoff

@markbrockhoff Thanks for the report!

I think this is still a bug on our end as we should be removing the dist/runtime symlink and creating a new directory instead of just placing files in it.

Now having two tasks who disagree on what a file (dist/runtime) should be is to be avoided as running one task will effectively "undo" the other.

chris-olszewski avatar Jun 14 '24 16:06 chris-olszewski

This issue persists in 2.5.5-canary.0. It's exemplified by other tools that create symlinks in dist for buildless development, for example, https://preconstruct.tools/ dev mode does this (that's how we encountered it).

The workaround I came up with is fairly annoying and manual, especially in a large monorepo. For every affected project, the follwoing must be added:

packages/pkg/package.json

{
  "scripts": {
    "clean": "node -e \"require('node:fs').rmSync('dist', { recursive:true, force:true })\"",
  }
}

packages/pkg/turbo.json

{
  "extends": ["//"],
  "tasks": {
    "clean": {
      "cache": false
    },
    "build": {
      "dependsOn": ["^build", "clean"],
      "outputs": ["dist"]
    }
  }
}

VanTanev avatar Jun 19 '25 08:06 VanTanev