Skip to content

Commit f3cbf0e

Browse files
authored
config(amazonq): simplify language server configuration options (#6570)
## Problem - lack of centralization for configuration options - no way to configure manifest url without changing the code directly - overriding the language server location will only work right now for amazonqLSP and not workspace context ## Solution - Fix all of the above ## TODO - In another PR i'll do the same thing for the workspace context language server --- - 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 8a297b8 commit f3cbf0e

File tree

8 files changed

+177
-34
lines changed

8 files changed

+177
-34
lines changed

CONTRIBUTING.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,19 @@ Example:
427427
}
428428
```
429429

430+
<a name="amazonqLsp-settings">Overrides specifically for the Amazon Q language server can be set using the `aws.dev.amazonqLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `locationOverride`.</a>
431+
432+
Example:
433+
434+
```json
435+
"aws.dev.amazonqLsp": {
436+
"manifestUrl": "https://custom.url/manifest.json",
437+
"supportedVersions": "4.0.0",
438+
"id": "AmazonQ",
439+
"locationOverride": "/custom/path/to/local/lsp/folder",
440+
}
441+
```
442+
430443
### Environment variables
431444

432445
Environment variables can be used to modify the behaviour of VSCode. The following are environment variables that can be used to configure the extension:
@@ -472,6 +485,10 @@ Unlike the user setting overrides, not all of these environment variables have t
472485

473486
- `__CODEWHISPERER_REGION`: for aws.dev.codewhispererService.region
474487
- `__CODEWHISPERER_ENDPOINT`: for aws.dev.codewhispererService.endpoint
488+
- `__AMAZONQLSP_MANIFEST_URL`: for aws.dev.amazonqLsp.manifestUrl
489+
- `__AMAZONQLSP_SUPPORTED_VERSIONS`: for aws.dev.amazonqLsp.supportedVersions
490+
- `__AMAZONQLSP_ID`: for aws.dev.amazonqLsp.id
491+
- `__AMAZONQLSP_LOCATION_OVERRIDE`: for aws.dev.amazonqLsp.locationOverride
475492

476493
#### Lambda
477494

packages/amazonq/.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"env": {
1515
"SSMDOCUMENT_LANGUAGESERVER_PORT": "6010",
1616
"WEBPACK_DEVELOPER_SERVER": "http://localhost:8080"
17-
// "AWS_LANGUAGE_SERVER_OVERRIDE": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/token-standalone.js",
17+
// "__AMAZONQLSP_LOCATION_OVERRIDE": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/token-standalone.js",
1818
},
1919
"envFile": "${workspaceFolder}/.local.env",
2020
"outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"],

packages/amazonq/src/lsp/config.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { DevSettings, getServiceEnvVarConfig } from 'aws-core-vscode/shared'
7+
8+
export interface AmazonQLspConfig {
9+
manifestUrl: string
10+
supportedVersions: string
11+
id: string
12+
locationOverride?: string
13+
}
14+
15+
export const defaultAmazonQLspConfig: AmazonQLspConfig = {
16+
manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json',
17+
supportedVersions: '^3.1.1',
18+
id: 'AmazonQ', // used for identification in global storage/local disk location. Do not change.
19+
locationOverride: undefined,
20+
}
21+
22+
export function getAmazonQLspConfig(): AmazonQLspConfig {
23+
return {
24+
...defaultAmazonQLspConfig,
25+
...(DevSettings.instance.getServiceConfig('amazonqLsp', {}) as AmazonQLspConfig),
26+
...getServiceEnvVarConfig('amazonqLsp', Object.keys(defaultAmazonQLspConfig)),
27+
}
28+
}

packages/amazonq/src/lsp/lspInstaller.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,34 @@ import {
1616
} from 'aws-core-vscode/shared'
1717
import path from 'path'
1818
import { Range } from 'semver'
19+
import { getAmazonQLspConfig } from './config'
1920

20-
export const manifestURL = 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json'
21-
export const supportedLspServerVersions = new Range('^3.1.1', {
22-
includePrerelease: true,
23-
})
2421
const logger = getLogger('amazonqLsp')
2522

