From 056bc17f2868c2856aff5c4ebdb2e5042e8269f5 Mon Sep 17 00:00:00 2001 From: Ilya Kazakevich Date: Wed, 23 Apr 2025 14:10:26 +0000 Subject: [PATCH] PY-71499: Spaces in a conda env path break terminal action on **nix. To activate conda on **nix, we provide a path to `active` script and a path to the env directory. Terminal shell integration scripts then "source" `activate` providing an env path as an argument. The latter is called `JEDITERM_SOURCE` env var, the former is `JEDITERM_SOURCE_ARGS`. The problem is those integration scripts treat `JEDITERM_SOURCE_ARGS` as a list, so they use shell magic to break it into several arguments, so `/path/foo bar/` effectively presented as `['/path/foo', 'bar/']`. To fix it, we introduce the ` JEDITERM_SOURCE_SINGLE_ARG ` key which means "do not explode argument." (cherry picked from commit a0a7c7a7bc8789078dd6cf109f4fd4386c9b7da6) IJ-MR-159065 PY-78762: Activate conda in Powershell. We used to use `Invoke-Expression` but then migrated to `&`. However, a conda activation script is a command (code block), not a file, so we need to use `Invoke-Expression` as '&' doesn't support it. We check if a file exists, and if it does -- we use '&' which is safe and fast. We use `Invoke-Expression` otherwise. Merge-request: IJ-MR-158505 Merged-by: Ilya Kazakevich PY-78762 (partially): Conda activation for PS cleanup: 1. Use a conda path from SDK 2. report error using echo Merge-request: IJ-MR-160629 Merged-by: Ilya Kazakevich GitOrigin-RevId: b16bd0b1babb1ea3b685daa5697426418356d089 --- .../bash/bash-integration.bash | 8 +- .../powershell/powershell-integration.ps1 | 8 +- .../zsh/zsh-integration.zsh | 9 +- ....community.junit5Tests.framework.conda.iml | 2 + .../framework/conda/condaEnvTool.kt | 30 ++++ .../intellij.python.terminal.iml | 3 + .../PyVirtualEnvTerminalCustomizer.kt | 46 ++++-- .../PyVirtualEnvTerminalCustomizerTest.kt | 143 ++++++++++++++---- 8 files changed, 199 insertions(+), 50 deletions(-) create mode 100644 python/junit5Tests-framework/conda/src/com/intellij/python/community/junit5Tests/framework/conda/condaEnvTool.kt diff --git a/plugins/terminal/resources/shell-integrations/bash/bash-integration.bash b/plugins/terminal/resources/shell-integrations/bash/bash-integration.bash index 0381fafdc2a9..9fda8b262c68 100644 --- a/plugins/terminal/resources/shell-integrations/bash/bash-integration.bash +++ b/plugins/terminal/resources/shell-integrations/bash/bash-integration.bash @@ -71,8 +71,12 @@ then fi if [ -n "${JEDITERM_SOURCE-}" ] -then - source "$JEDITERM_SOURCE" ${JEDITERM_SOURCE_ARGS-} +then # JEDITERM_SOURCE_ARGS might be either list of args or one arg depending on JEDITERM_SOURCE_SINGLE_ARG + if [ -n "${JEDITERM_SOURCE_SINGLE_ARG}" ]; then + source "$JEDITERM_SOURCE" "${JEDITERM_SOURCE_ARGS}" + else + source "$JEDITERM_SOURCE" ${JEDITERM_SOURCE_ARGS-} + fi unset JEDITERM_SOURCE unset JEDITERM_SOURCE_ARGS fi diff --git a/plugins/terminal/resources/shell-integrations/powershell/powershell-integration.ps1 b/plugins/terminal/resources/shell-integrations/powershell/powershell-integration.ps1 index 4a38429ef7a8..c9078e724901 100644 --- a/plugins/terminal/resources/shell-integrations/powershell/powershell-integration.ps1 +++ b/plugins/terminal/resources/shell-integrations/powershell/powershell-integration.ps1 @@ -16,8 +16,12 @@ Get-ChildItem env:_INTELLIJ_FORCE_PREPEND_* | ForEach-Object { } # `JEDITERM_SOURCE` is executed in its own scope now. That means, it can only run code, and export env vars. It can't export PS variables. # It might be better to source it. See MSDN for the difference between "Call operator &" and "Script scope and dot sourcing" -if (($Env:JEDITERM_SOURCE -ne $null) -and (Test-Path $Env:JEDITERM_SOURCE)) { - & $Env:JEDITERM_SOURCE +if ($Env:JEDITERM_SOURCE -ne $null) { + if (Test-Path "$Env:JEDITERM_SOURCE" -ErrorAction SilentlyContinue) { + & "$Env:JEDITERM_SOURCE" + } else { # If file doesn't exist it might be a script + Invoke-Expression "$Env:JEDITERM_SOURCE" + } Remove-Item "env:JEDITERM_SOURCE" } diff --git a/plugins/terminal/resources/shell-integrations/zsh/zsh-integration.zsh b/plugins/terminal/resources/shell-integrations/zsh/zsh-integration.zsh index a6d540265db3..e4170c791328 100644 --- a/plugins/terminal/resources/shell-integrations/zsh/zsh-integration.zsh +++ b/plugins/terminal/resources/shell-integrations/zsh/zsh-integration.zsh @@ -2,9 +2,14 @@ function __jetbrains_intellij_update_environment() { if [[ -n "${JEDITERM_SOURCE:-}" ]]; then - builtin source -- "$JEDITERM_SOURCE" ${=JEDITERM_SOURCE_ARGS:-} + if [[ -n "${JEDITERM_SOURCE_SINGLE_ARG}" ]]; then + # JEDITERM_SOURCE_ARGS might be either list of args or one arg depending on JEDITERM_SOURCE_SINGLE_ARG + builtin source -- "$JEDITERM_SOURCE" "${JEDITERM_SOURCE_ARGS}" + else + builtin source -- "$JEDITERM_SOURCE" ${=JEDITERM_SOURCE_ARGS:-} + fi fi - builtin unset JEDITERM_SOURCE JEDITERM_SOURCE_ARGS + # Enable native zsh options to make coding easier. builtin emulate -L zsh diff --git a/python/junit5Tests-framework/conda/intellij.python.community.junit5Tests.framework.conda.iml b/python/junit5Tests-framework/conda/intellij.python.community.junit5Tests.framework.conda.iml index 657e2bbdea22..84f3f4cc1a46 100644 --- a/python/junit5Tests-framework/conda/intellij.python.community.junit5Tests.framework.conda.iml +++ b/python/junit5Tests-framework/conda/intellij.python.community.junit5Tests.framework.conda.iml @@ -13,5 +13,7 @@ + + \ No newline at end of file diff --git a/python/junit5Tests-framework/conda/src/com/intellij/python/community/junit5Tests/framework/conda/condaEnvTool.kt b/python/junit5Tests-framework/conda/src/com/intellij/python/community/junit5Tests/framework/conda/condaEnvTool.kt new file mode 100644 index 000000000000..e03f116bf579 --- /dev/null +++ b/python/junit5Tests-framework/conda/src/com/intellij/python/community/junit5Tests/framework/conda/condaEnvTool.kt @@ -0,0 +1,30 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.python.community.junit5Tests.framework.conda + +import com.intellij.execution.processTools.getResultStdoutStr +import com.jetbrains.python.psi.LanguageLevel +import com.jetbrains.python.sdk.flavors.conda.NewCondaEnvRequest +import com.jetbrains.python.sdk.flavors.conda.PyCondaCommand +import com.jetbrains.python.sdk.flavors.conda.PyCondaEnv +import com.jetbrains.python.sdk.flavors.conda.PyCondaEnvIdentity +import org.jetbrains.annotations.ApiStatus +import java.nio.file.Path +import kotlin.io.path.pathString + +@ApiStatus.Internal +/** + * Create conda env in [pathToCreateNewEnvIn] using [existingEnv] as a base + */ +suspend fun createCondaEnv( + existingEnv: PyCondaEnv, + pathToCreateNewEnvIn: Path, +): PyCondaEnv { + val process = PyCondaEnv.createEnv( + PyCondaCommand(existingEnv.fullCondaPathOnTarget, null), + NewCondaEnvRequest.EmptyUnnamedEnv(LanguageLevel.PYTHON311, pathToCreateNewEnvIn.pathString) + ).getOrThrow() + process.getResultStdoutStr().getOrThrow() + + val env = PyCondaEnv(PyCondaEnvIdentity.UnnamedEnv(pathToCreateNewEnvIn.pathString, false), existingEnv.fullCondaPathOnTarget) + return env +} \ No newline at end of file diff --git a/python/python-terminal/intellij.python.terminal.iml b/python/python-terminal/intellij.python.terminal.iml index f75a1ea448ed..fb4ce8487ace 100644 --- a/python/python-terminal/intellij.python.terminal.iml +++ b/python/python-terminal/intellij.python.terminal.iml @@ -25,5 +25,8 @@ + + + \ No newline at end of file diff --git a/python/python-terminal/src/com/intellij/python/terminal/PyVirtualEnvTerminalCustomizer.kt b/python/python-terminal/src/com/intellij/python/terminal/PyVirtualEnvTerminalCustomizer.kt index af908958ec10..02b468be6824 100644 --- a/python/python-terminal/src/com/intellij/python/terminal/PyVirtualEnvTerminalCustomizer.kt +++ b/python/python-terminal/src/com/intellij/python/terminal/PyVirtualEnvTerminalCustomizer.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.components.Service import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.diagnostic.fileLogger import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.options.UnnamedConfigurable import com.intellij.openapi.project.Project @@ -14,12 +15,12 @@ import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VirtualFile -import com.jetbrains.python.packaging.PyCondaPackageService import com.jetbrains.python.run.findActivateScript import com.jetbrains.python.sdk.PySdkUtil import com.jetbrains.python.sdk.PythonSdkAdditionalData import com.jetbrains.python.sdk.PythonSdkUtil -import com.jetbrains.python.sdk.flavors.conda.CondaEnvSdkFlavor +import com.jetbrains.python.sdk.flavors.conda.PyCondaFlavorData +import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.LocalTerminalCustomizer import org.jetbrains.plugins.terminal.TerminalOptionsProvider import java.io.File @@ -31,18 +32,28 @@ import kotlin.io.path.exists import kotlin.io.path.isExecutable import kotlin.io.path.name + class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() { + private companion object { + const val JEDITERM_SOURCE = "JEDITERM_SOURCE" + const val JEDITERM_SOURCE_ARGS = "JEDITERM_SOURCE_ARGS" + const val JEDITERM_SOURCE_SINGLE_ARG = "JEDITERM_SOURCE_SINGLE_ARG" + val logger = fileLogger() + } + private fun generatePowerShellActivateScript(sdk: Sdk, sdkHomePath: VirtualFile): String? { // TODO: This should be migrated to Targets API: each target provides terminal - if ((sdk.sdkAdditionalData as? PythonSdkAdditionalData)?.flavor is CondaEnvSdkFlavor) { + val condaData = (sdk.sdkAdditionalData as? PythonSdkAdditionalData)?.flavorAndData?.data as? PyCondaFlavorData + if (condaData != null) { // Activate conda - val condaPath = PyCondaPackageService.getCondaExecutable(sdk.homePath)?.let { Path(it) } - return if (condaPath != null && condaPath.exists() && condaPath.isExecutable()) { + val condaPath = Path(condaData.env.fullCondaPathOnTarget) + return if (condaPath.exists() && condaPath.isExecutable()) { getCondaActivationCommand(condaPath, sdkHomePath) } else { logger().warn("Can't find $condaPath, will not activate conda") - PyTerminalBundle.message("powershell.conda.not.activated", "conda") + val message = PyTerminalBundle.message("powershell.conda.not.activated", "conda") + "echo '$message'" } } @@ -72,10 +83,12 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() { """.trimIndent() } - override fun customizeCommandAndEnvironment(project: Project, - workingDirectory: String?, - command: Array, - envs: MutableMap): Array { + override fun customizeCommandAndEnvironment( + project: Project, + workingDirectory: String?, + command: Array, + envs: MutableMap, + ): Array { var sdkByDirectory: Sdk? = null if (workingDirectory != null) { runReadAction { @@ -99,13 +112,18 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() { val shellName = Path(shellPath).name if (isPowerShell(shellName)) { generatePowerShellActivateScript(sdk, sdkHomePath)?.let { - envs.put("JEDITERM_SOURCE", it) + envs.put(JEDITERM_SOURCE, it) } } else { findActivateScript(sdkHomePath.path, shellPath)?.let { activate -> - envs.put("JEDITERM_SOURCE", activate.first) - envs.put("JEDITERM_SOURCE_ARGS", activate.second ?: "") + envs.put(JEDITERM_SOURCE, activate.first) + envs.put(JEDITERM_SOURCE_ARGS, activate.second ?: "") + // **nix shell integration scripts split arguments; + // since a path may contain spaces, we do not want it to be split into several arguments. + if (activate.second != null) { + envs.put(JEDITERM_SOURCE_SINGLE_ARG, "1") + } } } } @@ -120,6 +138,7 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() { } } + logger.debug("Running ${command.joinToString(" ")} with ${envs.entries.joinToString("\n")}") return command } @@ -158,6 +177,7 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() { } +@ApiStatus.Internal class SettingsState { var virtualEnvActivate: Boolean = true } diff --git a/python/python-terminal/tests/com/intellij/python/junit5Tests/env/terminal/PyVirtualEnvTerminalCustomizerTest.kt b/python/python-terminal/tests/com/intellij/python/junit5Tests/env/terminal/PyVirtualEnvTerminalCustomizerTest.kt index d150f0b42a8e..6ab8a858c8ea 100644 --- a/python/python-terminal/tests/com/intellij/python/junit5Tests/env/terminal/PyVirtualEnvTerminalCustomizerTest.kt +++ b/python/python-terminal/tests/com/intellij/python/junit5Tests/env/terminal/PyVirtualEnvTerminalCustomizerTest.kt @@ -2,44 +2,62 @@ package com.intellij.python.junit5Tests.env.terminal import com.intellij.execution.configurations.PathEnvironmentVariableUtil +import com.intellij.openapi.application.edtWriteAction +import com.intellij.openapi.diagnostic.fileLogger +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.util.SystemInfo import com.intellij.platform.eel.EelExecApi import com.intellij.platform.eel.getOrThrow import com.intellij.platform.eel.provider.localEel import com.intellij.platform.eel.provider.utils.readWholeText import com.intellij.platform.eel.provider.utils.sendWholeText import com.intellij.python.community.impl.venv.tests.pyVenvFixture -import com.intellij.python.junit5Tests.framework.env.PyEnvTestCase +import com.intellij.python.community.junit5Tests.framework.conda.CondaEnv +import com.intellij.python.community.junit5Tests.framework.conda.PyEnvTestCaseWithConda +import com.intellij.python.community.junit5Tests.framework.conda.createCondaEnv import com.intellij.python.junit5Tests.framework.env.pySdkFixture import com.intellij.python.terminal.PyVirtualEnvTerminalCustomizer import com.intellij.testFramework.common.timeoutRunBlocking import com.intellij.testFramework.junit5.fixture.moduleFixture import com.intellij.testFramework.junit5.fixture.projectFixture import com.intellij.testFramework.junit5.fixture.tempPathFixture +import com.jetbrains.python.sdk.flavors.conda.PyCondaEnv +import com.jetbrains.python.sdk.persist import com.jetbrains.python.venvReader.VirtualEnvReader +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.hamcrest.CoreMatchers.* import org.hamcrest.MatcherAssert.assertThat import org.jetbrains.plugins.terminal.ShellStartupOptions import org.jetbrains.plugins.terminal.runner.LocalShellIntegrationInjector import org.jetbrains.plugins.terminal.util.ShellIntegration import org.jetbrains.plugins.terminal.util.ShellType +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.condition.EnabledOnOs -import org.junit.jupiter.api.condition.OS +import org.junit.jupiter.api.Assumptions +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.io.TempDir +import org.junitpioneer.jupiter.cartesian.CartesianTest import java.io.IOException +import java.nio.file.Path import kotlin.io.path.Path +import kotlin.io.path.exists +import kotlin.io.path.isExecutable import kotlin.io.path.name import kotlin.io.path.pathString import kotlin.time.Duration.Companion.minutes -private const val WHERE_EXE = "where.exe" /** * Run `powershell.exe` with a venv activation script and make sure there are no errors and python is correct */ -@PyEnvTestCase +@PyEnvTestCaseWithConda +@Disabled class PyVirtualEnvTerminalCustomizerTest { private val projectFixture = projectFixture() private val tempDirFixture = tempPathFixture(prefix = "some dir with spaces") @@ -52,15 +70,63 @@ class PyVirtualEnvTerminalCustomizerTest { moduleFixture = moduleFixture ) + private var sdkToDelete: Sdk? = null - private val powerShell = - PathEnvironmentVariableUtil.findInPath("powershell.exe")?.toPath() - ?: Path((System.getenv("SystemRoot") ?: "c:\\windows"), "system32", "WindowsPowerShell", "v1.0", "powershell.exe") + @AfterEach + fun tearDown(): Unit = timeoutRunBlocking { + sdkToDelete?.let { sdk -> + edtWriteAction { + ProjectJdkTable.getInstance().removeJdk(sdk) + } + } + } - @EnabledOnOs(value = [OS.WINDOWS]) - @Test - fun powershellActivationTest(): Unit = timeoutRunBlocking(10.minutes) { - val pythonBinary = VirtualEnvReader.Instance.findPythonInPythonRoot(tempDirFixture.get())!! + + private fun getShellPath(shellType: ShellType): Path = when (shellType) { + ShellType.POWERSHELL -> PathEnvironmentVariableUtil.findInPath("powershell.exe")?.toPath() + ?: Path((System.getenv("SystemRoot") + ?: "c:\\windows"), "system32", "WindowsPowerShell", "v1.0", "powershell.exe") + ShellType.FISH, ShellType.BASH, ShellType.ZSH -> Path("/usr/bin/${shellType.name.lowercase()}") + } + + + @CartesianTest + fun shellActivationTest( + @CartesianTest.Values(booleans = [true, false]) useConda: Boolean, + @CartesianTest.Enum shellType: ShellType, + @CondaEnv condaEnv: PyCondaEnv, + @TempDir venvPath: Path, + ): Unit = timeoutRunBlocking(10.minutes) { + when (shellType) { + ShellType.POWERSHELL -> Assumptions.assumeTrue(SystemInfo.isWindows, "PowerShell is Windows only") + ShellType.FISH -> Assumptions.abort("Fish terminal activation isn't supported") + ShellType.ZSH, ShellType.BASH -> Assumptions.assumeFalse(SystemInfo.isWindows, "Unix shells do not work on Windows") + } + + val shellPath = getShellPath(shellType) + if (!withContext(Dispatchers.IO) { shellPath.exists() && shellPath.isExecutable() }) { + when (shellType) { + ShellType.ZSH -> Assumptions.assumeFalse(SystemInfo.isMac, "Zsh is mandatory on mac") + ShellType.BASH -> error("$shellPath not found") + ShellType.FISH -> error("Fish must be ignored") + ShellType.POWERSHELL -> error("Powershell is mandatory on Windows") + } + } + + + val (pythonBinary, venvDirName) = + if (useConda) { + val envDir = venvPath.resolve("some path with spaces") + val sdk = createCondaEnv(condaEnv, envDir).createSdkFromThisEnv(null, emptyList()) + sdkToDelete = sdk + sdk.persist() + ModuleRootModificationUtil.setModuleSdk(moduleFixture.get(), sdk) + Pair(Path(sdk.homePath!!), envDir.toRealPath().pathString) + } + else { + val venv = VirtualEnvReader.Instance.findPythonInPythonRoot(tempDirFixture.get())!! + Pair(venv, tempDirFixture.get().name) + } // binary might be like ~8.3, we need to expand it as venv might report both val pythonBinaryReal = try { @@ -69,34 +135,49 @@ class PyVirtualEnvTerminalCustomizerTest { catch (_: IOException) { pythonBinary } - val shellOptions = getShellStartupOptions() + val shellOptions = getShellStartupOptions(pythonBinary.parent, shellType) val command = shellOptions.shellCommand!! val exe = command[0] val args = if (command.size == 1) emptyList() else command.subList(1, command.size) val execOptions = EelExecApi.ExecuteProcessOptions.Builder(exe) .args(args) - .env(shellOptions.envVariables) + .env(shellOptions.envVariables + mapOf(Pair("TERM", "dumb"))) + // Unix shells do not activate with out tty + .ptyOrStdErrSettings(if (SystemInfo.isWindows) null else EelExecApi.Pty(100, 100, true)) .build() val process = localEel.exec.execute(execOptions).getOrThrow() try { - val stderr = launch { - val error = process.stderr.readWholeText().getOrThrow() - Assertions.assertTrue(error.isEmpty(), "Unexpected text in stderr: $error") + val stderr = async { + process.stderr.readWholeText().getOrThrow() } val stdout = async { - process.stdout.readWholeText().getOrThrow().split("\n").map { it.trim() } + val separator = if (SystemInfo.isWindows) "\n" else "\r\n" + process.stdout.readWholeText().getOrThrow().split(separator).map { it.trim() } } - val where = PathEnvironmentVariableUtil.findInPath(WHERE_EXE)?.toString() ?: WHERE_EXE - process.stdin.sendWholeText("$where python\nexit\n").getOrThrow() - stderr.join() - val output = stdout.await() + // tool -- where.exe Windows, "type(1)" **nix + // "$TOOL python" returns $PREFIX [path-to-python] $POSTFIX + val (locateTool, prefix, postfix) = if (SystemInfo.isWindows) { + Triple(PathEnvironmentVariableUtil.findInPath("where.exe")?.toString() ?: "where.exe", "", "") + } + else { + // zsh wraps text in '' + val quot = if (shellType == ShellType.ZSH) "'" else "" + Triple("type", "python is $quot", quot) + } + process.stdin.sendWholeText("$locateTool python\nexit\n").getOrThrow() + val error = stderr.await() - assertThat("We ran `$where`, so we there should be python path", output, - anyOf(hasItem(pythonBinary.pathString), hasItem(pythonBinaryReal.pathString))) - val vendDirName = tempDirFixture.get().name - assertThat("There must be a line with ($vendDirName)", output, hasItem(containsString("($vendDirName)"))) + Assertions.assertTrue(error.isEmpty(), "Unexpected text in stderr: $error") + val output = stdout.await() + fileLogger().info("Output was $output") + + assertThat("We ran `$locateTool`, so we there should be python path", output, + anyOf(hasItem(prefix + pythonBinary.pathString + postfix), hasItem(prefix + pythonBinaryReal.pathString + postfix))) + if (SystemInfo.isWindows) { + assertThat("There must be a line with ($venvDirName)", output, hasItem(containsString("($venvDirName)"))) + } process.exitCode.await() } @@ -107,19 +188,19 @@ class PyVirtualEnvTerminalCustomizerTest { } } - private fun getShellStartupOptions(): ShellStartupOptions { + private fun getShellStartupOptions(workDir: Path, shellType: ShellType): ShellStartupOptions { val sut = PyVirtualEnvTerminalCustomizer() val env = mutableMapOf() val command = sut.customizeCommandAndEnvironment( projectFixture.get(), - tempDirFixture.get().pathString, - arrayOf(powerShell.pathString), + workDir.pathString, + arrayOf(getShellPath(shellType).pathString), env) val options = ShellStartupOptions.Builder() .envVariables(env) .shellCommand(command.toList()) - .shellIntegration(ShellIntegration(ShellType.POWERSHELL, null)) + .shellIntegration(ShellIntegration(shellType, null)) .build() return LocalShellIntegrationInjector.injectShellIntegration(options, false, false) }