Skip to content

Commit 70afbcf

Browse files
committed
config(amazonq): simplify language server configuration options
1 parent 79ab9f8 commit 70afbcf

File tree

7 files changed

+159
-30
lines changed

7 files changed

+159
-30
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 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": "AmazonQSetting",
439+
"locationOverride": "/custom/path",
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/src/lsp/config.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
// Environment variable overrides
27+
...getServiceEnvVarConfig('amazonqLsp', Object.keys(defaultAmazonQLspConfig)),
28+
}
29+
}

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: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
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 {
1010
fs,
1111
globals,
@@ -19,6 +19,7 @@ import * as semver from 'semver'
1919
import { assertTelemetry } from 'aws-core-vscode/test'
2020
import { LspController } from 'aws-core-vscode/amazonq'
2121
import { LanguageServerSetup } from 'aws-core-vscode/telemetry'
22+
import { AmazonQLspConfig, getAmazonQLspConfig } from '../../../src/lsp/config'
2223

2324
function createVersion(version: string) {
2425
return {
@@ -48,9 +49,11 @@ describe('AmazonQLSPInstaller', () => {
4849
// If globalState contains an ETag that is up to date with remote, we won't fetch it resulting in inconsistent behavior.
4950
// Therefore, we clear it temporarily for these tests to ensure consistent behavior.
5051
let manifestStorage: { [key: string]: any }
52+
let lspConfig: AmazonQLspConfig
5153

5254
before(async () => {
5355
manifestStorage = globals.globalState.get(manifestStorageKey) || {}
56+
lspConfig = getAmazonQLspConfig()
5457
})
5558

5659
beforeEach(async () => {
@@ -93,14 +96,14 @@ describe('AmazonQLSPInstaller', () => {
9396

9497
assert.ok(download.assetDirectory.startsWith(tempDir))
9598
assert.deepStrictEqual(download.location, 'remote')
96-
assert.ok(semver.satisfies(download.version, supportedLspServerVersions))
99+
assert.ok(semver.satisfies(download.version, lspConfig.supportedVersions))
97100

98101
// Second try - Should see the contents in the cache
99102
const cache = await resolver.resolve()
100103

101104
assert.ok(cache.assetDirectory.startsWith(tempDir))
102105
assert.deepStrictEqual(cache.location, 'cache')
103-
assert.ok(semver.satisfies(cache.version, supportedLspServerVersions))
106+
assert.ok(semver.satisfies(cache.version, lspConfig.supportedVersions))
104107

105108
/**
106109
* Always make sure the latest version is one patch higher. This stops a problem
@@ -135,7 +138,7 @@ describe('AmazonQLSPInstaller', () => {
135138

136139
assert.ok(fallback.assetDirectory.startsWith(tempDir))
137140
assert.deepStrictEqual(fallback.location, 'fallback')
138-
assert.ok(semver.satisfies(fallback.version, supportedLspServerVersions))
141+
assert.ok(semver.satisfies(fallback.version, lspConfig.supportedVersions))
139142

140143
/* First Try Telemetry
141144
getManifest: remote succeeds
@@ -144,25 +147,25 @@ describe('AmazonQLSPInstaller', () => {
144147
*/
145148
const firstTryTelemetry: Partial<LanguageServerSetup>[] = [
146149
{
147-
id: 'AmazonQ',
150+
id: lspConfig.id,
148151
manifestLocation: 'remote',
149152
languageServerSetupStage: 'getManifest',
150153
result: 'Succeeded',
151154
},
152155
{
153-
id: 'AmazonQ',
156+
id: lspConfig.id,
154157
languageServerLocation: 'cache',
155158
languageServerSetupStage: 'getServer',
156159
result: 'Failed',
157160
},
158161
{
159-
id: 'AmazonQ',
162+
id: lspConfig.id,
160163
languageServerLocation: 'remote',
161164
languageServerSetupStage: 'validate',
162165
result: 'Succeeded',
163166
},
164167
{
165-
id: 'AmazonQ',
168+
id: lspConfig.id,
166169
languageServerLocation: 'remote',
167170
languageServerSetupStage: 'getServer',
168171
result: 'Succeeded',
@@ -176,19 +179,19 @@ describe('AmazonQLSPInstaller', () => {
176179
*/
177180
const secondTryTelemetry: Partial<LanguageServerSetup>[] = [
178181
{
179-
id: 'AmazonQ',
182+
id: lspConfig.id,
180183
manifestLocation: 'remote',
181184
languageServerSetupStage: 'getManifest',
182185
result: 'Failed',
183186
},
184187
{
185-
id: 'AmazonQ',
188+
id: lspConfig.id,
186189
manifestLocation: 'cache',
187190
languageServerSetupStage: 'getManifest',
188191
result: 'Succeeded',
189192
},
190193
{
191-
id: 'AmazonQ',
194+
id: lspConfig.id,
192195
languageServerLocation: 'cache',
193196
languageServerSetupStage: 'getServer',
194197
result: 'Succeeded',
@@ -202,19 +205,19 @@ describe('AmazonQLSPInstaller', () => {
202205
*/
203206
const thirdTryTelemetry: Partial<LanguageServerSetup>[] = [
204207
{
205-
id: 'AmazonQ',
208+
id: lspConfig.id,
206209
languageServerLocation: 'cache',
207210
languageServerSetupStage: 'getServer',
208211
result: 'Failed',
209212
},
210213
{
211-
id: 'AmazonQ',
214+
id: lspConfig.id,
212215
languageServerLocation: 'remote',
213216
languageServerSetupStage: 'getServer',
214217
result: 'Failed',
215218
},
216219
{
217-
id: 'AmazonQ',
220+
id: lspConfig.id,
218221
languageServerLocation: 'fallback',
219222
languageServerSetupStage: 'getServer',
220223
result: 'Succeeded',
@@ -227,7 +230,7 @@ describe('AmazonQLSPInstaller', () => {
227230
})
228231

229232
it('resolves release candidiates', async () => {
230-
const original = new ManifestResolver(manifestURL, 'AmazonQ').resolve()
233+
const original = new ManifestResolver(lspConfig.manifestUrl, lspConfig.id).resolve()
231234
sandbox.stub(ManifestResolver.prototype, 'resolve').callsFake(async () => {
232235
const originalManifest = await original
233236

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
26+
// Create the DevSettings mock with the properly typed stub
27+
sandbox.stub(DevSettings, 'instance').get(() => ({
28+
getServiceConfig: serviceConfigStub,
29+
}))
30+
})
31+
32+
afterEach(() => {
33+
sandbox.restore()
34+
})
35+
36+
it('uses default config when no overrides are present', () => {
37+
serviceConfigStub.returns({})
38+
const config = getAmazonQLspConfig()
39+
assert.deepStrictEqual(config, defaultAmazonQLspConfig)
40+
})
41+
42+
it('overrides location', () => {
43+
const locationOverride = '/custom/path/to/lsp'
44+
serviceConfigStub.returns({ locationOverride })
45+
46+
const config = getAmazonQLspConfig()
47+
assert.deepStrictEqual(config, {
48+
...defaultAmazonQLspConfig,
49+
locationOverride,
50+
})
51+
})
52+
53+
it('override default settings', () => {
54+
serviceConfigStub.returns(settingConfig)
55+
56+
const config = getAmazonQLspConfig()
57+
assert.deepStrictEqual(config, settingConfig)
58+
})
59+
60+
it('environment variable takes precedence over settings', () => {
61+
const envConfig = {
62+
manifestUrl: 'https://another-custom.url/manifest.json',
63+
supportedVersions: '5.1.1',
64+
id: 'AmazonQEnv',
65+
locationOverride: '/some/new/custom/path',
66+
}
67+
68+
process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl
69+
process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions
70+
process.env.__AMAZONQLSP_ID = envConfig.id
71+
process.env.__AMAZONQLSP_LOCATION_OVERRIDE = envConfig.locationOverride
72+
73+
serviceConfigStub.returns(settingConfig)
74+
75+
const config = getAmazonQLspConfig()
76+
assert.deepStrictEqual(config, {
77+
...defaultAmazonQLspConfig,
78+
...envConfig,
79+
})
80+
})
81+
})

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'

packages/core/src/shared/settings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,7 @@ const devSettings = {
757757
endpoints: Record(String, String),
758758
codecatalystService: Record(String, String),
759759
codewhispererService: Record(String, String),
760+
amazonqLsp: Record(String, String),
760761
ssoCacheDirectory: String,
761762
autofillStartUrl: String,
762763
webAuth: Boolean,
@@ -769,6 +770,7 @@ type ServiceClients = keyof ServiceTypeMap
769770
interface ServiceTypeMap {
770771
codecatalystService: codecatalyst.CodeCatalystConfig
771772
codewhispererService: codewhisperer.CodeWhispererConfig
773+
amazonqLsp: object // type is provided inside of amazon q
772774
}
773775

774776
/**

0 commit comments

Comments
 (0)