2623
export class AmazonQLSPResolver implements LspResolver {
2724
async resolve(): Promise<LspResolution> {
28-
const overrideLocation = process.env.AWS_LANGUAGE_SERVER_OVERRIDE
29-
if (overrideLocation) {
30-
logger.info(`Using language server override location: ${overrideLocation}`)
31-
void vscode.window.showInformationMessage(`Using language server override location: ${overrideLocation}`)
25+
const { id, manifestUrl, supportedVersions, locationOverride } = getAmazonQLspConfig()
26+
if (locationOverride) {
27+
void vscode.window.showInformationMessage(`Using language server override location: ${locationOverride}`)
3228
return {
33-
assetDirectory: overrideLocation,
29+
assetDirectory: locationOverride,
3430
location: 'override',
3531
version: '0.0.0',
3632
resourcePaths: {
37-
lsp: overrideLocation,
33+
lsp: locationOverride,
3834
node: getNodeExecutableName(),
3935
},
4036
}
4137
}
4238

4339
// "AmazonQ" is shared across toolkits to provide a common access point, don't change it
44-
const name = 'AmazonQ'
45-
const manifest = await new ManifestResolver(manifestURL, name).resolve()
40+
const manifest = await new ManifestResolver(manifestUrl, id).resolve()
4641
const installationResult = await new LanguageServerResolver(
4742
manifest,
48-
name,
49-
supportedLspServerVersions
43+
id,
44+
new Range(supportedVersions, {
45+
includePrerelease: true,
46+
})
5047
).resolve()
5148

5249
const nodePath = path.join(installationResult.assetDirectory, `servers/${getNodeExecutableName()}`)

packages/amazonq/test/e2e/lsp/lspInstaller.test.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
import assert from 'assert'
77
import sinon from 'sinon'
8-
import { AmazonQLSPResolver, manifestURL, supportedLspServerVersions } from '../../../src/lsp/lspInstaller'
8+
import { AmazonQLSPResolver } from '../../../src/lsp/lspInstaller'
99
import {
10+
DevSettings,
1011
fs,
1112
globals,
1213
LanguageServerResolver,
@@ -19,6 +20,7 @@ import * as semver from 'semver'
1920
import { assertTelemetry } from 'aws-core-vscode/test'
2021
import { LspController } from 'aws-core-vscode/amazonq'
2122
import { LanguageServerSetup } from 'aws-core-vscode/telemetry'
23+
import { AmazonQLspConfig, getAmazonQLspConfig } from '../../../src/lsp/config'
2224

2325
function createVersion(version: string) {
2426
return {
@@ -48,9 +50,11 @@ describe('AmazonQLSPInstaller', () => {
4850
// If globalState contains an ETag that is up to date with remote, we won't fetch it resulting in inconsistent behavior.
4951
// Therefore, we clear it temporarily for these tests to ensure consistent behavior.
5052
let manifestStorage: { [key: string]: any }
53+
let lspConfig: AmazonQLspConfig
5154

5255
before(async () => {
5356
manifestStorage = globals.globalState.get(manifestStorageKey) || {}
57+
lspConfig = getAmazonQLspConfig()
5458
})
5559

5660
beforeEach(async () => {
@@ -64,7 +68,7 @@ describe('AmazonQLSPInstaller', () => {
6468
})
6569

6670
afterEach(async () => {
67-
delete process.env.AWS_LANGUAGE_SERVER_OVERRIDE
71+
delete process.env.__AMAZONQLSP_LOCATION_OVERRIDE
6872
sandbox.restore()
6973
await fs.delete(tempDir, {
7074
recursive: true,
@@ -76,9 +80,25 @@ describe('AmazonQLSPInstaller', () => {
7680
})
7781

7882
describe('resolve()', () => {
79-
it('uses AWS_LANGUAGE_SERVER_OVERRIDE', async () => {
83+
it('uses dev setting override', async () => {
84+
const locationOverride = '/custom/path/to/lsp'
85+
const serviceConfigStub = sandbox.stub().returns({
86+
locationOverride,
87+
})
88+
sandbox.stub(DevSettings, 'instance').get(() => ({
89+
getServiceConfig: serviceConfigStub,
90+
}))
91+
92+
const result = await resolver.resolve()
93+
94+
assert.strictEqual(result.assetDirectory, locationOverride)
95+
assert.strictEqual(result.location, 'override')
96+
assert.strictEqual(result.version, '0.0.0')
97+
})
98+
99+
it('uses environment variable override', async () => {
80100
const overridePath = '/custom/path/to/lsp'
81-
process.env.AWS_LANGUAGE_SERVER_OVERRIDE = overridePath
101+
process.env.__AMAZONQLSP_LOCATION_OVERRIDE = overridePath
82102

83103
const result = await resolver.resolve()
84104

@@ -93,14 +113,14 @@ describe('AmazonQLSPInstaller', () => {
93113

94114
assert.ok(download.assetDirectory.startsWith(tempDir))
95115
assert.deepStrictEqual(download.location, 'remote')
96-
assert.ok(semver.satisfies(download.version, supportedLspServerVersions))
116+
assert.ok(semver.satisfies(download.version, lspConfig.supportedVersions))
97117

98118
// Second try - Should see the contents in the cache
99119
const cache = await resolver.resolve()
100120

101121
assert.ok(cache.assetDirectory.startsWith(tempDir))
102122
assert.deepStrictEqual(cache.location, 'cache')
103-
assert.ok(semver.satisfies(cache.version, supportedLspServerVersions))
123+
assert.ok(semver.satisfies(cache.version, lspConfig.supportedVersions))
104124

105125
/**
106126
* Always make sure the latest version is one patch higher. This stops a problem
@@ -135,7 +155,7 @@ describe('AmazonQLSPInstaller', () => {
135155

136156
assert.ok(fallback.assetDirectory.startsWith(tempDir))
137157
assert.deepStrictEqual(fallback.location, 'fallback')
138-
assert.ok(semver.satisfies(fallback.version, supportedLspServerVersions))
158+
assert.ok(semver.satisfies(fallback.version, lspConfig.supportedVersions))
139159

140160
/* First Try Telemetry
141161
getManifest: remote succeeds
@@ -144,25 +164,25 @@ describe('AmazonQLSPInstaller', () => {
144164
*/
145165
const firstTryTelemetry: Partial<LanguageServerSetup>[] = [
146166
{
147-
id: 'AmazonQ',
167+
id: lspConfig.id,
148168
manifestLocation: 'remote',
149169
languageServerSetupStage: 'getManifest',
150170
result: 'Succeeded',
151171
},
152172
{
153-
id: 'AmazonQ',
173+
id: lspConfig.id,
154174
languageServerLocation: 'cache',
155175
languageServerSetupStage: 'getServer',
156176
result: 'Failed',
157177
},
158178
{
159-
id: 'AmazonQ',
179+
id: lspConfig.id,
160180
languageServerLocation: 'remote',
161181
languageServerSetupStage: 'validate',
162182
result: 'Succeeded',
163183
},
164184
{
165-
id: 'AmazonQ',
185+
id: lspConfig.id,
166186
languageServerLocation: 'remote',
167187
languageServerSetupStage: 'getServer',
168188
result: 'Succeeded',
@@ -176,19 +196,19 @@ describe('AmazonQLSPInstaller', () => {
176196
*/
177197
const secondTryTelemetry: Partial<LanguageServerSetup>[] = [
178198
{
179-
id: 'AmazonQ',
199+
id: lspConfig.id,
180200
manifestLocation: 'remote',
181201
languageServerSetupStage: 'getManifest',
182202
result: 'Failed',
183203
},
184204
{
185-
id: 'AmazonQ',
205+
id: lspConfig.id,
186206
manifestLocation: 'cache',
187207
languageServerSetupStage: 'getManifest',
188208
result: 'Succeeded',
189209
},
190210
{
191-
id: 'AmazonQ',
211+
id: lspConfig.id,
192212
languageServerLocation: 'cache',
193213
languageServerSetupStage: 'getServer',
194214
result: 'Succeeded',
@@ -202,19 +222,19 @@ describe('AmazonQLSPInstaller', () => {
202222
*/
203223
const thirdTryTelemetry: Partial<LanguageServerSetup>[] = [
204224
{
205-
id: 'AmazonQ',
225+
id: lspConfig.id,
206226
languageServerLocation: 'cache',
207227
languageServerSetupStage: 'getServer',
208228
result: 'Failed',
209229
},
210230
{
211-
id: 'AmazonQ',
231+
id: lspConfig.id,
212232
languageServerLocation: 'remote',
213233
languageServerSetupStage: 'getServer',
214234
result: 'Failed',
215235
},
216236
{
217-
id: 'AmazonQ',
237+
id: lspConfig.id,
218238
languageServerLocation: 'fallback',
219239
languageServerSetupStage: 'getServer',
220240
result: 'Succeeded',
@@ -227,7 +247,7 @@ describe('AmazonQLSPInstaller', () => {
227247
})
228248

229249
it('resolves release candidiates', async () => {
230-
const original = new ManifestResolver(manifestURL, 'AmazonQ').resolve()
250+
const original = new ManifestResolver(lspConfig.manifestUrl, lspConfig.id).resolve()
231251
sandbox.stub(ManifestResolver.prototype, 'resolve').callsFake(async () => {
232252
const originalManifest = await original
233253

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import { DevSettings } from 'aws-core-vscode/shared'
8+
import sinon from 'sinon'
9+
import { defaultAmazonQLspConfig, getAmazonQLspConfig } from '../../../../src/lsp/config'
10+
11+
describe('getAmazonQLspConfig', () => {
12+
let sandbox: sinon.SinonSandbox
13+
let serviceConfigStub: sinon.SinonStub
14+
const settingConfig = {
15+
manifestUrl: 'https://custom.url/manifest.json',
16+
supportedVersions: '4.0.0',
17+
id: 'AmazonQSetting',
18+
locationOverride: '/custom/path',
19+
}
20+
21+
beforeEach(() => {
22+
sandbox = sinon.createSandbox()
23+
24+
serviceConfigStub = sandbox.stub()
25+
sandbox.stub(DevSettings, 'instance').get(() => ({
26+
getServiceConfig: serviceConfigStub,
27+
}))
28+
})
29+
30+
afterEach(() => {
31+
sandbox.restore()
32+
})
33+
34+
it('uses default config', () => {
35+
serviceConfigStub.returns({})
36+
const config = getAmazonQLspConfig()
37+
assert.deepStrictEqual(config, defaultAmazonQLspConfig)
38+
})
39+
40+
it('overrides location', () => {
41+
const locationOverride = '/custom/path/to/lsp'
42+
serviceConfigStub.returns({ locationOverride })
43+
44+
const config = getAmazonQLspConfig()
45+
assert.deepStrictEqual(config, {
46+
...defaultAmazonQLspConfig,
47+
locationOverride,
48+
})
49+
})
50+
51+
it('overrides default settings', () => {
52+
serviceConfigStub.returns(settingConfig)
53+
54+
const config = getAmazonQLspConfig()
55+
assert.deepStrictEqual(config, settingConfig)
56+
})
57+
58+
it('environment variable takes precedence over settings', () => {
59+
const envConfig = {
60+
manifestUrl: 'https://another-custom.url/manifest.json',
61+
supportedVersions: '5.1.1',
62+
id: 'AmazonQEnv',
63+
locationOverride: '/some/new/custom/path',
64+
}
65+
66+
process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl
67+
process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions
68+
process.env.__AMAZONQLSP_ID = envConfig.id
69+
process.env.__AMAZONQLSP_LOCATION_OVERRIDE = envConfig.locationOverride
70+
71+
serviceConfigStub.returns(settingConfig)
72+
73+
const config = getAmazonQLspConfig()
74+
assert.deepStrictEqual(config, {
75+
...defaultAmazonQLspConfig,
76+
...envConfig,
77+
})
78+
})
79+
})

packages/core/src/shared/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export * from './extensionUtilities'
1818
export * from './extensionStartup'
1919
export { RegionProvider } from './regions/regionProvider'
2020
export { Commands } from './vscode/commands2'
21-
export { getMachineId } from './vscode/env'
21+
export { getMachineId, getServiceEnvVarConfig } from './vscode/env'
2222
export { getLogger } from './logger/logger'
2323
export { activateExtension, openUrl } from './utilities/vsCodeUtils'
2424
export { waitUntil, sleep, Timeout } from './utilities/timeoutUtils'

0 commit comments

Comments
 (0)