Skip to content

Commit 91f584f

Browse files
authored
fix(amazonq): enable agentic chat in AL2 (aws#7212)
## Problem LSP depends on node 18 which depends on GLIBC >=2.28. Amzn al2 instances do not have GLIBC >=2.28. ## Solution Use the patch of glibc if it is available. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 81fa3b1 commit 91f584f

File tree

5 files changed

+47
-17
lines changed

5 files changed

+47
-17
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Enable Amazon Q LSP in AL2 instances"
4+
}

packages/amazonq/src/extension.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
maybeShowMinVscodeWarning,
3434
Experiments,
3535
isSageMaker,
36+
isAmazonInternalOs,
3637
} from 'aws-core-vscode/shared'
3738
import { ExtStartUpSources } from 'aws-core-vscode/telemetry'
3839
import { VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils'
@@ -43,7 +44,7 @@ import { registerCommands } from './commands'
4344
import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat'
4445
import { activate as activateAmazonqLsp } from './lsp/activation'
4546
import { activate as activateInlineCompletion } from './app/inline/activation'
46-
import { isAmazonInternalOs } from 'aws-core-vscode/shared'
47+
import { hasGlibcPatch } from './lsp/client'
4748

4849
export const amazonQContextPrefix = 'amazonq'
4950

@@ -122,9 +123,10 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is
122123
await activateCodeWhisperer(extContext as ExtContext)
123124
if (
124125
(Experiments.instance.get('amazonqLSP', true) || Auth.instance.isInternalAmazonUser()) &&
125-
!isAmazonInternalOs()
126+
(!isAmazonInternalOs() || (await hasGlibcPatch()))
126127
) {
127128
// start the Amazon Q LSP for internal users first
129+
// for AL2, start LSP if glibc patch is found
128130
await activateAmazonqLsp(context)
129131
}
130132
if (!Experiments.instance.get('amazonqLSPInline', false)) {

packages/amazonq/src/lsp/client.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import {
3232
getLogger,
3333
undefinedIfEmpty,
3434
getOptOutPreference,
35+
isAmazonInternalOs,
36+
fs,
3537
} from 'aws-core-vscode/shared'
3638
import { activate } from './chat/activation'
3739
import { AmazonQResourcePaths } from './lspInstaller'
@@ -40,6 +42,10 @@ import { ConfigSection, isValidConfigSection, toAmazonQLSPLogLevel } from './con
4042
const localize = nls.loadMessageBundle()
4143
const logger = getLogger('amazonqLsp.lspClient')
4244

45+
export async function hasGlibcPatch(): Promise<boolean> {
46+
return await fs.exists('/opt/vsc-sysroot/lib64/ld-linux-x86-64.so.2')
47+
}
48+
4349
export async function startLanguageServer(
4450
extensionContext: vscode.ExtensionContext,
4551
resourcePaths: AmazonQResourcePaths
@@ -55,19 +61,33 @@ export async function startLanguageServer(
5561
'--pre-init-encryption',
5662
'--set-credentials-encryption-key',
5763
]
58-
const serverOptions = createServerOptions({
59-
encryptionKey,
60-
executable: resourcePaths.node,
61-
serverModule,
62-
execArgv: argv,
63-
})
6464

6565
const documentSelector = [{ scheme: 'file', language: '*' }]
6666

6767
const clientId = 'amazonq'
6868
const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`)
69+
let executable: string[] = []
70+
// apply the GLIBC 2.28 path to node js runtime binary
71+
if (isAmazonInternalOs() && (await hasGlibcPatch())) {
72+
executable = [
73+
'/opt/vsc-sysroot/lib64/ld-linux-x86-64.so.2',
74+
'--library-path',
75+
'/opt/vsc-sysroot/lib64',
76+
resourcePaths.node,
77+
]
78+
getLogger('amazonqLsp').info(`Patched node runtime with GLIBC to ${executable}`)
79+
} else {
80+
executable = [resourcePaths.node]
81+
}
82+
83+
const serverOptions = createServerOptions({
84+
encryptionKey,
85+
executable: executable,
86+
serverModule,
87+
execArgv: argv,
88+
})
6989

70-
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, argv, logger)
90+
await validateNodeExe(executable, resourcePaths.lsp, argv, logger)
7191

7292
// Options to control the language client
7393
const clientOptions: LanguageClientOptions = {

packages/core/src/amazonq/lsp/lspClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,15 +255,15 @@ export async function activate(extensionContext: ExtensionContext, resourcePaths
255255

256256
const serverOptions = createServerOptions({
257257
encryptionKey: key,
258-
executable: resourcePaths.node,
258+
executable: [resourcePaths.node],
259259
serverModule,
260260
// TODO(jmkeyes): we always use the debug options...?
261261
execArgv: debugOptions.execArgv,
262262
})
263263

264264
const documentSelector = [{ scheme: 'file', language: '*' }]
265265

266-
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, debugOptions.execArgv, logger)
266+
await validateNodeExe([resourcePaths.node], resourcePaths.lsp, debugOptions.execArgv, logger)
267267

268268
// Options to control the language client
269269
const clientOptions: LanguageClientOptions = {

packages/core/src/shared/lsp/utils/platform.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ function getEncryptionInit(key: Buffer): string {
2929
/**
3030
* Checks that we can actually run the `node` executable and execute code with it.
3131
*/
32-
export async function validateNodeExe(nodePath: string, lsp: string, args: string[], logger: Logger) {
32+
export async function validateNodeExe(nodePath: string[], lsp: string, args: string[], logger: Logger) {
33+
const bin = nodePath[0]
3334
// Check that we can start `node` by itself.
34-
const proc = new ChildProcess(nodePath, ['-e', 'console.log("ok " + process.version)'], { logging: 'no' })
35+
const proc = new ChildProcess(bin, [...nodePath.slice(1), '-e', 'console.log("ok " + process.version)'], {
36+
logging: 'no',
37+
})
3538
const r = await proc.run()
3639
const ok = r.exitCode === 0 && r.stdout.includes('ok')
3740
if (!ok) {
@@ -41,7 +44,7 @@ export async function validateNodeExe(nodePath: string, lsp: string, args: strin
4144
}
4245

4346
// Check that we can start `node …/lsp.js --stdio …`.
44-
const lspProc = new ChildProcess(nodePath, [lsp, ...args], { logging: 'no' })
47+
const lspProc = new ChildProcess(bin, [...nodePath.slice(1), lsp, ...args], { logging: 'no' })
4548
try {
4649
// Start asynchronously (it never stops; we need to stop it below).
4750
lspProc.run().catch((e) => logger.error('failed to run: %s', lspProc.toString(false, true)))
@@ -80,16 +83,17 @@ export function createServerOptions({
8083
execArgv,
8184
}: {
8285
encryptionKey: Buffer
83-
executable: string
86+
executable: string[]
8487
serverModule: string
8588
execArgv: string[]
8689
}) {
8790
return async () => {
88-
const args = [serverModule, ...execArgv]
91+
const bin = executable[0]
92+
const args = [...executable.slice(1), serverModule, ...execArgv]
8993
if (isDebugInstance()) {
9094
args.unshift('--inspect=6080')
9195
}
92-
const lspProcess = new ChildProcess(executable, args)
96+
const lspProcess = new ChildProcess(bin, args)
9397

9498
// this is a long running process, awaiting it will never resolve
9599
void lspProcess.run()

0 commit comments

Comments
 (0)