diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt index cb33a19b53f..4f4e75acdf8 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt @@ -372,35 +372,35 @@ private class AmazonQServerInstance(private val project: Project, private val cs } val node = if (SystemInfo.isWindows) "node.exe" else "node" - val cmd = GeneralCommandLine( - artifact.resolve(node).toString(), - LspSettings.getInstance().getArtifactPath() ?: artifact.resolve("aws-lsp-codewhisperer.js").toString(), - "--stdio", - "--set-credentials-encryption-key", - ).withEnvironment( - buildMap { - put("NODE_EXTRA_CA_CERTS", extraCaCerts.toAbsolutePath().toString()) - - val proxy = JdkProxyProvider.getInstance().proxySelector.select(qUri) - // log if only socks proxy available - .firstOrNull { it.type() == Proxy.Type.HTTP } - - if (proxy != null) { - val address = proxy.address() - if (address is java.net.InetSocketAddress) { - put( - "HTTPS_PROXY", - URIBuilder("http://${address.hostName}:${address.port}").apply { - val login = HttpConfigurable.getInstance().proxyLogin - if (login != null) { - setUserInfo(login, HttpConfigurable.getInstance().plainProxyPassword) - } - }.build().toASCIIString() - ) + val cmd = NodeExePatcher.patch(artifact.resolve(node)) + .withParameters( + LspSettings.getInstance().getArtifactPath() ?: artifact.resolve("aws-lsp-codewhisperer.js").toString(), + "--stdio", + "--set-credentials-encryption-key", + ).withEnvironment( + buildMap { + put("NODE_EXTRA_CA_CERTS", extraCaCerts.toAbsolutePath().toString()) + + val proxy = JdkProxyProvider.getInstance().proxySelector.select(qUri) + // log if only socks proxy available + .firstOrNull { it.type() == Proxy.Type.HTTP } + + if (proxy != null) { + val address = proxy.address() + if (address is java.net.InetSocketAddress) { + put( + "HTTPS_PROXY", + URIBuilder("http://${address.hostName}:${address.port}").apply { + val login = HttpConfigurable.getInstance().proxyLogin + if (login != null) { + setUserInfo(login, HttpConfigurable.getInstance().plainProxyPassword) + } + }.build().toASCIIString() + ) + } } } - } - ) + ) .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE) launcherHandler = KillableColoredProcessHandler.Silent(cmd) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/NodeExePatcher.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/NodeExePatcher.kt new file mode 100644 index 00000000000..a7d3c8a505d --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/NodeExePatcher.kt @@ -0,0 +1,35 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.amazonq.lsp + +import com.intellij.execution.configurations.GeneralCommandLine +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.core.utils.info +import java.nio.file.Path + +/** + * Hacky nonsense to support old glibc platforms like AL2 + * @see "https://github.com/microsoft/vscode/issues/231623" + * @see "https://github.com/aws/aws-toolkit-vscode/commit/6081f890bdbb91fcd8b60c4cc0abb65b15d4a38d" + */ +object NodeExePatcher { + const val GLIBC_LINKER_VAR = "VSCODE_SERVER_CUSTOM_GLIBC_LINKER" + const val GLIBC_PATH_VAR = "VSCODE_SERVER_CUSTOM_GLIBC_PATH" + + fun patch(node: Path): GeneralCommandLine { + val linker = System.getenv(GLIBC_LINKER_VAR) + val glibc = System.getenv(GLIBC_PATH_VAR) + val nodePath = node.toAbsolutePath().toString() + + return if (!linker.isNullOrEmpty() && !glibc.isNullOrEmpty()) { + GeneralCommandLine(linker) + .withParameters("--library-path", glibc, nodePath) + .also { + getLogger().info { "Using glibc patch: $it" } + } + } else { + GeneralCommandLine(nodePath) + } + } +} diff --git a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/NodeExePatcherTest.kt b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/NodeExePatcherTest.kt new file mode 100644 index 00000000000..ece36e91cf0 --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/NodeExePatcherTest.kt @@ -0,0 +1,35 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.amazonq.lsp + +import com.intellij.execution.configurations.GeneralCommandLine +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test +import software.aws.toolkits.core.rules.EnvironmentVariableHelper +import kotlin.io.path.Path + +class NodeExePatcherTest { + @get:Rule + val envVarHelper = EnvironmentVariableHelper() + + private val pathToNode = Path("/path/to/node").toAbsolutePath().toString() + + @Test + fun `patches if path available`() { + envVarHelper[NodeExePatcher.GLIBC_LINKER_VAR] = "/opt/vsc-sysroot/lib/ld-linux-x86-64.so.2" + envVarHelper[NodeExePatcher.GLIBC_PATH_VAR] = "/opt/vsc-sysroot/lib/" + + assertThat(NodeExePatcher.patch(Path("/path/to/node"))) + .usingComparator(Comparator.comparing { it.commandLineString }) + .isEqualTo(GeneralCommandLine("/opt/vsc-sysroot/lib/ld-linux-x86-64.so.2", "--library-path", "/opt/vsc-sysroot/lib/", pathToNode)) + } + + @Test + fun `noop if no patch available`() { + assertThat(NodeExePatcher.patch(Path("/path/to/node"))) + .usingComparator(Comparator.comparing { it.commandLineString }) + .isEqualTo(GeneralCommandLine(pathToNode)) + } +}