From 93aefc20994f04be8c48951e9272540b49f0ca82 Mon Sep 17 00:00:00 2001 From: Benoit Sautel Date: Tue, 6 Apr 2021 09:38:31 +0200 Subject: [PATCH] #67 Update the pull request #89 by applying all recent changes to the pnpm part. --- .../com/github/gradle/node/NodeExtension.kt | 19 ++++ .../node/npm/exec/NpmExecConfiguration.kt | 8 +- .../gradle/node/npm/exec/NpmExecRunner.kt | 95 +++++++++------- .../github/gradle/node/npm/proxy/NpmProxy.kt | 48 +++++--- .../gradle/node/pnpm/exec/PnpmExecRunner.kt | 106 +++++++++++------- .../gradle/node/pnpm/task/PnpmInstallTask.kt | 31 ++--- .../gradle/node/pnpm/task/PnpmSetupTask.kt | 15 ++- .../github/gradle/node/pnpm/task/PnpmTask.kt | 40 ++++--- .../github/gradle/node/pnpm/task/PnpxTask.kt | 36 ++++-- .../gradle/node/yarn/exec/YarnExecRunner.kt | 46 ++++---- .../gradle/node/KotlinDsl_integTest.groovy | 15 ++- .../pnpm/task/PnpmInstall_integTest.groovy | 4 +- .../node/pnpm/task/PnpmSetupTaskTest.groovy | 8 +- .../gradle/node/pnpm/task/PnpmTaskTest.groovy | 6 +- .../node/pnpm/task/PnpmTask_integTest.groovy | 55 +++++---- .../gradle/node/pnpm/task/PnpxTaskTest.groovy | 11 +- .../node/pnpm/task/PnpxTask_integTest.groovy | 62 +++++----- src/test/resources/fixtures/env/package.json | 2 +- .../fixtures/kotlin/build.gradle.kts | 72 +++++++++++- .../resources/fixtures/pnpm-env/build.gradle | 10 +- src/test/resources/fixtures/pnpm/build.gradle | 16 ++- .../resources/fixtures/pnpx-env/build.gradle | 24 +++- src/test/resources/fixtures/pnpx/build.gradle | 10 +- 23 files changed, 480 insertions(+), 259 deletions(-) diff --git a/src/main/kotlin/com/github/gradle/node/NodeExtension.kt b/src/main/kotlin/com/github/gradle/node/NodeExtension.kt index 3275a2fc..000c1daa 100644 --- a/src/main/kotlin/com/github/gradle/node/NodeExtension.kt +++ b/src/main/kotlin/com/github/gradle/node/NodeExtension.kt @@ -71,10 +71,29 @@ open class NodeExtension(project: Project) { */ val distBaseUrl = project.objects.property() + /** + * The command used to start npm (default npm) + */ val npmCommand = project.objects.property().convention("npm") + + /** + * The command used to start npx (default npx) + */ val npxCommand = project.objects.property().convention("npx") + + /** + * The command used to start yarn (default yarn) + */ val yarnCommand = project.objects.property().convention("yarn") + + /** + * The command used to start pnpm (default pnpm) + */ val pnpmCommand = project.objects.property().convention("pnpm") + + /** + * The command used to start pnpx (default pnpx) + */ val pnpxCommand = project.objects.property().convention("pnpx") /** diff --git a/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecConfiguration.kt b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecConfiguration.kt index e37322cb..94b6f5c3 100644 --- a/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecConfiguration.kt +++ b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecConfiguration.kt @@ -1,14 +1,12 @@ package com.github.gradle.node.npm.exec -import com.github.gradle.node.NodeExtension import com.github.gradle.node.variant.VariantComputer import org.gradle.api.file.Directory import org.gradle.api.provider.Provider -internal typealias CommandExecComputer = (variantComputer: VariantComputer, nodeExtension: NodeExtension, - npmBinDir: Provider) -> Provider +internal typealias CommandExecComputer = (variantComputer: VariantComputer, npmBinDir: Provider) -> Provider internal data class NpmExecConfiguration( - val command: String, - val commandExecComputer: CommandExecComputer + val command: String, + val commandExecComputer: CommandExecComputer ) diff --git a/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecRunner.kt b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecRunner.kt index cd482b74..a20857cc 100644 --- a/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecRunner.kt +++ b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecRunner.kt @@ -5,7 +5,6 @@ import com.github.gradle.node.exec.ExecConfiguration import com.github.gradle.node.exec.ExecRunner import com.github.gradle.node.exec.NodeExecConfiguration import com.github.gradle.node.npm.proxy.NpmProxy -import com.github.gradle.node.npm.proxy.NpmProxy.Companion.computeNpmProxyEnvironmentVariables import com.github.gradle.node.util.ProjectApiHelper import com.github.gradle.node.util.zip import com.github.gradle.node.variant.VariantComputer @@ -20,54 +19,66 @@ internal abstract class NpmExecRunner { private val variantComputer = VariantComputer() - fun executeNpmCommand(project: ProjectApiHelper, extension: NodeExtension, nodeExecConfiguration: NodeExecConfiguration) { - val npmExecConfiguration = NpmExecConfiguration("npm" - ) { variantComputer, nodeExtension, npmBinDir -> variantComputer.computeNpmExec(nodeExtension, npmBinDir) } - executeCommand(project, extension, addProxyEnvironmentVariables(extension, nodeExecConfiguration), - npmExecConfiguration) + fun executeNpmCommand( + project: ProjectApiHelper, + nodeExtension: NodeExtension, + nodeExecConfiguration: NodeExecConfiguration + ) { + val npmExecConfiguration = NpmExecConfiguration( + "npm" + ) { variantComputer, npmBinDir -> variantComputer.computeNpmExec(nodeExtension, npmBinDir) } + executeCommand( + project, nodeExtension, addProxyEnvironmentVariables(nodeExtension, nodeExecConfiguration), + npmExecConfiguration + ) } - private fun addProxyEnvironmentVariables(nodeExtension: NodeExtension, - nodeExecConfiguration: NodeExecConfiguration): NodeExecConfiguration { - if (NpmProxy.shouldConfigureProxy(System.getenv(), nodeExtension.nodeProxySettings.get())) { - val npmProxyEnvironmentVariables = computeNpmProxyEnvironmentVariables() - if (npmProxyEnvironmentVariables.isNotEmpty()) { - val environmentVariables = - nodeExecConfiguration.environment.plus(npmProxyEnvironmentVariables) - return nodeExecConfiguration.copy(environment = environmentVariables) - } - } - return nodeExecConfiguration + private fun addProxyEnvironmentVariables( + nodeExtension: NodeExtension, + nodeExecConfiguration: NodeExecConfiguration + ): NodeExecConfiguration { + val environment = NpmProxy.addProxyEnvironmentVariables(nodeExtension, nodeExecConfiguration.environment) + return nodeExecConfiguration.copy(environment = environment) } - fun executeNpxCommand(project: ProjectApiHelper, extension: NodeExtension, nodeExecConfiguration: NodeExecConfiguration) { - val npxExecConfiguration = NpmExecConfiguration("npx") { variantComputer, nodeExtension, npmBinDir -> + fun executeNpxCommand( + project: ProjectApiHelper, + nodeExtension: NodeExtension, + nodeExecConfiguration: NodeExecConfiguration + ) { + val npxExecConfiguration = NpmExecConfiguration("npx") { variantComputer, npmBinDir -> variantComputer.computeNpxExec(nodeExtension, npmBinDir) } - executeCommand(project, extension, nodeExecConfiguration, npxExecConfiguration) + executeCommand(project, nodeExtension, nodeExecConfiguration, npxExecConfiguration) } - private fun executeCommand(project: ProjectApiHelper, extension: NodeExtension, nodeExecConfiguration: NodeExecConfiguration, - npmExecConfiguration: NpmExecConfiguration) { + private fun executeCommand( + project: ProjectApiHelper, extension: NodeExtension, nodeExecConfiguration: NodeExecConfiguration, + npmExecConfiguration: NpmExecConfiguration + ) { val execConfiguration = - computeExecConfiguration(extension, npmExecConfiguration, nodeExecConfiguration).get() + computeExecConfiguration(extension, npmExecConfiguration, nodeExecConfiguration).get() val execRunner = ExecRunner() execRunner.execute(project, extension, execConfiguration) } - private fun computeExecConfiguration(extension: NodeExtension, npmExecConfiguration: NpmExecConfiguration, - nodeExecConfiguration: NodeExecConfiguration): Provider { + private fun computeExecConfiguration( + extension: NodeExtension, npmExecConfiguration: NpmExecConfiguration, + nodeExecConfiguration: NodeExecConfiguration + ): Provider { val additionalBinPathProvider = computeAdditionalBinPath(extension) val executableAndScriptProvider = computeExecutable(extension, npmExecConfiguration) return zip(additionalBinPathProvider, executableAndScriptProvider) - .map { (additionalBinPath, executableAndScript) -> - val argsPrefix = - if (executableAndScript.script != null) listOf(executableAndScript.script) else listOf() - val args = argsPrefix.plus(nodeExecConfiguration.command) - ExecConfiguration(executableAndScript.executable, args, additionalBinPath, - nodeExecConfiguration.environment, nodeExecConfiguration.workingDir, - nodeExecConfiguration.ignoreExitValue, nodeExecConfiguration.execOverrides) - } + .map { (additionalBinPath, executableAndScript) -> + val argsPrefix = + if (executableAndScript.script != null) listOf(executableAndScript.script) else listOf() + val args = argsPrefix.plus(nodeExecConfiguration.command) + ExecConfiguration( + executableAndScript.executable, args, additionalBinPath, + nodeExecConfiguration.environment, nodeExecConfiguration.workingDir, + nodeExecConfiguration.ignoreExitValue, nodeExecConfiguration.execOverrides + ) + } } private fun computeExecutable(nodeExtension: NodeExtension, npmExecConfiguration: NpmExecConfiguration): @@ -78,16 +89,18 @@ internal abstract class NpmExecRunner { val npmBinDirProvider = variantComputer.computeNpmBinDir(npmDirProvider) val nodeExecProvider = variantComputer.computeNodeExec(nodeExtension, nodeBinDirProvider) val executableProvider = - npmExecConfiguration.commandExecComputer(variantComputer, nodeExtension, npmBinDirProvider) + npmExecConfiguration.commandExecComputer(variantComputer, npmBinDirProvider) val npmScriptFileProvider = - variantComputer.computeNpmScriptFile(nodeDirProvider, npmExecConfiguration.command) - return zip(nodeExtension.download, nodeExtension.nodeProjectDir, executableProvider, nodeExecProvider, - npmScriptFileProvider).map { + variantComputer.computeNpmScriptFile(nodeDirProvider, npmExecConfiguration.command) + return zip( + nodeExtension.download, nodeExtension.nodeProjectDir, executableProvider, nodeExecProvider, + npmScriptFileProvider + ).map { val (download, nodeProjectDir, executable, nodeExec, - npmScriptFile) = it + npmScriptFile) = it if (download) { val localCommandScript = nodeProjectDir.dir("node_modules/npm/bin") - .file("${npmExecConfiguration.command}-cli.js").asFile + .file("${npmExecConfiguration.command}-cli.js").asFile if (localCommandScript.exists()) { return@map ExecutableAndScript(nodeExec, localCommandScript.absolutePath) } else if (!File(executable).exists()) { @@ -99,8 +112,8 @@ internal abstract class NpmExecRunner { } private data class ExecutableAndScript( - val executable: String, - val script: String? = null + val executable: String, + val script: String? = null ) private fun computeAdditionalBinPath(nodeExtension: NodeExtension): Provider> { diff --git a/src/main/kotlin/com/github/gradle/node/npm/proxy/NpmProxy.kt b/src/main/kotlin/com/github/gradle/node/npm/proxy/NpmProxy.kt index 46c13b00..8cad6e43 100644 --- a/src/main/kotlin/com/github/gradle/node/npm/proxy/NpmProxy.kt +++ b/src/main/kotlin/com/github/gradle/node/npm/proxy/NpmProxy.kt @@ -1,5 +1,6 @@ package com.github.gradle.node.npm.proxy +import com.github.gradle.node.NodeExtension import java.net.URLEncoder import java.util.stream.Collectors.toList import java.util.stream.Stream @@ -11,15 +12,28 @@ internal class NpmProxy { // These are the environment variables that HTTPing applications checks, proxy is on and off. // FTP skipped in hopes of a better future. private val proxyVariables = listOf( - "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY", "PROXY" + "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY", "PROXY" ) // And since npm also takes settings in the form of environment variables with the // NPM_CONFIG_ format, we should check those. Hopefully nobody does this. private val npmProxyVariables = listOf( - "NPM_CONFIG_PROXY", "NPM_CONFIG_HTTPS-PROXY", "NPM_CONFIG_NOPROXY" + "NPM_CONFIG_PROXY", "NPM_CONFIG_HTTPS-PROXY", "NPM_CONFIG_NOPROXY" ) + fun addProxyEnvironmentVariables( + nodeExtension: NodeExtension, + environment: Map + ): Map { + if (shouldConfigureProxy(System.getenv(), nodeExtension.nodeProxySettings.get())) { + val npmProxyEnvironmentVariables = computeNpmProxyEnvironmentVariables() + if (npmProxyEnvironmentVariables.isNotEmpty()) { + return environment.plus(npmProxyEnvironmentVariables) + } + } + return environment + } + fun computeNpmProxyEnvironmentVariables(): Map { val proxyEnvironmentVariables = computeProxyUrlEnvironmentVariables() if (proxyEnvironmentVariables.isNotEmpty()) { @@ -62,7 +76,7 @@ internal class NpmProxy { val proxyPassword = System.getProperty("$proxyProto.proxyPassword") if (proxyUser != null && proxyPassword != null) { proxyArgs[proxyParam] = - "http://${encode(proxyUser)}:${encode(proxyPassword)}@$proxyHost:$proxyPort" + "http://${encode(proxyUser)}:${encode(proxyPassword)}@$proxyHost:$proxyPort" } else { proxyArgs[proxyParam] = "http://$proxyHost:$proxyPort" } @@ -84,21 +98,21 @@ internal class NpmProxy { private fun computeProxyIgnoredHosts(): List { return Stream.of("http.nonProxyHosts", "https.nonProxyHosts") - .map { property -> - val propertyValue = System.getProperty(property) - if (propertyValue != null) { - val hosts = propertyValue.split("|") - return@map hosts - .map { host -> - if (host.contains(":")) host.split(":")[0] - else host - } - } - return@map listOf() + .map { property -> + val propertyValue = System.getProperty(property) + if (propertyValue != null) { + val hosts = propertyValue.split("|") + return@map hosts + .map { host -> + if (host.contains(":")) host.split(":")[0] + else host + } } - .flatMap(List::stream) - .distinct() - .collect(toList()) + return@map listOf() + } + .flatMap(List::stream) + .distinct() + .collect(toList()) } } } diff --git a/src/main/kotlin/com/github/gradle/node/pnpm/exec/PnpmExecRunner.kt b/src/main/kotlin/com/github/gradle/node/pnpm/exec/PnpmExecRunner.kt index faa87a77..470f97de 100644 --- a/src/main/kotlin/com/github/gradle/node/pnpm/exec/PnpmExecRunner.kt +++ b/src/main/kotlin/com/github/gradle/node/pnpm/exec/PnpmExecRunner.kt @@ -5,59 +5,83 @@ import com.github.gradle.node.exec.ExecConfiguration import com.github.gradle.node.exec.ExecRunner import com.github.gradle.node.exec.NodeExecConfiguration import com.github.gradle.node.npm.exec.NpmExecConfiguration -import com.github.gradle.node.npm.proxy.NpmProxy.Companion.computeNpmProxyCliArgs +import com.github.gradle.node.npm.proxy.NpmProxy +import com.github.gradle.node.util.ProjectApiHelper import com.github.gradle.node.util.zip import com.github.gradle.node.variant.VariantComputer -import org.gradle.api.Project import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import javax.inject.Inject + +// TODO to be factorized with NpmExecRunner +internal abstract class PnpmExecRunner { + @get:Inject + abstract val providers: ProviderFactory -internal class PnpmExecRunner { private val variantComputer = VariantComputer() - fun executePnpmCommand(project: Project, nodeExecConfiguration: NodeExecConfiguration) { - val pnpmExecConfiguration = NpmExecConfiguration("pnpm" - ) { variantComputer, nodeExtension, pnpmBinDir -> variantComputer.computePnpmExec(nodeExtension, pnpmBinDir) } - executeCommand(project, addProxyCliArgs(nodeExecConfiguration), pnpmExecConfiguration) + fun executePnpmCommand( + project: ProjectApiHelper, + nodeExtension: NodeExtension, + nodeExecConfiguration: NodeExecConfiguration + ) { + val pnpmExecConfiguration = NpmExecConfiguration( + "pnpm" + ) { variantComputer, pnpmBinDir -> variantComputer.computePnpmExec(nodeExtension, pnpmBinDir) } + executeCommand( + project, nodeExtension, + addProxyEnvironmentVariables(nodeExtension, nodeExecConfiguration), + pnpmExecConfiguration + ) } - private fun addProxyCliArgs(nodeExecConfiguration: NodeExecConfiguration): NodeExecConfiguration { - val pnpmProxyCliArgs = computeNpmProxyCliArgs() - if (pnpmProxyCliArgs.isNotEmpty()) { - val commandWithProxy = pnpmProxyCliArgs.plus(nodeExecConfiguration.command) - return nodeExecConfiguration.copy(command = commandWithProxy) - } - return nodeExecConfiguration + private fun addProxyEnvironmentVariables( + nodeExtension: NodeExtension, + nodeExecConfiguration: NodeExecConfiguration + ): NodeExecConfiguration { + val environment = NpmProxy.addProxyEnvironmentVariables(nodeExtension, nodeExecConfiguration.environment) + return nodeExecConfiguration.copy(environment = environment) } - fun executePnpxCommand(project: Project, nodeExecConfiguration: NodeExecConfiguration) { - val pnpxExecConfiguration = NpmExecConfiguration("pnpx" - ) { variantComputer, nodeExtension, pnpmBinDir -> variantComputer.computePnpxExec(nodeExtension, pnpmBinDir) } - executeCommand(project, nodeExecConfiguration, pnpxExecConfiguration) + fun executePnpxCommand( + project: ProjectApiHelper, + nodeExtension: NodeExtension, + nodeExecConfiguration: NodeExecConfiguration + ) { + val pnpxExecConfiguration = NpmExecConfiguration( + "pnpx" + ) { variantComputer, pnpmBinDir -> variantComputer.computePnpxExec(nodeExtension, pnpmBinDir) } + executeCommand(project, nodeExtension, nodeExecConfiguration, pnpxExecConfiguration) } - private fun executeCommand(project: Project, nodeExecConfiguration: NodeExecConfiguration, - pnpmExecConfiguration: NpmExecConfiguration) { + private fun executeCommand( + project: ProjectApiHelper, nodeExtension: NodeExtension, nodeExecConfiguration: NodeExecConfiguration, + pnpmExecConfiguration: NpmExecConfiguration + ) { val execConfiguration = - computeExecConfiguration(project, pnpmExecConfiguration, nodeExecConfiguration).get() + computeExecConfiguration(nodeExtension, pnpmExecConfiguration, nodeExecConfiguration).get() val execRunner = ExecRunner() - execRunner.execute(project, execConfiguration) + execRunner.execute(project, nodeExtension, execConfiguration) } - private fun computeExecConfiguration(project: Project, pnpmExecConfiguration: NpmExecConfiguration, - nodeExecConfiguration: NodeExecConfiguration): Provider { - val nodeExtension = NodeExtension[project] - val additionalBinPathProvider = computeAdditionalBinPath(project, nodeExtension) + private fun computeExecConfiguration( + nodeExtension: NodeExtension, pnpmExecConfiguration: NpmExecConfiguration, + nodeExecConfiguration: NodeExecConfiguration + ): Provider { + val additionalBinPathProvider = computeAdditionalBinPath(nodeExtension) val executableAndScriptProvider = - computeExecutable(nodeExtension, pnpmExecConfiguration) + computeExecutable(nodeExtension, pnpmExecConfiguration) return zip(additionalBinPathProvider, executableAndScriptProvider) - .map { (additionalBinPath, executableAndScript) -> - val argsPrefix = - if (executableAndScript.script != null) listOf(executableAndScript.script) else listOf() - val args = argsPrefix.plus(nodeExecConfiguration.command) - ExecConfiguration(executableAndScript.executable, args, additionalBinPath, - nodeExecConfiguration.environment, nodeExecConfiguration.workingDir, - nodeExecConfiguration.ignoreExitValue, nodeExecConfiguration.execOverrides) - } + .map { (additionalBinPath, executableAndScript) -> + val argsPrefix = + if (executableAndScript.script != null) listOf(executableAndScript.script) else listOf() + val args = argsPrefix.plus(nodeExecConfiguration.command) + ExecConfiguration( + executableAndScript.executable, args, additionalBinPath, + nodeExecConfiguration.environment, nodeExecConfiguration.workingDir, + nodeExecConfiguration.ignoreExitValue, nodeExecConfiguration.execOverrides + ) + } } private fun computeExecutable(nodeExtension: NodeExtension, pnpmExecConfiguration: NpmExecConfiguration): @@ -68,13 +92,13 @@ internal class PnpmExecRunner { val pnpmBinDirProvider = variantComputer.computePnpmBinDir(pnpmDirProvider) val nodeExecProvider = variantComputer.computeNodeExec(nodeExtension, nodeBinDirProvider) val executableProvider = - pnpmExecConfiguration.commandExecComputer(variantComputer, nodeExtension, pnpmBinDirProvider) + pnpmExecConfiguration.commandExecComputer(variantComputer, pnpmBinDirProvider) return zip(nodeExtension.download, nodeExtension.nodeProjectDir, executableProvider, nodeExecProvider).map { val (download, nodeProjectDir, executable, nodeExec) = it if (download) { val localCommandScript = nodeProjectDir.dir("node_modules/pnpm/bin") - .file("${pnpmExecConfiguration.command}.js").asFile + .file("${pnpmExecConfiguration.command}.js").asFile if (localCommandScript.exists()) { return@map ExecutableAndScript(nodeExec, localCommandScript.absolutePath) } @@ -84,14 +108,14 @@ internal class PnpmExecRunner { } private data class ExecutableAndScript( - val executable: String, - val script: String? = null + val executable: String, + val script: String? = null ) - private fun computeAdditionalBinPath(project: Project, nodeExtension: NodeExtension): Provider> { + private fun computeAdditionalBinPath(nodeExtension: NodeExtension): Provider> { return nodeExtension.download.flatMap { download -> if (!download) { - project.providers.provider { listOf() } + providers.provider { listOf() } } val nodeDirProvider = variantComputer.computeNodeDir(nodeExtension) val nodeBinDirProvider = variantComputer.computeNodeBinDir(nodeDirProvider) diff --git a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmInstallTask.kt b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmInstallTask.kt index 194cdb53..5161dfd4 100644 --- a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmInstallTask.kt +++ b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmInstallTask.kt @@ -1,6 +1,5 @@ package com.github.gradle.node.pnpm.task -import com.github.gradle.node.NodeExtension import com.github.gradle.node.NodePlugin import com.github.gradle.node.util.zip import org.gradle.api.Action @@ -8,19 +7,23 @@ import org.gradle.api.file.ConfigurableFileTree import org.gradle.api.file.Directory import org.gradle.api.file.FileTree import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE import org.gradle.kotlin.dsl.property import java.io.File +import javax.inject.Inject /** * pnpm install that only gets executed if gradle decides so. */ -open class PnpmInstallTask : PnpmTask() { - private val nodeExtension by lazy { NodeExtension[project] } +abstract class PnpmInstallTask : PnpmTask() { + @get:Inject + abstract val providers: ProviderFactory @get:Internal val nodeModulesOutputFilter = - project.objects.property>() + objects.property>() init { group = NodePlugin.PNPM_GROUP @@ -29,7 +32,7 @@ open class PnpmInstallTask : PnpmTask() { pnpmCommand.set(nodeExtension.npmInstallCommand.map { listOf(it) }) } - @PathSensitive(PathSensitivity.RELATIVE) + @PathSensitive(RELATIVE) @InputFile protected fun getPackageJsonFile(): Provider { return projectFileIfExists("package.json") @@ -43,7 +46,7 @@ open class PnpmInstallTask : PnpmTask() { private fun projectFileIfExists(name: String): Provider { return nodeExtension.nodeProjectDir.map { it.file(name).asFile } - .flatMap { if (it.exists()) project.providers.provider { it } else project.providers.provider { null } } + .flatMap { if (it.exists()) providers.provider { it } else providers.provider { null } } } @Optional @@ -52,7 +55,7 @@ open class PnpmInstallTask : PnpmTask() { protected fun getNodeModulesDirectory(): Provider { val filter = nodeModulesOutputFilter.orNull return if (filter == null) nodeExtension.nodeProjectDir.dir("node_modules") - else project.providers.provider { null } + else providers.provider { null } } @Optional @@ -61,13 +64,13 @@ open class PnpmInstallTask : PnpmTask() { protected fun getNodeModulesFiles(): Provider { val nodeModulesDirectoryProvider = nodeExtension.nodeProjectDir.dir("node_modules") return zip(nodeModulesDirectoryProvider, nodeModulesOutputFilter) - .flatMap { (nodeModulesDirectory, nodeModulesOutputFilter) -> - if (nodeModulesOutputFilter != null) { - val fileTree = project.fileTree(nodeModulesDirectory) - nodeModulesOutputFilter.execute(fileTree) - project.providers.provider { fileTree } - } else project.providers.provider { null } - } + .flatMap { (nodeModulesDirectory, nodeModulesOutputFilter) -> + if (nodeModulesOutputFilter != null) { + val fileTree = projectHelper.fileTree(nodeModulesDirectory) + nodeModulesOutputFilter.execute(fileTree) + providers.provider { fileTree } + } else providers.provider { null } + } } // For DSL diff --git a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmSetupTask.kt b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmSetupTask.kt index 3ab1a96f..e7a0365d 100644 --- a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmSetupTask.kt +++ b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmSetupTask.kt @@ -1,7 +1,6 @@ package com.github.gradle.node.pnpm.task import com.github.gradle.node.NodePlugin -import com.github.gradle.node.npm.proxy.NpmProxy import com.github.gradle.node.npm.task.NpmSetupTask import com.github.gradle.node.variant.VariantComputer import org.gradle.api.provider.Provider @@ -11,7 +10,7 @@ import org.gradle.api.tasks.OutputDirectory /** * pnpm install that only gets executed if gradle decides so. */ -open class PnpmSetupTask : NpmSetupTask() { +abstract class PnpmSetupTask : NpmSetupTask() { init { group = NodePlugin.PNPM_GROUP @@ -33,8 +32,14 @@ open class PnpmSetupTask : NpmSetupTask() { val version = nodeExtension.pnpmVersion.get() val pnpmDir = pnpmDir.get() val pnpmPackage = if (version.isNotBlank()) "pnpm@$version" else "pnpm" - return listOf("install", "--global", "--no-save", *NpmProxy.computeNpmProxyCliArgs().toTypedArray(), - "--prefix", pnpmDir.asFile.absolutePath, pnpmPackage) + args.get() + return listOf( + "install", + "--global", + "--no-save", + "--prefix", + pnpmDir.asFile.absolutePath, + pnpmPackage + ) + args.get() } override fun isTaskEnabled(): Boolean { @@ -45,4 +50,4 @@ open class PnpmSetupTask : NpmSetupTask() { const val NAME = "pnpmSetup" } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmTask.kt b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmTask.kt index 924fc13e..2d096e58 100644 --- a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmTask.kt +++ b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpmTask.kt @@ -1,10 +1,13 @@ package com.github.gradle.node.pnpm.task +import com.github.gradle.node.NodeExtension import com.github.gradle.node.NodePlugin import com.github.gradle.node.exec.NodeExecConfiguration import com.github.gradle.node.pnpm.exec.PnpmExecRunner +import com.github.gradle.node.util.ProjectApiHelper import org.gradle.api.Action import org.gradle.api.DefaultTask +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional @@ -13,31 +16,40 @@ import org.gradle.kotlin.dsl.listProperty import org.gradle.kotlin.dsl.mapProperty import org.gradle.kotlin.dsl.property import org.gradle.process.ExecSpec +import javax.inject.Inject -open class PnpmTask: DefaultTask() { +abstract class PnpmTask : DefaultTask() { + @get:Inject + abstract val objects: ObjectFactory @get:Optional @get:Input - val pnpmCommand = project.objects.listProperty() + val pnpmCommand = objects.listProperty() @get:Optional @get:Input - val args = project.objects.listProperty() + val args = objects.listProperty() @get:Input - val ignoreExitValue = project.objects.property().convention(false) + val ignoreExitValue = objects.property().convention(false) @get:Internal - val workingDir = project.objects.directoryProperty() + val workingDir = objects.fileProperty() @get:Input - val environment = project.objects.mapProperty() + val environment = objects.mapProperty() @get:Internal - val execOverrides = project.objects.property>() + val execOverrides = objects.property>() + + @get:Internal + val projectHelper = ProjectApiHelper.newInstance(project) + + @get:Internal + val nodeExtension = NodeExtension[project] init { - group = NodePlugin.NODE_GROUP + group = NodePlugin.PNPM_GROUP dependsOn(PnpmSetupTask.NAME) } @@ -51,9 +63,11 @@ open class PnpmTask: DefaultTask() { fun exec() { val command = pnpmCommand.get().plus(args.get()) val nodeExecConfiguration = - NodeExecConfiguration(command, environment.get(), workingDir.asFile.orNull, - ignoreExitValue.get(), execOverrides.orNull) - val pnpmExecRunner = PnpmExecRunner() - pnpmExecRunner.executePnpmCommand(project, nodeExecConfiguration) + NodeExecConfiguration( + command, environment.get(), workingDir.asFile.orNull, + ignoreExitValue.get(), execOverrides.orNull + ) + val pnpmExecRunner = objects.newInstance(PnpmExecRunner::class.java) + pnpmExecRunner.executePnpmCommand(projectHelper, nodeExtension, nodeExecConfiguration) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpxTask.kt b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpxTask.kt index cd800d7c..df21fc31 100644 --- a/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpxTask.kt +++ b/src/main/kotlin/com/github/gradle/node/pnpm/task/PnpxTask.kt @@ -1,10 +1,13 @@ package com.github.gradle.node.pnpm.task +import com.github.gradle.node.NodeExtension import com.github.gradle.node.NodePlugin import com.github.gradle.node.exec.NodeExecConfiguration import com.github.gradle.node.pnpm.exec.PnpmExecRunner +import com.github.gradle.node.util.ProjectApiHelper import org.gradle.api.Action import org.gradle.api.DefaultTask +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction @@ -12,26 +15,35 @@ import org.gradle.kotlin.dsl.listProperty import org.gradle.kotlin.dsl.mapProperty import org.gradle.kotlin.dsl.property import org.gradle.process.ExecSpec +import javax.inject.Inject -open class PnpxTask : DefaultTask() { +abstract class PnpxTask : DefaultTask() { + @get:Inject + abstract val objects: ObjectFactory @get:Input - val command = project.objects.property() + val command = objects.property() @get:Input - val args = project.objects.listProperty() + val args = objects.listProperty() @get:Input - val ignoreExitValue = project.objects.property().convention(false) + val ignoreExitValue = objects.property().convention(false) @get:Internal - val workingDir = project.objects.directoryProperty() + val workingDir = objects.directoryProperty() @get:Input - val environment = project.objects.mapProperty() + val environment = objects.mapProperty() @get:Internal - val execOverrides = project.objects.property>() + val execOverrides = objects.property>() + + @get:Internal + val projectHelper = ProjectApiHelper.newInstance(project) + + @get:Internal + val extension = NodeExtension[project] init { group = NodePlugin.PNPM_GROUP @@ -50,9 +62,11 @@ open class PnpxTask : DefaultTask() { command.orNull?.let { fullCommand.add(it) } fullCommand.addAll(args.get()) val nodeExecConfiguration = - NodeExecConfiguration(fullCommand, environment.get(), workingDir.asFile.orNull, - ignoreExitValue.get(), execOverrides.orNull) - val pnpmExecRunner = PnpmExecRunner() - pnpmExecRunner.executePnpxCommand(project, nodeExecConfiguration) + NodeExecConfiguration( + fullCommand, environment.get(), workingDir.asFile.orNull, + ignoreExitValue.get(), execOverrides.orNull + ) + val pnpmExecRunner = objects.newInstance(PnpmExecRunner::class.java) + pnpmExecRunner.executePnpxCommand(projectHelper, extension, nodeExecConfiguration) } } diff --git a/src/main/kotlin/com/github/gradle/node/yarn/exec/YarnExecRunner.kt b/src/main/kotlin/com/github/gradle/node/yarn/exec/YarnExecRunner.kt index 533cac87..54b8cbf1 100644 --- a/src/main/kotlin/com/github/gradle/node/yarn/exec/YarnExecRunner.kt +++ b/src/main/kotlin/com/github/gradle/node/yarn/exec/YarnExecRunner.kt @@ -19,35 +19,35 @@ internal abstract class YarnExecRunner { private val variantComputer = VariantComputer() - fun executeYarnCommand(project: ProjectApiHelper, nodeExtension: NodeExtension, nodeExecConfiguration: NodeExecConfiguration) { + fun executeYarnCommand( + project: ProjectApiHelper, + nodeExtension: NodeExtension, + nodeExecConfiguration: NodeExecConfiguration + ) { val nodeDirProvider = variantComputer.computeNodeDir(nodeExtension) val yarnDirProvider = variantComputer.computeYarnDir(nodeExtension) val yarnBinDirProvider = variantComputer.computeYarnBinDir(yarnDirProvider) val yarnExecProvider = variantComputer.computeYarnExec(nodeExtension, yarnBinDirProvider) val additionalBinPathProvider = - computeAdditionalBinPath(nodeExtension, nodeDirProvider, yarnBinDirProvider) - val execConfiguration = ExecConfiguration(yarnExecProvider.get(), - nodeExecConfiguration.command, additionalBinPathProvider.get(), - addNpmProxyEnvironment(nodeExtension, nodeExecConfiguration), nodeExecConfiguration.workingDir, - nodeExecConfiguration.ignoreExitValue, nodeExecConfiguration.execOverrides) + computeAdditionalBinPath(nodeExtension, nodeDirProvider, yarnBinDirProvider) + val execConfiguration = ExecConfiguration( + yarnExecProvider.get(), + nodeExecConfiguration.command, + additionalBinPathProvider.get(), + NpmProxy.addProxyEnvironmentVariables(nodeExtension, nodeExecConfiguration.environment), + nodeExecConfiguration.workingDir, + nodeExecConfiguration.ignoreExitValue, + nodeExecConfiguration.execOverrides + ) val execRunner = ExecRunner() execRunner.execute(project, nodeExtension, execConfiguration) } - private fun addNpmProxyEnvironment(nodeExtension: NodeExtension, - nodeExecConfiguration: NodeExecConfiguration): Map { - if (NpmProxy.shouldConfigureProxy(System.getenv(), nodeExtension.nodeProxySettings.get())) { - val npmProxyEnvironmentVariables = NpmProxy.computeNpmProxyEnvironmentVariables() - if (npmProxyEnvironmentVariables.isNotEmpty()) { - return nodeExecConfiguration.environment.plus(npmProxyEnvironmentVariables) - } - } - return nodeExecConfiguration.environment - } - - private fun computeAdditionalBinPath(nodeExtension: NodeExtension, - nodeDirProvider: Provider, - yarnBinDirProvider: Provider): Provider> { + private fun computeAdditionalBinPath( + nodeExtension: NodeExtension, + nodeDirProvider: Provider, + yarnBinDirProvider: Provider + ): Provider> { return nodeExtension.download.flatMap { download -> if (!download) { providers.provider { listOf() } @@ -56,9 +56,9 @@ internal abstract class YarnExecRunner { val npmDirProvider = variantComputer.computeNpmDir(nodeExtension, nodeDirProvider) val npmBinDirProvider = variantComputer.computeNpmBinDir(npmDirProvider) zip(nodeBinDirProvider, npmBinDirProvider, yarnBinDirProvider) - .map { (nodeBinDir, npmBinDir, yarnBinDir) -> - listOf(yarnBinDir, npmBinDir, nodeBinDir).map { file -> file.asFile.absolutePath } - } + .map { (nodeBinDir, npmBinDir, yarnBinDir) -> + listOf(yarnBinDir, npmBinDir, nodeBinDir).map { file -> file.asFile.absolutePath } + } } } } diff --git a/src/test/groovy/com/github/gradle/node/KotlinDsl_integTest.groovy b/src/test/groovy/com/github/gradle/node/KotlinDsl_integTest.groovy index 67b359b6..e714f7ad 100644 --- a/src/test/groovy/com/github/gradle/node/KotlinDsl_integTest.groovy +++ b/src/test/groovy/com/github/gradle/node/KotlinDsl_integTest.groovy @@ -23,14 +23,17 @@ class KotlinDsl_integTest extends AbstractIntegTest { result1.task(":nodeSetup").outcome == TaskOutcome.SKIPPED result1.task(":npmSetup").outcome == TaskOutcome.SKIPPED result1.task(":yarnSetup").outcome == TaskOutcome.SUCCESS + result1.task(":pnpmSetup").outcome == TaskOutcome.SUCCESS result1.task(":npmInstall").outcome == TaskOutcome.SUCCESS result1.task(":testNpx").outcome == TaskOutcome.SUCCESS result1.task(":testNpm").outcome == TaskOutcome.SUCCESS result1.task(":testYarn").outcome == TaskOutcome.SUCCESS + result1.task(":testPnpx").outcome == TaskOutcome.SUCCESS + result1.task(":testPnpm").outcome == TaskOutcome.SUCCESS result1.task(":run").outcome == TaskOutcome.SUCCESS result1.output.contains("Hello Bobby!") - // Ensure tests were executed 3 times - result1.output.split("1 passing").length == 4 + // Ensure tests were executed 5 times + result1.output.split("1 passing").length == 6 when: def result2 = build("package") @@ -42,13 +45,15 @@ class KotlinDsl_integTest extends AbstractIntegTest { result2.task(":buildNpx").outcome == TaskOutcome.SUCCESS result2.task(":buildNpm").outcome == TaskOutcome.SUCCESS result2.task(":buildYarn").outcome == TaskOutcome.SUCCESS + result2.task(":buildPnpx").outcome == TaskOutcome.SUCCESS + result2.task(":buildPnpm").outcome == TaskOutcome.SUCCESS result2.task(":package").outcome == TaskOutcome.SUCCESS def outputFile = createFile("build/app.zip") outputFile.exists() def zipFile = new ZipFile(outputFile) def zipFileEntries = Collections.list(zipFile.entries()) - zipFileEntries.findAll { it.name.endsWith("/index.js") }.size() == 3 - zipFileEntries.findAll { it.name.endsWith("/main.js") }.size() == 3 - zipFileEntries.size() == 9 + zipFileEntries.findAll { it.name.endsWith("/index.js") }.size() == 5 + zipFileEntries.findAll { it.name.endsWith("/main.js") }.size() == 5 + zipFileEntries.size() == 15 } } diff --git a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmInstall_integTest.groovy b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmInstall_integTest.groovy index d5c34f5f..cb8161e3 100644 --- a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmInstall_integTest.groovy +++ b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmInstall_integTest.groovy @@ -124,10 +124,10 @@ class PnpmInstall_integTest task verifyIO { doLast { - if (!tasks.named("pnpmInstall").get().outputs.files.contains(project.file('pnpm-lock.yaml'))) { + if (!tasks.named("pnpmInstall").get().outputs.files.contains(file('pnpm-lock.yaml'))) { throw new RuntimeException("pnpm-lock.yaml is not in INSTALL'S outputs!") } - if (tasks.named("pnpmInstall").get().inputs.files.contains(project.file('pnpm-lock.yaml'))) { + if (tasks.named("pnpmInstall").get().inputs.files.contains(file('pnpm-lock.yaml'))) { throw new RuntimeException("pnpm-lock.yaml is in INSTALL'S inputs!") } } diff --git a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmSetupTaskTest.groovy b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmSetupTaskTest.groovy index b6af5139..25093c82 100644 --- a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmSetupTaskTest.groovy +++ b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmSetupTaskTest.groovy @@ -4,13 +4,11 @@ import com.github.gradle.node.task.AbstractTaskTest import org.gradle.process.ExecSpec class PnpmSetupTaskTest - extends AbstractTaskTest -{ + extends AbstractTaskTest { def "exec pnpmSetup task without any pnpm version specified"() { given: - execSpec = Mock(ExecSpec) - def task = project.tasks.create('simple', PnpmSetupTask) + mockProjectApiHelperExec(task) when: project.evaluate() @@ -29,9 +27,9 @@ class PnpmSetupTaskTest def "exec pnpmSetup task with pnpm version specified"() { given: nodeExtension.pnpmVersion = '4.12.4' - execSpec = Mock(ExecSpec) def task = project.tasks.create('simple', PnpmSetupTask) + mockProjectApiHelperExec(task) when: project.evaluate() diff --git a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTaskTest.groovy b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTaskTest.groovy index 5709912b..576b6119 100644 --- a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTaskTest.groovy +++ b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTaskTest.groovy @@ -7,9 +7,9 @@ class PnpmTaskTest extends AbstractTaskTest { def "exec pnpm task"() { given: props.setProperty('os.name', 'Linux') - execSpec = Mock(ExecSpec) def task = project.tasks.create('simple', PnpmTask) + mockProjectApiHelperExec(task) task.args.set(['a', 'b']) task.environment.set(['a': '1']) task.ignoreExitValue.set(true) @@ -30,9 +30,9 @@ class PnpmTaskTest extends AbstractTaskTest { def "exec pnpm task (windows)"() { given: props.setProperty('os.name', 'Windows') - execSpec = Mock(ExecSpec) def task = project.tasks.create('simple', PnpmTask) + mockProjectApiHelperExec(task) task.args.set(['a', 'b']) task.environment.set(['a': '1']) task.ignoreExitValue.set(true) @@ -54,9 +54,9 @@ class PnpmTaskTest extends AbstractTaskTest { given: props.setProperty('os.name', 'Linux') nodeExtension.download.set(true) - execSpec = Mock(ExecSpec) def task = project.tasks.create('simple', PnpmTask) + mockProjectApiHelperExec(task) when: project.evaluate() diff --git a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTask_integTest.groovy b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTask_integTest.groovy index 496c01ab..eb93e7bc 100644 --- a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTask_integTest.groovy +++ b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpmTask_integTest.groovy @@ -11,8 +11,8 @@ class PnpmTask_integTest extends AbstractIntegTest { def 'execute pnpm command with a package.json file and check inputs up-to-date detection'() { given: - copyResources('fixtures/pnpm/', '') - copyResources('fixtures/javascript-project/', '') + copyResources("fixtures/pnpm/") + copyResources("fixtures/javascript-project/") when: def result1 = build(":test") @@ -52,8 +52,8 @@ class PnpmTask_integTest extends AbstractIntegTest { def 'execute pnpm command with custom execution configuration and check up-to-date-detection'() { given: - copyResources('fixtures/pnpm-env/', '') - copyResources('fixtures/env/', '') + copyResources("fixtures/pnpm-env/") + copyResources("fixtures/env/") when: def result1 = build(":env") @@ -83,7 +83,7 @@ class PnpmTask_integTest extends AbstractIntegTest { result3.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result3.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE result3.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE - result3.task(":env").outcome == TaskOutcome.UP_TO_DATE + result3.task(":env").outcome == (isConfigurationCacheEnabled() ? TaskOutcome.SUCCESS : TaskOutcome.UP_TO_DATE) when: def result4 = build(":env", "-DignoreExitValue=true", "-DnotExistingCommand=true") @@ -106,48 +106,61 @@ class PnpmTask_integTest extends AbstractIntegTest { result5.output.contains("Unknown command 'notExistingCommand'") when: - def result6 = build(":pwd") + def result6 = build(":env", "-DoutputFile=true") then: result6.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result6.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE result6.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE - result6.task(":pwd").outcome == TaskOutcome.SUCCESS - result6.output.contains("Working directory is '${projectDir}'") + result6.task(":env").outcome == TaskOutcome.SUCCESS + !environmentDumpContainsPathVariable(result6.output) + def outputFile = file("build/standard-output.txt") + outputFile.exists() + environmentDumpContainsPathVariable(outputFile.text) when: - def result7 = build(":pwd", "-DcustomWorkingDir=true") + def result7 = build(":pwd") then: result7.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result7.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE result7.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE - result7.task(":pwd").outcome == TaskOutcome.UP_TO_DATE + result7.task(":pwd").outcome == TaskOutcome.SUCCESS + result7.output.contains("Working directory is '${projectDir}'") when: - def result8 = build(":pwd", "-DcustomWorkingDir=true", "--rerun-tasks") + def result8 = build(":pwd", "-DcustomWorkingDir=true") then: - result8.task(":nodeSetup").outcome == TaskOutcome.SUCCESS - result8.task(":pnpmSetup").outcome == TaskOutcome.SUCCESS - result8.task(":pnpmInstall").outcome == TaskOutcome.SUCCESS - result8.task(":pwd").outcome == TaskOutcome.SUCCESS + result8.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE + result8.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE + result8.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE + result8.task(":pwd").outcome == TaskOutcome.UP_TO_DATE + + when: + def result9 = build(":pwd", "-DcustomWorkingDir=true", "--rerun-tasks") + + then: + result9.task(":nodeSetup").outcome == TaskOutcome.SUCCESS + result9.task(":pnpmSetup").outcome == TaskOutcome.SUCCESS + result9.task(":pnpmInstall").outcome == TaskOutcome.SUCCESS + result9.task(":pwd").outcome == TaskOutcome.SUCCESS def expectedWorkingDirectory = "${projectDir}${File.separator}build${File.separator}customWorkingDirectory" - result8.output.contains("Working directory is '${expectedWorkingDirectory}'") + result9.output.contains("Working directory is '${expectedWorkingDirectory}'") new File(expectedWorkingDirectory).isDirectory() when: - def result9 = build(":version") + def result10 = build(":version") then: - result9.task(":version").outcome == TaskOutcome.SUCCESS - result9.output.contains("> Task :version${System.lineSeparator()}4.12.4") + result10.task(":version").outcome == TaskOutcome.SUCCESS + result10.output.contains("> Task :version${System.lineSeparator()}4.12.4") } def 'execute pnpm command using the pnpm version specified in the package.json file'() { given: - copyResources('fixtures/pnpm/', '') - copyResources('fixtures/pnpm-present/', '') + copyResources("fixtures/pnpm/") + copyResources("fixtures/pnpm-present/") when: def result = build(":version") diff --git a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTaskTest.groovy b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTaskTest.groovy index 2301bf7a..b72e5b07 100644 --- a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTaskTest.groovy +++ b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTaskTest.groovy @@ -1,16 +1,14 @@ package com.github.gradle.node.pnpm.task - import com.github.gradle.node.task.AbstractTaskTest -import org.gradle.process.ExecSpec class PnpxTaskTest extends AbstractTaskTest { def "exec pnpx task"() { given: props.setProperty('os.name', 'Linux') - execSpec = Mock(ExecSpec) def task = project.tasks.create('simple', PnpxTask) + mockProjectApiHelperExec(task) task.command.set('command') task.args.set(['a', 'b']) task.environment.set(['a': '1']) @@ -34,9 +32,9 @@ class PnpxTaskTest extends AbstractTaskTest { def "exec pnpx task (windows)"() { given: props.setProperty('os.name', 'Windows') - execSpec = Mock(ExecSpec) def task = project.tasks.create('simple', PnpxTask) + mockProjectApiHelperExec(task) task.command.set('command') task.args.set(['a', 'b']) task.environment.set(['a': '1']) @@ -57,14 +55,13 @@ class PnpxTaskTest extends AbstractTaskTest { 1 * execSpec.setWorkingDir(projectDir) } - def "exec pnpx task (download)"() - { + def "exec pnpx task (download)"() { given: props.setProperty('os.name', 'Linux') nodeExtension.download.set(true) - execSpec = Mock(ExecSpec) def task = project.tasks.create('simple', PnpxTask) + mockProjectApiHelperExec(task) task.command.set('command') when: diff --git a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTask_integTest.groovy b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTask_integTest.groovy index a1bf8e90..a506b0e8 100644 --- a/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTask_integTest.groovy +++ b/src/test/groovy/com/github/gradle/node/pnpm/task/PnpxTask_integTest.groovy @@ -26,11 +26,6 @@ class PnpxTask_integTest id 'com.github.node-gradle.node' } - node { - download = true - workDir = file('build/node') - } - task camelCase(type: PnpxTask) { command = '-p' args = ['chcase-cli', 'chcase', '--help'] @@ -41,7 +36,7 @@ class PnpxTask_integTest def result = build(":camelCase") then: - result.task(":nodeSetup").outcome == TaskOutcome.SUCCESS + result.task(":nodeSetup").outcome == TaskOutcome.SKIPPED result.task(":pnpmSetup").outcome == TaskOutcome.SUCCESS result.task(":camelCase").outcome == TaskOutcome.SUCCESS result.output.contains("--case, -C Which case to convert to") @@ -49,8 +44,8 @@ class PnpxTask_integTest def 'execute pnpx command with a package.json file and check inputs up-to-date detection'() { given: - copyResources('fixtures/pnpx/', '') - copyResources('fixtures/javascript-project/', '') + copyResources("fixtures/pnpx/") + copyResources("fixtures/javascript-project/") when: def result1 = build(":test") @@ -94,8 +89,8 @@ class PnpxTask_integTest def 'execute pnpx command with custom execution configuration and check up-to-date-detection'() { given: - copyResources('fixtures/pnpx-env/', '') - copyResources('fixtures/env/', '') + copyResources("fixtures/pnpx-env/") + copyResources("fixtures/env/") when: def result1 = build(":env") @@ -126,7 +121,7 @@ class PnpxTask_integTest result3.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result3.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE result3.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE - result3.task(":env").outcome == TaskOutcome.UP_TO_DATE + result3.task(":env").outcome == (isConfigurationCacheEnabled() ? TaskOutcome.SUCCESS : TaskOutcome.UP_TO_DATE) when: def result4 = build(":env", "-DignoreExitValue=true", "-DnotExistingCommand=true") @@ -149,48 +144,61 @@ class PnpxTask_integTest result5.output.contains("404 Not Found: notExistingCommand") when: - def result6 = build(":pwd") + def result6 = build(":env", "-DoutputFile=true") then: result6.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result6.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE result6.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE - result6.task(":pwd").outcome == TaskOutcome.SUCCESS - result6.output.contains("Working directory is '${projectDir}'") + result6.task(":env").outcome == TaskOutcome.SUCCESS + !environmentDumpContainsPathVariable(result6.output) + def outputFile = file("build/standard-output.txt") + outputFile.exists() + environmentDumpContainsPathVariable(outputFile.text) when: - def result7 = build(":pwd", "-DcustomWorkingDir=true") + def result7 = build(":pwd") then: result7.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result7.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE result7.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE - result7.task(":pwd").outcome == TaskOutcome.UP_TO_DATE + result7.task(":pwd").outcome == TaskOutcome.SUCCESS + result7.output.contains("workingDirectory='${projectDir}'") + + when: + def result8 = build(":pwd", "-DcustomWorkingDir=true") + + then: + result8.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE + result8.task(":pnpmSetup").outcome == TaskOutcome.UP_TO_DATE + result8.task(":pnpmInstall").outcome == TaskOutcome.UP_TO_DATE + result8.task(":pwd").outcome == TaskOutcome.UP_TO_DATE when: - def result8 = build(":pwd", "-DcustomWorkingDir=true", "--rerun-tasks") + def result9 = build(":pwd", "-DcustomWorkingDir=true", "--rerun-tasks") then: - result8.task(":nodeSetup").outcome == TaskOutcome.SUCCESS - result8.task(":pnpmSetup").outcome == TaskOutcome.SUCCESS - result8.task(":pnpmInstall").outcome == TaskOutcome.SUCCESS - result8.task(":pwd").outcome == TaskOutcome.SUCCESS + result9.task(":nodeSetup").outcome == TaskOutcome.SUCCESS + result9.task(":pnpmSetup").outcome == TaskOutcome.SUCCESS + result9.task(":pnpmInstall").outcome == TaskOutcome.SUCCESS + result9.task(":pwd").outcome == TaskOutcome.SUCCESS def expectedWorkingDirectory = "${projectDir}${File.separator}build${File.separator}customWorkingDirectory" - result8.output.contains("Working directory is '${expectedWorkingDirectory}'") + result9.output.contains("workingDirectory='${expectedWorkingDirectory}'") new File(expectedWorkingDirectory).isDirectory() when: - def result9 = build(":version") + def result10 = build(":version") then: - result9.task(":version").outcome == TaskOutcome.SUCCESS - result9.output.contains("> Task :version${System.lineSeparator()}4.12.4") + result10.task(":version").outcome == TaskOutcome.SUCCESS + result10.output.contains("> Task :version${System.lineSeparator()}4.12.4") } def 'execute pnpx command using the pnpm version specified in the package.json file'() { given: - copyResources('fixtures/pnpx/', '') - copyResources('fixtures/pnpm-present/', '') + copyResources("fixtures/pnpx/") + copyResources("fixtures/pnpm-present/") when: def result = build(":version") diff --git a/src/test/resources/fixtures/env/package.json b/src/test/resources/fixtures/env/package.json index 1494a917..e4cacf6b 100644 --- a/src/test/resources/fixtures/env/package.json +++ b/src/test/resources/fixtures/env/package.json @@ -1,7 +1,7 @@ { "name": "env", "dependencies": { - "@bahmutov/print-env": "2.0.2", + "@bahmutov/print-env": "2.1.2", "utils-eval": "1.0.1" }, "scripts": { diff --git a/src/test/resources/fixtures/kotlin/build.gradle.kts b/src/test/resources/fixtures/kotlin/build.gradle.kts index f47f8827..cebe3c8a 100644 --- a/src/test/resources/fixtures/kotlin/build.gradle.kts +++ b/src/test/resources/fixtures/kotlin/build.gradle.kts @@ -1,6 +1,8 @@ import com.github.gradle.node.npm.proxy.ProxySettings import com.github.gradle.node.npm.task.NpmTask import com.github.gradle.node.npm.task.NpxTask +import com.github.gradle.node.pnpm.task.PnpmTask +import com.github.gradle.node.pnpm.task.PnpxTask import com.github.gradle.node.task.NodeTask import com.github.gradle.node.yarn.task.YarnTask @@ -41,6 +43,12 @@ tasks.yarn { } } +tasks.pnpmInstall { + nodeModulesOutputFilter { + exclude("notExistingFile") + } +} + val testTaskUsingNpx = tasks.register("testNpx") { dependsOn(tasks.npmInstall) command.set("mocha") @@ -98,8 +106,46 @@ val testTaskUsingYarn = tasks.register("testYarn") { } } +val testTaskUsingPnpx = tasks.register("testPnpx") { + dependsOn(tasks.npmInstall) + command.set("mocha") + args.set(listOf("test", "--grep", "should say hello")) + ignoreExitValue.set(false) + environment.set(mapOf("MY_CUSTOM_VARIABLE" to "hello")) + workingDir.set(projectDir) + execOverrides { + standardOutput = System.out + } + inputs.dir("node_modules") + inputs.file("package.json") + inputs.dir("src") + inputs.dir("test") + outputs.upToDateWhen { + true + } +} + +val testTaskUsingPnpm = tasks.register("testPnpm") { + dependsOn(tasks.npmInstall) + pnpmCommand.set(listOf("run", "test")) + args.set(listOf("test")) + ignoreExitValue.set(false) + environment.set(mapOf("MY_CUSTOM_VARIABLE" to "hello")) + workingDir.set(projectDir) + execOverrides { + standardOutput = System.out + } + inputs.dir("node_modules") + inputs.file("package.json") + inputs.dir("src") + inputs.dir("test") + outputs.upToDateWhen { + true + } +} + tasks.register("run") { - dependsOn(testTaskUsingNpx, testTaskUsingNpm, testTaskUsingYarn) + dependsOn(testTaskUsingNpx, testTaskUsingNpm, testTaskUsingYarn, testTaskUsingPnpx, testTaskUsingPnpm) script.set(file("src/main.js")) args.set(listOf("Bobby")) ignoreExitValue.set(false) @@ -138,6 +184,22 @@ val buildTaskUsingYarn = tasks.register("buildYarn") { outputs.dir("${buildDir}/yarn-output") } +val buildTaskUsingPnpx = tasks.register("buildPnpx") { + dependsOn(tasks.npmInstall) + command.set("babel") + args.set(listOf("src", "--out-dir", "${buildDir}/pnpx-output")) + inputs.dir("src") + outputs.dir("${buildDir}/pnpx-output") +} + +val buildTaskUsingPnpm = tasks.register("buildPnpm") { + dependsOn(tasks.npmInstall) + pnpmCommand.set(listOf("run", "build")) + args.set(listOf("--", "--out-dir", "${buildDir}/pnpm-output")) + inputs.dir("src") + outputs.dir("${buildDir}/pnpm-output") +} + tasks.register("package") { archiveFileName.set("app.zip") destinationDirectory.set(buildDir) @@ -147,7 +209,13 @@ tasks.register("package") { from(buildTaskUsingNpm) { into("npm") } - from (buildTaskUsingYarn) { + from(buildTaskUsingYarn) { into("yarn") } + from(buildTaskUsingPnpx) { + into("pnpx") + } + from(buildTaskUsingPnpm) { + into("pnpm") + } } diff --git a/src/test/resources/fixtures/pnpm-env/build.gradle b/src/test/resources/fixtures/pnpm-env/build.gradle index c949af6f..9cc49bc5 100644 --- a/src/test/resources/fixtures/pnpm-env/build.gradle +++ b/src/test/resources/fixtures/pnpm-env/build.gradle @@ -1,3 +1,5 @@ +import org.gradle.util.GradleVersion + plugins { id "com.github.node-gradle.node" } @@ -56,7 +58,9 @@ if (isPropertyEnabled("outputFile")) { } } -def isPropertyEnabled(name) { - def property = System.properties[name] - return property == "true" +def isPropertyEnabled(String name) { + if (GradleVersion.current() >= GradleVersion.version("6.6")) { + return providers.systemProperty(name).forUseAtConfigurationTime().isPresent() + } + return System.properties[name] != null } diff --git a/src/test/resources/fixtures/pnpm/build.gradle b/src/test/resources/fixtures/pnpm/build.gradle index a3aaab2a..38d6cfde 100644 --- a/src/test/resources/fixtures/pnpm/build.gradle +++ b/src/test/resources/fixtures/pnpm/build.gradle @@ -1,8 +1,10 @@ +import org.gradle.util.GradleVersion + plugins { id 'com.github.node-gradle.node' } -def changeInputs = System.properties["changeInputs"] ? System.properties["changeInputs"] == 'true' : false +def changeInputs = isPropertyEnabled("changeInputs") node { pnpmVersion = "4.12.4" @@ -14,9 +16,8 @@ task test(type: PnpmTask) { dependsOn pnpmInstall pnpmCommand = changeInputs ? ['run', 'test'] : ['run'] args = changeInputs ? [] : ['test'] - inputs.dir('node_modules') - inputs.file('package.json') - inputs.files('index.js', 'test.js') + inputs.dir('src') + inputs.dir('test') outputs.upToDateWhen { true } @@ -26,3 +27,10 @@ task version(type: PnpmTask) { dependsOn pnpmInstall pnpmCommand = ["--version"] } + +def isPropertyEnabled(String name) { + if (GradleVersion.current() >= GradleVersion.version("6.6")) { + return providers.systemProperty(name).forUseAtConfigurationTime().isPresent() + } + return System.properties[name] != null +} diff --git a/src/test/resources/fixtures/pnpx-env/build.gradle b/src/test/resources/fixtures/pnpx-env/build.gradle index 915366a1..e2d990d2 100644 --- a/src/test/resources/fixtures/pnpx-env/build.gradle +++ b/src/test/resources/fixtures/pnpx-env/build.gradle @@ -1,3 +1,5 @@ +import org.gradle.util.GradleVersion + plugins { id "com.github.node-gradle.node" } @@ -10,7 +12,8 @@ node { task env(type: PnpxTask) { dependsOn pnpmInstall - command = "printenv" + command = "print-env" + args = ["PATH", "CUSTOM"] outputs.upToDateWhen { true } @@ -19,7 +22,7 @@ task env(type: PnpxTask) { task pwd(type: PnpxTask) { dependsOn pnpmInstall command = "jseval" - args = ["console.log(`Working directory is '\${process.cwd()}'`);"] + args = ["console.log(`workingDirectory='\${process.cwd()}'`);"] outputs.upToDateWhen { true } @@ -31,7 +34,8 @@ task version(type: PnpxTask) { } if (isPropertyEnabled("customEnv")) { - env.environment = [CUSTOM: "custom value"] + def qualifier = "custom" + env.environment = [CUSTOM: "${qualifier} value"] } if (isPropertyEnabled("ignoreExitValue")) { @@ -46,7 +50,15 @@ if (isPropertyEnabled("customWorkingDir")) { pwd.workingDir = file("${project.buildDir}/customWorkingDirectory/") } -def isPropertyEnabled(name) { - def property = System.properties[name] - return property == "true" +if (isPropertyEnabled("outputFile")) { + env.execOverrides { + standardOutput = new FileOutputStream("${buildDir}/standard-output.txt") + } +} + +def isPropertyEnabled(String name) { + if (GradleVersion.current() >= GradleVersion.version("6.6")) { + return providers.systemProperty(name).forUseAtConfigurationTime().isPresent() + } + return System.properties[name] != null } diff --git a/src/test/resources/fixtures/pnpx/build.gradle b/src/test/resources/fixtures/pnpx/build.gradle index abcc5605..0c48bb74 100644 --- a/src/test/resources/fixtures/pnpx/build.gradle +++ b/src/test/resources/fixtures/pnpx/build.gradle @@ -1,3 +1,5 @@ +import org.gradle.util.GradleVersion + plugins { id "com.github.node-gradle.node" } @@ -57,7 +59,9 @@ if (isPropertyEnabled("changeInputs")) { test.command = "_mocha" } -def isPropertyEnabled(name) { - def property = System.properties[name] - return property == "true" +def isPropertyEnabled(String name) { + if (GradleVersion.current() >= GradleVersion.version("6.6")) { + return providers.systemProperty(name).forUseAtConfigurationTime().isPresent() + } + return System.properties[name] != null }