Repository navigation
Feature request: Add ability to extend PATH environment variable for the npm process #1381
Description
Activity
- changed the title
[-]Feature request: Add ability to specify PATH environment variable for the npm process[/-][+]Feature request: Add ability to extend PATH environment variable for the npm process[/+]on Oct 21, 2022 @yevhensayenko I'm trying to reproduce your problem, but it works for me when I'm using the node plugin and specify that npm using
npmExecutable(...)Can you provide extracts from your build file regarding
spotlessandnode? Maybe I'm doing something differently?
Also: which spotless version are you using?I'm currently experimenting with an API like this (Option 1):
prettier() .npmExecutable('/path/to/npm') .npmEnv(['PATH': '/path/to/node-dir' + ':' + System.getenv('PATH')])This would allow for passing various env-vars to the node process and would also solve the gradle-node-plugin requirements. But I'm unsure if this is the way to go, as this api seems to be a bit too "open"?
Maybe it would be sufficient to offer an API like this (Option 2):
prettier() .npmExecutable('/path/to/npm') .nodeExecutable('/path/to/node')-> I would then internally use the nodeExecutable-path to add it to the PATH-env variable passed to the node process.
This would also solve the gradle-node-plugin issue, but keeps the API tighter.I'm leaning toward Option 2 as it is cleaner and fits better with the current API, but would like to hear your thoughts and comments @nedtwigg and @yevhensayenko ?
I agree that option 2 is clearer.
Just a small note. This will not actually solve the use of the gradle node plugin. That plugin needs to use a task in order to install node. Since prettier is evaluated at configuration time, and attempt to do the npm install at this time, the node gradle plugin can't resolve the issue. Below is an example of using the objects of the node gradle plugin to install at configuration time. Maybe it would be best to have a SetupPrettier task that registerInternalDependencies (or some other task) that verifies npm and does the npm install instead of doing this at configuration time.
package plugin.nodejs import com.github.gradle.node.NodeExtension import com.github.gradle.node.util.PlatformHelper import com.github.gradle.node.util.ProjectApiHelper import com.github.gradle.node.variant.VariantComputer import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.file.Directory import org.gradle.api.file.RegularFile import org.gradle.api.provider.Provider import org.gradle.util.GradleVersion import java.io.File import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths class NodePlugin : Plugin<Project> { override fun apply(project: Project) { val nodeExtension = NodeExtension.create(project) // val nodeExtension = project.extensions.create<NodeExtension>("nodeWrapper", project) project.task("hello") { doLast{ println(nodeExtension.version); } } if (nodeExtension.download.get()) { nodeExtension.distBaseUrl.orNull?.let { addRepository(it, nodeExtension.allowInsecureProtocol.orNull, project) } val platformHelper = PlatformHelper.INSTANCE val variantComputer = VariantComputer(platformHelper) val nodeArchiveDependencyProvider = variantComputer.computeNodeArchiveDependency(nodeExtension) val archiveFileProvider = nodeArchiveDependencyProvider .map { nodeArchiveDependency -> resolveNodeArchiveFile(nodeArchiveDependency, project) } val nodeArchiveFile = project.objects.fileProperty(); nodeArchiveFile.set(project.layout.file(archiveFileProvider)) unpackNodeArchive(variantComputer, nodeExtension, nodeArchiveFile, project) setExecutableFlag(platformHelper, variantComputer, nodeExtension); } } private fun unpackNodeArchive(variantComputer: VariantComputer, nodeExtension: NodeExtension, nodeArchiveFile: Provider<RegularFile>, project: Project) { val archiveFile = nodeArchiveFile.get().asFile val nodeDirProvider = variantComputer.computeNodeDir(nodeExtension) if (nodeDirProvider.get().asFile.exists()) return; val nodeBinDirProvider = variantComputer.computeNodeBinDir(nodeDirProvider) val archivePath = nodeDirProvider.map { it.dir("../") } val projectHelper = ProjectApiHelper.newInstance(project) System.out.println(String.format("archiveFile=%s nodeDirProvider=%s nodeBinProvider=%s archivePath=%s", archiveFile.toString(), nodeDirProvider.map { it }.get(), nodeBinDirProvider.map { it }.get(), archivePath.get())) if (archiveFile.name.endsWith("zip")) { projectHelper.copy { from(projectHelper.zipTree(archiveFile)) into(archivePath) } } else { projectHelper.copy { from(projectHelper.tarTree(archiveFile)) into(archivePath) } // Fix broken symlink val nodeBinDirPath = nodeBinDirProvider.get().asFile.toPath() fixBrokenSymlink("npm", nodeBinDirPath, nodeDirProvider, variantComputer) fixBrokenSymlink("npx", nodeBinDirPath, nodeDirProvider, variantComputer) } } private fun fixBrokenSymlink(name: String, nodeBinDirPath: Path, nodeDirProvider: Provider<Directory>, variantComputer: VariantComputer) { val script = nodeBinDirPath.resolve(name) val scriptFile = variantComputer.computeNpmScriptFile(nodeDirProvider, name).get() System.out.println(String.format("The script is %s the script file is %s", script, scriptFile)); if (Files.deleteIfExists(script)) { System.out.println("Deleting script " + script) Files.createSymbolicLink(script, nodeBinDirPath.relativize(Paths.get(scriptFile))) } } private fun setExecutableFlag(platformHelper: PlatformHelper, variantComputer: VariantComputer, nodeExtension: NodeExtension) { if (!platformHelper.isWindows) { val nodeDirProvider = variantComputer.computeNodeDir(nodeExtension) val nodeBinDirProvider = variantComputer.computeNodeBinDir(nodeDirProvider) val nodeExecProvider = variantComputer.computeNodeExec(nodeExtension, nodeBinDirProvider) File(nodeExecProvider.get()).setExecutable(true) } } private fun addRepository(distUrl: String, allowInsecureProtocol: Boolean?, project: Project) { System.out.println(String.format("The distUrl=%s", distUrl)) project.repositories.ivy { name = "Node.js" setUrl(distUrl) patternLayout { artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]") } metadataSources { artifact() } content { includeModule("org.nodejs", "node") } if (GradleVersion.current() >= GradleVersion.version("6.0")) { allowInsecureProtocol?.let { isAllowInsecureProtocol = it } } } } private fun resolveNodeArchiveFile(name: String, project: Project): File { val dependency = project.dependencies.create(name) val configuration = project.configurations.detachedConfiguration(dependency) configuration.isTransitive = false return configuration.resolve().single() } }Reacted by Simon GammaCurrently one needs to first setup node (using a gradle call like
gradlew nodeSetup npmSetupand then invoke spotless 'gradlew spotlessApply` ->,linking the spotless tasks to the setup-tasks does not work as of now. We will need to resolve #1499 for this. Feel free to follow that issue to be notified of full support.Released in
plugin-gradle 6.14.0andplugin-maven 2.31.0.Reacted by Scienko
I would like to use local nodeJS, that is downloaded into project dir using https://github.com/node-gradle/gradle-node-plugin. I tried to specify npm executable using using
prettier().npmExecutable('<path to project>/.gradle/nodejs/node-v16.18.0-linux-x64/bin/npm'). But it still expects node command to be present. So the only way to set it up for me was to override PATH using command line likePATH=/<path to project>/.gradle/nodejs/node-v16.18.0-linux-x64/bin:$PATH ./gradlew.But it seems that we could do it by overriding PATH variable using
java.lang.ProcessBuilder#environment()incom.diffplug.spotless.npm.NpmProcess#npm. So, I think it would be helpful to be able to set up node bin directory in settings, so it can be then added into PATH environment variable for the process.