Skip to content

Commit 3b693c6

Browse files
committed
fix: generalize cleanup to apply to other installer
1 parent 3b9878f commit 3b693c6

File tree

6 files changed

+132
-134
lines changed

6 files changed

+132
-134
lines changed

packages/amazonq/src/lsp/lspInstaller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getNodeExecutableName,
1515
} from 'aws-core-vscode/shared'
1616
import path from 'path'
17+
import { cleanUpLSPDownloads } from 'aws-core-vscode/amazonq'
1718

1819
const manifestURL = 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json'
1920
export const supportedLspServerVersions = '^2.3.0'
@@ -46,7 +47,7 @@ export class AmazonQLSPResolver implements LspResolver {
4647
const nodePath = path.join(installationResult.assetDirectory, `servers/${getNodeExecutableName()}`)
4748
await fs.chmod(nodePath, 0o755)
4849

49-
// TODO Cleanup old versions of language servers
50+
await cleanUpLSPDownloads(manifest.versions, path.dirname(installationResult.assetDirectory))
5051
return {
5152
...installationResult,
5253
resourcePaths: {

packages/core/src/amazonq/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export {
1717
} from './onboardingPage/walkthrough'
1818
export { LspController } from './lsp/lspController'
1919
export { LspClient } from './lsp/lspClient'
20+
export { cleanUpLSPDownloads } from './lsp/util'
2021
export { api } from './extApi'
2122
export { AmazonQChatViewProvider } from './webview/webView'
2223
export { init as cwChatAppInit } from '../codewhispererChat/app'

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,39 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
import path from 'path'
7+
import { LspVersion } from '../../shared'
68
import { fs } from '../../shared/fs/fs'
9+
import { partition } from '../../shared/utilities/tsUtils'
10+
import { sort } from 'semver'
711

812
export async function getDownloadedVersions(installLocation: string) {
913
return (await fs.readdir(installLocation)).map(([f, _], __) => f)
1014
}
15+
16+
function isDelisted(manifestVersions: LspVersion[], targetVersion: string): boolean {
17+
return manifestVersions.find((v) => v.serverVersion === targetVersion)?.isDelisted ?? false
18+
}
19+
20+
/**
21+
* Delete all delisted versions and keep the two newest versions that remain
22+
* @param manifest
23+
* @param downloadDirectory
24+
*/
25+
export async function cleanUpLSPDownloads(manifestVersions: LspVersion[], downloadDirectory: string): Promise<void> {
26+
const downloadedVersions = await getDownloadedVersions(downloadDirectory)
27+
const [delistedVersions, remainingVersions] = partition(downloadedVersions, (v: string) =>
28+
isDelisted(manifestVersions, v)
29+
)
30+
for (const v of delistedVersions) {
31+
await fs.delete(path.join(downloadDirectory, v), { force: true, recursive: true })
32+
}
33+
34+
if (remainingVersions.length <= 2) {
35+
return
36+
}
37+
38+
for (const v of sort(remainingVersions).slice(0, -2)) {
39+
await fs.delete(path.join(downloadDirectory, v), { force: true, recursive: true })
40+
}
41+
}

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

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
*/
55

66
import path from 'path'
7-
import { LspResolution, LspResolver, LspVersion } from '../../shared/lsp/types'
7+
import { LspResolution, LspResolver } from '../../shared/lsp/types'
88
import { ManifestResolver } from '../../shared/lsp/manifestResolver'
99
import { LanguageServerResolver } from '../../shared/lsp/lspResolver'
10-
import { Range, sort } from 'semver'
10+
import { Range } from 'semver'
1111
import { getNodeExecutableName } from '../../shared/lsp/utils/platform'
1212
import { fs } from '../../shared/fs/fs'
13-
import { partition } from '../../shared/utilities/tsUtils'
14-
import { getDownloadedVersions } from './util'
13+
import { cleanUpLSPDownloads } from './util'
1514

1615
export const lspManifestUrl = 'https://aws-toolkit-language-servers.amazonaws.com/q-context/manifest.json'
1716
// this LSP client in Q extension is only going to work with these LSP server versions
@@ -32,7 +31,7 @@ export class WorkspaceLSPResolver implements LspResolver {
3231
const nodePath = path.join(installationResult.assetDirectory, nodeName)
3332
await fs.chmod(nodePath, 0o755)
3433

35-
await this.cleanUp(manifest.versions, path.dirname(installationResult.assetDirectory))
34+
await cleanUpLSPDownloads(manifest.versions, path.dirname(installationResult.assetDirectory))
3635
return {
3736
...installationResult,
3837
resourcePaths: {
@@ -44,31 +43,4 @@ export class WorkspaceLSPResolver implements LspResolver {
4443
},
4544
}
4645
}
47-
48-
private isDelisted(manifestVersions: LspVersion[], targetVersion: string): boolean {
49-
return manifestVersions.find((v) => v.serverVersion === targetVersion)?.isDelisted ?? false
50-
}
51-
52-
/**
53-
* Delete all delisted versions and keep the two newest versions that remain
54-
* @param manifest
55-
* @param downloadDirectory
56-
*/
57-
async cleanUp(manifestVersions: LspVersion[], downloadDirectory: string): Promise<void> {
58-
const downloadedVersions = await getDownloadedVersions(downloadDirectory)
59-
const [delistedVersions, remainingVersions] = partition(downloadedVersions, (v: string) =>
60-
this.isDelisted(manifestVersions, v)
61-
)
62-
for (const v of delistedVersions) {
63-
await fs.delete(path.join(downloadDirectory, v), { force: true, recursive: true })
64-
}
65-
66-
if (remainingVersions.length <= 2) {
67-
return
68-
}
69-
70-
for (const v of sort(remainingVersions).slice(0, -2)) {
71-
await fs.delete(path.join(downloadDirectory, v), { force: true, recursive: true })
72-
}
73-
}
7446
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { Uri } from 'vscode'
7+
import { fs } from '../../../shared'
8+
import { createTestWorkspaceFolder } from '../../testUtil'
9+
import path from 'path'
10+
import assert from 'assert'
11+
import { cleanUpLSPDownloads } from '../../../amazonq/lsp/util'
12+
13+
async function fakeInstallVersion(version: string, installationDir: string): Promise<void> {
14+
const versionDir = path.join(installationDir, version)
15+
await fs.mkdir(versionDir)
16+
await fs.writeFile(path.join(versionDir, 'file.txt'), 'content')
17+
}
18+
19+
async function fakeInstallVersions(versions: string[], installationDir: string): Promise<void> {
20+
for (const v of versions) {
21+
await fakeInstallVersion(v, installationDir)
22+
}
23+
}
24+
25+
describe('cleanUpLSPDownloads', function () {
26+
let installationDir: Uri
27+
28+
before(async function () {
29+
installationDir = (await createTestWorkspaceFolder()).uri
30+
})
31+
32+
afterEach(async function () {
33+
const files = await fs.readdir(installationDir.fsPath)
34+
for (const [name, _type] of files) {
35+
await fs.delete(path.join(installationDir.fsPath, name), { force: true, recursive: true })
36+
}
37+
})
38+
39+
after(async function () {
40+
await fs.delete(installationDir, { force: true, recursive: true })
41+
})
42+
43+
it('keeps two newest versions', async function () {
44+
await fakeInstallVersions(['1.0.0', '1.0.1', '1.1.1', '2.1.1'], installationDir.fsPath)
45+
await cleanUpLSPDownloads([], installationDir.fsPath)
46+
47+
const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename)
48+
assert.strictEqual(result.length, 2)
49+
assert.ok(result.includes('2.1.1'))
50+
assert.ok(result.includes('1.1.1'))
51+
})
52+
53+
it('deletes delisted versions', async function () {
54+
await fakeInstallVersions(['1.0.0', '1.0.1', '1.1.1', '2.1.1'], installationDir.fsPath)
55+
await cleanUpLSPDownloads([{ serverVersion: '1.1.1', isDelisted: true, targets: [] }], installationDir.fsPath)
56+
57+
const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename)
58+
assert.strictEqual(result.length, 2)
59+
assert.ok(result.includes('2.1.1'))
60+
assert.ok(result.includes('1.0.1'))
61+
})
62+
63+
it('handles case where less than 2 versions are not delisted', async function () {
64+
await fakeInstallVersions(['1.0.0', '1.0.1', '1.1.1', '2.1.1'], installationDir.fsPath)
65+
await cleanUpLSPDownloads(
66+
[
67+
{ serverVersion: '1.1.1', isDelisted: true, targets: [] },
68+
{ serverVersion: '2.1.1', isDelisted: true, targets: [] },
69+
{ serverVersion: '1.0.0', isDelisted: true, targets: [] },
70+
],
71+
installationDir.fsPath
72+
)
73+
74+
const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename)
75+
assert.strictEqual(result.length, 1)
76+
assert.ok(result.includes('1.0.1'))
77+
})
78+
79+
it('handles case where less than 2 versions exist', async function () {
80+
await fakeInstallVersions(['1.0.0'], installationDir.fsPath)
81+
await cleanUpLSPDownloads([], installationDir.fsPath)
82+
83+
const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename)
84+
assert.strictEqual(result.length, 1)
85+
})
86+
87+
it('does not install delisted version when no other option exists', async function () {
88+
await fakeInstallVersions(['1.0.0'], installationDir.fsPath)
89+
await cleanUpLSPDownloads([{ serverVersion: '1.0.0', isDelisted: true, targets: [] }], installationDir.fsPath)
90+
91+
const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename)
92+
assert.strictEqual(result.length, 0)
93+
})
94+
})

packages/core/src/test/amazonq/lsp/workplaceInstaller.test.ts

Lines changed: 0 additions & 101 deletions
This file was deleted.

0 commit comments

Comments
 (0)