Skip to content

Commit aec5ff3

Browse files
committed
Allow overriding the extensions directory
This will allow users to override the default directory in which extension packs are created by using the `codeQL.dataExtensions.extensionsDirectory` setting. This setting can be overriden per language, so the user could create the following configuration to set the extension pack setting for Java only: ```json "[java]": { "codeQL.dataExtensions.extensionsDirectory": "/Users/user/github/vscode-codeql-starter/codeql-custom-queries-java", } ```
1 parent 82cdf03 commit aec5ff3

File tree

3 files changed

+151
-7
lines changed

3 files changed

+151
-7
lines changed

extensions/ql-vscode/src/config.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
EventEmitter,
66
ConfigurationChangeEvent,
77
ConfigurationTarget,
8+
ConfigurationScope,
89
} from "vscode";
910
import { DistributionManager } from "./codeql-cli/distribution";
1011
import { extLogger } from "./common/logging/vscode";
@@ -44,12 +45,12 @@ export class Setting {
4445
}
4546
}
4647

47-
getValue<T>(): T {
48+
getValue<T>(scope?: ConfigurationScope | null): T {
4849
if (this.parent === undefined) {
4950
throw new Error("Cannot get the value of a root setting.");
5051
}
5152
return workspace
52-
.getConfiguration(this.parent.qualifiedName)
53+
.getConfiguration(this.parent.qualifiedName, scope)
5354
.get<T>(this.name)!;
5455
}
5556

@@ -718,6 +719,10 @@ const DISABLE_AUTO_NAME_EXTENSION_PACK = new Setting(
718719
"disableAutoNameExtensionPack",
719720
DATA_EXTENSIONS,
720721
);
722+
const EXTENSIONS_DIRECTORY = new Setting(
723+
"extensionsDirectory",
724+
DATA_EXTENSIONS,
725+
);
721726

722727
export function showLlmGeneration(): boolean {
723728
return !!LLM_GENERATION.getValue<boolean>();
@@ -726,3 +731,9 @@ export function showLlmGeneration(): boolean {
726731
export function disableAutoNameExtensionPack(): boolean {
727732
return !!DISABLE_AUTO_NAME_EXTENSION_PACK.getValue<boolean>();
728733
}
734+
735+
export function getExtensionsDirectory(languageId: string): string | undefined {
736+
return EXTENSIONS_DIRECTORY.getValue<string>({
737+
languageId,
738+
});
739+
}

extensions/ql-vscode/src/data-extensions-editor/extension-pack-picker.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { join } from "path";
22
import { outputFile, pathExists, readFile } from "fs-extra";
33
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
4-
import { CancellationToken, window } from "vscode";
4+
import { CancellationToken, Uri, window } from "vscode";
55
import { CodeQLCliServer, QlpacksInfo } from "../codeql-cli/cli";
66
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
77
import { ProgressCallback } from "../common/vscode/progress";
@@ -10,7 +10,10 @@ import { getQlPackPath, QLPACK_FILENAMES } from "../common/ql";
1010
import { getErrorMessage } from "../common/helpers-pure";
1111
import { ExtensionPack } from "./shared/extension-pack";
1212
import { NotificationLogger, showAndLogErrorMessage } from "../common/logging";
13-
import { disableAutoNameExtensionPack } from "../config";
13+
import {
14+
disableAutoNameExtensionPack,
15+
getExtensionsDirectory,
16+
} from "../config";
1417
import {
1518
autoNameExtensionPack,
1619
ExtensionPackName,
@@ -219,8 +222,14 @@ async function autoCreateExtensionPack(
219222
extensionPacksInfo: QlpacksInfo,
220223
logger: NotificationLogger,
221224
): Promise<ExtensionPack | undefined> {
222-
// Get the extensions directory to create the extension pack in
223-
const extensionsDirectory = await autoPickExtensionsDirectory();
225+
// Get the `codeQL.dataExtensions.extensionsDirectory` setting for the language
226+
const userExtensionsDirectory = getExtensionsDirectory(language);
227+
228+
// If the setting is not set, automatically pick a suitable directory
229+
const extensionsDirectory = userExtensionsDirectory
230+
? Uri.file(userExtensionsDirectory)
231+
: await autoPickExtensionsDirectory();
232+
224233
if (!extensionsDirectory) {
225234
return undefined;
226235
}

extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/extension-pack-picker.test.ts

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {
22
CancellationTokenSource,
3+
ConfigurationScope,
34
QuickPickItem,
45
Uri,
56
window,
67
workspace,
8+
WorkspaceConfiguration as VSCodeWorkspaceConfiguration,
79
WorkspaceFolder,
810
} from "vscode";
911
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
@@ -17,6 +19,7 @@ import * as config from "../../../../src/config";
1719
import { pickExtensionPack } from "../../../../src/data-extensions-editor/extension-pack-picker";
1820
import { ExtensionPack } from "../../../../src/data-extensions-editor/shared/extension-pack";
1921
import { createMockLogger } from "../../../__mocks__/loggerMock";
22+
import { vscodeGetConfigurationMock } from "../../test-config";
2023

2124
describe("pickExtensionPack", () => {
2225
let tmpDir: string;
@@ -153,6 +156,31 @@ describe("pickExtensionPack", () => {
153156

154157
it("automatically selects an extension pack", async () => {
155158
disableAutoNameExtensionPackSpy.mockReturnValue(false);
159+
vscodeGetConfigurationMock.mockImplementation(
160+
(
161+
section?: string,
162+
scope?: ConfigurationScope | null,
163+
): VSCodeWorkspaceConfiguration => {
164+
expect(section).toEqual("codeQL.dataExtensions");
165+
expect((scope as any)?.languageId).toEqual("java");
166+
167+
return {
168+
get: (key: string) => {
169+
expect(key).toEqual("extensionsDirectory");
170+
return undefined;
171+
},
172+
has: (key: string) => {
173+
return key === "extensionsDirectory";
174+
},
175+
inspect: () => {
176+
throw new Error("inspect not implemented");
177+
},
178+
update: () => {
179+
throw new Error("update not implemented");
180+
},
181+
};
182+
},
183+
);
156184

157185
const cliServer = mockCliServer(qlPacks);
158186

@@ -167,8 +195,33 @@ describe("pickExtensionPack", () => {
167195
);
168196
});
169197

170-
it("automatically creates an extension pack", async () => {
198+
it("automatically creates an extension pack and selects an extensions directory", async () => {
171199
disableAutoNameExtensionPackSpy.mockReturnValue(false);
200+
vscodeGetConfigurationMock.mockImplementation(
201+
(
202+
section?: string,
203+
scope?: ConfigurationScope | null,
204+
): VSCodeWorkspaceConfiguration => {
205+
expect(section).toEqual("codeQL.dataExtensions");
206+
expect((scope as any)?.languageId).toEqual("java");
207+
208+
return {
209+
get: (key: string) => {
210+
expect(key).toEqual("extensionsDirectory");
211+
return undefined;
212+
},
213+
has: (key: string) => {
214+
return key === "extensionsDirectory";
215+
},
216+
inspect: () => {
217+
throw new Error("inspect not implemented");
218+
},
219+
update: () => {
220+
throw new Error("update not implemented");
221+
},
222+
};
223+
},
224+
);
172225

173226
const tmpDir = await dir({
174227
unsafeCleanup: true,
@@ -237,6 +290,77 @@ describe("pickExtensionPack", () => {
237290
});
238291
});
239292

293+
it("automatically creates an extension pack when extensions directory is set in config", async () => {
294+
disableAutoNameExtensionPackSpy.mockReturnValue(false);
295+
296+
const tmpDir = await dir({
297+
unsafeCleanup: true,
298+
});
299+
300+
const configExtensionsDir = join(
301+
Uri.file(tmpDir.path).fsPath,
302+
"my-custom-extensions-directory",
303+
);
304+
305+
vscodeGetConfigurationMock.mockImplementation(
306+
(
307+
section?: string,
308+
scope?: ConfigurationScope | null,
309+
): VSCodeWorkspaceConfiguration => {
310+
expect(section).toEqual("codeQL.dataExtensions");
311+
expect((scope as any)?.languageId).toEqual("java");
312+
313+
return {
314+
get: (key: string) => {
315+
expect(key).toEqual("extensionsDirectory");
316+
return configExtensionsDir;
317+
},
318+
has: (key: string) => {
319+
return key === "extensionsDirectory";
320+
},
321+
inspect: () => {
322+
throw new Error("inspect not implemented");
323+
},
324+
update: () => {
325+
throw new Error("update not implemented");
326+
},
327+
};
328+
},
329+
);
330+
331+
const newPackDir = join(configExtensionsDir, "vscode-codeql-java");
332+
333+
const cliServer = mockCliServer({});
334+
335+
expect(
336+
await pickExtensionPack(cliServer, databaseItem, logger, progress, token),
337+
).toEqual({
338+
path: newPackDir,
339+
yamlPath: join(newPackDir, "codeql-pack.yml"),
340+
name: "github/vscode-codeql-java",
341+
version: "0.0.0",
342+
extensionTargets: {
343+
"codeql/java-all": "*",
344+
},
345+
dataExtensions: ["models/**/*.yml"],
346+
});
347+
expect(showQuickPickSpy).not.toHaveBeenCalled();
348+
expect(showInputBoxSpy).not.toHaveBeenCalled();
349+
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
350+
351+
expect(
352+
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
353+
).toEqual({
354+
name: "github/vscode-codeql-java",
355+
version: "0.0.0",
356+
library: true,
357+
extensionTargets: {
358+
"codeql/java-all": "*",
359+
},
360+
dataExtensions: ["models/**/*.yml"],
361+
});
362+
});
363+
240364
it("allows cancelling the prompt", async () => {
241365
const cliServer = mockCliServer(qlPacks);
242366

0 commit comments

Comments
 (0)