Merge pull request #3539 from github/koesie10/pack-location-config
Add `codeQL.model.packLocation` setting
This commit is contained in:
@@ -463,8 +463,20 @@
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Log insights",
|
||||
"title": "Model Editor",
|
||||
"order": 9,
|
||||
"properties": {
|
||||
"codeQL.model.packLocation": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Log insights",
|
||||
"order": 10,
|
||||
"properties": {
|
||||
"codeQL.logInsights.joinOrderWarningThreshold": {
|
||||
"type": "number",
|
||||
@@ -478,7 +490,7 @@
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Telemetry",
|
||||
"order": 10,
|
||||
"order": 11,
|
||||
"properties": {
|
||||
"codeQL.telemetry.enableTelemetry": {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
FilterKey,
|
||||
SortKey,
|
||||
} from "./variant-analysis/shared/variant-analysis-filter-sort";
|
||||
import { substituteConfigVariables } from "./common/config-template";
|
||||
|
||||
export const ALL_SETTINGS: Setting[] = [];
|
||||
|
||||
@@ -734,18 +735,28 @@ const LLM_GENERATION_DEV_ENDPOINT = new Setting(
|
||||
MODEL_SETTING,
|
||||
);
|
||||
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
||||
const EXTENSIONS_DIRECTORY = new Setting("extensionsDirectory", MODEL_SETTING);
|
||||
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
|
||||
const ENABLE_PYTHON = new Setting("enablePython", MODEL_SETTING);
|
||||
const ENABLE_ACCESS_PATH_SUGGESTIONS = new Setting(
|
||||
"enableAccessPathSuggestions",
|
||||
MODEL_SETTING,
|
||||
);
|
||||
|
||||
export type ModelConfigPackVariables = {
|
||||
database: string;
|
||||
owner: string;
|
||||
name: string;
|
||||
language: string;
|
||||
};
|
||||
|
||||
export interface ModelConfig {
|
||||
flowGeneration: boolean;
|
||||
llmGeneration: boolean;
|
||||
showTypeModels: boolean;
|
||||
getExtensionsDirectory(languageId: string): string | undefined;
|
||||
getPackLocation(
|
||||
languageId: string,
|
||||
variables: ModelConfigPackVariables,
|
||||
): string;
|
||||
enablePython: boolean;
|
||||
enableAccessPathSuggestions: boolean;
|
||||
}
|
||||
@@ -787,10 +798,16 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||
return !!MODEL_EVALUATION.getValue<boolean>();
|
||||
}
|
||||
|
||||
public getExtensionsDirectory(languageId: string): string | undefined {
|
||||
return EXTENSIONS_DIRECTORY.getValue<string>({
|
||||
languageId,
|
||||
});
|
||||
public getPackLocation(
|
||||
languageId: string,
|
||||
variables: ModelConfigPackVariables,
|
||||
): string {
|
||||
return substituteConfigVariables(
|
||||
MODEL_PACK_LOCATION.getValue<string>({
|
||||
languageId,
|
||||
}),
|
||||
variables,
|
||||
);
|
||||
}
|
||||
|
||||
public get enablePython(): boolean {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { join } from "path";
|
||||
import { outputFile, pathExists, readFile } from "fs-extra";
|
||||
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
|
||||
import type { CancellationToken } from "vscode";
|
||||
import { Uri } from "vscode";
|
||||
import Ajv from "ajv";
|
||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
||||
@@ -14,10 +13,13 @@ import { getErrorMessage } from "../common/helpers-pure";
|
||||
import type { ExtensionPack } from "./shared/extension-pack";
|
||||
import type { NotificationLogger } from "../common/logging";
|
||||
import { showAndLogErrorMessage } from "../common/logging";
|
||||
import type { ModelConfig } from "../config";
|
||||
import type { ModelConfig, ModelConfigPackVariables } from "../config";
|
||||
import type { ExtensionPackName } from "./extension-pack-name";
|
||||
import { autoNameExtensionPack, formatPackName } from "./extension-pack-name";
|
||||
import { autoPickExtensionsDirectory } from "./extensions-workspace-folder";
|
||||
import {
|
||||
ensurePackLocationIsInWorkspaceFolder,
|
||||
packLocationToAbsolute,
|
||||
} from "./extensions-workspace-folder";
|
||||
|
||||
import type { ExtensionPackMetadata } from "./extension-pack-metadata";
|
||||
import extensionPackMetadataSchemaJson from "./extension-pack-metadata.schema.json";
|
||||
@@ -27,7 +29,7 @@ const extensionPackValidate = ajv.compile(extensionPackMetadataSchemaJson);
|
||||
|
||||
export async function pickExtensionPack(
|
||||
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">,
|
||||
databaseItem: Pick<DatabaseItem, "name" | "language">,
|
||||
databaseItem: Pick<DatabaseItem, "name" | "language" | "origin">,
|
||||
modelConfig: ModelConfig,
|
||||
logger: NotificationLogger,
|
||||
progress: ProgressCallback,
|
||||
@@ -64,20 +66,20 @@ export async function pickExtensionPack(
|
||||
maxStep,
|
||||
});
|
||||
|
||||
// Get the `codeQL.model.extensionsDirectory` setting for the language
|
||||
const userExtensionsDirectory = modelConfig.getExtensionsDirectory(
|
||||
databaseItem.language,
|
||||
// The default is .github/codeql/extensions/${name}-${language}
|
||||
const packPath = await packLocationToAbsolute(
|
||||
modelConfig.getPackLocation(
|
||||
databaseItem.language,
|
||||
getModelConfigPackVariables(databaseItem),
|
||||
),
|
||||
logger,
|
||||
);
|
||||
|
||||
// If the setting is not set, automatically pick a suitable directory
|
||||
const extensionsDirectory = userExtensionsDirectory
|
||||
? Uri.file(userExtensionsDirectory)
|
||||
: await autoPickExtensionsDirectory(logger);
|
||||
|
||||
if (!extensionsDirectory) {
|
||||
if (!packPath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(packPath, modelConfig, logger);
|
||||
|
||||
// Generate the name of the extension pack
|
||||
const packName = autoNameExtensionPack(
|
||||
databaseItem.name,
|
||||
@@ -139,14 +141,12 @@ export async function pickExtensionPack(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const packPath = join(extensionsDirectory.fsPath, packName.name);
|
||||
|
||||
if (await pathExists(packPath)) {
|
||||
void showAndLogErrorMessage(
|
||||
logger,
|
||||
`Directory ${packPath} already exists for extension pack ${formatPackName(
|
||||
packName,
|
||||
)}`,
|
||||
)}, but wasn't returned by codeql resolve qlpacks --kind extension --no-recursive`,
|
||||
);
|
||||
|
||||
return undefined;
|
||||
@@ -155,6 +155,26 @@ export async function pickExtensionPack(
|
||||
return writeExtensionPack(packPath, packName, databaseItem.language);
|
||||
}
|
||||
|
||||
function getModelConfigPackVariables(
|
||||
databaseItem: Pick<DatabaseItem, "name" | "language" | "origin">,
|
||||
): ModelConfigPackVariables {
|
||||
const database = databaseItem.name;
|
||||
const language = databaseItem.language;
|
||||
let name = databaseItem.name;
|
||||
let owner = "";
|
||||
|
||||
if (databaseItem.origin?.type === "github") {
|
||||
[owner, name] = databaseItem.origin.repository.split("/");
|
||||
}
|
||||
|
||||
return {
|
||||
database,
|
||||
language,
|
||||
name,
|
||||
owner,
|
||||
};
|
||||
}
|
||||
|
||||
async function writeExtensionPack(
|
||||
packPath: string,
|
||||
packName: ExtensionPackName,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { WorkspaceFolder } from "vscode";
|
||||
import { FileType, Uri, workspace } from "vscode";
|
||||
import { getOnDiskWorkspaceFoldersObjects } from "../common/vscode/workspace-folders";
|
||||
import { tmpdir } from "../common/files";
|
||||
import { containsPath, tmpdir } from "../common/files";
|
||||
import type { NotificationLogger } from "../common/logging";
|
||||
import { showAndLogErrorMessage } from "../common/logging";
|
||||
import { isAbsolute, normalize, resolve } from "path";
|
||||
import { nanoid } from "nanoid";
|
||||
import type { ModelConfig } from "../config";
|
||||
|
||||
/**
|
||||
* Returns the ancestors of this path in order from furthest to closest (i.e. root of filesystem to parent directory)
|
||||
@@ -22,26 +25,17 @@ function getAncestors(uri: Uri): Uri[] {
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
async function getRootWorkspaceDirectory(): Promise<Uri | undefined> {
|
||||
// If there is a valid workspace file, just use its directory as the directory for the extensions
|
||||
const workspaceFile = workspace.workspaceFile;
|
||||
if (workspaceFile?.scheme === "file") {
|
||||
return Uri.joinPath(workspaceFile, "..");
|
||||
function findCommonAncestor(uris: Uri[]): Uri | undefined {
|
||||
if (uris.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const allWorkspaceFolders = getOnDiskWorkspaceFoldersObjects();
|
||||
|
||||
// Get the system temp directory and convert it to a URI so it's normalized
|
||||
const systemTmpdir = Uri.file(tmpdir());
|
||||
|
||||
const workspaceFolders = allWorkspaceFolders.filter((folder) => {
|
||||
// Never use a workspace folder that is in the system temp directory
|
||||
return !folder.uri.fsPath.startsWith(systemTmpdir.fsPath);
|
||||
});
|
||||
if (uris.length === 1) {
|
||||
return uris[0];
|
||||
}
|
||||
|
||||
// Find the common root directory of all workspace folders by finding the longest common prefix
|
||||
const commonRoot = workspaceFolders.reduce((commonRoot, folder) => {
|
||||
const folderUri = folder.uri;
|
||||
const commonRoot = uris.reduce((commonRoot, folderUri) => {
|
||||
const ancestors = getAncestors(folderUri);
|
||||
|
||||
const minLength = Math.min(commonRoot.length, ancestors.length);
|
||||
@@ -55,10 +49,10 @@ async function getRootWorkspaceDirectory(): Promise<Uri | undefined> {
|
||||
}
|
||||
|
||||
return commonRoot.slice(0, commonLength);
|
||||
}, getAncestors(workspaceFolders[0].uri));
|
||||
}, getAncestors(uris[0]));
|
||||
|
||||
if (commonRoot.length === 0) {
|
||||
return await findGitFolder(workspaceFolders);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// The path closest to the workspace folders is the last element of the common root
|
||||
@@ -67,6 +61,54 @@ async function getRootWorkspaceDirectory(): Promise<Uri | undefined> {
|
||||
// If we are at the root of the filesystem, we can't go up any further and there's something
|
||||
// wrong, so just return undefined
|
||||
if (commonRootUri.fsPath === Uri.joinPath(commonRootUri, "..").fsPath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return commonRootUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the root directory of this workspace. It is determined
|
||||
* heuristically based on the on-disk workspace folders.
|
||||
*
|
||||
* The heuristic is as follows:
|
||||
* 1. If there is a workspace file (`<basename>.code-workspace`), use the directory containing that file
|
||||
* 2. If there is only 1 workspace folder, use that folder
|
||||
* 3. If there is a common root directory for all workspace folders, use that directory
|
||||
* - Workspace folders in the system temp directory are ignored
|
||||
* - If the common root directory is the root of the filesystem, then it's not used
|
||||
* 4. If there is a .git directory in any workspace folder, use the directory containing that .git directory
|
||||
* for which the .git directory is closest to a workspace folder
|
||||
* 5. If none of the above apply, return `undefined`
|
||||
*/
|
||||
export async function getRootWorkspaceDirectory(): Promise<Uri | undefined> {
|
||||
// If there is a valid workspace file, just use its directory as the directory for the extensions
|
||||
const workspaceFile = workspace.workspaceFile;
|
||||
if (workspaceFile?.scheme === "file") {
|
||||
return Uri.joinPath(workspaceFile, "..");
|
||||
}
|
||||
|
||||
const allWorkspaceFolders = getOnDiskWorkspaceFoldersObjects();
|
||||
|
||||
if (allWorkspaceFolders.length === 1) {
|
||||
return allWorkspaceFolders[0].uri;
|
||||
}
|
||||
|
||||
// Get the system temp directory and convert it to a URI so it's normalized
|
||||
const systemTmpdir = Uri.file(tmpdir());
|
||||
|
||||
const workspaceFolders = allWorkspaceFolders.filter((folder) => {
|
||||
// Never use a workspace folder that is in the system temp directory
|
||||
return !folder.uri.fsPath.startsWith(systemTmpdir.fsPath);
|
||||
});
|
||||
|
||||
// The path closest to the workspace folders is the last element of the common root
|
||||
const commonRootUri = findCommonAncestor(
|
||||
workspaceFolders.map((folder) => folder.uri),
|
||||
);
|
||||
|
||||
// If there is no common root URI, try to find a .git folder in the workspace folders
|
||||
if (commonRootUri === undefined) {
|
||||
return await findGitFolder(workspaceFolders);
|
||||
}
|
||||
|
||||
@@ -126,90 +168,130 @@ async function findGitFolder(
|
||||
return closestFolder?.[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a suitable directory for extension packs to be created in. This will
|
||||
* always be a path ending in `.github/codeql/extensions`. The parent directory
|
||||
* will be determined heuristically based on the on-disk workspace folders.
|
||||
*
|
||||
* The heuristic is as follows (`.github/codeql/extensions` is added automatically unless
|
||||
* otherwise specified):
|
||||
* 1. If there is only 1 workspace folder, use that folder
|
||||
* 2. If there is a workspace folder for which the path ends in `.github/codeql/extensions`, use that folder
|
||||
* - If there are multiple such folders, use the first one
|
||||
* - Does not append `.github/codeql/extensions` to the path
|
||||
* 3. If there is a workspace file (`<basename>.code-workspace`), use the directory containing that file
|
||||
* 4. If there is a common root directory for all workspace folders, use that directory
|
||||
* - Workspace folders in the system temp directory are ignored
|
||||
* - If the common root directory is the root of the filesystem, then it's not used
|
||||
* 5. If there is a .git directory in any workspace folder, use the directory containing that .git directory
|
||||
* for which the .git directory is closest to a workspace folder
|
||||
* 6. If none of the above apply, return `undefined`
|
||||
*/
|
||||
export async function autoPickExtensionsDirectory(
|
||||
export async function packLocationToAbsolute(
|
||||
packLocation: string,
|
||||
logger: NotificationLogger,
|
||||
): Promise<Uri | undefined> {
|
||||
const workspaceFolders = getOnDiskWorkspaceFoldersObjects();
|
||||
): Promise<string | undefined> {
|
||||
let userPackLocation = packLocation.trim();
|
||||
|
||||
// If there are no on-disk workspace folders, we can't do anything
|
||||
if (workspaceFolders.length === 0) {
|
||||
if (!isAbsolute(userPackLocation)) {
|
||||
const rootDirectory = await getRootWorkspaceDirectory();
|
||||
if (!rootDirectory) {
|
||||
void logger.log("Unable to determine root workspace directory");
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
userPackLocation = resolve(rootDirectory.fsPath, userPackLocation);
|
||||
}
|
||||
|
||||
userPackLocation = normalize(userPackLocation);
|
||||
|
||||
if (!isAbsolute(userPackLocation)) {
|
||||
// This shouldn't happen, but just in case
|
||||
void showAndLogErrorMessage(
|
||||
logger,
|
||||
`Could not find any on-disk workspace folders. Please ensure that you have opened a folder or workspace.`,
|
||||
`Invalid pack location: ${userPackLocation}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If there's only 1 workspace folder, use the `.github/codeql/extensions` directory in that folder
|
||||
if (workspaceFolders.length === 1) {
|
||||
return Uri.joinPath(
|
||||
workspaceFolders[0].uri,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
);
|
||||
}
|
||||
|
||||
// Now try to find a workspace folder for which the path ends in `.github/codeql/extensions`
|
||||
const workspaceFolderForExtensions = workspaceFolders.find((folder) =>
|
||||
// Using path instead of fsPath because path always uses forward slashes
|
||||
folder.uri.path.endsWith(".github/codeql/extensions"),
|
||||
);
|
||||
if (workspaceFolderForExtensions) {
|
||||
return workspaceFolderForExtensions.uri;
|
||||
}
|
||||
|
||||
// Get the root workspace directory, i.e. the common root directory of all workspace folders
|
||||
const rootDirectory = await getRootWorkspaceDirectory();
|
||||
if (!rootDirectory) {
|
||||
void logger.log("Unable to determine root workspace directory");
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// We'll create a new workspace folder for the extensions in the root workspace directory
|
||||
// at `.github/codeql/extensions`
|
||||
const extensionsUri = Uri.joinPath(
|
||||
rootDirectory,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
// If we are at the root of the filesystem, then something is wrong since
|
||||
// this should never be the location of a pack
|
||||
if (userPackLocation === resolve(userPackLocation, "..")) {
|
||||
void showAndLogErrorMessage(
|
||||
logger,
|
||||
`Invalid pack location: ${userPackLocation}`,
|
||||
);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return userPackLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will try to add the pack location as a workspace folder if it's not already in a
|
||||
* workspace folder and the workspace is a multi-root workspace.
|
||||
*/
|
||||
export async function ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation: string,
|
||||
modelConfig: ModelConfig,
|
||||
logger: NotificationLogger,
|
||||
): Promise<void> {
|
||||
const workspaceFolders = getOnDiskWorkspaceFoldersObjects();
|
||||
|
||||
const existsInWorkspaceFolder = workspaceFolders.some((folder) =>
|
||||
containsPath(folder.uri.fsPath, packLocation),
|
||||
);
|
||||
|
||||
if (existsInWorkspaceFolder) {
|
||||
// If the pack location is already in a workspace folder, we don't need to do anything
|
||||
return;
|
||||
}
|
||||
|
||||
if (workspace.workspaceFile === undefined) {
|
||||
// If we're not in a workspace, we can't add a workspace folder without reloading the window,
|
||||
// so we'll not do anything
|
||||
return;
|
||||
}
|
||||
|
||||
// To find the "correct" directory to add as a workspace folder, we'll generate a few different
|
||||
// pack locations and find the common ancestor of the directories. This is the directory that
|
||||
// we'll add as a workspace folder.
|
||||
|
||||
// Generate a few different pack locations to get an accurate common ancestor
|
||||
const otherPackLocations = await Promise.all(
|
||||
Array.from({ length: 3 }).map(() =>
|
||||
packLocationToAbsolute(
|
||||
modelConfig.getPackLocation(nanoid(), {
|
||||
database: nanoid(),
|
||||
language: nanoid(),
|
||||
name: nanoid(),
|
||||
owner: nanoid(),
|
||||
}),
|
||||
logger,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const otherPackLocationUris = otherPackLocations
|
||||
.filter((loc): loc is string => loc !== undefined)
|
||||
.map((loc) => Uri.file(loc));
|
||||
|
||||
if (otherPackLocationUris.length === 0) {
|
||||
void logger.log(
|
||||
`Failed to generate different pack locations, not adding workspace folder.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const commonRootUri = findCommonAncestor([
|
||||
Uri.file(packLocation),
|
||||
...otherPackLocationUris,
|
||||
]);
|
||||
|
||||
if (commonRootUri === undefined) {
|
||||
void logger.log(
|
||||
`Failed to find common ancestor for ${packLocation} and ${otherPackLocationUris[0].fsPath}, not adding workspace folder.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!workspace.updateWorkspaceFolders(
|
||||
workspace.workspaceFolders?.length ?? 0,
|
||||
0,
|
||||
{
|
||||
name: "CodeQL Extension Packs",
|
||||
uri: extensionsUri,
|
||||
uri: commonRootUri,
|
||||
},
|
||||
)
|
||||
) {
|
||||
void logger.log(
|
||||
`Failed to add workspace folder for extensions at ${extensionsUri.fsPath}`,
|
||||
`Failed to add workspace folder for extensions at ${commonRootUri.fsPath}`,
|
||||
);
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
return extensionsUri;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { ExtensionPack } from "../../../../src/model-editor/shared/extensio
|
||||
import { createMockLogger } from "../../../__mocks__/loggerMock";
|
||||
import type { ModelConfig } from "../../../../src/config";
|
||||
import { mockedObject } from "../../utils/mocking.helpers";
|
||||
import type { DatabaseItem } from "../../../../src/databases/local-databases";
|
||||
|
||||
describe("pickExtensionPack", () => {
|
||||
let tmpDir: string;
|
||||
@@ -19,9 +20,16 @@ describe("pickExtensionPack", () => {
|
||||
let autoExtensionPack: ExtensionPack;
|
||||
|
||||
let qlPacks: QlpacksInfo;
|
||||
const databaseItem = {
|
||||
const databaseItem: Pick<DatabaseItem, "name" | "language" | "origin"> = {
|
||||
name: "github/vscode-codeql",
|
||||
language: "java",
|
||||
origin: {
|
||||
type: "github",
|
||||
repository: "github/vscode-codeql",
|
||||
databaseId: 123578,
|
||||
databaseCreatedAt: "2021-01-01T00:00:00Z",
|
||||
commitOid: "1234567890abcdef",
|
||||
},
|
||||
};
|
||||
|
||||
const progress = jest.fn();
|
||||
@@ -67,7 +75,12 @@ describe("pickExtensionPack", () => {
|
||||
.mockReturnValue([workspaceFolder]);
|
||||
|
||||
modelConfig = mockedObject<ModelConfig>({
|
||||
getExtensionsDirectory: jest.fn().mockReturnValue(undefined),
|
||||
getPackLocation: jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name }) =>
|
||||
`.github/codeql/extensions/${name}-${language}`,
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,10 +103,15 @@ describe("pickExtensionPack", () => {
|
||||
additionalPacks,
|
||||
true,
|
||||
);
|
||||
expect(modelConfig.getExtensionsDirectory).toHaveBeenCalledWith("java");
|
||||
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack using default extensions directory", async () => {
|
||||
it("creates a new extension pack using default pack location", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
@@ -154,7 +172,12 @@ describe("pickExtensionPack", () => {
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(modelConfig.getExtensionsDirectory).toHaveBeenCalledWith("java");
|
||||
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
|
||||
@@ -169,22 +192,147 @@ describe("pickExtensionPack", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack when extensions directory is set in config", async () => {
|
||||
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),
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
expect(
|
||||
await pickExtensionPack(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
path: packLocation,
|
||||
yamlPath: join(packLocation, "codeql-pack.yml"),
|
||||
name: autoExtensionPackName,
|
||||
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: autoExtensionPackName,
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
});
|
||||
|
||||
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`),
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
expect(
|
||||
await pickExtensionPack(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
path: packLocation,
|
||||
yamlPath: join(packLocation, "codeql-pack.yml"),
|
||||
name: autoExtensionPackName,
|
||||
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: autoExtensionPackName,
|
||||
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",
|
||||
language: "java",
|
||||
origin: {
|
||||
type: "archive",
|
||||
path: "/path/to/codeql-database.zip",
|
||||
},
|
||||
};
|
||||
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const configExtensionsDir = join(
|
||||
workspaceFoldersSpy.mockReturnValue([
|
||||
{
|
||||
uri: Uri.joinPath(Uri.file(tmpDir.path), "codeql-custom-queries-java"),
|
||||
name: "codeql-custom-queries-java",
|
||||
index: 2,
|
||||
},
|
||||
]);
|
||||
|
||||
jest
|
||||
.spyOn(workspace, "workspaceFile", "get")
|
||||
.mockReturnValue(
|
||||
Uri.joinPath(Uri.file(tmpDir.path), "workspace.code-workspace"),
|
||||
);
|
||||
jest.spyOn(workspace, "updateWorkspaceFolders").mockReturnValue(true);
|
||||
|
||||
const newPackDir = join(
|
||||
Uri.file(tmpDir.path).fsPath,
|
||||
"my-custom-extensions-directory",
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
"vscode-codeql-java",
|
||||
);
|
||||
|
||||
const modelConfig = mockedObject<ModelConfig>({
|
||||
getExtensionsDirectory: jest.fn().mockReturnValue(configExtensionsDir),
|
||||
});
|
||||
|
||||
const newPackDir = join(configExtensionsDir, "vscode-codeql-java");
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
expect(
|
||||
@@ -200,7 +348,7 @@ describe("pickExtensionPack", () => {
|
||||
).toEqual({
|
||||
path: newPackDir,
|
||||
yamlPath: join(newPackDir, "codeql-pack.yml"),
|
||||
name: autoExtensionPackName,
|
||||
name: "pack/vscode-codeql-java",
|
||||
version: "0.0.0",
|
||||
language: "java",
|
||||
extensionTargets: {
|
||||
@@ -209,12 +357,17 @@ describe("pickExtensionPack", () => {
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(modelConfig.getExtensionsDirectory).toHaveBeenCalledWith("java");
|
||||
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
|
||||
database: "vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
|
||||
).toEqual({
|
||||
name: autoExtensionPackName,
|
||||
name: "pack/vscode-codeql-java",
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
|
||||
@@ -3,27 +3,28 @@ import { Uri, workspace } from "vscode";
|
||||
import type { DirectoryResult } from "tmp-promise";
|
||||
import { dir } from "tmp-promise";
|
||||
import { join } from "path";
|
||||
import { autoPickExtensionsDirectory } from "../../../../src/model-editor/extensions-workspace-folder";
|
||||
import {
|
||||
ensurePackLocationIsInWorkspaceFolder,
|
||||
getRootWorkspaceDirectory,
|
||||
packLocationToAbsolute,
|
||||
} from "../../../../src/model-editor/extensions-workspace-folder";
|
||||
import * as files from "../../../../src/common/files";
|
||||
import { mkdirp } from "fs-extra";
|
||||
import type { NotificationLogger } from "../../../../src/common/logging";
|
||||
import { createMockLogger } from "../../../__mocks__/loggerMock";
|
||||
import { mockedObject } from "../../../mocked-object";
|
||||
import type { ModelConfig } from "../../../../src/config";
|
||||
|
||||
describe("autoPickExtensionsDirectory", () => {
|
||||
describe("getRootWorkspaceDirectory", () => {
|
||||
let tmpDir: DirectoryResult;
|
||||
let rootDirectory: Uri;
|
||||
let extensionsDirectory: Uri;
|
||||
|
||||
let workspaceFoldersSpy: jest.SpyInstance<
|
||||
readonly WorkspaceFolder[] | undefined,
|
||||
[]
|
||||
>;
|
||||
let workspaceFileSpy: jest.SpyInstance<Uri | undefined, []>;
|
||||
let updateWorkspaceFoldersSpy: jest.SpiedFunction<
|
||||
typeof workspace.updateWorkspaceFolders
|
||||
>;
|
||||
let mockedTmpDirUri: Uri;
|
||||
let logger: NotificationLogger;
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await dir({
|
||||
@@ -31,12 +32,6 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
});
|
||||
|
||||
rootDirectory = Uri.joinPath(Uri.file(tmpDir.path), "root");
|
||||
extensionsDirectory = Uri.joinPath(
|
||||
rootDirectory,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
);
|
||||
|
||||
const mockedTmpDir = join(tmpDir.path, ".tmp", "tmp");
|
||||
mockedTmpDirUri = Uri.file(mockedTmpDir);
|
||||
@@ -47,44 +42,14 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
workspaceFileSpy = jest
|
||||
.spyOn(workspace, "workspaceFile", "get")
|
||||
.mockReturnValue(undefined);
|
||||
updateWorkspaceFoldersSpy = jest
|
||||
.spyOn(workspace, "updateWorkspaceFolders")
|
||||
.mockReturnValue(true);
|
||||
|
||||
jest.spyOn(files, "tmpdir").mockReturnValue(mockedTmpDir);
|
||||
|
||||
logger = createMockLogger();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await tmpDir.cleanup();
|
||||
});
|
||||
|
||||
it("when a workspace folder with the correct path exists", async () => {
|
||||
workspaceFoldersSpy.mockReturnValue([
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-java"),
|
||||
name: "codeql-custom-queries-java",
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-python"),
|
||||
name: "codeql-custom-queries-python",
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
uri: extensionsDirectory,
|
||||
name: "CodeQL Extension Packs",
|
||||
index: 2,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(
|
||||
extensionsDirectory,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("when a workspace file exists", async () => {
|
||||
workspaceFoldersSpy.mockReturnValue([
|
||||
{
|
||||
@@ -103,36 +68,7 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
Uri.joinPath(rootDirectory, "workspace.code-workspace"),
|
||||
);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(
|
||||
extensionsDirectory,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).toHaveBeenCalledWith(2, 0, {
|
||||
name: "CodeQL Extension Packs",
|
||||
uri: extensionsDirectory,
|
||||
});
|
||||
});
|
||||
|
||||
it("when updating the workspace folders fails", async () => {
|
||||
updateWorkspaceFoldersSpy.mockReturnValue(false);
|
||||
|
||||
workspaceFoldersSpy.mockReturnValue([
|
||||
{
|
||||
uri: Uri.file("/a/b/c"),
|
||||
name: "codeql-custom-queries-java",
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-python"),
|
||||
name: "codeql-custom-queries-python",
|
||||
index: 1,
|
||||
},
|
||||
]);
|
||||
|
||||
workspaceFileSpy.mockReturnValue(
|
||||
Uri.joinPath(rootDirectory, "workspace.code-workspace"),
|
||||
);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(undefined);
|
||||
expect(await getRootWorkspaceDirectory()).toEqual(rootDirectory);
|
||||
});
|
||||
|
||||
it("when a workspace file does not exist and there is a common root directory", async () => {
|
||||
@@ -149,13 +85,9 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(
|
||||
extensionsDirectory,
|
||||
expect((await getRootWorkspaceDirectory())?.fsPath).toEqual(
|
||||
rootDirectory.fsPath,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).toHaveBeenCalledWith(2, 0, {
|
||||
name: "CodeQL Extension Packs",
|
||||
uri: extensionsDirectory,
|
||||
});
|
||||
});
|
||||
|
||||
it("when a workspace file does not exist and there is a temp dir as workspace folder", async () => {
|
||||
@@ -177,13 +109,9 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(
|
||||
extensionsDirectory,
|
||||
expect((await getRootWorkspaceDirectory())?.fsPath).toEqual(
|
||||
rootDirectory.fsPath,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).toHaveBeenCalledWith(3, 0, {
|
||||
name: "CodeQL Extension Packs",
|
||||
uri: extensionsDirectory,
|
||||
});
|
||||
});
|
||||
|
||||
it("when a workspace file does not exist and there is no common root directory", async () => {
|
||||
@@ -200,8 +128,7 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(undefined);
|
||||
expect(updateWorkspaceFoldersSpy).not.toHaveBeenCalled();
|
||||
expect(await getRootWorkspaceDirectory()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("when a workspace file does not exist and there is a .git folder", async () => {
|
||||
@@ -220,13 +147,7 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(
|
||||
extensionsDirectory,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).toHaveBeenCalledWith(2, 0, {
|
||||
name: "CodeQL Extension Packs",
|
||||
uri: extensionsDirectory,
|
||||
});
|
||||
expect(await getRootWorkspaceDirectory()).toEqual(rootDirectory);
|
||||
});
|
||||
|
||||
it("when there is no on-disk workspace folder", async () => {
|
||||
@@ -238,10 +159,268 @@ describe("autoPickExtensionsDirectory", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(await autoPickExtensionsDirectory(logger)).toEqual(undefined);
|
||||
expect(await getRootWorkspaceDirectory()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("packLocationToAbsolute", () => {
|
||||
let tmpDir: DirectoryResult;
|
||||
let rootDirectory: Uri;
|
||||
let extensionsDirectory: Uri;
|
||||
|
||||
let logger: NotificationLogger;
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
rootDirectory = Uri.joinPath(Uri.file(tmpDir.path), "root");
|
||||
extensionsDirectory = Uri.joinPath(
|
||||
rootDirectory,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
);
|
||||
|
||||
const mockedTmpDir = join(tmpDir.path, ".tmp", "tmp");
|
||||
|
||||
jest.spyOn(workspace, "workspaceFolders", "get").mockReturnValue([]);
|
||||
jest
|
||||
.spyOn(workspace, "workspaceFile", "get")
|
||||
.mockReturnValue(Uri.joinPath(rootDirectory, "workspace.code-workspace"));
|
||||
|
||||
jest.spyOn(files, "tmpdir").mockReturnValue(mockedTmpDir);
|
||||
|
||||
logger = createMockLogger();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await tmpDir.cleanup();
|
||||
});
|
||||
|
||||
it("when the location is absolute", async () => {
|
||||
expect(
|
||||
await packLocationToAbsolute(extensionsDirectory.fsPath, logger),
|
||||
).toEqual(extensionsDirectory.fsPath);
|
||||
});
|
||||
|
||||
it("when the location is relative", async () => {
|
||||
expect(
|
||||
await packLocationToAbsolute(".github/codeql/extensions/my-pack", logger),
|
||||
).toEqual(Uri.joinPath(extensionsDirectory, "my-pack").fsPath);
|
||||
});
|
||||
|
||||
it("when the location is invalid", async () => {
|
||||
expect(
|
||||
await packLocationToAbsolute("../".repeat(100), logger),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("ensurePackLocationIsInWorkspaceFolder", () => {
|
||||
let tmpDir: DirectoryResult;
|
||||
let rootDirectory: Uri;
|
||||
let extensionsDirectory: Uri;
|
||||
let packLocation: string;
|
||||
|
||||
let workspaceFoldersSpy: jest.SpyInstance<
|
||||
readonly WorkspaceFolder[] | undefined,
|
||||
[]
|
||||
>;
|
||||
let workspaceFileSpy: jest.SpyInstance<Uri | undefined, []>;
|
||||
let updateWorkspaceFoldersSpy: jest.SpiedFunction<
|
||||
typeof workspace.updateWorkspaceFolders
|
||||
>;
|
||||
|
||||
let getPackLocation: jest.MockedFunction<ModelConfig["getPackLocation"]>;
|
||||
let modelConfig: ModelConfig;
|
||||
let logger: NotificationLogger;
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
rootDirectory = Uri.joinPath(Uri.file(tmpDir.path), "root");
|
||||
extensionsDirectory = Uri.joinPath(
|
||||
rootDirectory,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
);
|
||||
packLocation = Uri.joinPath(extensionsDirectory, "my-pack").fsPath;
|
||||
|
||||
workspaceFoldersSpy = jest
|
||||
.spyOn(workspace, "workspaceFolders", "get")
|
||||
.mockReturnValue([
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-java"),
|
||||
name: "codeql-custom-queries-java",
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-python"),
|
||||
name: "codeql-custom-queries-python",
|
||||
index: 1,
|
||||
},
|
||||
]);
|
||||
workspaceFileSpy = jest
|
||||
.spyOn(workspace, "workspaceFile", "get")
|
||||
.mockReturnValue(Uri.joinPath(rootDirectory, "workspace.code-workspace"));
|
||||
updateWorkspaceFoldersSpy = jest
|
||||
.spyOn(workspace, "updateWorkspaceFolders")
|
||||
.mockReturnValue(true);
|
||||
|
||||
logger = createMockLogger();
|
||||
|
||||
getPackLocation = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name }) =>
|
||||
Uri.joinPath(extensionsDirectory, `${name}-${language}`).fsPath,
|
||||
);
|
||||
modelConfig = mockedObject<ModelConfig>({
|
||||
getPackLocation,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await tmpDir.cleanup();
|
||||
});
|
||||
|
||||
it("when there is no workspace file", async () => {
|
||||
workspaceFileSpy.mockReturnValue(undefined);
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).not.toHaveBeenCalled();
|
||||
expect(logger.showErrorMessage).toHaveBeenCalledWith(
|
||||
"Could not find any on-disk workspace folders. Please ensure that you have opened a folder or workspace.",
|
||||
});
|
||||
|
||||
it("when a workspace folder with the correct path exists", async () => {
|
||||
workspaceFoldersSpy.mockReturnValue([
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-java"),
|
||||
name: "codeql-custom-queries-java",
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-python"),
|
||||
name: "codeql-custom-queries-python",
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
uri: extensionsDirectory,
|
||||
name: "CodeQL Extension Packs",
|
||||
index: 2,
|
||||
},
|
||||
]);
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("when a workspace folder with the correct path does not exist in an unsaved workspace", async () => {
|
||||
workspaceFileSpy.mockReturnValue(Uri.parse("untitled:1555503116870"));
|
||||
workspaceFoldersSpy.mockReturnValue([
|
||||
{
|
||||
uri: Uri.file("/a/b/c"),
|
||||
name: "codeql-custom-queries-java",
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-python"),
|
||||
name: "codeql-custom-queries-python",
|
||||
index: 1,
|
||||
},
|
||||
]);
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).toHaveBeenLastCalledWith(2, 0, {
|
||||
name: "CodeQL Extension Packs",
|
||||
uri: expect.any(Uri),
|
||||
});
|
||||
expect(updateWorkspaceFoldersSpy.mock.lastCall?.[2].uri.fsPath).toEqual(
|
||||
extensionsDirectory.fsPath,
|
||||
);
|
||||
});
|
||||
|
||||
it("when a workspace folder with the correct path does not exist in a saved workspace", async () => {
|
||||
workspaceFoldersSpy.mockReturnValue([
|
||||
{
|
||||
uri: Uri.file("/a/b/c"),
|
||||
name: "codeql-custom-queries-java",
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
uri: Uri.joinPath(rootDirectory, "codeql-custom-queries-python"),
|
||||
name: "codeql-custom-queries-python",
|
||||
index: 1,
|
||||
},
|
||||
]);
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
expect(updateWorkspaceFoldersSpy).toHaveBeenLastCalledWith(2, 0, {
|
||||
name: "CodeQL Extension Packs",
|
||||
uri: expect.any(Uri),
|
||||
});
|
||||
expect(updateWorkspaceFoldersSpy.mock.lastCall?.[2].uri.fsPath).toEqual(
|
||||
extensionsDirectory.fsPath,
|
||||
);
|
||||
});
|
||||
|
||||
it("when all other pack locations are invalid", async () => {
|
||||
getPackLocation.mockReturnValue("/");
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
|
||||
expect(updateWorkspaceFoldersSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("when there is no common root directory", async () => {
|
||||
getPackLocation.mockImplementation(
|
||||
(language, { name }) => `/${name}-${language}`,
|
||||
);
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
|
||||
expect(updateWorkspaceFoldersSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("when updating the workspace folders fails", async () => {
|
||||
updateWorkspaceFoldersSpy.mockReturnValue(false);
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(
|
||||
packLocation,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith(
|
||||
`Failed to add workspace folder for extensions at ${extensionsDirectory.fsPath}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user