diff --git a/src/main/kotlin/com/github/gradle/node/NodeExtension.kt b/src/main/kotlin/com/github/gradle/node/NodeExtension.kt index 378a832d..e63dafc8 100644 --- a/src/main/kotlin/com/github/gradle/node/NodeExtension.kt +++ b/src/main/kotlin/com/github/gradle/node/NodeExtension.kt @@ -1,13 +1,29 @@ package com.github.gradle.node +import com.github.gradle.node.npm.exec.NpmExecResult +import com.github.gradle.node.npm.exec.NpmExecSource +import com.github.gradle.node.npm.exec.NpmExecSpec import com.github.gradle.node.npm.proxy.ProxySettings import com.github.gradle.node.util.Platform +import com.github.gradle.node.variant.VariantComputer +import com.github.gradle.node.variant.computeNodeExec import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.of import org.gradle.kotlin.dsl.property - -open class NodeExtension(project: Project) { +import javax.inject.Inject + +abstract class NodeExtension +@Inject +internal constructor( + project: Project, + private val providers: ProviderFactory, +) { private val cacheDir = project.layout.projectDirectory.dir(".gradle") /** @@ -18,7 +34,7 @@ open class NodeExtension(project: Project) { /** * The directory where npm is installed (when a specific version is defined) */ - val npmWorkDir = project.objects.directoryProperty().convention(cacheDir.dir("npm")) + val npmWorkDir: DirectoryProperty = project.objects.directoryProperty().convention(cacheDir.dir("npm")) /** * The directory where pnpm is installed (when a pnpm task is used) @@ -53,7 +69,7 @@ open class NodeExtension(project: Project) { * If specified, installs it in the npmWorkDir * If empty, the plugin will use the npm command bundled with Node.js */ - val npmVersion = project.objects.property().convention("") + val npmVersion: Property = project.objects.property().convention("") /** * Version of pnpm to use @@ -111,7 +127,7 @@ open class NodeExtension(project: Project) { * If true, it will download node using above parameters * Note that npm is bundled with Node.js */ - val download = project.objects.property().convention(false) + val download: Property = project.objects.property().convention(false) /** * Whether the plugin automatically should add the proxy configuration to npm and yarn commands @@ -168,7 +184,7 @@ open class NodeExtension(project: Project) { /** * Computed path to nodejs directory */ - val resolvedNodeDir = project.objects.directoryProperty() + val resolvedNodeDir: DirectoryProperty = project.objects.directoryProperty() /** * Operating system and architecture @@ -179,7 +195,7 @@ open class NodeExtension(project: Project) { /** * Operating system and architecture */ - val resolvedPlatform = project.objects.property() + val resolvedPlatform: Property = project.objects.property() init { distBaseUrl.set("https://nodejs.org/dist") @@ -193,6 +209,30 @@ open class NodeExtension(project: Project) { nodeProxySettings.set(if (value) ProxySettings.SMART else ProxySettings.OFF) } + @Suppress("UnstableApiUsage") + fun npmExec( + configuration: NpmExecSpec.() -> Unit + ): Provider { + + val vc = VariantComputer() + val nodeDirProvider = resolvedNodeDir + val npmDirProvider = vc.computeNpmDir(this, nodeDirProvider) + val nodeBinDirProvider = vc.computeNodeBinDir(nodeDirProvider, resolvedPlatform) + val npmBinDirProvider = vc.computeNpmBinDir(npmDirProvider, resolvedPlatform) + val nodeExecProvider = computeNodeExec(this, nodeBinDirProvider) + + vc.computeNpmExec(this, npmBinDirProvider) + + return providers.of(NpmExecSource::class) { + parameters.apply(configuration) +// parameters { +// executable +// ignoreExitValue +// workingDir +// } + } + } + companion object { /** * Extension name in Gradle diff --git a/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecResult.kt b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecResult.kt new file mode 100644 index 00000000..492178ee --- /dev/null +++ b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecResult.kt @@ -0,0 +1,30 @@ +package com.github.gradle.node.npm.exec + +import org.gradle.api.GradleException +import org.gradle.process.ExecResult + +class NpmExecResult internal constructor( + val exitValue: Int, + val failure: GradleException?, + val capturedOutput: String, +) { + + internal fun asExecResult(): ExecResult = object : ExecResult { + + override fun assertNormalExitValue(): ExecResult { + if (failure != null) { + throw failure + } + return this + } + + override fun getExitValue(): Int = exitValue + + override fun rethrowFailure(): ExecResult { + assertNormalExitValue() + return this + } + } + + override fun toString(): String = "NpmExecResult(exitValue=$exitValue, failure=$failure)" +} 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 167aeeb2..e2680e6f 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 @@ -24,17 +24,26 @@ abstract class NpmExecRunner { project: ProjectApiHelper, extension: NodeExtension, nodeExecConfiguration: NodeExecConfiguration, - variants: VariantComputer + variants: VariantComputer, ): ExecResult { val npmExecConfiguration = NpmExecConfiguration( - "npm" - ) { variantComputer, nodeExtension, npmBinDir -> variantComputer.computeNpmExec(nodeExtension, npmBinDir) } + command = "npm", + commandExecComputer = { variantComputer, nodeExtension, npmBinDir -> + variantComputer.computeNpmExec( + nodeExtension, + npmBinDir + ) + } + ) return executeCommand( - project, - extension, - NpmProxy.addProxyEnvironmentVariables(extension.nodeProxySettings.get(), nodeExecConfiguration), - npmExecConfiguration, - variants + project = project, + extension = extension, + nodeExecConfiguration = NpmProxy.addProxyEnvironmentVariables( + proxySettings = extension.nodeProxySettings.get(), + nodeExecConfiguration = nodeExecConfiguration + ), + npmExecConfiguration = npmExecConfiguration, + variantComputer = variants ) } @@ -78,9 +87,13 @@ abstract class NpmExecRunner { 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 + executable = executableAndScript.executable, + args = args, + additionalBinPaths = additionalBinPath, + environment = nodeExecConfiguration.environment, + workingDir = nodeExecConfiguration.workingDir, + ignoreExitValue = nodeExecConfiguration.ignoreExitValue, + execOverrides = nodeExecConfiguration.execOverrides, ) } } @@ -101,11 +114,12 @@ abstract class NpmExecRunner { val npmScriptFileProvider = computeNpmScriptFile(nodeDirProvider, npmExecConfiguration.command, isWindows) return zip( - nodeExtension.download, nodeExtension.nodeProjectDir, executableProvider, nodeExecProvider, - npmScriptFileProvider - ).map { - val (download, nodeProjectDir, executable, nodeExec, - npmScriptFile) = it + nodeExtension.download, + nodeExtension.nodeProjectDir, + executableProvider, + nodeExecProvider, + npmScriptFileProvider, + ).map { (download, nodeProjectDir, executable, nodeExec, npmScriptFile) -> if (download) { val localCommandScript = nodeProjectDir.dir("node_modules/npm/bin") .file("${npmExecConfiguration.command}-cli.js").asFile @@ -121,7 +135,7 @@ abstract class NpmExecRunner { private data class ExecutableAndScript( val executable: String, - val script: String? = null + val script: String? = null, ) private fun computeAdditionalBinPath( diff --git a/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecSource.kt b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecSource.kt new file mode 100644 index 00000000..346cd8e5 --- /dev/null +++ b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecSource.kt @@ -0,0 +1,355 @@ +// TODO remove suppress when updating Gradle to a version where ValueSource is stable +@file:Suppress("UnstableApiUsage") + +package com.github.gradle.node.npm.exec + +import com.github.gradle.node.npm.proxy.NpmProxy +import com.github.gradle.node.util.Platform +import com.github.gradle.node.util.mapIf +import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ValueSource +import org.gradle.api.provider.ValueSourceParameters +import org.gradle.process.ExecOperations +import org.gradle.process.ExecSpec +import java.io.ByteArrayOutputStream +import java.io.File +import javax.inject.Inject + +/** + * Runs `npm` using a [ValueSource]. + * + * All options can be configured using [NpmExecSource.Parameters], + * but this is a delicate API and may cause issues. + * Prefer using [NpmExecSpec]. + */ +abstract class NpmExecSource @Inject internal constructor( + private val execOps: ExecOperations, +) : ValueSource { + + abstract class Parameters internal constructor() : NpmExecSpec(), ValueSourceParameters { + abstract val ignoreExitValue: Property + abstract val workingDir: DirectoryProperty + abstract val coreNpmCommand: Property + abstract val npmCommand: ListProperty + } + + + private val coreNpmCommand get() = parameters.coreNpmCommand.get() + private val execNpmCommand get() = parameters.npmCommand.get() + private val nodeProxySettings get() = parameters.nodeProxySettings.get() + private val npmVersion get() = parameters.npmVersion.get() + private val npmWorkDir get() = parameters.npmWorkDir.get() + + private val download: Boolean get() = parameters.download.getOrElse(true) + private val platform get() = parameters.resolvedPlatform.get() + private val nodeProjectDir get() = parameters.nodeProjectDir.get() + + private val resolvedPlatform get() = parameters.resolvedPlatform.get() + private val resolvedNodeDir get() = parameters.resolvedNodeDir.get() + + + override fun obtain(): NpmExecResult { + val command = execNpmCommand.plus(parameters.arguments.get()) + val nodeExecConfiguration = + NodeExecConfiguration( + command = command, + environment = parameters.environment.orNull.orEmpty(), + workingDir = parameters.workingDir.asFile.orNull, + ) + return executeNpmCommand(nodeExecConfiguration) + } + + private fun computeEnvironment( + execConfiguration: ExecConfiguration + ): Map { + return mutableMapOf().apply { + if (parameters.includeSystemEnvironment.getOrElse(true)) { + putAll(System.getenv()) + } + putAll(parameters.environment.get()) + val additionalBinPaths = execConfiguration.additionalBinPaths + if (additionalBinPaths.isNotEmpty()) { + // Take care of Windows environments that may contain "Path" OR "PATH" - both existing + // possibly (but not in parallel as of now) + val pathEnvironmentVariableName = if (get("Path") != null) "Path" else "PATH" + val originalPath = get(pathEnvironmentVariableName) + val newPath = (additionalBinPaths + originalPath).filterNotNull().joinToString(File.pathSeparator) + put(pathEnvironmentVariableName, newPath) + } + } + } + + + private fun executeNpmCommand( + nodeExecConfiguration: NodeExecConfiguration, + ): NpmExecResult { + val npmExecConfiguration = NpmExecConfiguration( + command = "npm", + commandExecComputer = { npmBinDir -> + computeNpmExec( + npmBinDir + ) + } + ) + + val proxiedEnvVars = NpmProxy.createProxyEnvironmentVariables( + proxySettings = nodeProxySettings, + nodeExecConfigurationEnvironment = nodeExecConfiguration.environment + ) + + val proxiedNodeExecConfiguration = nodeExecConfiguration.copy(environment = proxiedEnvVars) + + return executeCommand( + nodeExecConfiguration = proxiedNodeExecConfiguration, + npmExecConfiguration = npmExecConfiguration, + ) + } + + private fun executeCommand( + nodeExecConfiguration: NodeExecConfiguration, + npmExecConfiguration: NpmExecConfiguration, + ): NpmExecResult { + val execConfiguration = + computeExecConfiguration(npmExecConfiguration, nodeExecConfiguration) + + ByteArrayOutputStream().use { capturedOutput -> + val result = execOps.exec { + + executable = execConfiguration.executable + args = execConfiguration.args + environment = computeEnvironment(execConfiguration) + workingDir = computeWorkingDir(nodeProjectDir, execConfiguration) + isIgnoreExitValue = parameters.ignoreExitValue.getOrElse(true) + standardOutput = capturedOutput + errorOutput = capturedOutput + } + + val failure = try { + result.rethrowFailure() + null + } catch (ex: GradleException) { + ex + } + + return NpmExecResult( + exitValue = result.exitValue, + failure = failure, + capturedOutput = capturedOutput.toString(), + ) + } + } + + private fun computeExecConfiguration( + npmExecConfiguration: NpmExecConfiguration, + nodeExecConfiguration: NodeExecConfiguration, + ): ExecConfiguration { + val additionalBinPath = computeAdditionalBinPath() + val executableAndScript = computeExecutable(npmExecConfiguration) + val argsPrefix = + if (executableAndScript.script != null) listOf(executableAndScript.script) else listOf() + val args = argsPrefix.plus(nodeExecConfiguration.command) + return ExecConfiguration( + executable = executableAndScript.executable, + args = args, + additionalBinPaths = additionalBinPath, + environment = nodeExecConfiguration.environment, + workingDir = nodeExecConfiguration.workingDir, + execOverrides = nodeExecConfiguration.execOverrides, + ) + } + + private fun computeExecutable( + npmExecConfiguration: NpmExecConfiguration, + ): ExecutableAndScript { + val nodeDirProvider = parameters.resolvedNodeDir.get() + val npmDirProvider = computeNpmDir(nodeDirProvider) + val nodeBinDirProvider = computeNodeBinDir(nodeDirProvider, parameters.resolvedPlatform.get()) + val npmBinDirProvider = computeNpmBinDir(npmDirProvider, parameters.resolvedPlatform.get()) + val nodeExecProvider = computeNodeExec(nodeBinDirProvider) + val executableProvider = + computeNpmExec(npmBinDirProvider) + val npmScriptFileProvider = + computeNpmScriptFile(nodeDirProvider, npmExecConfiguration.command, resolvedPlatform) + return computeExecutable( + npmExecConfiguration.command, + executableProvider, + nodeExecProvider, + npmScriptFileProvider + ) + } + + private fun computeExecutable( + command: String, + executable: String, + nodeExec: String, + npmScriptFile: String, + ): ExecutableAndScript { + if (download) { + val localCommandScript = nodeProjectDir.dir("node_modules/npm/bin") + .file("${command}-cli.js").asFile + if (localCommandScript.exists()) { + return ExecutableAndScript(nodeExec, localCommandScript.absolutePath) + } else if (!File(executable).exists()) { + return ExecutableAndScript(nodeExec, npmScriptFile) + } + } + return ExecutableAndScript(executable) + } + + private data class ExecutableAndScript( + val executable: String, + val script: String? = null, + ) + + private fun computeAdditionalBinPath(): List { + if (!download) { + return emptyList() + } + val nodeDirProvider = resolvedNodeDir + val nodeBinDirProvider = computeNodeBinDir(nodeDirProvider, resolvedPlatform) + val npmDirProvider = computeNpmDir(nodeDirProvider) + val npmBinDirProvider = computeNpmBinDir(npmDirProvider, resolvedPlatform) + return listOf(npmBinDirProvider, nodeBinDirProvider).map { file -> file.asFile.absolutePath } + } + + + /** + * Get the expected node binary directory, taking Windows specifics into account. + */ + private fun computeNodeBinDir( + nodeDirProvider: Directory, + platform: Platform, + ): Directory = + computeProductBinDir(nodeDirProvider, platform) + + /** + * Get the expected node binary name, node.exe on Windows and node everywhere else. + */ + private fun computeNodeExec( + nodeBinDir: Directory + ): String { + return if (download) { + val nodeCommand = if (platform.isWindows()) "node.exe" else "node" + nodeBinDir.dir(nodeCommand).asFile.absolutePath + } else { + "node" + } + } + + /** + * Get the expected directory for a given npm version. + */ + private fun computeNpmDir( + nodeDir: Directory, + ): Directory { + return if (npmVersion.isNotBlank()) { + val directoryName = "npm-v${npmVersion}" + npmWorkDir.dir(directoryName) + } else { + nodeDir + } + } + + /** + * Get the expected npm binary directory, taking Windows specifics into account. + */ + private fun computeNpmBinDir( + npmDirProvider: Directory, + platform: Platform + ): Directory = + computeProductBinDir(npmDirProvider, platform) + + /** + * Get the expected node binary name, `npm.cmd` on Windows and `npm` everywhere else. + * + * Can be overridden by setting `npmCommand`. + */ + private fun computeNpmExec(npmBinDirProvider: Directory): String { + return computeExec( + binDirProvider = npmBinDirProvider, + configurationCommand = coreNpmCommand, + ) + } + + /** + * Compute the path for a given command, from a given binary directory, taking Windows into account + */ + private fun computeExec( + binDirProvider: Directory, + configurationCommand: String, + unixCommand: String = "npm", + windowsCommand: String = "npm.cmd", + ): String { + val command = if (resolvedPlatform.isWindows()) { + configurationCommand.mapIf({ it == unixCommand }) { windowsCommand } + } else { + configurationCommand + } + return if (download) { + binDirProvider.dir(command).asFile.absolutePath + } else { + command + } + } + + private fun computeProductBinDir( + productDirProvider: Directory, + platform: Platform + ): Directory { + return if (platform.isWindows()) { + productDirProvider.dir("bin") + } else { + productDirProvider + } + } + + + private data class ExecConfiguration( + val executable: String, + val args: List = listOf(), + val additionalBinPaths: List = listOf(), + val environment: Map = mapOf(), + val workingDir: File? = null, + val execOverrides: Action? = null + ) + + private data class NpmExecConfiguration( + val command: String, + val commandExecComputer: ( + npmBinDir: Directory, + ) -> String, + ) + + private fun computeWorkingDir( + nodeProjectDir: Directory, + execConfiguration: ExecConfiguration + ): File? { + val workingDir = execConfiguration.workingDir ?: nodeProjectDir.asFile + workingDir.mkdirs() + return workingDir + } + + private fun computeNpmScriptFile( + nodeDir: Directory, + command: String, + platform: Platform, + ): String { + return if (platform.isWindows()) { + nodeDir.dir("node_modules/npm/bin/$command-cli.js").asFile.path + } else { + nodeDir.dir("lib/node_modules/npm/bin/$command-cli.js").asFile.path + } + } + + private data class NodeExecConfiguration( + val command: List = listOf(), + val environment: Map = mapOf(), + val workingDir: File? = null, + val execOverrides: Action? = null + ) +} diff --git a/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecSpec.kt b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecSpec.kt new file mode 100644 index 00000000..a74afe77 --- /dev/null +++ b/src/main/kotlin/com/github/gradle/node/npm/exec/NpmExecSpec.kt @@ -0,0 +1,45 @@ +package com.github.gradle.node.npm.exec + +import com.github.gradle.node.npm.proxy.ProxySettings +import com.github.gradle.node.util.Platform +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property + +/** + * Configurable options for executing `npm`. + * + * Intended for use in buildscripts by users + */ +abstract class NpmExecSpec internal constructor() { + abstract val arguments: ListProperty + abstract val environment: MapProperty + abstract val includeSystemEnvironment: Property +// abstract val additionalBinPaths: ListProperty + + /** @see com.github.gradle.node.NodeExtension.download */ + abstract val download: Property + + /** @see com.github.gradle.node.NodeExtension.resolvedNodeDir */ + abstract val resolvedNodeDir: DirectoryProperty + + /** @see com.github.gradle.node.NodeExtension.resolvedPlatform */ + abstract val resolvedPlatform: Property + + /** @see com.github.gradle.node.NodeExtension.npmVersion */ + abstract val npmVersion: Property + +// /** @see com.github.gradle.node.NodeExtension.npmCommand */ +// abstract val npmCommand: Property + + /** @see com.github.gradle.node.NodeExtension.npmWorkDir */ + abstract val npmWorkDir: DirectoryProperty + + /** @see com.github.gradle.node.NodeExtension.nodeProjectDir */ + abstract val nodeProjectDir: DirectoryProperty + + + /** @see com.github.gradle.node.NodeExtension.nodeProxySettings */ + abstract val nodeProxySettings: Property +} 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 fde5182d..0bc301c8 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 @@ -2,8 +2,6 @@ package com.github.gradle.node.npm.proxy import com.github.gradle.node.exec.NodeExecConfiguration import java.net.URLEncoder -import java.util.stream.Collectors.toList -import java.util.stream.Stream import kotlin.text.Charsets.UTF_8 class NpmProxy { @@ -12,7 +10,7 @@ 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 @@ -21,7 +19,7 @@ class NpmProxy { // on Linux will fight you. So you'll have to be pretty sneaky to do this. // I'm adding both here "just in case". private val npmProxyVariables = listOf( - "NPM_CONFIG_PROXY", "NPM_CONFIG_HTTPS-PROXY", "NPM_CONFIG_HTTPS_PROXY", "NPM_CONFIG_NOPROXY" + "NPM_CONFIG_PROXY", "NPM_CONFIG_HTTPS-PROXY", "NPM_CONFIG_HTTPS_PROXY", "NPM_CONFIG_NOPROXY" ) /** @@ -72,21 +70,38 @@ class NpmProxy { /** * Creates a new NodeExecConfiguration with the proxy environment variables configured */ - fun addProxyEnvironmentVariables(proxySettings: ProxySettings, nodeExecConfiguration: NodeExecConfiguration, - environment: Map = System.getenv()): NodeExecConfiguration { + fun addProxyEnvironmentVariables( + proxySettings: ProxySettings, + nodeExecConfiguration: NodeExecConfiguration, + environment: Map = System.getenv() + ): NodeExecConfiguration { + val environmentVariables = createProxyEnvironmentVariables( + proxySettings, + nodeExecConfiguration.environment, + environment, + ) + return nodeExecConfiguration.copy(environment = environmentVariables) + } + + fun createProxyEnvironmentVariables( + proxySettings: ProxySettings, + nodeExecConfigurationEnvironment: Map, + environment: Map = System.getenv() + ): Map { if (shouldConfigureProxy(environment, proxySettings)) { val npmProxyEnvironmentVariables = computeNpmProxyEnvironmentVariables() - val environmentVariablesToUnset = if (proxySettings == ProxySettings.FORCED) getKnownProxyConfigurationKeys() - else emptySet() + val environmentVariablesToUnset = + if (proxySettings == ProxySettings.FORCED) getKnownProxyConfigurationKeys() + else emptySet() if (npmProxyEnvironmentVariables.isNotEmpty()) { val environmentVariables = - nodeExecConfiguration.environment + nodeExecConfigurationEnvironment .minus(environmentVariablesToUnset) .plus(npmProxyEnvironmentVariables) - return nodeExecConfiguration.copy(environment = environmentVariables) + return environmentVariables } } - return nodeExecConfiguration + return emptyMap() } private fun computeProxyUrlEnvironmentVariables(): MutableMap { @@ -101,7 +116,7 @@ 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" } @@ -114,7 +129,9 @@ class NpmProxy { return URLEncoder.encode(value, UTF_8.toString()) } - private fun addProxyIgnoredHostsEnvironmentVariable(proxyEnvironmentVariables: MutableMap) { + private fun addProxyIgnoredHostsEnvironmentVariable( + proxyEnvironmentVariables: MutableMap, + ) { val proxyIgnoredHosts = computeProxyIgnoredHosts() if (proxyIgnoredHosts.isNotEmpty()) { proxyEnvironmentVariables["NO_PROXY"] = proxyIgnoredHosts.joinToString(", ") @@ -122,22 +139,20 @@ 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 listOf("http.nonProxyHosts", "https.nonProxyHosts") + .map { property -> + val propertyValue = System.getProperty(property) + if (propertyValue != null) { + val hosts = propertyValue.split("|") + return@map hosts.map { host -> + host.substringBefore(":") } - return@map listOf() } - .flatMap(List::stream) - .distinct() - .collect(toList()) + return@map listOf() + } + .flatten() + .distinct() } + } } diff --git a/src/main/kotlin/com/github/gradle/node/npm/task/NpmTask.kt b/src/main/kotlin/com/github/gradle/node/npm/task/NpmTask.kt index 4a90c5f3..0dc95afa 100644 --- a/src/main/kotlin/com/github/gradle/node/npm/task/NpmTask.kt +++ b/src/main/kotlin/com/github/gradle/node/npm/task/NpmTask.kt @@ -2,8 +2,7 @@ package com.github.gradle.node.npm.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.npm.exec.NpmExecRunner +import com.github.gradle.node.npm.exec.NpmExecSource import com.github.gradle.node.task.BaseTask import com.github.gradle.node.util.DefaultProjectApiHelper import org.gradle.api.Action @@ -13,10 +12,7 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.listProperty -import org.gradle.kotlin.dsl.mapProperty -import org.gradle.kotlin.dsl.newInstance -import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.* import org.gradle.process.ExecSpec import javax.inject.Inject @@ -66,11 +62,47 @@ abstract class NpmTask : BaseTask() { @TaskAction fun exec() { - val command = npmCommand.get().plus(args.get()) - val nodeExecConfiguration = - NodeExecConfiguration(command, environment.get(), workingDir.asFile.orNull, ignoreExitValue.get(), - execOverrides.orNull) - val npmExecRunner = objects.newInstance(NpmExecRunner::class.java) - result = npmExecRunner.executeNpmCommand(projectHelper, nodeExtension, nodeExecConfiguration, variantComputer) + @Suppress("UnstableApiUsage") + val npmExec = providers.of(NpmExecSource::class) { + parameters.arguments.set(args) + parameters.environment.set(environment) + parameters.npmCommand.set(npmCommand) + + parameters.ignoreExitValue.set(true) + parameters.workingDir.set(workingDir.asFile.orNull) + + parameters.download.set(nodeExtension.download) + parameters.resolvedNodeDir.set(nodeExtension.resolvedNodeDir) + parameters.resolvedPlatform.set(nodeExtension.resolvedPlatform) + parameters.npmVersion.set(nodeExtension.npmVersion) + parameters.npmWorkDir.set(nodeExtension.npmWorkDir) + parameters.nodeProjectDir.set(nodeExtension.nodeProjectDir) + parameters.nodeProxySettings.set(nodeExtension.nodeProxySettings) + parameters.coreNpmCommand.set(nodeExtension.npmCommand) + } + val result = npmExec.get() + if (result.failure != null) { + logger.error(result.capturedOutput) + throw RuntimeException("$path failed to execute npm command.", result.failure) + } else { + logger.info(result.capturedOutput) + } + this.result = result.asExecResult().rethrowFailure() +// val command = npmCommand.get().plus(args.get()) +// val nodeExecConfiguration = +// NodeExecConfiguration( +// command = command, +// environment = environment.get(), +// workingDir = workingDir.asFile.orNull, +// ignoreExitValue = ignoreExitValue.get(), +// execOverrides = execOverrides.orNull, +// ) +// val npmExecRunner = objects.newInstance(NpmExecRunner::class.java) +// result = npmExecRunner.executeNpmCommand( +// project = projectHelper, +// extension = nodeExtension, +// nodeExecConfiguration = nodeExecConfiguration, +// variants = variantComputer, +// ) } } diff --git a/src/main/kotlin/com/github/gradle/node/util/Platform.kt b/src/main/kotlin/com/github/gradle/node/util/Platform.kt index 381a0831..ac789326 100644 --- a/src/main/kotlin/com/github/gradle/node/util/Platform.kt +++ b/src/main/kotlin/com/github/gradle/node/util/Platform.kt @@ -1,6 +1,11 @@ package com.github.gradle.node.util -data class Platform(val name: String, val arch: String) { +import java.io.Serializable + +data class Platform( + val name: String, + val arch: String, +): Serializable { fun isWindows(): Boolean { return name == "win" } diff --git a/src/main/kotlin/com/github/gradle/node/util/Provider.kt b/src/main/kotlin/com/github/gradle/node/util/Provider.kt index 6097eda8..4964155c 100644 --- a/src/main/kotlin/com/github/gradle/node/util/Provider.kt +++ b/src/main/kotlin/com/github/gradle/node/util/Provider.kt @@ -3,40 +3,46 @@ package com.github.gradle.node.util import org.gradle.api.provider.Provider internal fun zip(aProvider: Provider, bProvider: Provider): Provider> { - return aProvider.flatMap { a -> bProvider.map { b -> Pair(a!!, b!!) } } + return aProvider.flatMap { a -> bProvider.map { b -> Pair(a, b) } } } internal fun zip(aProvider: Provider, bProvider: Provider, cProvider: Provider): Provider> { - return zip(aProvider, bProvider).flatMap { pair -> cProvider.map { c -> Triple(pair.first, pair.second, c!!) } } + return zip(aProvider, bProvider).flatMap { pair -> cProvider.map { c -> Triple(pair.first, pair.second, c) } } } -internal fun zip(aProvider: Provider, bProvider: Provider, cProvider: Provider, - dProvider: Provider): Provider> { +internal fun zip( + aProvider: Provider, bProvider: Provider, cProvider: Provider, + dProvider: Provider +): Provider> { return zip(zip(aProvider, bProvider), zip(cProvider, dProvider)) - .map { pairs -> Tuple4(pairs.first.first, pairs.first.second, pairs.second.first, pairs.second.second) } + .map { pairs -> Tuple4(pairs.first.first, pairs.first.second, pairs.second.first, pairs.second.second) } } internal data class Tuple4( - val first: A, - val second: B, - val third: C, - val fourth: D + val first: A, + val second: B, + val third: C, + val fourth: D ) -internal fun zip(aProvider: Provider, bProvider: Provider, cProvider: Provider, - dProvider: Provider, eProvider: Provider): Provider> { +internal fun zip( + aProvider: Provider, bProvider: Provider, cProvider: Provider, + dProvider: Provider, eProvider: Provider +): Provider> { return zip(zip(aProvider, bProvider), zip(cProvider, dProvider, eProvider)) - .map { pairs -> - Tuple5(pairs.first.first, pairs.first.second, pairs.second.first, pairs.second.second, - pairs.second.third) - } + .map { pairs -> + Tuple5( + pairs.first.first, pairs.first.second, pairs.second.first, pairs.second.second, + pairs.second.third + ) + } } internal data class Tuple5( - val first: A, - val second: B, - val third: C, - val fourth: D, - val fifth: E + val first: A, + val second: B, + val third: C, + val fourth: D, + val fifth: E ) diff --git a/src/main/kotlin/com/github/gradle/node/variant/VariantComputer.kt b/src/main/kotlin/com/github/gradle/node/variant/VariantComputer.kt index 0b3d2ff8..05a6fc13 100644 --- a/src/main/kotlin/com/github/gradle/node/variant/VariantComputer.kt +++ b/src/main/kotlin/com/github/gradle/node/variant/VariantComputer.kt @@ -12,13 +12,21 @@ import org.gradle.api.provider.Provider /** * Get the expected node binary name, node.exe on Windows and node everywhere else. */ -fun computeNodeExec(nodeExtension: NodeExtension, nodeBinDirProvider: Provider): Provider { - return zip(nodeExtension.download, nodeBinDirProvider).map { - val (download, nodeBinDir) = it +fun computeNodeExec( + nodeExtension: NodeExtension, + nodeBinDirProvider: Provider +): Provider { + return zip( + nodeExtension.download, + nodeBinDirProvider, + nodeExtension.resolvedPlatform, + ).map { (download, nodeBinDir, platform) -> if (download) { - val nodeCommand = if (nodeExtension.resolvedPlatform.get().isWindows()) "node.exe" else "node" + val nodeCommand = if (platform.isWindows()) "node.exe" else "node" nodeBinDir.dir(nodeCommand).asFile.absolutePath - } else "node" + } else { + "node" + } } } @@ -50,14 +58,24 @@ internal fun computeExec( binDirProvider: Provider, configurationCommand: Property, unixCommand: String, - windowsCommand: String + windowsCommand: String, ): Provider { - return zip(nodeExtension.download, configurationCommand, binDirProvider).map { - val (download, cfgCommand, binDir) = it - val command = if (nodeExtension.resolvedPlatform.get().isWindows()) { + return zip( + nodeExtension.download, + nodeExtension.resolvedPlatform, + configurationCommand, + binDirProvider, + ).map { (download, resolvedPlatform, cfgCommand, binDir) -> + val command = if (resolvedPlatform.isWindows()) { cfgCommand.mapIf({ it == unixCommand }) { windowsCommand } - } else cfgCommand - if (download) binDir.dir(command).asFile.absolutePath else command + } else { + cfgCommand + } + if (download) { + binDir.dir(command).asFile.absolutePath + } else { + command + } } } @@ -95,7 +113,10 @@ open class VariantComputer { /** * Get the expected node binary directory, taking Windows specifics into account. */ - fun computeNodeBinDir(nodeDirProvider: Provider, platform: Property) = + fun computeNodeBinDir( + nodeDirProvider: Provider, + platform: Property, + ): Provider = computeProductBinDir(nodeDirProvider, platform) /** @@ -113,20 +134,28 @@ open class VariantComputer { /** * Get the expected directory for a given npm version. */ - fun computeNpmDir(nodeExtension: NodeExtension, nodeDirProvider: Provider): Provider { - return zip(nodeExtension.npmVersion, nodeExtension.npmWorkDir, nodeDirProvider).map { - val (npmVersion, npmWorkDir, nodeDir) = it - if (npmVersion.isNotBlank()) { - val directoryName = "npm-v${npmVersion}" - npmWorkDir.dir(directoryName) - } else nodeDir - } + fun computeNpmDir( + nodeExtension: NodeExtension, + nodeDirProvider: Provider, + ): Provider { + return zip(nodeExtension.npmVersion, nodeExtension.npmWorkDir, nodeDirProvider) + .map { (npmVersion, npmWorkDir, nodeDir) -> + if (npmVersion.isNotBlank()) { + val directoryName = "npm-v${npmVersion}" + npmWorkDir.dir(directoryName) + } else { + nodeDir + } + } } /** * Get the expected npm binary directory, taking Windows specifics into account. */ - fun computeNpmBinDir(npmDirProvider: Provider, platform: Property) = + fun computeNpmBinDir( + npmDirProvider: Provider, + platform: Property + ): Provider = computeProductBinDir(npmDirProvider, platform) /** @@ -136,8 +165,11 @@ open class VariantComputer { */ fun computeNpmExec(nodeExtension: NodeExtension, npmBinDirProvider: Provider): Provider { return computeExec( - nodeExtension, npmBinDirProvider, - nodeExtension.npmCommand, "npm", "npm.cmd" + nodeExtension = nodeExtension, + binDirProvider = npmBinDirProvider, + configurationCommand = nodeExtension.npmCommand, + unixCommand = "npm", + windowsCommand = "npm.cmd", ) } @@ -211,8 +243,18 @@ open class VariantComputer { ) } - private fun computeProductBinDir(productDirProvider: Provider, platform: Property) = - if (platform.get().isWindows()) productDirProvider else productDirProvider.map { it.dir("bin") } + private fun computeProductBinDir( + productDirProvider: Provider, + platform: Property + ): Provider { + return platform.flatMap { p -> + if (p.isWindows()) { + productDirProvider.map { it.dir("bin") } + } else { + productDirProvider + } + } + } /** * Get the node archive name in Gradle dependency format, using zip for Windows and tar.gz everywhere else. diff --git a/src/test/groovy/com/github/gradle/node/bun/task/Bun_integTest.groovy b/src/test/groovy/com/github/gradle/node/bun/task/Bun_integTest.groovy index f0e7b25e..3e2735b3 100644 --- a/src/test/groovy/com/github/gradle/node/bun/task/Bun_integTest.groovy +++ b/src/test/groovy/com/github/gradle/node/bun/task/Bun_integTest.groovy @@ -14,7 +14,7 @@ class Bun_integTest extends AbstractIntegTest { copyResources("fixtures/javascript-project/", "javascript-project") when: - def result1 = build("build") + def result1 = build("build", "--stacktrace") then: result1.task(":bunInstall").outcome == TaskOutcome.SUCCESS @@ -28,7 +28,7 @@ class Bun_integTest extends AbstractIntegTest { createFile("javascript-project/output-bun/index.js").isFile() when: - def result2 = build("build") + def result2 = build("build", "--stacktrace") then: // Not up-to-date because the bun.lockb now exists @@ -37,7 +37,7 @@ class Bun_integTest extends AbstractIntegTest { result2.task(":buildBun").outcome == TaskOutcome.UP_TO_DATE when: - def result3 = build("build") + def result3 = build("build", "--stacktrace") then: result3.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE diff --git a/src/test/groovy/com/github/gradle/node/npm/task/NpmInstallTaskTest.groovy b/src/test/groovy/com/github/gradle/node/npm/task/NpmInstallTaskTest.groovy index 5142df68..c90cabf2 100644 --- a/src/test/groovy/com/github/gradle/node/npm/task/NpmInstallTaskTest.groovy +++ b/src/test/groovy/com/github/gradle/node/npm/task/NpmInstallTaskTest.groovy @@ -36,7 +36,7 @@ class NpmInstallTaskTest extends AbstractTaskTest { GradleProxyHelper.setHttpsProxyPort(11235) nodeExtension.nodeProxySettings.set(ProxySettings.OFF) - def task = project.tasks.getByName("npmInstall") + def task = project.tasks.named("npmInstall", NpmInstallTask.class).get() mockProjectApiHelperExec(task) when: diff --git a/src/test/groovy/com/github/gradle/node/npm/task/Npm_integTest.groovy b/src/test/groovy/com/github/gradle/node/npm/task/Npm_integTest.groovy index b549d065..2894d4f4 100644 --- a/src/test/groovy/com/github/gradle/node/npm/task/Npm_integTest.groovy +++ b/src/test/groovy/com/github/gradle/node/npm/task/Npm_integTest.groovy @@ -12,7 +12,7 @@ class Npm_integTest extends AbstractIntegTest { copyResources("fixtures/javascript-project/", "javascript-project") when: - def result1 = build("build") + def result1 = build("build", "--stacktrace") then: result1.task(":npmInstall").outcome == TaskOutcome.SUCCESS @@ -26,7 +26,7 @@ class Npm_integTest extends AbstractIntegTest { createFile("javascript-project/output-npm/index.js").isFile() when: - def result2 = build("build") + def result2 = build("build", "--stacktrace") then: // Not up-to-date because the package-lock.json now exists @@ -35,7 +35,7 @@ class Npm_integTest extends AbstractIntegTest { result2.task(":buildNpm").outcome == TaskOutcome.UP_TO_DATE when: - def result3 = build("build") + def result3 = build("build", "--stacktrace") then: result3.task(":npmInstall").outcome == TaskOutcome.UP_TO_DATE diff --git a/src/test/groovy/com/github/gradle/node/task/NodeTask_integTest.groovy b/src/test/groovy/com/github/gradle/node/task/NodeTask_integTest.groovy index 9c61b1a4..6cc90ed4 100644 --- a/src/test/groovy/com/github/gradle/node/task/NodeTask_integTest.groovy +++ b/src/test/groovy/com/github/gradle/node/task/NodeTask_integTest.groovy @@ -21,7 +21,7 @@ class NodeTask_integTest extends AbstractIntegTest { copyResources("fixtures/node") when: - def result1 = build("hello") + def result1 = build("hello", "--stacktrace") then: result1.task(":nodeSetup").outcome == TaskOutcome.SUCCESS @@ -29,7 +29,7 @@ class NodeTask_integTest extends AbstractIntegTest { result1.output.contains("Hello World") when: - def result2 = build("hello") + def result2 = build("hello", "--stacktrace") then: result2.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -37,7 +37,7 @@ class NodeTask_integTest extends AbstractIntegTest { !result2.output.contains("Hello World") when: - def result3 = build("hello", "-DchangeScript=true") + def result3 = build("hello", "-DchangeScript=true", "--stacktrace") then: result3.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -45,7 +45,7 @@ class NodeTask_integTest extends AbstractIntegTest { !result3.output.contains("Hello") when: - def result4 = build("hello", "-DchangeScript=true", "-DchangeArgs=true") + def result4 = build("hello", "-DchangeScript=true", "-DchangeArgs=true", "--stacktrace") then: result4.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -54,7 +54,7 @@ class NodeTask_integTest extends AbstractIntegTest { result4.output.contains("Hello Alice") when: - def result5 = build("hello", "-DchangeScript=true", "-DchangeArgs=true") + def result5 = build("hello", "-DchangeScript=true", "-DchangeArgs=true", "--stacktrace") then: result5.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -62,7 +62,7 @@ class NodeTask_integTest extends AbstractIntegTest { when: // Reset build arguments to ensure the next change is not up-to-date - def result6 = build("hello") + def result6 = build("hello", "--stacktrace") then: result6.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -70,7 +70,7 @@ class NodeTask_integTest extends AbstractIntegTest { when: writeFile("simple.js", "console.log('Hello Bobby');") - def result7 = build("hello") + def result7 = build("hello", "--stacktrace") then: result7.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -84,7 +84,7 @@ class NodeTask_integTest extends AbstractIntegTest { result8.task(":executeDirectoryScript").outcome == TaskOutcome.FAILED when: - def result9 = build(":version") + def result9 = build(":version", "--stacktrace") then: result9.task(":version").outcome == TaskOutcome.SUCCESS @@ -101,7 +101,7 @@ class NodeTask_integTest extends AbstractIntegTest { copyResources("fixtures/node-env") when: - def result1 = build("env") + def result1 = build("env", "--stacktrace") then: result1.task(":nodeSetup").outcome == TaskOutcome.SUCCESS @@ -109,14 +109,14 @@ class NodeTask_integTest extends AbstractIntegTest { result1.output.contains("No custom environment") when: - def result2 = build("env") + def result2 = build("env", "--stacktrace") then: result2.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result2.task(":env").outcome == TaskOutcome.UP_TO_DATE when: - def result3 = build("env", "-DchangeOptions=true") + def result3 = build("env", "-DchangeOptions=true", "--stacktrace") then: result3.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -124,7 +124,7 @@ class NodeTask_integTest extends AbstractIntegTest { result3.output.contains("1000000") when: - def result4 = build("env", "-DchangeOptions=true") + def result4 = build("env", "-DchangeOptions=true", "--stacktrace") then: result4.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -132,14 +132,14 @@ class NodeTask_integTest extends AbstractIntegTest { when: // Reset build arguments to ensure the next change is not up-to-date - def result5 = build("env") + def result5 = build("env", "--stacktrace") then: result5.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result5.task(":env").outcome == TaskOutcome.SUCCESS when: - def result6 = build("env", "-DchangeEnv=true") + def result6 = build("env", "-DchangeEnv=true", "--stacktrace") then: result6.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -148,7 +148,7 @@ class NodeTask_integTest extends AbstractIntegTest { when: environmentVariables.set("NEW_ENV_VARIABLE", "Let's make the whole environment change") - def result7 = build("env", "-DchangeEnv=true") + def result7 = build("env", "-DchangeEnv=true", "--stacktrace") then: result7.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -156,21 +156,21 @@ class NodeTask_integTest extends AbstractIntegTest { when: // Reset build arguments to ensure the next change is not up-to-date - def result8 = build("env") + def result8 = build("env", "--stacktrace") then: result8.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result8.task(":env").outcome == TaskOutcome.SUCCESS when: - def result9 = build("env", "-DchangeWorkingDir=true") + def result9 = build("env", "-DchangeWorkingDir=true", "--stacktrace") then: result9.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result9.task(":env").outcome == TaskOutcome.UP_TO_DATE when: - def result10 = build("env", "-DchangeWorkingDir=true", "--rerun-tasks") + def result10 = build("env", "-DchangeWorkingDir=true", "--rerun-tasks", "--stacktrace") then: result10.task(":nodeSetup").outcome == TaskOutcome.SUCCESS @@ -181,14 +181,14 @@ class NodeTask_integTest extends AbstractIntegTest { when: // Reset build arguments to ensure the next change is not up-to-date - def result11 = build("env") + def result11 = build("env", "--stacktrace") then: result11.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE result11.task(":env").outcome == TaskOutcome.UP_TO_DATE when: - def result12 = build("env", "-DignoreExitValue=true") + def result12 = build("env", "-DignoreExitValue=true", "--stacktrace") then: result12.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -196,7 +196,7 @@ class NodeTask_integTest extends AbstractIntegTest { result12.output.contains("No custom environment") when: - def result13 = build("env", "-Dfail=true", "-DignoreExitValue=true") + def result13 = build("env", "-Dfail=true", "-DignoreExitValue=true", "--stacktrace") then: result13.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -204,7 +204,7 @@ class NodeTask_integTest extends AbstractIntegTest { result13.output.contains("I had to fail") when: - def result14 = build("env", "-Dfail=true", "-DignoreExitValue=true") + def result14 = build("env", "-Dfail=true", "-DignoreExitValue=true", "--stacktrace") then: result14.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -219,7 +219,7 @@ class NodeTask_integTest extends AbstractIntegTest { result15.output.contains("I had to fail") when: - def result16 = build("env", "-DoutputFile=true") + def result16 = build("env", "-DoutputFile=true", "--stacktrace") then: result16.task(":nodeSetup").outcome == TaskOutcome.UP_TO_DATE @@ -230,7 +230,7 @@ class NodeTask_integTest extends AbstractIntegTest { outputFile.text.contains("No custom environment") when: - def result17 = build(":version") + def result17 = build(":version", "--stacktrace") then: result17.task(":version").outcome == TaskOutcome.SUCCESS @@ -264,7 +264,7 @@ class NodeTask_integTest extends AbstractIntegTest { copyResources("fixtures/node-fail-on-project-repos-download") when: - def result = build("hello") + def result = build("hello", "--stacktrace") then: result.task(":nodeSetup").outcome == TaskOutcome.SUCCESS @@ -282,7 +282,7 @@ class NodeTask_integTest extends AbstractIntegTest { copyResources("fixtures/node-fail-on-project-repos-no-download") when: - def result = build("hello") + def result = build("hello", "--stacktrace") then: result.task(":nodeSetup").outcome == TaskOutcome.SKIPPED @@ -300,7 +300,7 @@ class NodeTask_integTest extends AbstractIntegTest { copyResources("fixtures/node-disallow-insecure-protocol") when: - def result = build("hello") + def result = build("hello", "--stacktrace") then: result.task(":nodeSetup").outcome == TaskOutcome.SUCCESS @@ -318,7 +318,7 @@ class NodeTask_integTest extends AbstractIntegTest { copyResources("fixtures/node-allow-insecure-protocol") when: - def result = build("hello") + def result = build("hello", "--stacktrace") then: result.task(":nodeSetup").outcome == TaskOutcome.SUCCESS diff --git a/src/test/groovy/com/github/gradle/node/variant/VariantComputerTest.groovy b/src/test/groovy/com/github/gradle/node/variant/VariantComputerTest.groovy index 2f0d5e93..332f8457 100644 --- a/src/test/groovy/com/github/gradle/node/variant/VariantComputerTest.groovy +++ b/src/test/groovy/com/github/gradle/node/variant/VariantComputerTest.groovy @@ -23,7 +23,7 @@ class VariantComputerTest extends Specification { def platform = getPlatform("Windows 8", osArch) - def nodeExtension = new NodeExtension(project) + def nodeExtension = project.objects.newInstance(NodeExtension.class, project) nodeExtension.resolvedPlatform.set(platform) nodeExtension.download.set(true) nodeExtension.version.set(version) @@ -68,7 +68,7 @@ class VariantComputerTest extends Specification { def platform = getPlatform(osName, osArch) def project = ProjectBuilder.builder().build() - def nodeExtension = new NodeExtension(project) + def nodeExtension = project.objects.newInstance(NodeExtension.class, project) nodeExtension.resolvedPlatform.set(platform) nodeExtension.download.set(true) nodeExtension.version.set('5.12.0') @@ -113,7 +113,7 @@ class VariantComputerTest extends Specification { def platform = getPlatform(osName, osArch, sysOsArch) def project = ProjectBuilder.builder().build() - def nodeExtension = new NodeExtension(project) + def nodeExtension = project.objects.newInstance(NodeExtension.class, project) nodeExtension.resolvedPlatform.set(platform) nodeExtension.download.set(true) nodeExtension.version.set('5.12.0') @@ -153,7 +153,7 @@ class VariantComputerTest extends Specification { def platform = getPlatform("Windows 8", "x86") def project = ProjectBuilder.builder().build() - def nodeExtension = new NodeExtension(project) + def nodeExtension = project.objects.newInstance(NodeExtension.class, project) nodeExtension.resolvedPlatform.set(platform) nodeExtension.download.set(download) nodeExtension.npmVersion.set(npmVersion) @@ -204,7 +204,7 @@ class VariantComputerTest extends Specification { def platform = getPlatform("Linux", "x86") def project = ProjectBuilder.builder().build() - def nodeExtension = new NodeExtension(project) + def nodeExtension = project.objects.newInstance(NodeExtension.class, project) nodeExtension.resolvedPlatform.set(platform) nodeExtension.download.set(download) nodeExtension.npmVersion.set(npmVersion) @@ -257,7 +257,7 @@ class VariantComputerTest extends Specification { def platform = getPlatform("Linux", "x86") def project = ProjectBuilder.builder().build() - def nodeExtension = new NodeExtension(project) + def nodeExtension = project.objects.newInstance(NodeExtension.class, project) nodeExtension.resolvedPlatform.set(platform) nodeExtension.download.set(download)