Merge pull request #3543 from github/koesie10/pack-name-config
Add `codeQL.model.packName` setting
This commit is contained in:
@@ -470,6 +470,11 @@
|
||||
"type": "string",
|
||||
"default": ".github/codeql/extensions/${name}-${language}",
|
||||
"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"
|
||||
},
|
||||
"codeQL.model.packName": {
|
||||
"type": "string",
|
||||
"default": "${owner}/${name}-${language}",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -736,6 +736,7 @@ const LLM_GENERATION_DEV_ENDPOINT = new Setting(
|
||||
);
|
||||
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
||||
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
|
||||
const MODEL_PACK_NAME = new Setting("packName", MODEL_SETTING);
|
||||
const ENABLE_PYTHON = new Setting("enablePython", MODEL_SETTING);
|
||||
const ENABLE_ACCESS_PATH_SUGGESTIONS = new Setting(
|
||||
"enableAccessPathSuggestions",
|
||||
@@ -757,6 +758,7 @@ export interface ModelConfig {
|
||||
languageId: string,
|
||||
variables: ModelConfigPackVariables,
|
||||
): string;
|
||||
getPackName(languageId: string, variables: ModelConfigPackVariables): string;
|
||||
enablePython: boolean;
|
||||
enableAccessPathSuggestions: boolean;
|
||||
}
|
||||
@@ -810,6 +812,18 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||
);
|
||||
}
|
||||
|
||||
public getPackName(
|
||||
languageId: string,
|
||||
variables: ModelConfigPackVariables,
|
||||
): string {
|
||||
return substituteConfigVariables(
|
||||
MODEL_PACK_NAME.getValue<string>({
|
||||
languageId,
|
||||
}),
|
||||
variables,
|
||||
);
|
||||
}
|
||||
|
||||
public get enablePython(): boolean {
|
||||
return !!ENABLE_PYTHON.getValue<boolean>();
|
||||
}
|
||||
|
||||
@@ -15,11 +15,19 @@ export function formatPackName(packName: ExtensionPackName): string {
|
||||
return `${packName.scope}/${packName.name}`;
|
||||
}
|
||||
|
||||
export function autoNameExtensionPack(
|
||||
name: string,
|
||||
language: string,
|
||||
): ExtensionPackName | undefined {
|
||||
let packName = `${name}-${language}`;
|
||||
export function sanitizePackName(userPackName: string): ExtensionPackName {
|
||||
let packName = userPackName;
|
||||
|
||||
packName = packName.trim();
|
||||
|
||||
while (packName.startsWith("/")) {
|
||||
packName = packName.slice(1);
|
||||
}
|
||||
|
||||
while (packName.endsWith("/")) {
|
||||
packName = packName.slice(0, -1);
|
||||
}
|
||||
|
||||
if (!packName.includes("/")) {
|
||||
packName = `pack/${packName}`;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ import type { NotificationLogger } from "../common/logging";
|
||||
import { showAndLogErrorMessage } from "../common/logging";
|
||||
import type { ModelConfig, ModelConfigPackVariables } from "../config";
|
||||
import type { ExtensionPackName } from "./extension-pack-name";
|
||||
import { autoNameExtensionPack, formatPackName } from "./extension-pack-name";
|
||||
import {
|
||||
validatePackName,
|
||||
sanitizePackName,
|
||||
formatPackName,
|
||||
} from "./extension-pack-name";
|
||||
import {
|
||||
ensurePackLocationIsInWorkspaceFolder,
|
||||
packLocationToAbsolute,
|
||||
@@ -80,15 +84,20 @@ export async function pickExtensionPack(
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(packPath, modelConfig, logger);
|
||||
|
||||
// Generate the name of the extension pack
|
||||
const packName = autoNameExtensionPack(
|
||||
databaseItem.name,
|
||||
const userPackName = modelConfig.getPackName(
|
||||
databaseItem.language,
|
||||
getModelConfigPackVariables(databaseItem),
|
||||
);
|
||||
if (!packName) {
|
||||
|
||||
// Generate the name of the extension pack
|
||||
const packName = sanitizePackName(userPackName);
|
||||
|
||||
// Validate that the name isn't too long etc.
|
||||
const packNameError = validatePackName(formatPackName(packName));
|
||||
if (packNameError) {
|
||||
void showAndLogErrorMessage(
|
||||
logger,
|
||||
`Could not automatically name extension pack for database ${databaseItem.name}`,
|
||||
`Invalid model pack name '${formatPackName(packName)}' for database ${databaseItem.name}: ${packNameError}`,
|
||||
);
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -1,82 +1,81 @@
|
||||
import {
|
||||
autoNameExtensionPack,
|
||||
sanitizePackName,
|
||||
formatPackName,
|
||||
parsePackName,
|
||||
validatePackName,
|
||||
} from "../../../src/model-editor/extension-pack-name";
|
||||
|
||||
describe("autoNameExtensionPack", () => {
|
||||
describe("sanitizePackName", () => {
|
||||
const testCases: Array<{
|
||||
name: string;
|
||||
language: string;
|
||||
expected: string;
|
||||
}> = [
|
||||
{
|
||||
name: "github/vscode-codeql",
|
||||
language: "javascript",
|
||||
name: "github/vscode-codeql-javascript",
|
||||
expected: "github/vscode-codeql-javascript",
|
||||
},
|
||||
{
|
||||
name: "vscode-codeql",
|
||||
language: "a",
|
||||
name: "vscode-codeql-a",
|
||||
expected: "pack/vscode-codeql-a",
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
language: "java",
|
||||
name: "b-java",
|
||||
expected: "pack/b-java",
|
||||
},
|
||||
{
|
||||
name: "a/b",
|
||||
language: "csharp",
|
||||
name: "a/b-csharp",
|
||||
expected: "a/b-csharp",
|
||||
},
|
||||
{
|
||||
name: "-/b",
|
||||
language: "csharp",
|
||||
name: "-/b-csharp",
|
||||
expected: "pack/b-csharp",
|
||||
},
|
||||
{
|
||||
name: "a/b/c/d",
|
||||
language: "csharp",
|
||||
name: "a/b/c/d-csharp",
|
||||
expected: "a/b-c-d-csharp",
|
||||
},
|
||||
{
|
||||
name: "JAVA/CodeQL",
|
||||
language: "csharp",
|
||||
name: "JAVA/CodeQL-csharp",
|
||||
expected: "java/codeql-csharp",
|
||||
},
|
||||
{
|
||||
name: "my new pack",
|
||||
language: "swift",
|
||||
name: "my new pack-swift",
|
||||
expected: "pack/my-new-pack-swift",
|
||||
},
|
||||
{
|
||||
name: "gïthub/vscode-codeql",
|
||||
language: "javascript",
|
||||
name: "gïthub/vscode-codeql-javascript",
|
||||
expected: "gthub/vscode-codeql-javascript",
|
||||
},
|
||||
{
|
||||
name: "a/b-",
|
||||
language: "csharp",
|
||||
name: "a/b-csharp",
|
||||
expected: "a/b-csharp",
|
||||
},
|
||||
{
|
||||
name: "-a-/b",
|
||||
language: "ruby",
|
||||
name: "-a-/b-ruby",
|
||||
expected: "a/b-ruby",
|
||||
},
|
||||
{
|
||||
name: "a/b--d--e-d-",
|
||||
language: "csharp",
|
||||
name: "a/b--d--e-d-csharp",
|
||||
expected: "a/b-d-e-d-csharp",
|
||||
},
|
||||
{
|
||||
name: "/github/vscode-codeql",
|
||||
expected: "github/vscode-codeql",
|
||||
},
|
||||
{
|
||||
name: "github/vscode-codeql/",
|
||||
expected: "github/vscode-codeql",
|
||||
},
|
||||
{
|
||||
name: "///github/vscode-codeql///",
|
||||
expected: "github/vscode-codeql",
|
||||
},
|
||||
];
|
||||
|
||||
test.each(testCases)(
|
||||
"$name with $language = $expected",
|
||||
({ name, language, expected }) => {
|
||||
const result = autoNameExtensionPack(name, language);
|
||||
({ name, expected }) => {
|
||||
const result = sanitizePackName(name);
|
||||
expect(result).not.toBeUndefined();
|
||||
if (!result) {
|
||||
return;
|
||||
|
||||
@@ -37,6 +37,9 @@ describe("pickExtensionPack", () => {
|
||||
let workspaceFoldersSpy: jest.SpyInstance;
|
||||
let additionalPacks: string[];
|
||||
let workspaceFolder: WorkspaceFolder;
|
||||
|
||||
let getPackLocation: jest.MockedFunction<ModelConfig["getPackLocation"]>;
|
||||
let getPackName: jest.MockedFunction<ModelConfig["getPackName"]>;
|
||||
let modelConfig: ModelConfig;
|
||||
|
||||
const logger = createMockLogger();
|
||||
@@ -74,13 +77,20 @@ describe("pickExtensionPack", () => {
|
||||
.spyOn(workspace, "workspaceFolders", "get")
|
||||
.mockReturnValue([workspaceFolder]);
|
||||
|
||||
getPackLocation = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name }) => `.github/codeql/extensions/${name}-${language}`,
|
||||
);
|
||||
getPackName = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name, owner }) => `${owner}/${name}-${language}`,
|
||||
);
|
||||
|
||||
modelConfig = mockedObject<ModelConfig>({
|
||||
getPackLocation: jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name }) =>
|
||||
`.github/codeql/extensions/${name}-${language}`,
|
||||
),
|
||||
getPackLocation,
|
||||
getPackName,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -178,6 +188,12 @@ describe("pickExtensionPack", () => {
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
expect(modelConfig.getPackName).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
|
||||
@@ -195,9 +211,7 @@ describe("pickExtensionPack", () => {
|
||||
it("creates a new extension pack when absolute custom pack location is set in config", async () => {
|
||||
const packLocation = join(Uri.file(tmpDir).fsPath, "java/ql/lib");
|
||||
|
||||
const modelConfig = mockedObject<ModelConfig>({
|
||||
getPackLocation: jest.fn().mockReturnValue(packLocation),
|
||||
});
|
||||
getPackLocation.mockReturnValue(packLocation);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
@@ -246,11 +260,7 @@ describe("pickExtensionPack", () => {
|
||||
it("creates a new extension pack when relative custom pack location is set in config", async () => {
|
||||
const packLocation = join(Uri.file(tmpDir).fsPath, "java/ql/lib");
|
||||
|
||||
const modelConfig = mockedObject<ModelConfig>({
|
||||
getPackLocation: jest
|
||||
.fn()
|
||||
.mockImplementation((language) => `${language}/ql/lib`),
|
||||
});
|
||||
getPackLocation.mockImplementation((language) => `${language}/ql/lib`);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
@@ -296,6 +306,120 @@ describe("pickExtensionPack", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack when valid custom pack name is set in config", async () => {
|
||||
const packName = "codeql/java-extensions";
|
||||
const packLocation = join(
|
||||
Uri.file(tmpDir).fsPath,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
"vscode-codeql-java",
|
||||
);
|
||||
|
||||
getPackName.mockImplementation(
|
||||
(language) => `codeql/${language}-extensions`,
|
||||
);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
expect(
|
||||
await pickExtensionPack(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
path: packLocation,
|
||||
yamlPath: join(packLocation, "codeql-pack.yml"),
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
language: "java",
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(packLocation, "codeql-pack.yml"), "utf8")),
|
||||
).toEqual({
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack when invalid custom pack name is set in config", async () => {
|
||||
const packName = "pack/java-extensions";
|
||||
const packLocation = join(
|
||||
Uri.file(tmpDir).fsPath,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
"vscode-codeql-java",
|
||||
);
|
||||
|
||||
getPackName.mockImplementation((language) => `${language} Extensions`);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
expect(
|
||||
await pickExtensionPack(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
path: packLocation,
|
||||
yamlPath: join(packLocation, "codeql-pack.yml"),
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
language: "java",
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(packLocation, "codeql-pack.yml"), "utf8")),
|
||||
).toEqual({
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack with non-github origin database", async () => {
|
||||
const databaseItem: Pick<DatabaseItem, "name" | "language" | "origin"> = {
|
||||
name: "vscode-codeql",
|
||||
|
||||
Reference in New Issue
Block a user