Skip to content

Commit bf9bfb1

Browse files
committed
Add codeQL.model.packName setting
1 parent 4dc126d commit bf9bfb1

6 files changed

Lines changed: 190 additions & 54 deletions

File tree

extensions/ql-vscode/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,11 @@
470470
"type": "string",
471471
"default": ".github/codeql/extensions/${name}-${language}",
472472
"markdownDescription": "Location for newly created CodeQL model packs. The location can be either absolute or relative. If relative, it is relative to the workspace root.\n\nThe following variables are supported:\n* **${database}** - the name of the database\n* **${language}** - the name of the language\n* **${name}** - the name of the GitHub repository, or the name of the database if the database was not downloaded from GitHub\n* **${owner}** - the owner of the GitHub repository, or an empty string if the database was not downloaded from GitHub"
473+
},
474+
"codeQL.model.packName": {
475+
"type": "string",
476+
"default": "${owner}/${name}-${language}",
477+
"markdownDescription": "Name of newly created CodeQL model packs. If the result is not a valid pack name, it will be converted to a valid pack name.\n\nThe following variables are supported:\n* **${database}** - the name of the database\n* **${language}** - the name of the language\n* **${name}** - the name of the GitHub repository, or the name of the database if the database was not downloaded from GitHub\n* **${owner}** - the owner of the GitHub repository, or an empty string if the database was not downloaded from GitHub"
473478
}
474479
}
475480
},

extensions/ql-vscode/src/config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ const LLM_GENERATION_DEV_ENDPOINT = new Setting(
736736
);
737737
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
738738
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
739+
const MODEL_PACK_NAME = new Setting("packName", MODEL_SETTING);
739740
const ENABLE_PYTHON = new Setting("enablePython", MODEL_SETTING);
740741
const ENABLE_ACCESS_PATH_SUGGESTIONS = new Setting(
741742
"enableAccessPathSuggestions",
@@ -757,6 +758,7 @@ export interface ModelConfig {
757758
languageId: string,
758759
variables: ModelConfigPackVariables,
759760
): string;
761+
getPackName(languageId: string, variables: ModelConfigPackVariables): string;
760762
enablePython: boolean;
761763
enableAccessPathSuggestions: boolean;
762764
}
@@ -810,6 +812,18 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
810812
);
811813
}
812814

815+
public getPackName(
816+
languageId: string,
817+
variables: ModelConfigPackVariables,
818+
): string {
819+
return substituteConfigVariables(
820+
MODEL_PACK_NAME.getValue<string>({
821+
languageId,
822+
}),
823+
variables,
824+
);
825+
}
826+
813827
public get enablePython(): boolean {
814828
return !!ENABLE_PYTHON.getValue<boolean>();
815829
}

extensions/ql-vscode/src/model-editor/extension-pack-name.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@ export function formatPackName(packName: ExtensionPackName): string {
1515
return `${packName.scope}/${packName.name}`;
1616
}
1717

