node-gradle / gradle-node-plugin

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

Enabling Yarn 2-4, Yarn and corepack interaction on Windows causes yarn binary not to be found, when it could be #296

Open doctorpangloss opened 8 months ago

doctorpangloss commented 8 months ago

Core issue

    private fun computeProductBinDir(productDirProvider: Provider<Directory>, platform: Property<Platform>) =
            if (platform.get().isWindows()) productDirProvider else productDirProvider.map { it.dir("bin") }

This isn't true. On Windows, corepack also installs yarn.cmd (along with yarn the bash script and yarn.ps1, etc.) to it.dir("bin") too.

This line should be

            productDirProvider.map { it.dir("bin") }

related to #176 , this enables Yarn 2-4 and works around the posted issue:

import groovy.json.JsonSlurper

def packageJson = new JsonSlurper().parseText(rootProject.file('package.json').text)
def yarnVersionStr = (packageJson.packageManager as String).split("@")[1]

node {
  download = false
  yarnVersion = yarnVersionStr
  yarnWorkDir.set(rootProject.layout.projectDirectory.dir(".gradle/yarn"))
}

def isWindows = org.gradle.internal.os.OperatingSystem.current().isWindows()
def binExt = isWindows ? ".cmd" : ""
tasks.register("corepack") {
  def dirPath = ".gradle/yarn/yarn-v${yarnVersionStr}/${isWindows ? "" : "/bin"}"
  outputs.dir(dirPath)
  doLast {
    mkdir(dirPath)
    exec {
      commandLine "corepack${binExt}", "enable", "--install-directory", dirPath
    }
  }
}

tasks.named("yarnSetup").configure {
  enabled = false
  dependsOn "corepack"
}

// as an alternative to yarn install, which has a lot going on, and also supports workspaces
tasks.register('yarnWorkspace', YarnTask) {
  // todo: you don't want to inspect workspaces if you don't use it
  description("Installs all the necessary modules to develop ${packageJson.workspaces.join(', ')}")
  inputs.files("package.json", "yarn.lock")
          .withPropertyName("yarnLock")
          .withPathSensitivity(PathSensitivity.RELATIVE)
  inputs.files(packageJson.workspaces.collect { "$it/package.json" })
          .withPropertyName("workspacePackageJsons")
          .withPathSensitivity(PathSensitivity.RELATIVE)
  // todo: this only makes sense for node-modules resolution and will need to inspect the .yarnrc.yml file
  outputs.file("node_modules/.yarn-state.yml")
  outputs.cacheIf { false }
  args = ['install', '--immutable']
}
deepy commented 8 months ago

This is an interesting quirk, because depending on whether we use --global --prefix foo or not we get different results

C:\dev\test> npm install yarn

added 1 package in 600ms

C:\dev\test> ls .\node_modules\yarn\bin\

    Directory: C:\dev\test\node_modules\yarn\bin

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        19/12/2023     18:06           1025 yarn
-a----        19/12/2023     18:06             34 yarn.cmd
-a----        19/12/2023     18:06           1015 yarn.js
-a----        19/12/2023     18:06             42 yarnpkg
-a----        19/12/2023     18:06             30 yarnpkg.cmd

C:\dev\gradle-node-plugin> ls .\examples\simple-node\yarn\.gradle\yarn\yarn-latest\

    Directory: C:\dev\gradle-node-plugin\examples\simple-node\yarn\.gradle\yarn\yarn-latest

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        17/08/2023     17:16                lib
d-----        19/12/2023     18:12                node_modules
-a----        19/12/2023     18:12            320 yarn
-a----        19/12/2023     18:12            331 yarn.cmd
-a----        19/12/2023     18:12            829 yarn.ps1
-a----        19/12/2023     18:12            320 yarnpkg
-a----        19/12/2023     18:12            331 yarnpkg.cmd
-a----        19/12/2023     18:12            829 yarnpkg.ps1

C:\dev\test> .\node_modules\yarn\bin\yarn.cmd --version
1.22.21

C:\dev\gradle-node-plugin> .\examples\simple-node\yarn\.gradle\yarn\yarn-latest\yarn.cmd --version
1.22.21

And for comparison, here's the same command the plugin uses:

C:\dev\test> npm install --global --no-save --prefix . yarn

added 1 package in 453ms

C:\dev\test> ls

    Directory: C:\dev\test

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        19/12/2023     18:18                node_modules
-a----        19/12/2023     18:18            320 yarn
-a----        19/12/2023     18:18            331 yarn.cmd
-a----        19/12/2023     18:18            829 yarn.ps1
-a----        19/12/2023     18:18            320 yarnpkg
-a----        19/12/2023     18:18            331 yarnpkg.cmd
-a----        19/12/2023     18:18            829 yarnpkg.ps1

# Luckily we have both!

C:\dev\test> ls .\node_modules\yarn\bin\

    Directory: C:\dev\test\node_modules\yarn\bin

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        19/12/2023     18:18           1025 yarn
-a----        19/12/2023     18:18             34 yarn.cmd
-a----        19/12/2023     18:18           1015 yarn.js
-a----        19/12/2023     18:18             42 yarnpkg
-a----        19/12/2023     18:18             30 yarnpkg.cmd

I need to look into this again, and read up a bit more on --global, --prefix, and yarn 1 vs 2+

doctorpangloss commented 8 months ago

thanks for investigating. Everything seems robust enough for Yarn 4 and cache support which is great.