Open sirianni opened 7 years ago
This seems to be the culprit:
getOutputs().dir( new File( (File) this.project.node.nodeModulesDir, 'node_modules' ) )
Is there any harm in removing this?
Removing this would cause the npmInstall
task to skip execution despite the node_modules dir having been altered (e.g. deleted).
It should be possible to disable the output caching via npmInstall.outputs.doNotCacheIf('broken symlink') { true }
, but I tried it and Gradle still attempts to read the node_modules dir.
You could run the rule-generated npm_install
task instead, and specify only the inputs:
npm_install.inputs.file 'package.json'
npm_install.outputs.upToDateWhen { true }
npm_install.outputs.upToDateWhen { true }
Shouldn't that be false
?
Here is what I ended up doing:
task npmInstall(overwrite: true, type: NpmTask, dependsOn: 'npmSetup') {
npmCommand = 'install'
inputs.file('package.json')
}
npm_install.outputs.upToDateWhen { true }
That would cause the task to execute every single time, which I would consider extremely annoying due to its impact on build execution time.
The way I suggested, npm_install is only rerun when you change the package.json
. Of course that could potentially break the build if you delete the node_modules
folder, but occasionally using --rerun-tasks
(or manually executing npm i
) is probably better than running the npm install on every build, at least for performance.
task npmInstall(overwrite: true, type: NpmTask, dependsOn: 'npmSetup')
Overwriting the npmInstall task is, of course, also an option instead of using npm_install.
Regarding the outputs, I hope I outlined the options clearly in my comment above so you can decide what's worth more to you - performance (settings the outputs to always up-to-date to avoid npm installing every time) or build stability (omitting the outputs).
Yes, thanks for clarifying.
I had incorrectly assumed that npm install
itself was cheap if nothing had changed. Now that I understand it better, I have opted for the outputs.upToDateWhen { true }
as you suggested.
I ended up going with this:
task npmInstall(overwrite: true, type: NpmTask, dependsOn: 'npmSetup') {
inputs.file('package.json')
outputs.upToDateWhen { file('node_modules').exists() }
npmCommand = ['install']
}
This supports a common use case when developers nuke the node_modules
directory before triggering rebuild.
I had incorrectly assumed that npm install itself was cheap if nothing had changed
Yes, I'm still wondering what the heck npm is doing in that case every time ;)
outputs.upToDateWhen { file('node_modules').exists() }
That's a nice workaround!
BTW, here's the relevant Gradle issue dealing with the dangling symlinks as outputs.
Note to others, that setting npmCommand
as an array is critical. Without the enclosing brackets, groovy seems to coerce 'install'
into ['i', 'n', 's', 't', 'a', 'l', 'l']
. Which ends up npm install
ing the packages n
, s
, t
, and a
instead of what is in your package.json
. This took me quite a while to debug
Unfortunately the workaround provided @sirianni did not work for me. Based on comments in the original gradle issue, I added a method in the script to delete bad sym links which fixed the issue for us. Relevant code below:
import java.nio.file.Files
import java.nio.file.Path
ext.deleteInvalidSymLinks = {dir ->
println ("deleting bad links in $dir")
Files.walk(dir.toPath())
.filter { it ->
logger.debug("file: " + it + ", symLink?:" + Files.isSymbolicLink(it))
def isInvalidLink = Files.isSymbolicLink(it) && !Files.exists(it)
return isInvalidLink;
}
.forEach{ it ->
try {
logger.warn("Deleting broken symlink '$it'")
Files.delete(it)
} catch (Exception e) {
logger.error("Couldn't delete broken symlink '$it' : $e ")
}
}
}
npmInstall.doLast {
deleteInvalidSymLinks(project.file('node_modules'))
}
Even though
npm install
succeeds, the gradle build fails with:Sure enough, the symlink is broken:
However, I'm not sure why gradle (or the gradle-node-plugin) cares. My guess is somehow the plugin is requesting that the entire
node_modules
directory be fingerprinted for some sort of "up to date" checking? Can this be disabled?Here is the full stacktrace: