Skip to content

Commit 77c49cd

Browse files
committed
Address comments
1. Add abiility for tests to override environment behaviour 2. Fix tests.
1 parent f951da8 commit 77c49cd

File tree

8 files changed

+207
-49
lines changed

8 files changed

+207
-49
lines changed

src/main/kotlin/com/github/gradle/node/NodeExtension.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.github.gradle.node.util.Platform
55
import org.gradle.api.Project
66
import org.gradle.kotlin.dsl.create
77
import org.gradle.kotlin.dsl.getByType
8+
import org.gradle.kotlin.dsl.mapProperty
89
import org.gradle.kotlin.dsl.property
910

1011
open class NodeExtension(project: Project) {
@@ -181,6 +182,9 @@ open class NodeExtension(project: Project) {
181182
*/
182183
val resolvedPlatform = project.objects.property<Platform>()
183184

185+
186+
val environment = project.objects.mapProperty<String, String>().convention(System.getenv())
187+
184188
init {
185189
distBaseUrl.set("https://nodejs.org/dist")
186190
}

src/main/kotlin/com/github/gradle/node/exec/ExecRunner.kt

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import java.io.File
1313
*
1414
* @param execConfiguration configuration to get environment variables from
1515
*/
16-
fun computeEnvironment(execConfiguration: ExecConfiguration): Map<String, String> {
16+
fun computeEnvironment(extension: NodeExtension, execConfiguration: ExecConfiguration): Map<String, String> {
1717
val execEnvironment = mutableMapOf<String, String>()
1818
execEnvironment += System.getenv()
19+
execEnvironment += extension.environment.getOrElse(emptyMap())
1920
execEnvironment += execConfiguration.environment
2021
if (execConfiguration.additionalBinPaths.isNotEmpty()) {
2122
// Take care of Windows environments that may contain "Path" OR "PATH" - both existing
@@ -34,25 +35,6 @@ fun computeWorkingDir(nodeProjectDir: DirectoryProperty, execConfiguration: Exec
3435
workingDir.mkdirs()
3536
return workingDir
3637
}
37-
38-
/**
39-
* Helper function to find the best matching executable in the system PATH.
40-
*
41-
* @param executableName The name of the executable to search for.
42-
* @return The best matching executable path as a String.
43-
*/
44-
fun findBestExecutableMatch(executableName: String): String {
45-
val pathVariable = System.getenv("PATH") ?: return executableName
46-
val paths = pathVariable.split(File.pathSeparator)
47-
for (path in paths) {
48-
val executableFile = File(path, executableName)
49-
if (executableFile.exists() && executableFile.canExecute()) {
50-
return executableFile.absolutePath
51-
}
52-
}
53-
return executableName // Return the original executable if no match is found
54-
}
55-
5638
/**
5739
* Basic execution runner that runs a given ExecConfiguration.
5840
*
@@ -61,11 +43,10 @@ fun findBestExecutableMatch(executableName: String): String {
6143
*/
6244
class ExecRunner {
6345
fun execute(projectHelper: ProjectApiHelper, extension: NodeExtension, execConfiguration: ExecConfiguration): ExecResult {
64-
val executablePath = findBestExecutableMatch(execConfiguration.executable)
6546
return projectHelper.exec {
66-
executable = executablePath
47+
executable = execConfiguration.executable
6748
args = execConfiguration.args
68-
environment = computeEnvironment(execConfiguration)
49+
environment = computeEnvironment(extension, execConfiguration)
6950
isIgnoreExitValue = execConfiguration.ignoreExitValue
7051
workingDir = computeWorkingDir(extension.nodeProjectDir, execConfiguration)
7152
execConfiguration.execOverrides?.execute(this)

src/main/kotlin/com/github/gradle/node/variant/VariantComputer.kt

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,47 @@ import org.gradle.api.file.Directory
88
import org.gradle.api.file.DirectoryProperty
99
import org.gradle.api.provider.Property
1010
import org.gradle.api.provider.Provider
11+
import java.io.File
12+
13+
/**
14+
* Helper function to find the best matching executable in the system PATH.
15+
*
16+
* @param executableName The name of the executable to search for.
17+
* @return The best matching executable path as a String.
18+
*/
19+
fun findBestExecutableMatch(
20+
executableName: String,
21+
environmentProvider: Provider<Map<String, String>>,
22+
): String {
23+
val environment = environmentProvider.orNull ?: return executableName
24+
val pathEnvironmentVariableName = if (environment["Path"] != null) "Path" else "PATH"
25+
val pathVariable = environment[pathEnvironmentVariableName] ?: return executableName
26+
val paths = pathVariable.split(File.pathSeparator)
27+
for (path in paths) {
28+
val executableFile = File(path, executableName)
29+
if (executableFile.exists() && executableFile.canExecute()) {
30+
return executableFile.absolutePath
31+
}
32+
}
33+
return executableName // Return the original executable if no match is found
34+
}
1135

1236
/**
1337
* Get the expected node binary name, node.exe on Windows and node everywhere else.
1438
*/
15-
fun computeNodeExec(nodeExtension: NodeExtension, nodeBinDirProvider: Provider<Directory>): Provider<String> {
39+
fun computeNodeExec(
40+
nodeExtension: NodeExtension,
41+
nodeBinDirProvider: Provider<Directory>,
42+
): Provider<String> {
1643
return zip(nodeExtension.download, nodeBinDirProvider).map {
1744
val (download, nodeBinDir) = it
1845
if (download) {
1946
val nodeCommand = if (nodeExtension.resolvedPlatform.get().isWindows()) "node.exe" else "node"
2047
nodeBinDir.dir(nodeCommand).asFile.absolutePath
21-
} else "node"
48+
} else findBestExecutableMatch("node", nodeExtension.environment)
2249
}
2350
}
51+
2452
fun computeNpmScriptFile(nodeDirProvider: Provider<Directory>, command: String, isWindows: Boolean): Provider<String> {
2553
return nodeDirProvider.map { nodeDir ->
2654
if (isWindows) nodeDir.dir("node_modules/npm/bin/$command-cli.js").asFile.path
@@ -44,21 +72,27 @@ fun computeNodeDir(nodeExtension: NodeExtension, osName: String, osArch: String)
4472
/**
4573
* Compute the path for a given command, from a given binary directory, taking Windows into account
4674
*/
47-
internal fun computeExec(nodeExtension: NodeExtension, binDirProvider: Provider<Directory>,
48-
configurationCommand: Property<String>, unixCommand: String, windowsCommand: String): Provider<String> {
75+
internal fun computeExec(
76+
nodeExtension: NodeExtension, binDirProvider: Provider<Directory>,
77+
configurationCommand: Property<String>, unixCommand: String, windowsCommand: String
78+
): Provider<String> {
4979
return zip(nodeExtension.download, configurationCommand, binDirProvider).map {
5080
val (download, cfgCommand, binDir) = it
5181
val command = if (nodeExtension.resolvedPlatform.get().isWindows()) {
5282
cfgCommand.mapIf({ it == unixCommand }) { windowsCommand }
5383
} else cfgCommand
54-
if (download) binDir.dir(command).asFile.absolutePath else command
84+
if (download) binDir.dir(command).asFile.absolutePath else findBestExecutableMatch(command, nodeExtension.environment)
5585
}
5686
}
5787

5888
/**
5989
* Compute the path for a given package, taken versions and user-configured working directories into account
6090
*/
61-
internal fun computePackageDir(packageName: String, packageVersion: Property<String>, packageWorkDir: DirectoryProperty): Provider<Directory> {
91+
internal fun computePackageDir(
92+
packageName: String,
93+
packageVersion: Property<String>,
94+
packageWorkDir: DirectoryProperty
95+
): Provider<Directory> {
6296
return zip(packageVersion, packageWorkDir).map {
6397
val (version, workDir) = it
6498
val dirnameSuffix = if (version.isNotBlank()) {
@@ -85,14 +119,17 @@ open class VariantComputer {
85119
/**
86120
* Get the expected node binary directory, taking Windows specifics into account.
87121
*/
88-
fun computeNodeBinDir(nodeDirProvider: Provider<Directory>, platform: Property<Platform>) = computeProductBinDir(nodeDirProvider, platform)
122+
fun computeNodeBinDir(nodeDirProvider: Provider<Directory>, platform: Property<Platform>) =
123+
computeProductBinDir(nodeDirProvider, platform)
89124

90125
/**
91126
* Get the expected node binary name, node.exe on Windows and node everywhere else.
92127
*/
93-
@Deprecated(message = "replaced by package-level function",
128+
@Deprecated(
129+
message = "replaced by package-level function",
94130
replaceWith =
95-
ReplaceWith("com.github.gradle.node.variant.computeNodeExec(nodeExtension, nodeBinDirProvider)"))
131+
ReplaceWith("com.github.gradle.node.variant.computeNodeExec(nodeExtension, nodeBinDirProvider)")
132+
)
96133
fun computeNodeExec(nodeExtension: NodeExtension, nodeBinDirProvider: Provider<Directory>): Provider<String> {
97134
return com.github.gradle.node.variant.computeNodeExec(nodeExtension, nodeBinDirProvider)
98135
}
@@ -113,16 +150,19 @@ open class VariantComputer {
113150
/**
114151
* Get the expected npm binary directory, taking Windows specifics into account.
115152
*/
116-
fun computeNpmBinDir(npmDirProvider: Provider<Directory>, platform: Property<Platform>) = computeProductBinDir(npmDirProvider, platform)
153+
fun computeNpmBinDir(npmDirProvider: Provider<Directory>, platform: Property<Platform>) =
154+
computeProductBinDir(npmDirProvider, platform)
117155

118156
/**
119157
* Get the expected node binary name, npm.cmd on Windows and npm everywhere else.
120158
*
121159
* Can be overridden by setting npmCommand.
122160
*/
123161
fun computeNpmExec(nodeExtension: NodeExtension, npmBinDirProvider: Provider<Directory>): Provider<String> {
124-
return computeExec(nodeExtension, npmBinDirProvider,
125-
nodeExtension.npmCommand, "npm", "npm.cmd")
162+
return computeExec(
163+
nodeExtension, npmBinDirProvider,
164+
nodeExtension.npmCommand, "npm", "npm.cmd"
165+
)
126166
}
127167

128168
/**
@@ -131,26 +171,32 @@ open class VariantComputer {
131171
* Can be overridden by setting npxCommand.
132172
*/
133173
fun computeNpxExec(nodeExtension: NodeExtension, npmBinDirProvider: Provider<Directory>): Provider<String> {
134-
return computeExec(nodeExtension, npmBinDirProvider,
135-
nodeExtension.npxCommand, "npx", "npx.cmd")
174+
return computeExec(
175+
nodeExtension, npmBinDirProvider,
176+
nodeExtension.npxCommand, "npx", "npx.cmd"
177+
)
136178
}
137179

138180
fun computePnpmDir(nodeExtension: NodeExtension): Provider<Directory> {
139181
return computePackageDir("pnpm", nodeExtension.pnpmVersion, nodeExtension.pnpmWorkDir)
140182
}
141183

142-
fun computePnpmBinDir(pnpmDirProvider: Provider<Directory>, platform: Property<Platform>) = computeProductBinDir(pnpmDirProvider, platform)
184+
fun computePnpmBinDir(pnpmDirProvider: Provider<Directory>, platform: Property<Platform>) =
185+
computeProductBinDir(pnpmDirProvider, platform)
143186

144187
fun computePnpmExec(nodeExtension: NodeExtension, pnpmBinDirProvider: Provider<Directory>): Provider<String> {
145-
return computeExec(nodeExtension, pnpmBinDirProvider,
146-
nodeExtension.pnpmCommand, "pnpm", "pnpm.cmd")
188+
return computeExec(
189+
nodeExtension, pnpmBinDirProvider,
190+
nodeExtension.pnpmCommand, "pnpm", "pnpm.cmd"
191+
)
147192
}
148193

149194
fun computeYarnDir(nodeExtension: NodeExtension): Provider<Directory> {
150195
return computePackageDir("yarn", nodeExtension.yarnVersion, nodeExtension.yarnWorkDir)
151196
}
152197

153-
fun computeYarnBinDir(yarnDirProvider: Provider<Directory>, platform: Property<Platform>) = computeProductBinDir(yarnDirProvider, platform)
198+
fun computeYarnBinDir(yarnDirProvider: Provider<Directory>, platform: Property<Platform>) =
199+
computeProductBinDir(yarnDirProvider, platform)
154200

155201
fun computeYarnExec(nodeExtension: NodeExtension, yarnBinDirProvider: Provider<Directory>): Provider<String> {
156202
return zip(nodeExtension.yarnCommand, yarnBinDirProvider).map {
@@ -167,11 +213,14 @@ open class VariantComputer {
167213
return computePackageDir("bun", nodeExtension.bunVersion, nodeExtension.bunWorkDir)
168214
}
169215

170-
fun computeBunBinDir(bunDirProvider: Provider<Directory>, platform: Property<Platform>) = computeProductBinDir(bunDirProvider, platform)
216+
fun computeBunBinDir(bunDirProvider: Provider<Directory>, platform: Property<Platform>) =
217+
computeProductBinDir(bunDirProvider, platform)
171218

172219
fun computeBunExec(nodeExtension: NodeExtension, bunBinDirProvider: Provider<Directory>): Provider<String> {
173-
return computeExec(nodeExtension, bunBinDirProvider,
174-
nodeExtension.bunCommand, "bun", "bun.cmd")
220+
return computeExec(
221+
nodeExtension, bunBinDirProvider,
222+
nodeExtension.bunCommand, "bun", "bun.cmd"
223+
)
175224
}
176225

177226
/**
@@ -180,20 +229,24 @@ open class VariantComputer {
180229
* Can be overridden by setting bunxCommand.
181230
*/
182231
fun computeBunxExec(nodeExtension: NodeExtension, bunBinDirProvider: Provider<Directory>): Provider<String> {
183-
return computeExec(nodeExtension, bunBinDirProvider,
184-
nodeExtension.bunxCommand, "bunx", "bunx.cmd")
232+
return computeExec(
233+
nodeExtension, bunBinDirProvider,
234+
nodeExtension.bunxCommand, "bunx", "bunx.cmd"
235+
)
185236
}
186237

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

190241
/**
191242
* Get the node archive name in Gradle dependency format, using zip for Windows and tar.gz everywhere else.
192243
*
193244
* Essentially: org.nodejs:node:$version:$osName-$osArch@tar.gz
194245
*/
195-
@Deprecated(message = "replaced by package-level function",
196-
replaceWith = ReplaceWith("com.github.gradle.node.variant.computeNodeArchiveDependency(nodeExtension)"))
246+
@Deprecated(
247+
message = "replaced by package-level function",
248+
replaceWith = ReplaceWith("com.github.gradle.node.variant.computeNodeArchiveDependency(nodeExtension)")
249+
)
197250
fun computeNodeArchiveDependency(nodeExtension: NodeExtension): Provider<String> {
198251
return com.github.gradle.node.variant.computeNodeArchiveDependency(nodeExtension)
199252
}

src/test/groovy/com/github/gradle/node/npm/task/NpmInstallTaskTest.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class NpmInstallTaskTest extends AbstractTaskTest {
1313
def "exec npm install task with configured proxy"() {
1414
given:
1515
nodeExtension.resolvedPlatform.set(PlatformHelperKt.parsePlatform("Linux", "x86_64", {}))
16+
nodeExtension.environment.set([:])
1617
GradleProxyHelper.setHttpsProxyHost("my-super-proxy.net")
1718
GradleProxyHelper.setHttpsProxyPort(11235)
1819

@@ -32,6 +33,7 @@ class NpmInstallTaskTest extends AbstractTaskTest {
3233
def "exec npm install task with configured proxy but disabled"() {
3334
given:
3435
nodeExtension.resolvedPlatform.set(PlatformHelperKt.parsePlatform("Linux", "x86_64", {}))
36+
nodeExtension.environment.set([:])
3537
GradleProxyHelper.setHttpsProxyHost("my-super-proxy.net")
3638
GradleProxyHelper.setHttpsProxyPort(11235)
3739
nodeExtension.nodeProxySettings.set(ProxySettings.OFF)

0 commit comments

Comments
 (0)