Remove the disableAutoNameExtensionPack feature flag

This commit is contained in:
Robert
2023-08-31 15:53:41 +01:00
parent 2d033bff57
commit 480fa517ea
5 changed files with 46 additions and 691 deletions

View File

@@ -707,20 +707,12 @@ export function showQueriesPanel(): boolean {
const MODEL_SETTING = new Setting("model", ROOT_SETTING); const MODEL_SETTING = new Setting("model", ROOT_SETTING);
const LLM_GENERATION = new Setting("llmGeneration", MODEL_SETTING); const LLM_GENERATION = new Setting("llmGeneration", MODEL_SETTING);
const DISABLE_AUTO_NAME_EXTENSION_PACK = new Setting(
"disableAutoNameExtensionPack",
MODEL_SETTING,
);
const EXTENSIONS_DIRECTORY = new Setting("extensionsDirectory", MODEL_SETTING); const EXTENSIONS_DIRECTORY = new Setting("extensionsDirectory", MODEL_SETTING);
export function showLlmGeneration(): boolean { export function showLlmGeneration(): boolean {
return !!LLM_GENERATION.getValue<boolean>(); return !!LLM_GENERATION.getValue<boolean>();
} }
export function disableAutoNameExtensionPack(): boolean {
return !!DISABLE_AUTO_NAME_EXTENSION_PACK.getValue<boolean>();
}
export function getExtensionsDirectory(languageId: string): string | undefined { export function getExtensionsDirectory(languageId: string): string | undefined {
return EXTENSIONS_DIRECTORY.getValue<string>({ return EXTENSIONS_DIRECTORY.getValue<string>({
languageId, languageId,

View File

@@ -1,8 +1,8 @@
import { join } from "path"; import { join } from "path";
import { outputFile, pathExists, readFile } from "fs-extra"; import { outputFile, pathExists, readFile } from "fs-extra";
import { dump as dumpYaml, load as loadYaml } from "js-yaml"; import { dump as dumpYaml, load as loadYaml } from "js-yaml";
import { CancellationToken, Uri, window } from "vscode"; import { Uri } from "vscode";
import { CodeQLCliServer, QlpacksInfo } from "../codeql-cli/cli"; import { CodeQLCliServer } from "../codeql-cli/cli";
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
import { ProgressCallback } from "../common/vscode/progress"; import { ProgressCallback } from "../common/vscode/progress";
import { DatabaseItem } from "../databases/local-databases"; import { DatabaseItem } from "../databases/local-databases";
@@ -10,21 +10,13 @@ import { getQlPackPath, QLPACK_FILENAMES } from "../common/ql";
import { getErrorMessage } from "../common/helpers-pure"; import { getErrorMessage } from "../common/helpers-pure";
import { ExtensionPack } from "./shared/extension-pack"; import { ExtensionPack } from "./shared/extension-pack";
import { NotificationLogger, showAndLogErrorMessage } from "../common/logging"; import { NotificationLogger, showAndLogErrorMessage } from "../common/logging";
import { import { getExtensionsDirectory } from "../config";
disableAutoNameExtensionPack,
getExtensionsDirectory,
} from "../config";
import { import {
autoNameExtensionPack, autoNameExtensionPack,
ExtensionPackName, ExtensionPackName,
formatPackName, formatPackName,
parsePackName,
validatePackName,
} from "./extension-pack-name"; } from "./extension-pack-name";
import { import { autoPickExtensionsDirectory } from "./extensions-workspace-folder";
askForWorkspaceFolder,
autoPickExtensionsDirectory,
} from "./extensions-workspace-folder";
const maxStep = 3; const maxStep = 3;
@@ -33,7 +25,6 @@ export async function pickExtensionPack(
databaseItem: Pick<DatabaseItem, "name" | "language">, databaseItem: Pick<DatabaseItem, "name" | "language">,
logger: NotificationLogger, logger: NotificationLogger,
progress: ProgressCallback, progress: ProgressCallback,
token: CancellationToken,
): Promise<ExtensionPack | undefined> { ): Promise<ExtensionPack | undefined> {
progress({ progress({
message: "Resolving extension packs...", message: "Resolving extension packs...",
@@ -52,182 +43,14 @@ export async function pickExtensionPack(
true, true,
); );
if (!disableAutoNameExtensionPack()) {
progress({
message: "Creating extension pack...",
step: 2,
maxStep,
});
return autoCreateExtensionPack(
databaseItem.name,
databaseItem.language,
extensionPacksInfo,
logger,
);
}
if (Object.keys(extensionPacksInfo).length === 0) {
return pickNewExtensionPack(databaseItem, token);
}
const extensionPacks = (
await Promise.all(
Object.entries(extensionPacksInfo).map(async ([name, paths]) => {
if (paths.length !== 1) {
void showAndLogErrorMessage(
logger,
`Extension pack ${name} resolves to multiple paths`,
{
fullMessage: `Extension pack ${name} resolves to multiple paths: ${paths.join(
", ",
)}`,
},
);
return undefined;
}
const path = paths[0];
let extensionPack: ExtensionPack;
try {
extensionPack = await readExtensionPack(path, databaseItem.language);
} catch (e: unknown) {
void showAndLogErrorMessage(
logger,
`Could not read extension pack ${name}`,
{
fullMessage: `Could not read extension pack ${name} at ${path}: ${getErrorMessage(
e,
)}`,
},
);
return undefined;
}
return extensionPack;
}),
)
).filter((info): info is ExtensionPack => info !== undefined);
const extensionPacksForLanguage = extensionPacks.filter(
(pack) =>
pack.extensionTargets[`codeql/${databaseItem.language}-all`] !==
undefined,
);
const options: Array<{
label: string;
description: string | undefined;
detail: string | undefined;
extensionPack: ExtensionPack | null;
}> = extensionPacksForLanguage.map((pack) => ({
label: pack.name,
description: pack.version,
detail: pack.path,
extensionPack: pack,
}));
options.push({
label: "Create new extension pack",
description: undefined,
detail: undefined,
extensionPack: null,
});
progress({ progress({
message: "Choosing extension pack...", message: "Creating extension pack...",
step: 2, step: 2,
maxStep, maxStep,
}); });
const extensionPackOption = await window.showQuickPick(
options,
{
title: "Select extension pack to use",
},
token,
);
if (!extensionPackOption) {
return undefined;
}
if (!extensionPackOption.extensionPack) {
return pickNewExtensionPack(databaseItem, token);
}
return extensionPackOption.extensionPack;
}
async function pickNewExtensionPack(
databaseItem: Pick<DatabaseItem, "name" | "language">,
token: CancellationToken,
): Promise<ExtensionPack | undefined> {
const workspaceFolder = await askForWorkspaceFolder();
if (!workspaceFolder) {
return undefined;
}
const examplePackName = autoNameExtensionPack(
databaseItem.name,
databaseItem.language,
);
const name = await window.showInputBox(
{
title: "Create new extension pack",
prompt: "Enter name of extension pack",
placeHolder: examplePackName
? `e.g. ${formatPackName(examplePackName)}`
: "",
validateInput: async (value: string): Promise<string | undefined> => {
const message = validatePackName(value);
if (message) {
return message;
}
const packName = parsePackName(value);
if (!packName) {
return "Invalid pack name";
}
const packPath = join(workspaceFolder.uri.fsPath, packName.name);
if (await pathExists(packPath)) {
return `A pack already exists at ${packPath}`;
}
return undefined;
},
},
token,
);
if (!name) {
return undefined;
}
const packName = parsePackName(name);
if (!packName) {
return undefined;
}
const packPath = join(workspaceFolder.uri.fsPath, packName.name);
if (await pathExists(packPath)) {
return undefined;
}
return writeExtensionPack(packPath, packName, databaseItem.language);
}
async function autoCreateExtensionPack(
name: string,
language: string,
extensionPacksInfo: QlpacksInfo,
logger: NotificationLogger,
): Promise<ExtensionPack | undefined> {
// Get the `codeQL.model.extensionsDirectory` setting for the language // Get the `codeQL.model.extensionsDirectory` setting for the language
const userExtensionsDirectory = getExtensionsDirectory(language); const userExtensionsDirectory = getExtensionsDirectory(databaseItem.language);
// If the setting is not set, automatically pick a suitable directory // If the setting is not set, automatically pick a suitable directory
const extensionsDirectory = userExtensionsDirectory const extensionsDirectory = userExtensionsDirectory
@@ -239,11 +62,14 @@ async function autoCreateExtensionPack(
} }
// Generate the name of the extension pack // Generate the name of the extension pack
const packName = autoNameExtensionPack(name, language); const packName = autoNameExtensionPack(
databaseItem.name,
databaseItem.language,
);
if (!packName) { if (!packName) {
void showAndLogErrorMessage( void showAndLogErrorMessage(
logger, logger,
`Could not automatically name extension pack for database ${name}`, `Could not automatically name extension pack for database ${databaseItem.name}`,
); );
return undefined; return undefined;
@@ -259,7 +85,7 @@ async function autoCreateExtensionPack(
try { try {
extensionPack = await readExtensionPack( extensionPack = await readExtensionPack(
existingExtensionPackPaths[0], existingExtensionPackPaths[0],
language, databaseItem.language,
); );
} catch (e: unknown) { } catch (e: unknown) {
void showAndLogErrorMessage( void showAndLogErrorMessage(
@@ -309,7 +135,7 @@ async function autoCreateExtensionPack(
return undefined; return undefined;
} }
return writeExtensionPack(packPath, packName, language); return writeExtensionPack(packPath, packName, databaseItem.language);
} }
async function writeExtensionPack( async function writeExtensionPack(

View File

@@ -101,7 +101,7 @@ export class ModelEditorModule extends DisposableObject {
} }
return withProgress( return withProgress(
async (progress, token) => { async (progress) => {
if (!(await this.cliServer.cliConstraints.supportsQlpacksKind())) { if (!(await this.cliServer.cliConstraints.supportsQlpacksKind())) {
void showAndLogErrorMessage( void showAndLogErrorMessage(
this.app.logger, this.app.logger,
@@ -125,7 +125,6 @@ export class ModelEditorModule extends DisposableObject {
db, db,
this.app.logger, this.app.logger,
progress, progress,
token,
); );
if (!modelFile) { if (!modelFile) {
return; return;

View File

@@ -424,7 +424,6 @@ export class ModelEditorView extends AbstractWebview<
addedDatabase, addedDatabase,
this.app.logger, this.app.logger,
progress, progress,
token,
); );
if (!modelFile) { if (!modelFile) {
return; return;

View File

@@ -1,9 +1,6 @@
import { import {
CancellationTokenSource,
ConfigurationScope, ConfigurationScope,
QuickPickItem,
Uri, Uri,
window,
workspace, workspace,
WorkspaceConfiguration as VSCodeWorkspaceConfiguration, WorkspaceConfiguration as VSCodeWorkspaceConfiguration,
WorkspaceFolder, WorkspaceFolder,
@@ -14,8 +11,6 @@ import { join } from "path";
import { dir } from "tmp-promise"; import { dir } from "tmp-promise";
import { QlpacksInfo } from "../../../../src/codeql-cli/cli"; import { QlpacksInfo } from "../../../../src/codeql-cli/cli";
import * as config from "../../../../src/config";
import { pickExtensionPack } from "../../../../src/model-editor/extension-pack-picker"; import { pickExtensionPack } from "../../../../src/model-editor/extension-pack-picker";
import { ExtensionPack } from "../../../../src/model-editor/shared/extension-pack"; import { ExtensionPack } from "../../../../src/model-editor/shared/extension-pack";
import { createMockLogger } from "../../../__mocks__/loggerMock"; import { createMockLogger } from "../../../__mocks__/loggerMock";
@@ -23,11 +18,8 @@ import { vscodeGetConfigurationMock } from "../../test-config";
describe("pickExtensionPack", () => { describe("pickExtensionPack", () => {
let tmpDir: string; let tmpDir: string;
let extensionPackPath: string; const autoExtensionPackName = "github/vscode-codeql-java";
let anotherExtensionPackPath: string;
let autoExtensionPackPath: string; let autoExtensionPackPath: string;
let extensionPack: ExtensionPack;
let anotherExtensionPack: ExtensionPack;
let autoExtensionPack: ExtensionPack; let autoExtensionPack: ExtensionPack;
let qlPacks: QlpacksInfo; let qlPacks: QlpacksInfo;
@@ -36,15 +28,7 @@ describe("pickExtensionPack", () => {
language: "java", language: "java",
}; };
const cancellationTokenSource = new CancellationTokenSource();
const token = cancellationTokenSource.token;
const progress = jest.fn(); const progress = jest.fn();
let showQuickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
let showInputBoxSpy: jest.SpiedFunction<typeof window.showInputBox>;
let disableAutoNameExtensionPackSpy: jest.SpiedFunction<
typeof config.disableAutoNameExtensionPack
>;
let workspaceFoldersSpy: jest.SpyInstance; let workspaceFoldersSpy: jest.SpyInstance;
let additionalPacks: string[]; let additionalPacks: string[];
let workspaceFolder: WorkspaceFolder; let workspaceFolder: WorkspaceFolder;
@@ -59,41 +43,17 @@ describe("pickExtensionPack", () => {
).path; ).path;
// Uri.file(...).fsPath normalizes the filenames so we can properly compare them on Windows // Uri.file(...).fsPath normalizes the filenames so we can properly compare them on Windows
extensionPackPath = Uri.file(join(tmpDir, "my-extension-pack")).fsPath;
anotherExtensionPackPath = Uri.file(
join(tmpDir, "another-extension-pack"),
).fsPath;
autoExtensionPackPath = Uri.file(join(tmpDir, "vscode-codeql-java")).fsPath; autoExtensionPackPath = Uri.file(join(tmpDir, "vscode-codeql-java")).fsPath;
qlPacks = { qlPacks = {
"my-extension-pack": [extensionPackPath],
"another-extension-pack": [anotherExtensionPackPath],
"github/vscode-codeql-java": [autoExtensionPackPath], "github/vscode-codeql-java": [autoExtensionPackPath],
}; };
extensionPack = await createMockExtensionPack(
extensionPackPath,
"my-extension-pack",
);
anotherExtensionPack = await createMockExtensionPack(
anotherExtensionPackPath,
"another-extension-pack",
);
autoExtensionPack = await createMockExtensionPack( autoExtensionPack = await createMockExtensionPack(
autoExtensionPackPath, autoExtensionPackPath,
"github/vscode-codeql-java", autoExtensionPackName,
); );
showQuickPickSpy = jest
.spyOn(window, "showQuickPick")
.mockRejectedValue(new Error("Unexpected call to showQuickPick"));
showInputBoxSpy = jest
.spyOn(window, "showInputBox")
.mockRejectedValue(new Error("Unexpected call to showInputBox"));
disableAutoNameExtensionPackSpy = jest
.spyOn(config, "disableAutoNameExtensionPack")
.mockReturnValue(true);
workspaceFolder = { workspaceFolder = {
uri: Uri.file(tmpDir), uri: Uri.file(tmpDir),
name: "codeql-custom-queries-java", name: "codeql-custom-queries-java",
@@ -108,57 +68,7 @@ describe("pickExtensionPack", () => {
.mockReturnValue([workspaceFolder]); .mockReturnValue([workspaceFolder]);
}); });
it("allows choosing an existing extension pack", async () => { it("selects an existing extension pack", async () => {
const cliServer = mockCliServer(qlPacks);
showQuickPickSpy.mockResolvedValueOnce({
label: "my-extension-pack",
extensionPack,
} as QuickPickItem);
expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token),
).toEqual(extensionPack);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showQuickPickSpy).toHaveBeenCalledWith(
[
{
label: "my-extension-pack",
description: "0.0.0",
detail: extensionPackPath,
extensionPack,
},
{
label: "another-extension-pack",
description: "0.0.0",
detail: anotherExtensionPackPath,
extensionPack: anotherExtensionPack,
},
{
label: "github/vscode-codeql-java",
description: "0.0.0",
detail: autoExtensionPackPath,
extensionPack: autoExtensionPack,
},
{
label: expect.stringMatching(/create/i),
extensionPack: null,
},
],
{
title: expect.any(String),
},
token,
);
expect(cliServer.resolveQlpacks).toHaveBeenCalledTimes(1);
expect(cliServer.resolveQlpacks).toHaveBeenCalledWith(
additionalPacks,
true,
);
});
it("automatically selects an extension pack", async () => {
disableAutoNameExtensionPackSpy.mockReturnValue(false);
vscodeGetConfigurationMock.mockImplementation( vscodeGetConfigurationMock.mockImplementation(
( (
section?: string, section?: string,
@@ -188,9 +98,8 @@ describe("pickExtensionPack", () => {
const cliServer = mockCliServer(qlPacks); const cliServer = mockCliServer(qlPacks);
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual(autoExtensionPack); ).toEqual(autoExtensionPack);
expect(showQuickPickSpy).not.toHaveBeenCalled();
expect(cliServer.resolveQlpacks).toHaveBeenCalledTimes(1); expect(cliServer.resolveQlpacks).toHaveBeenCalledTimes(1);
expect(cliServer.resolveQlpacks).toHaveBeenCalledWith( expect(cliServer.resolveQlpacks).toHaveBeenCalledWith(
additionalPacks, additionalPacks,
@@ -198,8 +107,7 @@ describe("pickExtensionPack", () => {
); );
}); });
it("automatically creates an extension pack and selects an extensions directory", async () => { it("creates a new extension pack using default extensions directory", async () => {
disableAutoNameExtensionPackSpy.mockReturnValue(false);
vscodeGetConfigurationMock.mockImplementation( vscodeGetConfigurationMock.mockImplementation(
( (
section?: string, section?: string,
@@ -265,11 +173,11 @@ describe("pickExtensionPack", () => {
const cliServer = mockCliServer({}); const cliServer = mockCliServer({});
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual({ ).toEqual({
path: newPackDir, path: newPackDir,
yamlPath: join(newPackDir, "codeql-pack.yml"), yamlPath: join(newPackDir, "codeql-pack.yml"),
name: "github/vscode-codeql-java", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
language: "java", language: "java",
extensionTargets: { extensionTargets: {
@@ -277,14 +185,12 @@ describe("pickExtensionPack", () => {
}, },
dataExtensions: ["models/**/*.yml"], dataExtensions: ["models/**/*.yml"],
}); });
expect(showQuickPickSpy).not.toHaveBeenCalled();
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(cliServer.resolveQlpacks).toHaveBeenCalled(); expect(cliServer.resolveQlpacks).toHaveBeenCalled();
expect( expect(
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")), loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
).toEqual({ ).toEqual({
name: "github/vscode-codeql-java", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
library: true, library: true,
extensionTargets: { extensionTargets: {
@@ -294,9 +200,7 @@ describe("pickExtensionPack", () => {
}); });
}); });
it("automatically creates an extension pack when extensions directory is set in config", async () => { it("creates a new extension pack when extensions directory is set in config", async () => {
disableAutoNameExtensionPackSpy.mockReturnValue(false);
const tmpDir = await dir({ const tmpDir = await dir({
unsafeCleanup: true, unsafeCleanup: true,
}); });
@@ -337,11 +241,11 @@ describe("pickExtensionPack", () => {
const cliServer = mockCliServer({}); const cliServer = mockCliServer({});
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual({ ).toEqual({
path: newPackDir, path: newPackDir,
yamlPath: join(newPackDir, "codeql-pack.yml"), yamlPath: join(newPackDir, "codeql-pack.yml"),
name: "github/vscode-codeql-java", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
language: "java", language: "java",
extensionTargets: { extensionTargets: {
@@ -349,14 +253,12 @@ describe("pickExtensionPack", () => {
}, },
dataExtensions: ["models/**/*.yml"], dataExtensions: ["models/**/*.yml"],
}); });
expect(showQuickPickSpy).not.toHaveBeenCalled();
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(cliServer.resolveQlpacks).toHaveBeenCalled(); expect(cliServer.resolveQlpacks).toHaveBeenCalled();
expect( expect(
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")), loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
).toEqual({ ).toEqual({
name: "github/vscode-codeql-java", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
library: true, library: true,
extensionTargets: { extensionTargets: {
@@ -366,208 +268,21 @@ describe("pickExtensionPack", () => {
}); });
}); });
it("allows cancelling the prompt", async () => {
const cliServer = mockCliServer(qlPacks);
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token),
).toEqual(undefined);
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
});
it("allows user to create an extension pack when there are no extension packs", async () => {
const cliServer = mockCliServer({});
const tmpDir = await dir({
unsafeCleanup: true,
});
const newPackDir = join(Uri.file(tmpDir.path).fsPath, "new-extension-pack");
showQuickPickSpy.mockResolvedValueOnce({
label: "codeql-custom-queries-java",
folder: {
uri: Uri.file(tmpDir.path),
name: "codeql-custom-queries-java",
index: 0,
},
} as QuickPickItem);
showInputBoxSpy.mockResolvedValueOnce("pack/new-extension-pack");
showInputBoxSpy.mockResolvedValue("models/my-model.yml");
expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token),
).toEqual({
path: newPackDir,
yamlPath: join(newPackDir, "codeql-pack.yml"),
name: "pack/new-extension-pack",
version: "0.0.0",
language: "java",
extensionTargets: {
"codeql/java-all": "*",
},
dataExtensions: ["models/**/*.yml"],
});
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showInputBoxSpy).toHaveBeenCalledTimes(1);
expect(showInputBoxSpy).toHaveBeenCalledWith(
{
title: expect.stringMatching(/extension pack/i),
prompt: expect.stringMatching(/extension pack/i),
placeHolder: expect.stringMatching(/github\/vscode-codeql-java/),
validateInput: expect.any(Function),
},
token,
);
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
expect(
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
).toEqual({
name: "pack/new-extension-pack",
version: "0.0.0",
library: true,
extensionTargets: {
"codeql/java-all": "*",
},
dataExtensions: ["models/**/*.yml"],
});
});
it("allows user to create an extension pack when there are no extension packs with a different language", async () => {
const cliServer = mockCliServer({});
const tmpDir = await dir({
unsafeCleanup: true,
});
const newPackDir = join(Uri.file(tmpDir.path).fsPath, "new-extension-pack");
showQuickPickSpy.mockResolvedValueOnce({
label: "codeql-custom-queries-java",
folder: {
uri: Uri.file(tmpDir.path),
name: "codeql-custom-queries-java",
index: 0,
},
} as QuickPickItem);
showInputBoxSpy.mockResolvedValueOnce("pack/new-extension-pack");
showInputBoxSpy.mockResolvedValue("models/my-model.yml");
expect(
await pickExtensionPack(
cliServer,
{
...databaseItem,
language: "csharp",
},
logger,
progress,
token,
),
).toEqual({
path: newPackDir,
yamlPath: join(newPackDir, "codeql-pack.yml"),
name: "pack/new-extension-pack",
version: "0.0.0",
language: "csharp",
extensionTargets: {
"codeql/csharp-all": "*",
},
dataExtensions: ["models/**/*.yml"],
});
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showInputBoxSpy).toHaveBeenCalledTimes(1);
expect(showInputBoxSpy).toHaveBeenCalledWith(
{
title: expect.stringMatching(/extension pack/i),
prompt: expect.stringMatching(/extension pack/i),
placeHolder: expect.stringMatching(/github\/vscode-codeql-csharp/),
validateInput: expect.any(Function),
},
token,
);
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
expect(
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
).toEqual({
name: "pack/new-extension-pack",
version: "0.0.0",
library: true,
extensionTargets: {
"codeql/csharp-all": "*",
},
dataExtensions: ["models/**/*.yml"],
});
});
it("allows cancelling the workspace folder selection", async () => {
const cliServer = mockCliServer({});
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token),
).toEqual(undefined);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showInputBoxSpy).toHaveBeenCalledTimes(0);
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
});
it("allows cancelling the extension pack name input", async () => {
const cliServer = mockCliServer({});
showQuickPickSpy.mockResolvedValueOnce({
label: "codeql-custom-queries-java",
folder: {
uri: Uri.file("/a/b/c"),
name: "codeql-custom-queries-java",
index: 0,
},
} as QuickPickItem);
showInputBoxSpy.mockResolvedValueOnce(undefined);
expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token),
).toEqual(undefined);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showInputBoxSpy).toHaveBeenCalledTimes(1);
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
});
it("shows an error when an extension pack resolves to more than 1 location", async () => { it("shows an error when an extension pack resolves to more than 1 location", async () => {
const cliServer = mockCliServer({ const cliServer = mockCliServer({
"my-extension-pack": [ "github/vscode-codeql-java": [
"/a/b/c/my-extension-pack", "/a/b/c/my-extension-pack",
"/a/b/c/my-extension-pack2", "/a/b/c/my-extension-pack2",
], ],
}); });
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual(undefined); ).toEqual(undefined);
expect(logger.showErrorMessage).toHaveBeenCalledTimes(1); expect(logger.showErrorMessage).toHaveBeenCalledTimes(1);
expect(logger.showErrorMessage).toHaveBeenCalledWith( expect(logger.showErrorMessage).toHaveBeenCalledWith(
expect.stringMatching(/resolves to multiple paths/), expect.stringMatching(/resolves to multiple paths/),
); );
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showQuickPickSpy).toHaveBeenCalledWith(
[
{
label: expect.stringMatching(/create/i),
extensionPack: null,
},
],
{
title: "Select extension pack to use",
},
token,
);
expect(cliServer.resolveQlpacks).toHaveBeenCalled(); expect(cliServer.resolveQlpacks).toHaveBeenCalled();
}); });
@@ -577,31 +292,15 @@ describe("pickExtensionPack", () => {
}); });
const cliServer = mockCliServer({ const cliServer = mockCliServer({
"my-extension-pack": [tmpDir.path], "github/vscode-codeql-java": [tmpDir.path],
}); });
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual(undefined); ).toEqual(undefined);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showQuickPickSpy).toHaveBeenCalledWith(
[
{
label: expect.stringMatching(/create/i),
extensionPack: null,
},
],
{
title: "Select extension pack to use",
},
token,
);
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(logger.showErrorMessage).toHaveBeenCalledTimes(1); expect(logger.showErrorMessage).toHaveBeenCalledTimes(1);
expect(logger.showErrorMessage).toHaveBeenCalledWith( expect(logger.showErrorMessage).toHaveBeenCalledWith(
expect.stringMatching(/my-extension-pack/), "Could not read extension pack github/vscode-codeql-java",
); );
expect(cliServer.resolveQlpacks).toHaveBeenCalled(); expect(cliServer.resolveQlpacks).toHaveBeenCalled();
}); });
@@ -612,33 +311,17 @@ describe("pickExtensionPack", () => {
}); });
const cliServer = mockCliServer({ const cliServer = mockCliServer({
"my-extension-pack": [tmpDir.path], "github/vscode-codeql-java": [tmpDir.path],
}); });
await outputFile(join(tmpDir.path, "codeql-pack.yml"), dumpYaml("java")); await outputFile(join(tmpDir.path, "codeql-pack.yml"), dumpYaml("java"));
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual(undefined); ).toEqual(undefined);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showQuickPickSpy).toHaveBeenCalledWith(
[
{
label: expect.stringMatching(/create/i),
extensionPack: null,
},
],
{
title: "Select extension pack to use",
},
token,
);
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(logger.showErrorMessage).toHaveBeenCalledTimes(1); expect(logger.showErrorMessage).toHaveBeenCalledTimes(1);
expect(logger.showErrorMessage).toHaveBeenCalledWith( expect(logger.showErrorMessage).toHaveBeenCalledWith(
expect.stringMatching(/my-extension-pack/), "Could not read extension pack github/vscode-codeql-java",
); );
expect(cliServer.resolveQlpacks).toHaveBeenCalled(); expect(cliServer.resolveQlpacks).toHaveBeenCalled();
}); });
@@ -649,13 +332,13 @@ describe("pickExtensionPack", () => {
}); });
const cliServer = mockCliServer({ const cliServer = mockCliServer({
"my-extension-pack": [tmpDir.path], "github/vscode-codeql-java": [tmpDir.path],
}); });
await outputFile( await outputFile(
join(tmpDir.path, "codeql-pack.yml"), join(tmpDir.path, "codeql-pack.yml"),
dumpYaml({ dumpYaml({
name: "my-extension-pack", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
library: true, library: true,
extensionTargets: { extensionTargets: {
@@ -664,28 +347,12 @@ describe("pickExtensionPack", () => {
}), }),
); );
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual(undefined); ).toEqual(undefined);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showQuickPickSpy).toHaveBeenCalledWith(
[
{
label: expect.stringMatching(/create/i),
extensionPack: null,
},
],
{
title: "Select extension pack to use",
},
token,
);
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(logger.showErrorMessage).toHaveBeenCalledTimes(1); expect(logger.showErrorMessage).toHaveBeenCalledTimes(1);
expect(logger.showErrorMessage).toHaveBeenCalledWith( expect(logger.showErrorMessage).toHaveBeenCalledWith(
expect.stringMatching(/my-extension-pack/), "Could not read extension pack github/vscode-codeql-java",
); );
expect(cliServer.resolveQlpacks).toHaveBeenCalled(); expect(cliServer.resolveQlpacks).toHaveBeenCalled();
}); });
@@ -696,13 +363,13 @@ describe("pickExtensionPack", () => {
}); });
const cliServer = mockCliServer({ const cliServer = mockCliServer({
"my-extension-pack": [tmpDir.path], "github/vscode-codeql-java": [tmpDir.path],
}); });
await outputFile( await outputFile(
join(tmpDir.path, "codeql-pack.yml"), join(tmpDir.path, "codeql-pack.yml"),
dumpYaml({ dumpYaml({
name: "my-extension-pack", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
library: true, library: true,
extensionTargets: { extensionTargets: {
@@ -714,94 +381,30 @@ describe("pickExtensionPack", () => {
}), }),
); );
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual(undefined); ).toEqual(undefined);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showQuickPickSpy).toHaveBeenCalledWith(
[
{
label: expect.stringMatching(/create/i),
extensionPack: null,
},
],
{
title: "Select extension pack to use",
},
token,
);
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(logger.showErrorMessage).toHaveBeenCalledTimes(1); expect(logger.showErrorMessage).toHaveBeenCalledTimes(1);
expect(logger.showErrorMessage).toHaveBeenCalledWith( expect(logger.showErrorMessage).toHaveBeenCalledWith(
expect.stringMatching(/my-extension-pack/), "Could not read extension pack github/vscode-codeql-java",
); );
expect(cliServer.resolveQlpacks).toHaveBeenCalled(); expect(cliServer.resolveQlpacks).toHaveBeenCalled();
}); });
it("validates the pack name input", async () => {
const cliServer = mockCliServer({});
showQuickPickSpy.mockResolvedValueOnce({
label: "a",
folder: {
uri: Uri.file("/a/b/c"),
name: "a",
index: 0,
},
} as QuickPickItem);
showInputBoxSpy.mockResolvedValue(undefined);
expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token),
).toEqual(undefined);
const validateFile = showInputBoxSpy.mock.calls[0][0]?.validateInput;
expect(validateFile).toBeDefined();
if (!validateFile) {
return;
}
expect(await validateFile("")).toEqual("Pack name must not be empty");
expect(await validateFile("a".repeat(129))).toEqual(
"Pack name must be no longer than 128 characters",
);
expect(await validateFile("github/vscode-codeql/extensions")).toEqual(
"Invalid package name: a pack name must contain only lowercase ASCII letters, ASCII digits, and hyphens",
);
expect(await validateFile("VSCODE")).toEqual(
"Invalid package name: a pack name must contain a slash to separate the scope from the pack name",
);
expect(await validateFile("github/")).toEqual(
"Invalid package name: a pack name must contain only lowercase ASCII letters, ASCII digits, and hyphens",
);
expect(await validateFile("github/VSCODE")).toEqual(
"Invalid package name: a pack name must contain only lowercase ASCII letters, ASCII digits, and hyphens",
);
expect(await validateFile("github/vscode-codeql-")).toEqual(
"Invalid package name: a pack name must contain only lowercase ASCII letters, ASCII digits, and hyphens",
);
expect(
await validateFile("github/vscode-codeql-extensions"),
).toBeUndefined();
expect(await validateFile("pack/vscode-codeql-extensions")).toBeUndefined();
});
it("allows the dataExtensions to be a string", async () => { it("allows the dataExtensions to be a string", async () => {
const tmpDir = await dir({ const tmpDir = await dir({
unsafeCleanup: true, unsafeCleanup: true,
}); });
const cliServer = mockCliServer({ const cliServer = mockCliServer({
"new-extension-pack": [tmpDir.path], "github/vscode-codeql-java": [tmpDir.path],
}); });
const qlpackPath = join(tmpDir.path, "codeql-pack.yml"); const qlpackPath = join(tmpDir.path, "codeql-pack.yml");
await outputFile( await outputFile(
qlpackPath, qlpackPath,
dumpYaml({ dumpYaml({
name: "new-extension-pack", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
library: true, library: true,
extensionTargets: { extensionTargets: {
@@ -820,7 +423,7 @@ describe("pickExtensionPack", () => {
const extensionPack = { const extensionPack = {
path: tmpDir.path, path: tmpDir.path,
yamlPath: qlpackPath, yamlPath: qlpackPath,
name: "new-extension-pack", name: autoExtensionPackName,
version: "0.0.0", version: "0.0.0",
language: "java", language: "java",
extensionTargets: { extensionTargets: {
@@ -828,75 +431,11 @@ describe("pickExtensionPack", () => {
}, },
dataExtensions: ["models/**/*.yml"], dataExtensions: ["models/**/*.yml"],
}; };
showQuickPickSpy.mockResolvedValueOnce({
label: "new-extension-pack",
extensionPack,
} as QuickPickItem);
showQuickPickSpy.mockResolvedValueOnce(undefined);
showInputBoxSpy.mockResolvedValue(undefined);
expect( expect(
await pickExtensionPack(cliServer, databaseItem, logger, progress, token), await pickExtensionPack(cliServer, databaseItem, logger, progress),
).toEqual(extensionPack); ).toEqual(extensionPack);
}); });
it("only shows extension packs for the database language", async () => {
const csharpPack = await createMockExtensionPack(
join(tmpDir, "csharp-extensions"),
"csharp-extension-pack",
{
version: "0.5.3",
language: "csharp",
extensionTargets: {
"codeql/csharp-all": "*",
},
},
);
const cliServer = mockCliServer({
...qlPacks,
"csharp-extension-pack": [csharpPack.path],
});
showQuickPickSpy.mockResolvedValueOnce(undefined);
expect(
await pickExtensionPack(
cliServer,
{
...databaseItem,
language: "csharp",
},
logger,
progress,
token,
),
).toEqual(undefined);
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(showQuickPickSpy).toHaveBeenCalledWith(
[
{
label: "csharp-extension-pack",
description: "0.5.3",
detail: csharpPack.path,
extensionPack: csharpPack,
},
{
label: expect.stringMatching(/create/i),
extensionPack: null,
},
],
{
title: expect.any(String),
},
token,
);
expect(cliServer.resolveQlpacks).toHaveBeenCalledTimes(1);
expect(cliServer.resolveQlpacks).toHaveBeenCalledWith(
additionalPacks,
true,
);
});
}); });
function mockCliServer(qlpacks: QlpacksInfo) { function mockCliServer(qlpacks: QlpacksInfo) {