18-
export function autoNameExtensionPack(
19-
name: string,
20-
language: string,
21-
): ExtensionPackName | undefined {
22-
let packName = `${name}-${language}`;
18+
export function sanitizePackName(userPackName: string): ExtensionPackName {
19+
let packName = userPackName;
2320
if (!packName.includes("/")) {
2421
packName = `pack/${packName}`;
2522
}

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import type { NotificationLogger } from "../common/logging";
1515
import { showAndLogErrorMessage } from "../common/logging";
1616
import type { ModelConfig, ModelConfigPackVariables } from "../config";
1717
import type { ExtensionPackName } from "./extension-pack-name";
18-
import { autoNameExtensionPack, formatPackName } from "./extension-pack-name";
18+
import {
19+
validatePackName,
20+
sanitizePackName,
21+
formatPackName,
22+
} from "./extension-pack-name";
1923
import {
2024
ensurePackLocationIsInWorkspaceFolder,
2125
packLocationToAbsolute,
@@ -80,15 +84,20 @@ export async function pickExtensionPack(
8084

8185
await ensurePackLocationIsInWorkspaceFolder(packPath, modelConfig, logger);
8286

83-
// Generate the name of the extension pack
84-
const packName = autoNameExtensionPack(
85-
databaseItem.name,
87+
const userPackName = modelConfig.getPackName(
8688
databaseItem.language,
89+
getModelConfigPackVariables(databaseItem),
8790
);
88-
if (!packName) {
91+
92+
// Generate the name of the extension pack
93+
const packName = sanitizePackName(userPackName);
94+
95+
// Validate that the name isn't too long etc.
96+
const packNameError = validatePackName(formatPackName(packName));
97+
if (packNameError) {
8998
void showAndLogErrorMessage(
9099
logger,
91-
`Could not automatically name extension pack for database ${databaseItem.name}`,
100+
`Invalid model pack name '${formatPackName(packName)}' for database ${databaseItem.name}: ${packNameError}`,
92101
);
93102

94103
return undefined;

extensions/ql-vscode/test/unit-tests/model-editor/extension-pack-name.test.ts

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,69 @@
11
import {
2-
autoNameExtensionPack,
2+
sanitizePackName,
33
formatPackName,
44
parsePackName,
55
validatePackName,
66
} from "../../../src/model-editor/extension-pack-name";
77

8-
describe("autoNameExtensionPack", () => {
8+
describe("sanitizePackName", () => {
99
const testCases: Array<{
1010
name: string;
11-
language: string;
1211
expected: string;
1312
}> = [
1413
{
15-
name: "github/vscode-codeql",
16-
language: "javascript",
14+
name: "github/vscode-codeql-javascript",
1715
expected: "github/vscode-codeql-javascript",
1816
},
1917
{
20-
name: "vscode-codeql",
21-
language: "a",
18+
name: "vscode-codeql-a",
2219
expected: "pack/vscode-codeql-a",
2320
},
2421
{
25-
name: "b",
26-
language: "java",
22+
name: "b-java",
2723
expected: "pack/b-java",
2824
},
2925
{
30-
name: "a/b",
31-
language: "csharp",
26+
name: "a/b-csharp",
3227
expected: "a/b-csharp",
3328
},
3429
{
35-
name: "-/b",
36-
language: "csharp",
30+
name: "-/b-csharp",
3731
expected: "pack/b-csharp",
3832
},
3933
{
40-
name: "a/b/c/d",
41-
language: "csharp",
34+
name: "a/b/c/d-csharp",
4235
expected: "a/b-c-d-csharp",
4336
},
4437
{
45-
name: "JAVA/CodeQL",
46-
language: "csharp",
38+
name: "JAVA/CodeQL-csharp",
4739
expected: "java/codeql-csharp",
4840
},
4941
{
50-
name: "my new pack",
51-
language: "swift",
42+
name: "my new pack-swift",
5243
expected: "pack/my-new-pack-swift",
5344
},
5445
{
55-
name: "gïthub/vscode-codeql",
56-
language: "javascript",
46+
name: "gïthub/vscode-codeql-javascript",
5747
expected: "gthub/vscode-codeql-javascript",
5848
},
5949
{
60-
name: "a/b-",
61-
language: "csharp",
50+
name: "a/b-csharp",
6251
expected: "a/b-csharp",
6352
},
6453
{
65-
name: "-a-/b",
66-
language: "ruby",
54+
name: "-a-/b-ruby",
6755
expected: "a/b-ruby",
6856
},
6957
{
70-
name: "a/b--d--e-d-",
71-
language: "csharp",
58+
name: "a/b--d--e-d-csharp",
7259
expected: "a/b-d-e-d-csharp",
7360
},
7461
];
7562

7663
test.each(testCases)(
7764
"$name with $language = $expected",
78-
({ name, language, expected }) => {
79-
const result = autoNameExtensionPack(name, language);
65+
({ name, expected }) => {
66+
const result = sanitizePackName(name);
8067
expect(result).not.toBeUndefined();
8168
if (!result) {
8269
return;

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

Lines changed: 138 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ describe("pickExtensionPack", () => {
3737
let workspaceFoldersSpy: jest.SpyInstance;
3838
let additionalPacks: string[];
3939
let workspaceFolder: WorkspaceFolder;
40+
41+
let getPackLocation: jest.MockedFunction<ModelConfig["getPackLocation"]>;
42+
let getPackName: jest.MockedFunction<ModelConfig["getPackName"]>;
4043
let modelConfig: ModelConfig;
4144

4245
const logger = createMockLogger();
@@ -74,13 +77,20 @@ describe("pickExtensionPack", () => {
7477
.spyOn(workspace, "workspaceFolders", "get")
7578
.mockReturnValue([workspaceFolder]);
7679

80+
getPackLocation = jest
81+
.fn()
82+
.mockImplementation(
83+
(language, { name }) => `.github/codeql/extensions/${name}-${language}`,
84+
);
85+
getPackName = jest
86+
.fn()
87+
.mockImplementation(
88+
(language, { name, owner }) => `${owner}/${name}-${language}`,
89+
);
90+
7791
modelConfig = mockedObject<ModelConfig>({
78-
getPackLocation: jest
79-
.fn()
80-
.mockImplementation(
81-
(language, { name }) =>
82-
`.github/codeql/extensions/${name}-${language}`,
83-
),
92+
getPackLocation,
93+
getPackName,
8494
});
8595
});
8696

@@ -178,6 +188,12 @@ describe("pickExtensionPack", () => {
178188
name: "vscode-codeql",
179189
owner: "github",
180190
});
191+
expect(modelConfig.getPackName).toHaveBeenCalledWith("java", {
192+
database: "github/vscode-codeql",
193+
language: "java",
194+
name: "vscode-codeql",
195+
owner: "github",
196+
});
181197

182198
expect(
183199
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
@@ -195,9 +211,7 @@ describe("pickExtensionPack", () => {
195211
it("creates a new extension pack when absolute custom pack location is set in config", async () => {
196212
const packLocation = join(Uri.file(tmpDir).fsPath, "java/ql/lib");
197213

198-
const modelConfig = mockedObject<ModelConfig>({
199-
getPackLocation: jest.fn().mockReturnValue(packLocation),
200-
});
214+
getPackLocation.mockReturnValue(packLocation);
201215

202216
const cliServer = mockCliServer({});
203217

@@ -246,11 +260,7 @@ describe("pickExtensionPack", () => {
246260
it("creates a new extension pack when relative custom pack location is set in config", async () => {
247261
const packLocation = join(Uri.file(tmpDir).fsPath, "java/ql/lib");
248262

249-
const modelConfig = mockedObject<ModelConfig>({
250-
getPackLocation: jest
251-
.fn()
252-
.mockImplementation((language) => `${language}/ql/lib`),
253-
});
263+
getPackLocation.mockImplementation((language) => `${language}/ql/lib`);
254264

255265
const cliServer = mockCliServer({});
256266

@@ -296,6 +306,120 @@ describe("pickExtensionPack", () => {
296306
});
297307
});
298308

309+
it("creates a new extension pack when valid custom pack name is set in config", async () => {
310+
const packName = "codeql/java-extensions";
311+
const packLocation = join(
312+
Uri.file(tmpDir).fsPath,
313+
".github",
314+
"codeql",
315+
"extensions",
316+
"vscode-codeql-java",
317+
);
318+
319+
getPackName.mockImplementation(
320+
(language) => `codeql/${language}-extensions`,
321+
);
322+
323+
const cliServer = mockCliServer({});
324+
325+
expect(
326+
await pickExtensionPack(
327+
cliServer,
328+
databaseItem,
329+
modelConfig,
330+
logger,
331+
progress,
332+
token,
333+
maxStep,
334+
),
335+
).toEqual({
336+
path: packLocation,
337+
yamlPath: join(packLocation, "codeql-pack.yml"),
338+
name: packName,
339+
version: "0.0.0",
340+
language: "java",
341+
extensionTargets: {
342+
"codeql/java-all": "*",
343+
},
344+
dataExtensions: ["models/**/*.yml"],
345+
});
346+
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
347+
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
348+
database: "github/vscode-codeql",
349+
language: "java",
350+
name: "vscode-codeql",
351+
owner: "github",
352+
});
353+
354+
expect(
355+
loadYaml(await readFile(join(packLocation, "codeql-pack.yml"), "utf8")),
356+
).toEqual({
357+
name: packName,
358+
version: "0.0.0",
359+
library: true,
360+
extensionTargets: {
361+
"codeql/java-all": "*",
362+
},
363+
dataExtensions: ["models/**/*.yml"],
364+
});
365+
});
366+
367+
it("creates a new extension pack when invalid custom pack name is set in config", async () => {
368+
const packName = "pack/java-extensions";
369+
const packLocation = join(
370+
Uri.file(tmpDir).fsPath,
371+
".github",
372+
"codeql",
373+
"extensions",
374+
"vscode-codeql-java",
375+
);
376+
377+
getPackName.mockImplementation((language) => `${language} Extensions`);
378+
379+
const cliServer = mockCliServer({});
380+
381+
expect(
382+
await pickExtensionPack(
383+
cliServer,
384+
databaseItem,
385+
modelConfig,
386+
logger,
387+
progress,
388+
token,
389+
maxStep,
390+
),
391+
).toEqual({
392+
path: packLocation,
393+
yamlPath: join(packLocation, "codeql-pack.yml"),
394+
name: packName,
395+
version: "0.0.0",
396+
language: "java",
397+
extensionTargets: {
398+
"codeql/java-all": "*",
399+
},
400+
dataExtensions: ["models/**/*.yml"],
401+
});
402+
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
403+
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
404+
database: "github/vscode-codeql",
405+
language: "java",
406+
name: "vscode-codeql",
407+
owner: "github",
408+
});
409+
410+
expect(
411+
loadYaml(await readFile(join(packLocation, "codeql-pack.yml"), "utf8")),
412+
).toEqual({
413+
name: packName,
414+
version: "0.0.0",
415+
library: true,
416+
extensionTargets: {
417+
"codeql/java-all": "*",
418+
},
419+
dataExtensions: ["models/**/*.yml"],
420+
});
421+
});
422+
299423
it("creates a new extension pack with non-github origin database", async () => {
300424
const databaseItem: Pick<DatabaseItem, "name" | "language" | "origin"> = {
301425
name: "vscode-codeql",

0 commit comments

Comments
 (0)