node-gradle / gradle-node-plugin

Gradle plugin for integrating NodeJS in your build. :rocket:
Apache License 2.0
592 stars 115 forks source link

pnpmInstall: Throws strange exceptions: Solved with "--no-parallel" #302

Open jschneider opened 5 months ago

jschneider commented 5 months ago

I get these exceptions when calling gradle pnpmInstall from my project root (with several sub projects configured for pnpm):

> Task :internal:closed:webapps:meistercharts.com:pnpmInstall FAILED
Caching disabled for task ':internal:closed:webapps:meistercharts.com:pnpmInstall' because:
  Caching has not been enabled for the task
Task ':internal:closed:webapps:meistercharts.com:pnpmInstall' is not up-to-date because:
  Output property 'nodeModulesDirectory' file /home/johannes/projects/com.cedarsoft.monorepo/internal/closed/webapps/meistercharts.com/node_modules/@astrojs/check has been removed.
  Output property 'nodeModulesDirectory' file /home/johannes/projects/com.cedarsoft.monorepo/internal/closed/webapps/meistercharts.com/node_modules/@astrojs/check/LICENSE has been removed.
  Output property 'nodeModulesDirectory' file /home/johannes/projects/com.cedarsoft.monorepo/internal/closed/webapps/meistercharts.com/node_modules/@astrojs/check/dist has been removed.
Starting process 'command '/home/johannes/projects/com.cedarsoft.monorepo/internal/closed/webapps/meistercharts.com/build/node/pnpm/pnpm-v8.15.1/bin/pnpm''. Working directory: /home/johannes/projects/com.cedarsoft.monorepo/internal/closed/webapps/meistercharts.com Command: /home/johannes/projects/com.cedarsoft.monorepo/internal/closed/webapps/meistercharts.com/build/node/pnpm/pnpm-v8.15.1/bin/pnpm install
Successfully started process 'command '/home/johannes/projects/com.cedarsoft.monorepo/internal/closed/webapps/meistercharts.com/build/node/pnpm/pnpm-v8.15.1/bin/pnpm''
Scope: all 12 workspace projects
../../../..                              | Progress: resolved 1, reused 0, downloaded 0, added 0
../../../..                              |    +2358 ++++++++++++++++++++++++++++
../../../..                              | Progress: resolved 2358, reused 288, downloaded 0, added 0
../../../..                              | Progress: resolved 2358, reused 1799, downloaded 0, added 0
../../../..                              | Progress: resolved 2358, reused 2352, downloaded 0, added 0
../../../..                              | Progress: resolved 2358, reused 2352, downloaded 0, added 0, done
 ENOENT  ENOENT: no such file or directory, unlink '/home/johannes/projects/com.cedarsoft.monorepo/node_modules/.pnpm/node_modules/tunnel-agent'

The exact error message changes every time:

 ERR_PNPM_LINKING_FAILED  Error: ENOTEMPTY: directory not empty, rmdir '/home/johannes/projects/com.cedarsoft.monorepo/node_modules/.pnpm/shikiji@0.9.19/node_modules/shikiji/dist/langs'

 ERR_PNPM_LINKING_FAILED  Error: ENOTEMPTY: directory not empty, rename '/home/johannes/projects/com.cedarsoft.monorepo/node_modules/.pnpm/postcss-calc@8.2.4_postcss@8.4.33/node_modules/postcss-calc_tmp_15783' -> '/home/johannes/projects/com.cedarsoft.monorepo/node_modules/.pnpm/postcss-calc@8.2.4_postcss@8.4.33/node_modules/postcss-calc'

The exception does not happen if adding "--no-parallel".

deepy commented 5 months ago

Since caching is not enabled we can rule out Gradle writing to the directory And unless your subprojects are installing to the same node_modules we can rule out the plugin as well

Which then sadly would leave pnpm, you can work around the issue by using a shared build service as a semaphore, limiting the number of installs that can run in parallel

But before going down that route, just to confirm, all the pnpm installs aren't writing to /home/johannes/projects/com.cedarsoft.monorepo/node_modules/ right?

jschneider commented 4 months ago

The pnpm install tasks are writing all to the same node_modules dir in the project root. We have a pnpm "monorepo" with a pnpm-workspace.yaml

bryanstern commented 2 months ago

I'm running into this as well with a similar project set up. Here's my workaround that I added in my root build.gradle.kts.

// Ensures that only one PnpmInstallTask runs at a time. The build service acts as a semaphore.
 class EmptyBuildService : BuildService<BuildServiceParameters.None> {
     override fun getParameters(): BuildServiceParameters.None = TODO("Not yet implemented")
 }
 val service = gradle.sharedServices
     .registerIfAbsent("pnpmInstallService", EmptyBuildService::class) {
         maxParallelUsages = 1
     }
 subprojects {
     tasks.withType<PnpmInstallTask>().configureEach {
         usesService(service)
     }
 }