From 61e493b7354850f24842e08d497ac39a6f0e9a29 Mon Sep 17 00:00:00 2001 From: kaitoy Date: Sun, 20 Nov 2016 16:18:27 -0700 Subject: [PATCH 1/3] [#145] Add support for Yarn. --- README.md | 55 ++++++++++++ .../gradle/node/YarnInstall_integTest.groovy | 64 +++++++++++++ .../gradle/node/YarnRule_integTest.groovy | 57 ++++++++++++ .../moowork/gradle/node/NodeExtension.groovy | 7 ++ .../com/moowork/gradle/node/NodePlugin.groovy | 31 +++++++ .../gradle/node/exec/YarnExecRunner.groovy | 36 ++++++++ .../gradle/node/task/YarnInstallTask.groovy | 23 +++++ .../gradle/node/task/YarnSetupTask.groovy | 36 ++++++++ .../moowork/gradle/node/task/YarnTask.groovy | 89 +++++++++++++++++++ .../moowork/gradle/node/NodePluginTest.groovy | 11 ++- .../gradle/node/task/YarnSetupTaskTest.groovy | 40 +++++++++ .../gradle/node/task/YarnTaskTest.groovy | 48 ++++++++++ 12 files changed, 494 insertions(+), 3 deletions(-) create mode 100644 src/integTest/groovy/com/moowork/gradle/node/YarnInstall_integTest.groovy create mode 100644 src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy create mode 100644 src/main/groovy/com/moowork/gradle/node/exec/YarnExecRunner.groovy create mode 100644 src/main/groovy/com/moowork/gradle/node/task/YarnInstallTask.groovy create mode 100644 src/main/groovy/com/moowork/gradle/node/task/YarnSetupTask.groovy create mode 100644 src/main/groovy/com/moowork/gradle/node/task/YarnTask.groovy create mode 100644 src/test/groovy/com/moowork/gradle/node/task/YarnSetupTaskTest.groovy create mode 100644 src/test/groovy/com/moowork/gradle/node/task/YarnTaskTest.groovy diff --git a/README.md b/README.md index 31ca72a..7afbfef 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,44 @@ the class `NpmTask`: args = ['install', 'express', '--save-dev'] } +Executing Yarn Tasks +--------------------- + +When adding the node plugin, you will have a yarn task already added. This +task will execute `yarn` and installs all dependencies in `package.json`. +It will only run when changes are made to `package.json`, `yarn.lock`, or `node_modules`. +Execute it like this: + + $ gradle yarn + +All npm command can also be invoked using underscore notation based on a gradle +rule: + + $ gradle yarn_install + $ gradle yarn_upgrade + $ gradle yarn_ls + $ gradle yarn_cache_clean + ... + +These however are not shown when running gradle tasks, as they generated +dynamically. However they can be used for dependency declarations, such as: + + yarn_install.dependsOn(yarn_cache_clean) + +More arguments can be passed via the build.gradle file: + + yarn_cache_clean { + args = ['--no-emoji', '--json'] + } + +If you want to extend the tasks more or create custom variants, you can extend +the class `YarnTask`: + + task addExpress(type: YarnTask) { + // add the express package only + args = ['add', 'express', '--dev'] + } + Configuring the Plugin ---------------------- @@ -140,6 +178,9 @@ You can configure the plugin using the "node" extension block, like this: // Version of npm to use. npmVersion = '2.1.5' + // Version of Yarn to use. + yarnVersion = '0.16.1' + // Base URL for fetching node distributions (change if you have a mirror). distBaseUrl = 'https://nodejs.org/dist' @@ -150,6 +191,9 @@ You can configure the plugin using the "node" extension block, like this: // Set the work directory for unpacking node workDir = file("${project.buildDir}/nodejs") + // Set the work directory for Yarn + yarnWorkDir = file("${project.buildDir}/yarn") + // Set the work directory where node_modules should be located nodeModulesDir = file("${project.projectDir}") } @@ -167,6 +211,17 @@ the one bundled with the version of node installation, you can set `npmVersion` in the `node` extension block. The plugin will install the npm to the project's `node_modules` directory by configuring the `npmSetup` task. +Using a Custom (project-local) Version of `yarn` +----------------------------------------------- + +The plugin never uses a locally-installed `yarn` because it may be deleted during +`yarn` execution. +Instead, it installs `yarn` into `yarnWorkDir` (`.gradle/yarn/` by default) by +the `yarnSetup` task and use it. + +If you would like the plugin to install use a custom version of yarn, you can set +`yarnVersion` in the `node` extension block. + Building the Plugin ------------------- diff --git a/src/integTest/groovy/com/moowork/gradle/node/YarnInstall_integTest.groovy b/src/integTest/groovy/com/moowork/gradle/node/YarnInstall_integTest.groovy new file mode 100644 index 0000000..7e1beed --- /dev/null +++ b/src/integTest/groovy/com/moowork/gradle/node/YarnInstall_integTest.groovy @@ -0,0 +1,64 @@ +package com.moowork.gradle.node + +import com.moowork.gradle.AbstractIntegTest + +class YarnInstall_integTest + extends AbstractIntegTest +{ + def 'install packages with yarn'() + { + given: + writeBuild( ''' + apply plugin: 'com.moowork.node' + + node { + version = "6.9.1" + yarnVersion = "0.16.1" + download = true + workDir = file('build/node') + yarnWorkDir = file('build/yarn') + } + ''' ) + writeEmptyPackageJson() + + when: + def result = runTasksSuccessfully( 'yarn' ) + + then: + result.wasExecuted( 'yarn' ) + + when: + result = runTasksSuccessfully( 'yarn' ) + + then: + result.wasUpToDate( 'yarn' ) + } + + def 'install packages with yarn in different directory'() + { + given: + writeBuild( ''' + apply plugin: 'com.moowork.node' + + node { + version = "6.9.1" + yarnVersion = "0.15.1" + download = true + workDir = file('build/node') + yarnWorkDir = file('build/yarn') + nodeModulesDir = file('subdirectory') + } + ''' ) + writeFile( 'subdirectory/package.json', """{ + "name": "example", + "dependencies": { + } + }""" ) + + when: + def result = runTasksSuccessfully( 'yarn' ) + + then: + result.wasExecuted( 'yarn' ) + } +} diff --git a/src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy b/src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy new file mode 100644 index 0000000..19e1733 --- /dev/null +++ b/src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy @@ -0,0 +1,57 @@ +package com.moowork.gradle.node + +import com.moowork.gradle.AbstractIntegTest + +class YarnRule_integTest + extends AbstractIntegTest +{ + def 'execute yarn_install rule'() + { + given: + writeBuild( ''' + apply plugin: 'com.moowork.node' + + node { + version = "6.9.1" + yarnVersion = "0.16.1" + download = true + workDir = file('build/node') + yarnWorkDir = file('build/yarn') + } + ''' ) + writeEmptyPackageJson() + + when: + def result = runTasksSuccessfully( 'yarn_install' ) + + then: + result.wasExecuted( 'yarn_install' ) + } + + def 'can execute an yarn module using yarn_run_'() + { + given: + writeBuild( ''' + apply plugin: 'com.moowork.node' + + node { + version = "6.9.1" + yarnVersion = "0.17.5" + download = true + } + ''' ) + + copyResources( 'fixtures/npm-missing/package.json', 'package.json' ) + + when: + def result = runTasksSuccessfully( 'yarn_run_parent' ) + + then: + result.success + fileExists( 'child1.txt' ) + fileExists( 'child2.txt' ) + fileExists( 'parent1.txt' ) + fileExists( 'parent2.txt' ) + } + +} diff --git a/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy b/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy index 3d8d0a9..e4ba9fe 100644 --- a/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy +++ b/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy @@ -9,16 +9,22 @@ class NodeExtension def File workDir + def File yarnWorkDir + def File nodeModulesDir def String version = '4.4.0' def String npmVersion = '' + def String yarnVersion = '' + def String distBaseUrl = 'https://nodejs.org/dist' def String npmCommand = 'npm' + def String yarnCommand = 'yarn' + def boolean download = false def Variant variant @@ -27,6 +33,7 @@ class NodeExtension { def cacheDir = new File( project.projectDir, '.gradle' ) this.workDir = new File( cacheDir, 'nodejs' ) + this.yarnWorkDir = new File( cacheDir, 'yarn' ) this.nodeModulesDir = project.projectDir } diff --git a/src/main/groovy/com/moowork/gradle/node/NodePlugin.groovy b/src/main/groovy/com/moowork/gradle/node/NodePlugin.groovy index 44e924d..b16d915 100644 --- a/src/main/groovy/com/moowork/gradle/node/NodePlugin.groovy +++ b/src/main/groovy/com/moowork/gradle/node/NodePlugin.groovy @@ -16,6 +16,8 @@ class NodePlugin private NpmSetupTask npmSetupTask + private YarnSetupTask yarnSetupTask + @Override void apply( final Project project ) { @@ -25,11 +27,13 @@ class NodePlugin addGlobalTypes() addTasks() addNpmRule() + addYarnRule() this.project.afterEvaluate { this.config.variant = new VariantBuilder( this.config ).build() configureSetupTask() configureNpmSetupTask() + configureYarnSetupTask() } } @@ -37,13 +41,16 @@ class NodePlugin { addGlobalTaskType( NodeTask ) addGlobalTaskType( NpmTask ) + addGlobalTaskType( YarnTask ) } private void addTasks() { this.project.tasks.create( NpmInstallTask.NAME, NpmInstallTask ) + this.project.tasks.create( YarnInstallTask.NAME, YarnInstallTask ) this.setupTask = this.project.tasks.create( SetupTask.NAME, SetupTask ) this.npmSetupTask = this.project.tasks.create( NpmSetupTask.NAME, NpmSetupTask ) + this.yarnSetupTask = this.project.tasks.create( YarnSetupTask.NAME, YarnSetupTask ) } private void addGlobalTaskType( Class type ) @@ -70,6 +77,25 @@ class NodePlugin } } + private void addYarnRule() + { + // note this rule also makes it possible to specify e.g. "dependsOn yarn_install" + project.getTasks().addRule( 'Pattern: "yarn_": Executes an Yarn command.' ) { String taskName -> + if ( taskName.startsWith( "yarn_" ) ) + { + YarnTask yarnTask = project.getTasks().create( taskName, YarnTask.class ) + String[] tokens = taskName.split( '_' ).tail() // all except first + yarnTask.yarnCommand = tokens + + if (tokens.head().equalsIgnoreCase("run")) { + yarnTask.dependsOn(YarnInstallTask.NAME) + } + + return yarnTask + } + } + } + private void configureSetupTask() { this.setupTask.setEnabled( this.config.download ) @@ -79,4 +105,9 @@ class NodePlugin { this.npmSetupTask.configureNpmVersion( this.config.npmVersion ) } + + private void configureYarnSetupTask() + { + this.yarnSetupTask.configureYarnVersion( this.config.yarnVersion ) + } } diff --git a/src/main/groovy/com/moowork/gradle/node/exec/YarnExecRunner.groovy b/src/main/groovy/com/moowork/gradle/node/exec/YarnExecRunner.groovy new file mode 100644 index 0000000..f1d81e5 --- /dev/null +++ b/src/main/groovy/com/moowork/gradle/node/exec/YarnExecRunner.groovy @@ -0,0 +1,36 @@ +package com.moowork.gradle.node.exec + +import org.gradle.api.InvalidUserDataException +import org.gradle.api.Project +import org.gradle.process.ExecResult + +class YarnExecRunner + extends ExecRunner +{ + public YarnExecRunner( final Project project ) + { + super( project ) + } + + @Override + protected ExecResult doExecute() + { + if ( this.ext.yarnCommand == 'yarn' && this.ext.variant.windows ) { + this.ext.yarnCommand = 'yarn.cmd' + } + + if ( !this.ext.download ) + { + return run( this.ext.yarnCommand, this.arguments ) + } + + def String yarnScriptFile = "${this.project.node.yarnWorkDir}/node_modules/yarn/bin/yarn.js" + def runner = new NodeExecRunner( this.project ) + runner.arguments = [yarnScriptFile] + this.arguments + runner.environment = this.environment + runner.workingDir = this.workingDir + runner.execOverrides = this.execOverrides + runner.ignoreExitValue = this.ignoreExitValue + return runner.execute() + } +} diff --git a/src/main/groovy/com/moowork/gradle/node/task/YarnInstallTask.groovy b/src/main/groovy/com/moowork/gradle/node/task/YarnInstallTask.groovy new file mode 100644 index 0000000..dd227b4 --- /dev/null +++ b/src/main/groovy/com/moowork/gradle/node/task/YarnInstallTask.groovy @@ -0,0 +1,23 @@ +package com.moowork.gradle.node.task + +/** + * yarn install that only gets executed if gradle decides so.*/ +class YarnInstallTask + extends YarnTask +{ + public final static String NAME = 'yarn' + + public YarnInstallTask() + { + this.group = 'Node' + this.description = 'Install node packages using Yarn.' + setYarnCommand( '' ) + dependsOn( [YarnSetupTask.NAME] ) + + this.project.afterEvaluate { + getInputs().file( new File( (File) this.project.node.nodeModulesDir, 'package.json' ) ) + getInputs().file( new File( (File) this.project.node.nodeModulesDir, 'yarn.lock' ) ) + getOutputs().dir( new File( (File) this.project.node.nodeModulesDir, 'node_modules' ) ) + } + } +} diff --git a/src/main/groovy/com/moowork/gradle/node/task/YarnSetupTask.groovy b/src/main/groovy/com/moowork/gradle/node/task/YarnSetupTask.groovy new file mode 100644 index 0000000..7f9bafc --- /dev/null +++ b/src/main/groovy/com/moowork/gradle/node/task/YarnSetupTask.groovy @@ -0,0 +1,36 @@ +package com.moowork.gradle.node.task + +/** + * Setup a specific version of Yarn to be used by the build. + **/ +class YarnSetupTask + extends NpmTask +{ + public final static String NAME = 'yarnSetup' + + public YarnSetupTask() + { + this.group = 'Node' + this.description = 'Setup a specific version of Yarn to be used by the build.' + dependsOn( [NpmSetupTask.NAME] ) + + this.project.afterEvaluate { + getOutputs().dir( this.project.node.yarnWorkDir ) + } + } + + void configureYarnVersion( String yarnVersion ) + { + def yarnDir = this.project.node.yarnWorkDir + if ( !yarnVersion.isEmpty() ) + { + logger.debug( "Setting yarnVersion to ${yarnVersion}" ) + setArgs( ['install', '--prefix', yarnDir, "yarn@${yarnVersion}"] ) + getInputs().property( 'yarnVersion', yarnVersion ) + } + else + { + setArgs( ['install', '--prefix', yarnDir, "yarn"] ) + } + } +} diff --git a/src/main/groovy/com/moowork/gradle/node/task/YarnTask.groovy b/src/main/groovy/com/moowork/gradle/node/task/YarnTask.groovy new file mode 100644 index 0000000..6e2726b --- /dev/null +++ b/src/main/groovy/com/moowork/gradle/node/task/YarnTask.groovy @@ -0,0 +1,89 @@ +package com.moowork.gradle.node.task + +import com.moowork.gradle.node.exec.YarnExecRunner +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecResult + +class YarnTask + extends DefaultTask +{ + protected YarnExecRunner runner + + private Iterable args = [] + + private ExecResult result + + private String[] yarnCommand + + public YarnTask() + { + this.runner = new YarnExecRunner( this.project ) + dependsOn( YarnSetupTask.NAME ) + + this.project.afterEvaluate { + if ( !this.runner.workingDir ) + { + def workingDir = this.project.node.nodeModulesDir + setWorkingDir( workingDir ) + } + + if ( !this.runner.workingDir.exists() ) + { + this.runner.workingDir.mkdirs(); + } + } + } + + void setArgs( final Iterable value ) + { + this.args = value + } + + void setYarnCommand( String[] cmd ) + { + this.yarnCommand = cmd + } + + Iterable getArgs() + { + return this.args + } + + void setEnvironment( final Map value ) + { + this.runner.environment = value + } + + void setWorkingDir( final Object value ) + { + this.runner.workingDir = value + } + + void setIgnoreExitValue( final boolean value ) + { + this.runner.ignoreExitValue = value + } + + void setExecOverrides( final Closure closure ) + { + this.runner.execOverrides = closure + } + + ExecResult getResult() + { + return this.result + } + + @TaskAction + void exec() + { + if ( this.yarnCommand != null ) + { + this.runner.arguments.addAll( this.yarnCommand ) + } + + this.runner.arguments.addAll( this.args ) + this.result = this.runner.execute() + } +} diff --git a/src/test/groovy/com/moowork/gradle/node/NodePluginTest.groovy b/src/test/groovy/com/moowork/gradle/node/NodePluginTest.groovy index 0a93d7f..68ad350 100644 --- a/src/test/groovy/com/moowork/gradle/node/NodePluginTest.groovy +++ b/src/test/groovy/com/moowork/gradle/node/NodePluginTest.groovy @@ -44,11 +44,16 @@ class NodePluginTest this.project.evaluate() then: - this.project.tasks.getRules().size() == 1 - def rule = this.project.tasks.getRules().get( 0 ) + this.project.tasks.getRules().size() == 2 + def npmRule = this.project.tasks.getRules().get( 0 ) + def yarnRule = this.project.tasks.getRules().get( 1 ) !this.project.tasks.getAsMap().containsKey( 'npm_install' ) - rule.apply( 'npm_install' ) + npmRule.apply( 'npm_install' ) this.project.tasks.getAsMap().containsKey( 'npm_install' ) + + !this.project.tasks.getAsMap().containsKey( 'yarn_upgrade' ) + yarnRule.apply( 'yarn_upgrade' ) + this.project.tasks.getAsMap().containsKey( 'yarn_upgrade' ) } } diff --git a/src/test/groovy/com/moowork/gradle/node/task/YarnSetupTaskTest.groovy b/src/test/groovy/com/moowork/gradle/node/task/YarnSetupTaskTest.groovy new file mode 100644 index 0000000..7e2af31 --- /dev/null +++ b/src/test/groovy/com/moowork/gradle/node/task/YarnSetupTaskTest.groovy @@ -0,0 +1,40 @@ +package com.moowork.gradle.node.task + +import org.gradle.process.ExecSpec + +class YarnSetupTaskTest + extends AbstractTaskTest +{ + def "exec yarnSetup task"() + { + given: + this.execSpec = Mock( ExecSpec ) + + def task = this.project.tasks.create( 'simple', YarnSetupTask ) + + when: + this.project.evaluate() + task.exec() + + then: + task.result.exitValue == 0 + 1 * this.execSpec.setArgs( [] ) + } + + def "exec yarnSetup task (version specified)"() + { + given: + this.ext.npmVersion = '0.15.1' + this.execSpec = Mock( ExecSpec ) + + def task = this.project.tasks.create( 'simple', YarnSetupTask ) + + when: + this.project.evaluate() + task.exec() + + then: + task.result.exitValue == 0 + 1 * this.execSpec.setArgs( [] ) + } +} diff --git a/src/test/groovy/com/moowork/gradle/node/task/YarnTaskTest.groovy b/src/test/groovy/com/moowork/gradle/node/task/YarnTaskTest.groovy new file mode 100644 index 0000000..4bbd1ef --- /dev/null +++ b/src/test/groovy/com/moowork/gradle/node/task/YarnTaskTest.groovy @@ -0,0 +1,48 @@ +package com.moowork.gradle.node.task + +import org.gradle.process.ExecSpec + +class YarnTaskTest + extends AbstractTaskTest +{ + def "exec yarn task"() + { + given: + this.execSpec = Mock( ExecSpec ) + + def task = this.project.tasks.create( 'simple', YarnTask ) + task.args = ['a', 'b'] + task.environment = ['a': '1'] + task.ignoreExitValue = true + task.workingDir = this.projectDir + task.execOverrides = {} + + when: + this.project.evaluate() + task.exec() + + then: + task.args == ['a', 'b'] + task.result.exitValue == 0 + 1 * this.execSpec.setIgnoreExitValue( true ) + 1 * this.execSpec.setEnvironment( ['a': '1'] ) + 1 * this.execSpec.setArgs( ['a', 'b'] ) + } + + def "exec npm task (download)"() + { + given: + this.ext.download = true + this.execSpec = Mock( ExecSpec ) + + def task = this.project.tasks.create( 'simple', YarnTask ) + + when: + this.project.evaluate() + task.exec() + + then: + task.result.exitValue == 0 + 1 * this.execSpec.setIgnoreExitValue( false ) + } +} From 3c68e19f9d790e2bcd80752d5df6aefe6240febf Mon Sep 17 00:00:00 2001 From: kaitoy Date: Tue, 22 Nov 2016 17:58:34 -0700 Subject: [PATCH 2/3] Change a yarn integration test to use concurrently instead of npm-run-all to address a Travis build issue. The issue is https://travis-ci.org/srs/gradle-node-plugin/builds/177526145. This issue occurred because yarn deleted the npm locally installed by npmSetup during the yarn task and so npm-run-all couldn't find npm executable. It could be fixed by adding devDependency to npm, but instead I replaced npm-run-all with concurrently because npm should not be used when yarn is being used. --- .../moowork/gradle/node/YarnRule_integTest.groovy | 2 +- .../resources/fixtures/yarn/package.json | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/integTest/resources/fixtures/yarn/package.json diff --git a/src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy b/src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy index 19e1733..91146ec 100644 --- a/src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy +++ b/src/integTest/groovy/com/moowork/gradle/node/YarnRule_integTest.groovy @@ -41,7 +41,7 @@ class YarnRule_integTest } ''' ) - copyResources( 'fixtures/npm-missing/package.json', 'package.json' ) + copyResources( 'fixtures/yarn/package.json', 'package.json' ) when: def result = runTasksSuccessfully( 'yarn_run_parent' ) diff --git a/src/integTest/resources/fixtures/yarn/package.json b/src/integTest/resources/fixtures/yarn/package.json new file mode 100644 index 0000000..3df432e --- /dev/null +++ b/src/integTest/resources/fixtures/yarn/package.json @@ -0,0 +1,15 @@ +{ + "name": "example", + "devDependencies": { + "yarn": "0.16.1", + "concurrently": "3.1.0" + }, + "dependencies": { + "less": "2.6.1" + }, + "scripts": { + "child1": "echo \"child1\" > child1.txt", + "child2": "echo \"child2\" > child2.txt", + "parent": "echo \"parent1\" > parent1.txt && concurrently \"yarn run child1\" \"yarn run child2\" && echo \"parent2\" > parent2.txt" + } +} From 00cc0c899fb7cb768696c888d60884656cf6f4ed Mon Sep 17 00:00:00 2001 From: kaitoy Date: Wed, 23 Nov 2016 10:17:14 -0700 Subject: [PATCH 3/3] Change npmSetup to install npm into the cache directory rather than node_modles directory so that it's not deleted by yarn. By this change, the npmSetup-installed npm is not deleted by npm with shrinkwrap. --- README.md | 15 +- .../gradle/node/NpmRule_integTest.groovy | 32 +- .../fixtures/npm-missing/npm-shrinkwrap.json | 537 ------------------ .../fixtures/npm-missing/package.json | 7 +- .../moowork/gradle/node/NodeExtension.groovy | 3 + .../gradle/node/exec/NpmExecRunner.groovy | 21 +- .../gradle/node/task/NpmSetupTask.groovy | 4 +- 7 files changed, 24 insertions(+), 595 deletions(-) delete mode 100644 src/integTest/resources/fixtures/npm-missing/npm-shrinkwrap.json diff --git a/README.md b/README.md index 7afbfef..ec7ab8a 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,9 @@ You can configure the plugin using the "node" extension block, like this: // Set the work directory for unpacking node workDir = file("${project.buildDir}/nodejs") + // Set the work directory for NPM + npmWorkDir = file("${project.buildDir}/npm") + // Set the work directory for Yarn yarnWorkDir = file("${project.buildDir}/yarn") @@ -203,13 +206,13 @@ You can configure the plugin using the "node" extension block, like this: Using a Custom (project-local) Version of `npm` ----------------------------------------------- -The plugin will use a locally-installed `npm` if it exists, regardless of the -method of installation. +If `npmVersion` is specified, the plugin installs that version of `npm` into `npmWorkDir` +by the `npmSetup` task and use it. + +If `npmVersion` is not specified and a locally-installed `npm` exists, The plugin will +use it. -If you would like the plugin to install use a custom version of npm rather than -the one bundled with the version of node installation, you can set `npmVersion` -in the `node` extension block. The plugin will install the npm to the project's -`node_modules` directory by configuring the `npmSetup` task. +Otherwise, the plugin will use the `npm` bundled with the version of node installation. Using a Custom (project-local) Version of `yarn` ----------------------------------------------- diff --git a/src/integTest/groovy/com/moowork/gradle/node/NpmRule_integTest.groovy b/src/integTest/groovy/com/moowork/gradle/node/NpmRule_integTest.groovy index 28a42c8..e13465f 100644 --- a/src/integTest/groovy/com/moowork/gradle/node/NpmRule_integTest.groovy +++ b/src/integTest/groovy/com/moowork/gradle/node/NpmRule_integTest.groovy @@ -43,39 +43,11 @@ class NpmRule_integTest copyResources( 'fixtures/npm-missing/package.json', 'package.json' ) when: - def result = runTasksSuccessfully( 'npm_run_parent' ) + def result = runTasksSuccessfully( 'npm_run_echoTest' ) then: result.success - fileExists( 'child1.txt' ) - fileExists( 'child2.txt' ) - fileExists( 'parent1.txt' ) - fileExists( 'parent2.txt' ) - } - - def 'fails to run npm module using npm_run_ when shrinkwrap removes local npm'() - { - given: - writeBuild( ''' - apply plugin: 'com.moowork.node' - - node { - version = "5.9.0" - npmVersion = "3.8.3" - download = true - } - ''' ) - - copyResources( 'fixtures/npm-missing/package.json', 'package.json' ) - copyResources( 'fixtures/npm-missing/npm-shrinkwrap.json', 'npm-shrinkwrap.json' ) - - when: - def result = runTasksWithFailure( 'npm_run_parent' ) - - then: - result.failure != null - result.standardError.contains( 'Could not run npm command - local npm not found but requested in gradle node' ) - result.standardError.contains( '3.8.3' ) + fileExists( 'test.txt' ) } def 'succeeds to run npm module using npm_run_ when shrinkwrap contains local npm'() diff --git a/src/integTest/resources/fixtures/npm-missing/npm-shrinkwrap.json b/src/integTest/resources/fixtures/npm-missing/npm-shrinkwrap.json deleted file mode 100644 index 562bb4d..0000000 --- a/src/integTest/resources/fixtures/npm-missing/npm-shrinkwrap.json +++ /dev/null @@ -1,537 +0,0 @@ -{ - "name": "example", - "dependencies": { - "ansi-regex": { - "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" - }, - "ansi-styles": { - "version": "2.2.1", - "from": "ansi-styles@>=2.2.1 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" - }, - "array-filter": { - "version": "0.0.1", - "from": "array-filter@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz" - }, - "array-map": { - "version": "0.0.0", - "from": "array-map@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz" - }, - "array-reduce": { - "version": "0.0.0", - "from": "array-reduce@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz" - }, - "asap": { - "version": "2.0.3", - "from": "asap@>=2.0.3 <2.1.0", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.3.tgz" - }, - "asn1": { - "version": "0.2.3", - "from": "asn1@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "async": { - "version": "1.5.2", - "from": "async@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - }, - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.3.2", - "from": "aws4@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.3.2.tgz" - }, - "babel-runtime": { - "version": "6.6.1", - "from": "babel-runtime@>=6.3.13 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz" - }, - "balanced-match": { - "version": "0.3.0", - "from": "balanced-match@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" - }, - "bl": { - "version": "1.0.3", - "from": "bl@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz" - }, - "boom": { - "version": "2.10.1", - "from": "boom@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "brace-expansion": { - "version": "1.1.3", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz" - }, - "caseless": { - "version": "0.11.0", - "from": "caseless@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz" - }, - "chalk": { - "version": "1.1.3", - "from": "chalk@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" - }, - "combined-stream": { - "version": "1.0.5", - "from": "combined-stream@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz" - }, - "commander": { - "version": "2.9.0", - "from": "commander@>=2.9.0 <3.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - }, - "core-js": { - "version": "2.2.1", - "from": "core-js@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.2.1.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "cross-spawn-async": { - "version": "2.1.9", - "from": "cross-spawn-async@>=2.1.9 <3.0.0", - "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.1.9.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "dashdash": { - "version": "1.13.0", - "from": "dashdash@>=1.10.1 <2.0.0", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.13.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - }, - "duplexer": { - "version": "0.1.1", - "from": "duplexer@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "ecc-jsbn@>=0.0.1 <1.0.0", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" - }, - "errno": { - "version": "0.1.4", - "from": "errno@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz" - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - }, - "event-stream": { - "version": "3.3.2", - "from": "event-stream@>=3.3.0 <3.4.0", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.2.tgz" - }, - "extend": { - "version": "3.0.0", - "from": "extend@>=3.0.0 <3.1.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" - }, - "extsprintf": { - "version": "1.0.2", - "from": "extsprintf@1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "1.0.0-rc4", - "from": "form-data@>=1.0.0-rc3 <1.1.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz" - }, - "from": { - "version": "0.1.3", - "from": "from@>=0.0.0 <1.0.0", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.3.tgz" - }, - "generate-function": { - "version": "2.0.0", - "from": "generate-function@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz" - }, - "generate-object-property": { - "version": "1.2.0", - "from": "generate-object-property@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz" - }, - "graceful-fs": { - "version": "4.1.3", - "from": "graceful-fs@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.3.tgz" - }, - "graceful-readlink": { - "version": "1.0.1", - "from": "graceful-readlink@>=1.0.0", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" - }, - "har-validator": { - "version": "2.0.6", - "from": "har-validator@>=2.0.6 <2.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz" - }, - "has-ansi": { - "version": "2.0.0", - "from": "has-ansi@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@>=3.1.0 <3.2.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" - }, - "hoek": { - "version": "2.16.3", - "from": "hoek@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" - }, - "image-size": { - "version": "0.4.0", - "from": "image-size@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.4.0.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - }, - "is-absolute": { - "version": "0.1.7", - "from": "is-absolute@>=0.1.7 <0.2.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz" - }, - "is-my-json-valid": { - "version": "2.13.1", - "from": "is-my-json-valid@>=2.12.4 <3.0.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz" - }, - "is-property": { - "version": "1.0.2", - "from": "is-property@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" - }, - "is-relative": { - "version": "0.1.3", - "from": "is-relative@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "isexe": { - "version": "1.1.2", - "from": "isexe@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "jodid25519": { - "version": "1.0.2", - "from": "jodid25519@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" - }, - "jsbn": { - "version": "0.1.0", - "from": "jsbn@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz" - }, - "json-schema": { - "version": "0.2.2", - "from": "json-schema@0.2.2", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@>=5.0.1 <5.1.0", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "jsonify": { - "version": "0.0.0", - "from": "jsonify@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" - }, - "jsonpointer": { - "version": "2.0.0", - "from": "jsonpointer@2.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz" - }, - "jsprim": { - "version": "1.2.2", - "from": "jsprim@>=1.2.2 <2.0.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz" - }, - "less": { - "version": "2.6.1", - "from": "less@2.6.1", - "resolved": "https://registry.npmjs.org/less/-/less-2.6.1.tgz" - }, - "lru-cache": { - "version": "4.0.1", - "from": "lru-cache@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz" - }, - "map-stream": { - "version": "0.1.0", - "from": "map-stream@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz" - }, - "mime": { - "version": "1.3.4", - "from": "mime@>=1.2.11 <2.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" - }, - "mime-db": { - "version": "1.22.0", - "from": "mime-db@>=1.22.0 <1.23.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.22.0.tgz" - }, - "mime-types": { - "version": "2.1.10", - "from": "mime-types@>=2.1.7 <2.2.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz" - }, - "minimatch": { - "version": "3.0.0", - "from": "minimatch@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz" - }, - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - }, - "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.7 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" - }, - "npm-run-all": { - "version": "1.6.0", - "from": "npm-run-all@1.6.0", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-1.6.0.tgz" - }, - "oauth-sign": { - "version": "0.8.1", - "from": "oauth-sign@>=0.8.0 <0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.1.tgz" - }, - "pause-stream": { - "version": "0.0.11", - "from": "pause-stream@0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz" - }, - "pinkie": { - "version": "2.0.4", - "from": "pinkie@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - }, - "pinkie-promise": { - "version": "2.0.0", - "from": "pinkie-promise@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.0.tgz" - }, - "process-nextick-args": { - "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" - }, - "promise": { - "version": "7.1.1", - "from": "promise@>=7.1.1 <8.0.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz" - }, - "prr": { - "version": "0.0.0", - "from": "prr@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz" - }, - "ps-tree": { - "version": "1.0.1", - "from": "ps-tree@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.0.1.tgz" - }, - "pseudomap": { - "version": "1.0.2", - "from": "pseudomap@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" - }, - "qs": { - "version": "6.0.2", - "from": "qs@>=6.0.2 <6.1.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.0.2.tgz" - }, - "readable-stream": { - "version": "2.0.6", - "from": "readable-stream@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" - }, - "request": { - "version": "2.69.0", - "from": "request@>=2.51.0 <3.0.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.69.0.tgz" - }, - "shell-quote": { - "version": "1.5.0", - "from": "shell-quote@>=1.4.3 <2.0.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.5.0.tgz" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - }, - "source-map": { - "version": "0.5.3", - "from": "source-map@>=0.5.3 <0.6.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.3.tgz" - }, - "split": { - "version": "0.3.3", - "from": "split@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz" - }, - "sshpk": { - "version": "1.7.4", - "from": "sshpk@>=1.7.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.7.4.tgz" - }, - "stream-combiner": { - "version": "0.0.4", - "from": "stream-combiner@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "stringstream": { - "version": "0.0.5", - "from": "stringstream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" - }, - "strip-ansi": { - "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - }, - "supports-color": { - "version": "2.0.0", - "from": "supports-color@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - }, - "through": { - "version": "2.3.8", - "from": "through@>=2.3.1 <2.4.0", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - }, - "tough-cookie": { - "version": "2.2.2", - "from": "tough-cookie@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" - }, - "tunnel-agent": { - "version": "0.4.2", - "from": "tunnel-agent@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.2.tgz" - }, - "tweetnacl": { - "version": "0.14.3", - "from": "tweetnacl@>=0.13.0 <1.0.0", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.3.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - }, - "verror": { - "version": "1.3.6", - "from": "verror@1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" - }, - "which": { - "version": "1.2.4", - "from": "which@>=1.2.4 <2.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.4.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - }, - "yallist": { - "version": "2.0.0", - "from": "yallist@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz" - } - } -} diff --git a/src/integTest/resources/fixtures/npm-missing/package.json b/src/integTest/resources/fixtures/npm-missing/package.json index a79b1a9..3ad50cf 100644 --- a/src/integTest/resources/fixtures/npm-missing/package.json +++ b/src/integTest/resources/fixtures/npm-missing/package.json @@ -1,14 +1,9 @@ { "name": "example", - "devDependencies": { - "npm-run-all": "1.6.0" - }, "dependencies": { "less": "2.6.1" }, "scripts": { - "child1": "echo \"child1\" > child1.txt", - "child2": "echo \"child2\" > child2.txt", - "parent": "echo \"parent1\" > parent1.txt && npm-run-all child1 child2 && echo \"parent2\" > parent2.txt" + "echoTest": "echo \"test\" > test.txt" } } diff --git a/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy b/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy index e4ba9fe..c400687 100644 --- a/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy +++ b/src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy @@ -9,6 +9,8 @@ class NodeExtension def File workDir + def File npmWorkDir + def File yarnWorkDir def File nodeModulesDir @@ -33,6 +35,7 @@ class NodeExtension { def cacheDir = new File( project.projectDir, '.gradle' ) this.workDir = new File( cacheDir, 'nodejs' ) + this.npmWorkDir = new File( cacheDir, 'npm' ) this.yarnWorkDir = new File( cacheDir, 'yarn' ) this.nodeModulesDir = project.projectDir } diff --git a/src/main/groovy/com/moowork/gradle/node/exec/NpmExecRunner.groovy b/src/main/groovy/com/moowork/gradle/node/exec/NpmExecRunner.groovy index 262d582..2a0e28c 100644 --- a/src/main/groovy/com/moowork/gradle/node/exec/NpmExecRunner.groovy +++ b/src/main/groovy/com/moowork/gradle/node/exec/NpmExecRunner.groovy @@ -26,23 +26,16 @@ class NpmExecRunner def String npmScriptFile = this.variant.npmScriptFile def File localNpm = project.file( new File( this.ext.nodeModulesDir, 'node_modules/npm/bin/npm-cli.js' ) ) + def File workNpm = project.file( new File( this.ext.npmWorkDir, 'node_modules/npm/bin/npm-cli.js' ) ) - // Use locally-installed npm if available - if ( localNpm.exists() ) + // Use npm specified by user if available + if ( workNpm.exists() ) { - npmScriptFile = localNpm.absolutePath + npmScriptFile = workNpm.absolutePath } - - boolean notInstalling = !arguments.join(' ').startsWith('install') - boolean configuredLocalNpm = !project.node.npmVersion.empty - - if ( !localNpm.exists() && configuredLocalNpm && notInstalling ) - { - throw new InvalidUserDataException(""" - Could not run npm command - local npm not found but requested in gradle node configuration. - A common reason for this is an npm-shrinkwrap.json file is present and un-installs npm. - To resolve this, add npm with version '${project.node.npmVersion}' to your package.json. - """.stripIndent()) + // Use locally-installed npm if available + else if ( localNpm.exists() ) { + npmScriptFile = localNpm.absolutePath } def runner = new NodeExecRunner( this.project ) diff --git a/src/main/groovy/com/moowork/gradle/node/task/NpmSetupTask.groovy b/src/main/groovy/com/moowork/gradle/node/task/NpmSetupTask.groovy index 9a57b45..ee1ac9f 100644 --- a/src/main/groovy/com/moowork/gradle/node/task/NpmSetupTask.groovy +++ b/src/main/groovy/com/moowork/gradle/node/task/NpmSetupTask.groovy @@ -15,7 +15,7 @@ class NpmSetupTask this.enabled = false this.project.afterEvaluate { - getOutputs().dir( new File( (File) this.project.node.nodeModulesDir, 'node_modules/npm' ) ) + getOutputs().dir( this.project.node.npmWorkDir ) setWorkingDir( this.project.node.nodeModulesDir ) } } @@ -25,7 +25,7 @@ class NpmSetupTask if ( !npmVersion.isEmpty() ) { logger.debug( "Setting npmVersion to ${npmVersion}" ) - setArgs( ['install', "npm@${npmVersion}"] ) + setArgs( ['install', '--prefix', this.project.node.npmWorkDir, "npm@${npmVersion}"] ) setEnabled( true ) getInputs().property( 'npmVersion', npmVersion ) }