Skip to content

Commit 2befa25

Browse files
authored
fix(lsp): verify that nodejs runs (again) #7076
Problem: The logic added in a264377 wasn't added in the other "lsp startup" module. Solution: Add it to the new module also.
1 parent 460321a commit 2befa25

File tree

3 files changed

+73
-60
lines changed

3 files changed

+73
-60
lines changed

packages/amazonq/src/lsp/client.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,21 @@ import { InlineCompletionManager } from '../app/inline/completion'
1111
import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth'
1212
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
1313
import { ConnectionMetadata } from '@aws/language-server-runtimes/protocol'
14-
import { Settings, oidcClientName, createServerOptions, globals, Experiments, Commands } from 'aws-core-vscode/shared'
14+
import {
15+
Settings,
16+
oidcClientName,
17+
createServerOptions,
18+
globals,
19+
Experiments,
20+
Commands,
21+
validateNodeExe,
22+
getLogger,
23+
} from 'aws-core-vscode/shared'
1524
import { activate } from './chat/activation'
1625
import { AmazonQResourcePaths } from './lspInstaller'
1726

1827
const localize = nls.loadMessageBundle()
28+
const logger = getLogger('amazonqLsp.lspClient')
1929

2030
export async function startLanguageServer(
2131
extensionContext: vscode.ExtensionContext,
@@ -25,24 +35,27 @@ export async function startLanguageServer(
2535

2636
const serverModule = resourcePaths.lsp
2737

38+
const argv = [
39+
'--nolazy',
40+
'--preserve-symlinks',
41+
'--stdio',
42+
'--pre-init-encryption',
43+
'--set-credentials-encryption-key',
44+
]
2845
const serverOptions = createServerOptions({
2946
encryptionKey,
3047
executable: resourcePaths.node,
3148
serverModule,
32-
execArgv: [
33-
'--nolazy',
34-
'--preserve-symlinks',
35-
'--stdio',
36-
'--pre-init-encryption',
37-
'--set-credentials-encryption-key',
38-
],
49+
execArgv: argv,
3950
})
4051

4152
const documentSelector = [{ scheme: 'file', language: '*' }]
4253

4354
const clientId = 'amazonq'
4455
const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`)
4556

57+
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, argv, logger)
58+
4659
// Options to control the language client
4760
const clientOptions: LanguageClientOptions = {
4861
// Register the server for json documents

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

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,13 @@ import { fs } from '../../shared/fs/fs'
3939
import { getLogger } from '../../shared/logger/logger'
4040
import globals from '../../shared/extensionGlobals'
4141
import { ResourcePaths } from '../../shared/lsp/types'
42-
import { createServerOptions } from '../../shared/lsp/utils/platform'
42+
import { createServerOptions, validateNodeExe } from '../../shared/lsp/utils/platform'
4343
import { waitUntil } from '../../shared/utilities/timeoutUtils'
44-
import { ToolkitError } from '../../shared/errors'
45-
import { ChildProcess } from '../../shared/utilities/processUtils'
4644

4745
const localize = nls.loadMessageBundle()
4846

4947
const key = crypto.randomBytes(32)
50-
const logger = getLogger('amazonqLsp.lspClient')
48+
const logger = getLogger('amazonqWorkspaceLsp')
5149

5250
/**
5351
* LspClient manages the API call between VS Code extension and LSP server
@@ -226,53 +224,6 @@ export class LspClient {
226224
}
227225
}
228226

229-
/**
230-
* Checks that we can actually run the `node` executable and execute code with it.
231-
*/
232-
async function validateNodeExe(nodePath: string, lsp: string, args: string[]) {
233-
// Check that we can start `node` by itself.
234-
const proc = new ChildProcess(nodePath, ['-e', 'console.log("ok " + process.version)'], { logging: 'no' })
235-
const r = await proc.run()
236-
const ok = r.exitCode === 0 && r.stdout.includes('ok')
237-
if (!ok) {
238-
const msg = `failed to run basic "node -e" test (exitcode=${r.exitCode}): ${proc.toString(false, true)}`
239-
logger.error(msg)
240-
throw new ToolkitError(`amazonqLsp: ${msg}`)
241-
}
242-
243-
// Check that we can start `node …/lsp.js --stdio …`.
244-
const lspProc = new ChildProcess(nodePath, [lsp, ...args], { logging: 'no' })
245-
try {
246-
// Start asynchronously (it never stops; we need to stop it below).
247-
lspProc.run().catch((e) => logger.error('failed to run: %s', lspProc.toString(false, true)))
248-
249-
const ok2 =
250-
!lspProc.stopped &&
251-
(await waitUntil(
252-
async () => {
253-
return lspProc.pid() !== undefined
254-
},
255-
{
256-
timeout: 5000,
257-
interval: 100,
258-
truthy: true,
259-
}
260-
))
261-
const selfExit = await waitUntil(async () => lspProc.stopped, {
262-
timeout: 500,
263-
interval: 100,
264-
truthy: true,
265-
})
266-
if (!ok2 || selfExit) {
267-
throw new ToolkitError(
268-
`amazonqLsp: failed to run (exitcode=${lspProc.exitCode()}): ${lspProc.toString(false, true)}`
269-
)
270-
}
271-
} finally {
272-
lspProc.stop(true)
273-
}
274-
}
275-
276227
/**
277228
* Activates the language server (assumes the LSP server has already been downloaded):
278229
* 1. start LSP server running over IPC protocol.
@@ -312,7 +263,7 @@ export async function activate(extensionContext: ExtensionContext, resourcePaths
312263

313264
const documentSelector = [{ scheme: 'file', language: '*' }]
314265

315-
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, debugOptions.execArgv)
266+
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, debugOptions.execArgv, logger)
316267

317268
// Options to control the language client
318269
const clientOptions: LanguageClientOptions = {

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
*/
55

66
import { ToolkitError } from '../../errors'
7+
import { Logger } from '../../logger/logger'
78
import { ChildProcess } from '../../utilities/processUtils'
9+
import { waitUntil } from '../../utilities/timeoutUtils'
810
import { isDebugInstance } from '../../vscode/env'
911

1012
export function getNodeExecutableName(): string {
@@ -24,6 +26,53 @@ function getEncryptionInit(key: Buffer): string {
2426
return JSON.stringify(request) + '\n'
2527
}
2628

29+
/**
30+
* Checks that we can actually run the `node` executable and execute code with it.
31+
*/
32+
export async function validateNodeExe(nodePath: string, lsp: string, args: string[], logger: Logger) {
33+
// Check that we can start `node` by itself.
34+
const proc = new ChildProcess(nodePath, ['-e', 'console.log("ok " + process.version)'], { logging: 'no' })
35+
const r = await proc.run()
36+
const ok = r.exitCode === 0 && r.stdout.includes('ok')
37+
if (!ok) {
38+
const msg = `failed to run basic "node -e" test (exitcode=${r.exitCode}): ${proc.toString(false, true)}`
39+
logger.error(msg)
40+
throw new ToolkitError(`amazonqLsp: ${msg}`)
41+
}
42+
43+
// Check that we can start `node …/lsp.js --stdio …`.
44+
const lspProc = new ChildProcess(nodePath, [lsp, ...args], { logging: 'no' })
45+
try {
46+
// Start asynchronously (it never stops; we need to stop it below).
47+
lspProc.run().catch((e) => logger.error('failed to run: %s', lspProc.toString(false, true)))
48+
49+
const ok2 =
50+
!lspProc.stopped &&
51+
(await waitUntil(
52+
async () => {
53+
return lspProc.pid() !== undefined
54+
},
55+
{
56+
timeout: 5000,
57+
interval: 100,
58+
truthy: true,
59+
}
60+
))
61+
const selfExit = await waitUntil(async () => lspProc.stopped, {
62+
timeout: 500,
63+
interval: 100,
64+
truthy: true,
65+
})
66+
if (!ok2 || selfExit) {
67+
throw new ToolkitError(
68+
`amazonqLsp: failed to run (exitcode=${lspProc.exitCode()}): ${lspProc.toString(false, true)}`
69+
)
70+
}
71+
} finally {
72+
lspProc.stop(true)
73+
}
74+
}
75+
2776
export function createServerOptions({
2877
encryptionKey,
2978
executable,

0 commit comments

Comments
 (0)