Merge remote-tracking branch 'origin/main' into koesie10/commands-with-progress
This commit is contained in:
38
extensions/ql-vscode/package-lock.json
generated
38
extensions/ql-vscode/package-lock.json
generated
@@ -142,7 +142,7 @@
|
||||
"ts-node": "^10.7.0",
|
||||
"ts-protoc-gen": "^0.9.0",
|
||||
"typescript": "^4.5.5",
|
||||
"webpack": "^5.62.2",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.6.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -40359,9 +40359,9 @@
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.73.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
|
||||
"integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
|
||||
"version": "5.76.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
|
||||
"integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
@@ -40369,11 +40369,11 @@
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/wasm-edit": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.9.3",
|
||||
"enhanced-resolve": "^5.10.0",
|
||||
"es-module-lexer": "^0.9.0",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
@@ -40386,7 +40386,7 @@
|
||||
"schema-utils": "^3.1.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"watchpack": "^2.3.1",
|
||||
"watchpack": "^2.4.0",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"bin": {
|
||||
@@ -40758,9 +40758,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/enhanced-resolve": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
|
||||
"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
|
||||
"version": "5.12.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
|
||||
"integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@@ -72205,9 +72205,9 @@
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.73.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
|
||||
"integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
|
||||
"version": "5.76.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
|
||||
"integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
@@ -72215,11 +72215,11 @@
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/wasm-edit": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.9.3",
|
||||
"enhanced-resolve": "^5.10.0",
|
||||
"es-module-lexer": "^0.9.0",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
@@ -72232,7 +72232,7 @@
|
||||
"schema-utils": "^3.1.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"watchpack": "^2.3.1",
|
||||
"watchpack": "^2.4.0",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -72261,9 +72261,9 @@
|
||||
}
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
|
||||
"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
|
||||
"version": "5.12.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
|
||||
"integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
|
||||
@@ -1535,7 +1535,7 @@
|
||||
"ts-node": "^10.7.0",
|
||||
"ts-protoc-gen": "^0.9.0",
|
||||
"typescript": "^4.5.5",
|
||||
"webpack": "^5.62.2",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.6.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
|
||||
@@ -1284,11 +1284,25 @@ export class CodeQLCliServer implements Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
async packInstall(dir: string, forceUpdate = false) {
|
||||
async packInstall(
|
||||
dir: string,
|
||||
{ forceUpdate = false, workspaceFolders = [] as string[] } = {},
|
||||
) {
|
||||
const args = [dir];
|
||||
if (forceUpdate) {
|
||||
args.push("--mode", "update");
|
||||
}
|
||||
if (workspaceFolders?.length > 0) {
|
||||
if (await this.cliConstraints.supportsAdditionalPacksInstall()) {
|
||||
args.push(
|
||||
// Allow prerelease packs from the ql submodule.
|
||||
"--allow-prerelease",
|
||||
// Allow the use of --additional-packs argument without issueing a warning
|
||||
"--no-strict-mode",
|
||||
...this.getAdditionalPacksArg(workspaceFolders),
|
||||
);
|
||||
}
|
||||
}
|
||||
return this.runJsonCodeQlCliCommandWithAuthentication(
|
||||
["pack", "install"],
|
||||
args,
|
||||
@@ -1692,6 +1706,13 @@ export class CliVersionConstraint {
|
||||
*/
|
||||
public static CLI_VERSION_WITH_QLPACKS_KIND = new SemVer("2.12.3");
|
||||
|
||||
/**
|
||||
* CLI version that supports the `--additional-packs` option for the `pack install` command.
|
||||
*/
|
||||
public static CLI_VERSION_WITH_ADDITIONAL_PACKS_INSTALL = new SemVer(
|
||||
"2.12.4",
|
||||
);
|
||||
|
||||
constructor(private readonly cli: CodeQLCliServer) {
|
||||
/**/
|
||||
}
|
||||
@@ -1755,4 +1776,10 @@ export class CliVersionConstraint {
|
||||
CliVersionConstraint.CLI_VERSION_WITH_QLPACKS_KIND,
|
||||
);
|
||||
}
|
||||
|
||||
async supportsAdditionalPacksInstall() {
|
||||
return this.isVersionAtLeast(
|
||||
CliVersionConstraint.CLI_VERSION_WITH_ADDITIONAL_PACKS_INSTALL,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Disposable } from "../pure/disposable-object";
|
||||
import { AppEventEmitter } from "./events";
|
||||
import { Logger } from "./logging";
|
||||
import { Memento } from "./memento";
|
||||
import { ExtensionCommandManager } from "./commands";
|
||||
import { AppCommandManager } from "./commands";
|
||||
|
||||
export interface App {
|
||||
createEventEmitter<T>(): AppEventEmitter<T>;
|
||||
@@ -16,7 +16,7 @@ export interface App {
|
||||
readonly workspaceStoragePath?: string;
|
||||
readonly workspaceState: Memento;
|
||||
readonly credentials: Credentials;
|
||||
readonly commandManager: ExtensionCommandManager;
|
||||
readonly commands: AppCommandManager;
|
||||
}
|
||||
|
||||
export enum AppMode {
|
||||
|
||||
@@ -8,8 +8,8 @@ import type { Uri } from "vscode";
|
||||
* the implementation in the corresponding `getCommands` function.
|
||||
*/
|
||||
|
||||
// Commands directly in the extension
|
||||
export type ExtensionCommands = {
|
||||
// Base commands not tied directly to a module like e.g. variant analysis.
|
||||
export type BaseCommands = {
|
||||
"codeQL.openDocumentation": () => Promise<void>;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,6 @@ export type VariantAnalysisCommands = {
|
||||
"codeQL.runVariantAnalysisContextEditor": (uri?: Uri) => Promise<void>;
|
||||
};
|
||||
|
||||
export type AllCommands = ExtensionCommands & VariantAnalysisCommands;
|
||||
export type AllCommands = BaseCommands & VariantAnalysisCommands;
|
||||
|
||||
export type ExtensionCommandManager = CommandManager<AllCommands>;
|
||||
export type AppCommandManager = CommandManager<AllCommands>;
|
||||
|
||||
@@ -3,16 +3,23 @@ import { commandRunner } from "../../commandRunner";
|
||||
import { CommandFunction, CommandManager } from "../../packages/commands";
|
||||
|
||||
/**
|
||||
* Intializes a command manager for VSCode, wrapping the commandRunner
|
||||
* Create a command manager for VSCode, wrapping the commandRunner
|
||||
* and vscode.executeCommand.
|
||||
*/
|
||||
export function initializeVSCodeCommandManager<
|
||||
export function createVSCodeCommandManager<
|
||||
Commands extends Record<string, CommandFunction>,
|
||||
>(): CommandManager<Commands> {
|
||||
return new CommandManager(commandRunner, wrappedExecuteCommand);
|
||||
return new CommandManager(commandRunner, wrapExecuteCommand);
|
||||
}
|
||||
|
||||
async function wrappedExecuteCommand<
|
||||
/**
|
||||
* wrapExecuteCommand wraps commands.executeCommand to satisfy that the
|
||||
* type is a Promise. Type script does not seem to be smart enough
|
||||
* to figure out that `ReturnType<Commands[CommandName]>` is actually
|
||||
* a Promise, so we need to add a second layer of wrapping and unwrapping
|
||||
* (The `Promise<Awaited<` part) to get the right types.
|
||||
*/
|
||||
async function wrapExecuteCommand<
|
||||
Commands extends Record<string, CommandFunction>,
|
||||
CommandName extends keyof Commands & string = keyof Commands & string,
|
||||
>(
|
||||
|
||||
@@ -6,19 +6,19 @@ import { AppEventEmitter } from "../events";
|
||||
import { extLogger, Logger } from "../logging";
|
||||
import { Memento } from "../memento";
|
||||
import { VSCodeAppEventEmitter } from "./events";
|
||||
import { ExtensionCommandManager } from "../commands";
|
||||
import { initializeVSCodeCommandManager } from "./commands";
|
||||
import { AppCommandManager } from "../commands";
|
||||
import { createVSCodeCommandManager } from "./commands";
|
||||
|
||||
export class ExtensionApp implements App {
|
||||
public readonly credentials: VSCodeCredentials;
|
||||
public readonly commandManager: ExtensionCommandManager;
|
||||
public readonly commands: AppCommandManager;
|
||||
|
||||
public constructor(
|
||||
public readonly extensionContext: vscode.ExtensionContext,
|
||||
) {
|
||||
this.credentials = new VSCodeCredentials();
|
||||
this.commandManager = initializeVSCodeCommandManager();
|
||||
extensionContext.subscriptions.push(this.commandManager);
|
||||
this.commands = createVSCodeCommandManager();
|
||||
extensionContext.subscriptions.push(this.commands);
|
||||
}
|
||||
|
||||
public get extensionPath(): string {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,7 @@
|
||||
* and then allow other parts to call those commands in a well-typed manner.
|
||||
*/
|
||||
|
||||
export interface Disposable {
|
||||
dispose(): void;
|
||||
}
|
||||
import { Disposable } from "./Disposable";
|
||||
|
||||
/**
|
||||
* A command function is a completely untyped command.
|
||||
@@ -32,7 +30,7 @@ export class CommandManager<
|
||||
constructor(
|
||||
private readonly commandRegister: <T extends CommandName>(
|
||||
commandName: T,
|
||||
definition: Commands[T],
|
||||
fn: Commands[T],
|
||||
) => Disposable,
|
||||
private readonly commandExecute: <T extends CommandName>(
|
||||
commandName: T,
|
||||
@@ -43,7 +41,7 @@ export class CommandManager<
|
||||
/**
|
||||
* Register a command with the specified name and implementation.
|
||||
*/
|
||||
registerCommand<T extends CommandName>(
|
||||
register<T extends CommandName>(
|
||||
commandName: T,
|
||||
definition: Commands[T],
|
||||
): void {
|
||||
@@ -53,7 +51,7 @@ export class CommandManager<
|
||||
/**
|
||||
* Execute a command with the specified name and the provided arguments.
|
||||
*/
|
||||
executeCommand<T extends CommandName>(
|
||||
execute<T extends CommandName>(
|
||||
commandName: T,
|
||||
...args: Parameters<Commands[T]>
|
||||
): Promise<Awaited<ReturnType<Commands[T]>>> {
|
||||
|
||||
7
extensions/ql-vscode/src/packages/commands/Disposable.ts
Normal file
7
extensions/ql-vscode/src/packages/commands/Disposable.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* This interface mirrors the vscode.Disaposable class, so that
|
||||
* the command manager does not depend on vscode directly.
|
||||
*/
|
||||
export interface Disposable {
|
||||
dispose(): void;
|
||||
}
|
||||
@@ -2,6 +2,10 @@ import { join } from "path";
|
||||
import { pathExists } from "fs-extra";
|
||||
|
||||
export const QLPACK_FILENAMES = ["qlpack.yml", "codeql-pack.yml"];
|
||||
export const QLPACK_LOCK_FILENAMES = [
|
||||
"qlpack.lock.yml",
|
||||
"codeql-pack.lock.yml",
|
||||
];
|
||||
export const FALLBACK_QLPACK_FILENAME = QLPACK_FILENAMES[0];
|
||||
|
||||
export async function getQlPackPath(
|
||||
|
||||
@@ -143,7 +143,7 @@ export async function displayQuickQuery(
|
||||
|
||||
if (shouldRewrite) {
|
||||
await cliServer.clearCache();
|
||||
await cliServer.packInstall(queriesDir, true);
|
||||
await cliServer.packInstall(queriesDir, { forceUpdate: true });
|
||||
}
|
||||
|
||||
await Window.showTextDocument(await workspace.openTextDocument(qlFile));
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
getQlPackPath,
|
||||
FALLBACK_QLPACK_FILENAME,
|
||||
QLPACK_FILENAMES,
|
||||
QLPACK_LOCK_FILENAMES,
|
||||
} from "../pure/ql";
|
||||
|
||||
export interface QlPack {
|
||||
@@ -70,42 +71,23 @@ async function generateQueryPack(
|
||||
const originalPackRoot = await findPackRoot(queryFile);
|
||||
const packRelativePath = relative(originalPackRoot, queryFile);
|
||||
const targetQueryFileName = join(queryPackDir, packRelativePath);
|
||||
const workspaceFolders = getOnDiskWorkspaceFolders();
|
||||
|
||||
let language: string | undefined;
|
||||
|
||||
// Check if the query is already in a query pack.
|
||||
// If so, copy the entire query pack to the temporary directory.
|
||||
// Otherwise, copy only the query file to the temporary directory
|
||||
// and generate a synthetic query pack.
|
||||
if (await getQlPackPath(originalPackRoot)) {
|
||||
// don't include ql files. We only want the queryFile to be copied.
|
||||
const toCopy = await cliServer.packPacklist(originalPackRoot, false);
|
||||
|
||||
// also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist.
|
||||
[
|
||||
join(originalPackRoot, "qlpack.lock.yml"),
|
||||
join(originalPackRoot, "codeql-pack.lock.yml"),
|
||||
await copyExistingQueryPack(
|
||||
cliServer,
|
||||
originalPackRoot,
|
||||
queryFile,
|
||||
].forEach((absolutePath) => {
|
||||
if (absolutePath) {
|
||||
toCopy.push(absolutePath);
|
||||
}
|
||||
});
|
||||
|
||||
let copiedCount = 0;
|
||||
await copy(originalPackRoot, queryPackDir, {
|
||||
filter: (file: string) =>
|
||||
// copy file if it is in the packlist, or it is a parent directory of a file in the packlist
|
||||
!!toCopy.find((f) => {
|
||||
// Normalized paths ensure that Windows drive letters are capitalized consistently.
|
||||
const normalizedPath = Uri.file(f).fsPath;
|
||||
const matches =
|
||||
normalizedPath === file || normalizedPath.startsWith(file + sep);
|
||||
if (matches) {
|
||||
copiedCount++;
|
||||
}
|
||||
return matches;
|
||||
}),
|
||||
});
|
||||
|
||||
void extLogger.log(`Copied ${copiedCount} files to ${queryPackDir}`);
|
||||
|
||||
await fixPackFile(queryPackDir, packRelativePath);
|
||||
queryPackDir,
|
||||
packRelativePath,
|
||||
);
|
||||
|
||||
language = await findLanguage(cliServer, Uri.file(targetQueryFileName));
|
||||
} else {
|
||||
@@ -114,20 +96,12 @@ async function generateQueryPack(
|
||||
|
||||
// copy only the query file to the query pack directory
|
||||
// and generate a synthetic query pack
|
||||
void extLogger.log(`Copying ${queryFile} to ${queryPackDir}`);
|
||||
await copy(queryFile, targetQueryFileName);
|
||||
void extLogger.log("Generating synthetic query pack");
|
||||
const syntheticQueryPack = {
|
||||
name: QUERY_PACK_NAME,
|
||||
version: "0.0.0",
|
||||
dependencies: {
|
||||
[`codeql/${language}-all`]: "*",
|
||||
},
|
||||
defaultSuite: generateDefaultSuite(packRelativePath),
|
||||
};
|
||||
await writeFile(
|
||||
join(queryPackDir, FALLBACK_QLPACK_FILENAME),
|
||||
dump(syntheticQueryPack),
|
||||
await createNewQueryPack(
|
||||
queryFile,
|
||||
queryPackDir,
|
||||
targetQueryFileName,
|
||||
language,
|
||||
packRelativePath,
|
||||
);
|
||||
}
|
||||
if (!language) {
|
||||
@@ -149,12 +123,21 @@ async function generateQueryPack(
|
||||
precompilationOpts = ["--no-precompile"];
|
||||
}
|
||||
|
||||
if (await cliServer.useExtensionPacks()) {
|
||||
await injectExtensionPacks(cliServer, queryPackDir, workspaceFolders);
|
||||
}
|
||||
|
||||
await cliServer.packInstall(queryPackDir, {
|
||||
workspaceFolders,
|
||||
});
|
||||
|
||||
// Clear the CLI cache so that the most recent qlpack lock file is used.
|
||||
await cliServer.clearCache();
|
||||
|
||||
const bundlePath = await getPackedBundlePath(queryPackDir);
|
||||
void extLogger.log(
|
||||
`Compiling and bundling query pack from ${queryPackDir} to ${bundlePath}. (This may take a while.)`,
|
||||
);
|
||||
await cliServer.packInstall(queryPackDir);
|
||||
const workspaceFolders = getOnDiskWorkspaceFolders();
|
||||
await cliServer.packBundle(
|
||||
queryPackDir,
|
||||
workspaceFolders,
|
||||
@@ -168,6 +151,70 @@ async function generateQueryPack(
|
||||
};
|
||||
}
|
||||
|
||||
async function createNewQueryPack(
|
||||
queryFile: string,
|
||||
queryPackDir: string,
|
||||
targetQueryFileName: string,
|
||||
language: string | undefined,
|
||||
packRelativePath: string,
|
||||
) {
|
||||
void extLogger.log(`Copying ${queryFile} to ${queryPackDir}`);
|
||||
await copy(queryFile, targetQueryFileName);
|
||||
void extLogger.log("Generating synthetic query pack");
|
||||
const syntheticQueryPack = {
|
||||
name: QUERY_PACK_NAME,
|
||||
version: "0.0.0",
|
||||
dependencies: {
|
||||
[`codeql/${language}-all`]: "*",
|
||||
},
|
||||
defaultSuite: generateDefaultSuite(packRelativePath),
|
||||
};
|
||||
await writeFile(
|
||||
join(queryPackDir, FALLBACK_QLPACK_FILENAME),
|
||||
dump(syntheticQueryPack),
|
||||
);
|
||||
}
|
||||
|
||||
async function copyExistingQueryPack(
|
||||
cliServer: cli.CodeQLCliServer,
|
||||
originalPackRoot: string,
|
||||
queryFile: string,
|
||||
queryPackDir: string,
|
||||
packRelativePath: string,
|
||||
) {
|
||||
const toCopy = await cliServer.packPacklist(originalPackRoot, false);
|
||||
|
||||
[
|
||||
// also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist.
|
||||
...QLPACK_LOCK_FILENAMES.map((f) => join(originalPackRoot, f)),
|
||||
queryFile,
|
||||
].forEach((absolutePath) => {
|
||||
if (absolutePath) {
|
||||
toCopy.push(absolutePath);
|
||||
}
|
||||
});
|
||||
|
||||
let copiedCount = 0;
|
||||
await copy(originalPackRoot, queryPackDir, {
|
||||
filter: (file: string) =>
|
||||
// copy file if it is in the packlist, or it is a parent directory of a file in the packlist
|
||||
!!toCopy.find((f) => {
|
||||
// Normalized paths ensure that Windows drive letters are capitalized consistently.
|
||||
const normalizedPath = Uri.file(f).fsPath;
|
||||
const matches =
|
||||
normalizedPath === file || normalizedPath.startsWith(file + sep);
|
||||
if (matches) {
|
||||
copiedCount++;
|
||||
}
|
||||
return matches;
|
||||
}),
|
||||
});
|
||||
|
||||
void extLogger.log(`Copied ${copiedCount} files to ${queryPackDir}`);
|
||||
|
||||
await fixPackFile(queryPackDir, packRelativePath);
|
||||
}
|
||||
|
||||
async function findPackRoot(queryFile: string): Promise<string> {
|
||||
// recursively find the directory containing qlpack.yml or codeql-pack.yml
|
||||
let dir = dirname(queryFile);
|
||||
@@ -329,19 +376,54 @@ async function fixPackFile(
|
||||
}
|
||||
const qlpack = load(await readFile(packPath, "utf8")) as QlPack;
|
||||
|
||||
// update pack name
|
||||
qlpack.name = QUERY_PACK_NAME;
|
||||
|
||||
// update default suite
|
||||
delete qlpack.defaultSuiteFile;
|
||||
qlpack.defaultSuite = generateDefaultSuite(packRelativePath);
|
||||
|
||||
// remove any ${workspace} version references
|
||||
updateDefaultSuite(qlpack, packRelativePath);
|
||||
removeWorkspaceRefs(qlpack);
|
||||
|
||||
await writeFile(packPath, dump(qlpack));
|
||||
}
|
||||
|
||||
async function injectExtensionPacks(
|
||||
cliServer: cli.CodeQLCliServer,
|
||||
queryPackDir: string,
|
||||
workspaceFolders: string[],
|
||||
) {
|
||||
const qlpackFile = await getQlPackPath(queryPackDir);
|
||||
if (!qlpackFile) {
|
||||
throw new Error(
|
||||
`Could not find ${QLPACK_FILENAMES.join(
|
||||
" or ",
|
||||
)} file in '${queryPackDir}'`,
|
||||
);
|
||||
}
|
||||
const syntheticQueryPack = load(await readFile(qlpackFile, "utf8")) as QlPack;
|
||||
|
||||
const extensionPacks = await cliServer.resolveQlpacks(workspaceFolders, true);
|
||||
Object.entries(extensionPacks).forEach(([name, paths]) => {
|
||||
// We are guaranteed that there is at least one path found for each extension pack.
|
||||
// If there are multiple paths, then we have a problem. This means that there is
|
||||
// ambiguity in which path to use. This is an error.
|
||||
if (paths.length > 1) {
|
||||
throw new Error(
|
||||
`Multiple versions of extension pack '${name}' found: ${paths.join(
|
||||
", ",
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
// Add this extension pack as a dependency. It doesn't matter which
|
||||
// version we specify, since we are guaranteed that the extension pack
|
||||
// is resolved from source at the given path.
|
||||
syntheticQueryPack.dependencies[name] = "*";
|
||||
});
|
||||
await writeFile(qlpackFile, dump(syntheticQueryPack));
|
||||
await cliServer.clearCache();
|
||||
}
|
||||
|
||||
function updateDefaultSuite(qlpack: QlPack, packRelativePath: string) {
|
||||
delete qlpack.defaultSuiteFile;
|
||||
qlpack.defaultSuite = generateDefaultSuite(packRelativePath);
|
||||
}
|
||||
|
||||
function generateDefaultSuite(packRelativePath: string) {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -66,10 +66,7 @@ import { URLSearchParams } from "url";
|
||||
import { DbManager } from "../databases/db-manager";
|
||||
import { App } from "../common/app";
|
||||
import { redactableError } from "../pure/errors";
|
||||
import {
|
||||
ExtensionCommandManager,
|
||||
VariantAnalysisCommands,
|
||||
} from "../common/commands";
|
||||
import { AppCommandManager, VariantAnalysisCommands } from "../common/commands";
|
||||
|
||||
export class VariantAnalysisManager
|
||||
extends DisposableObject
|
||||
@@ -144,8 +141,8 @@ export class VariantAnalysisManager
|
||||
};
|
||||
}
|
||||
|
||||
get commandManager(): ExtensionCommandManager {
|
||||
return this.app.commandManager;
|
||||
get commandManager(): AppCommandManager {
|
||||
return this.app.commands;
|
||||
}
|
||||
|
||||
private async runVariantAnalysisFromCommand(uri?: Uri) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisScannedRepositoryState,
|
||||
} from "./shared/variant-analysis";
|
||||
import { ExtensionCommandManager } from "../common/commands";
|
||||
import { AppCommandManager } from "../common/commands";
|
||||
|
||||
export interface VariantAnalysisViewInterface {
|
||||
variantAnalysisId: number;
|
||||
@@ -12,7 +12,7 @@ export interface VariantAnalysisViewInterface {
|
||||
export interface VariantAnalysisViewManager<
|
||||
T extends VariantAnalysisViewInterface,
|
||||
> {
|
||||
commandManager: ExtensionCommandManager;
|
||||
commandManager: AppCommandManager;
|
||||
|
||||
registerView(view: T): void;
|
||||
unregisterView(view: T): void;
|
||||
|
||||
@@ -145,7 +145,7 @@ export class VariantAnalysisView
|
||||
);
|
||||
break;
|
||||
case "openLogs":
|
||||
await this.manager.commandManager.executeCommand(
|
||||
await this.manager.commandManager.execute(
|
||||
"codeQL.openVariantAnalysisLogs",
|
||||
this.variantAnalysisId,
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createMockLogger } from "./loggerMock";
|
||||
import { createMockMemento } from "../mock-memento";
|
||||
import { testCredentialsWithStub } from "../factories/authentication";
|
||||
import { Credentials } from "../../src/common/authentication";
|
||||
import { ExtensionCommandManager } from "../../src/common/commands";
|
||||
import { AppCommandManager } from "../../src/common/commands";
|
||||
import { createMockCommandManager } from "./commandsMock";
|
||||
|
||||
export function createMockApp({
|
||||
@@ -17,7 +17,7 @@ export function createMockApp({
|
||||
executeCommand = jest.fn(() => Promise.resolve()),
|
||||
workspaceState = createMockMemento(),
|
||||
credentials = testCredentialsWithStub(),
|
||||
commandManager = createMockCommandManager(),
|
||||
commands = createMockCommandManager(),
|
||||
}: {
|
||||
extensionPath?: string;
|
||||
workspaceStoragePath?: string;
|
||||
@@ -26,7 +26,7 @@ export function createMockApp({
|
||||
executeCommand?: () => Promise<void>;
|
||||
workspaceState?: Memento;
|
||||
credentials?: Credentials;
|
||||
commandManager?: ExtensionCommandManager;
|
||||
commands?: AppCommandManager;
|
||||
}): App {
|
||||
return {
|
||||
mode: AppMode.Test,
|
||||
@@ -39,7 +39,7 @@ export function createMockApp({
|
||||
createEventEmitter,
|
||||
executeCommand,
|
||||
credentials,
|
||||
commandManager,
|
||||
commands,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ExtensionCommandManager } from "../../src/common/commands";
|
||||
import {
|
||||
CommandFunction,
|
||||
CommandManager,
|
||||
Disposable,
|
||||
} from "../../src/packages/commands";
|
||||
import { AppCommandManager } from "../../src/common/commands";
|
||||
import { CommandFunction, CommandManager } from "../../src/packages/commands";
|
||||
import { Disposable } from "../../src/packages/commands/Disposable";
|
||||
|
||||
export function createMockCommandManager({
|
||||
registerCommand = jest.fn(),
|
||||
@@ -11,6 +8,6 @@ export function createMockCommandManager({
|
||||
}: {
|
||||
registerCommand?: (commandName: string, fn: CommandFunction) => Disposable;
|
||||
executeCommand?: (commandName: string, ...args: any[]) => Promise<any>;
|
||||
} = {}): ExtensionCommandManager {
|
||||
} = {}): AppCommandManager {
|
||||
return new CommandManager(registerCommand, executeCommand);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ describe("CommandManager", () => {
|
||||
jest.fn(),
|
||||
);
|
||||
const myCommand = jest.fn();
|
||||
commandManager.registerCommand("abc", myCommand);
|
||||
commandManager.register("abc", myCommand);
|
||||
expect(commandRegister).toHaveBeenCalledTimes(1);
|
||||
expect(commandRegister).toHaveBeenCalledWith("abc", myCommand);
|
||||
});
|
||||
@@ -28,22 +28,22 @@ describe("CommandManager", () => {
|
||||
);
|
||||
|
||||
// @ts-expect-error wrong command name should give a type error
|
||||
commandManager.registerCommand("abc", jest.fn());
|
||||
commandManager.register("abc", jest.fn());
|
||||
|
||||
commandManager.registerCommand(
|
||||
commandManager.register(
|
||||
"codeQL.openVariantAnalysisLogs",
|
||||
// @ts-expect-error wrong function parameter type should give a type error
|
||||
async (variantAnalysisId: string): Promise<number> => 10,
|
||||
);
|
||||
|
||||
commandManager.registerCommand(
|
||||
commandManager.register(
|
||||
"codeQL.openVariantAnalysisLogs",
|
||||
// @ts-expect-error wrong function return type should give a type error
|
||||
async (variantAnalysisId: number): Promise<string> => "hello",
|
||||
);
|
||||
|
||||
// Working types
|
||||
commandManager.registerCommand(
|
||||
commandManager.register(
|
||||
"codeQL.openVariantAnalysisLogs",
|
||||
async (variantAnalysisId: number): Promise<number> =>
|
||||
variantAnalysisId * 10,
|
||||
@@ -61,8 +61,8 @@ describe("CommandManager", () => {
|
||||
commandRegister,
|
||||
jest.fn(),
|
||||
);
|
||||
commandManager.registerCommand("abc", jest.fn());
|
||||
commandManager.registerCommand("def", jest.fn());
|
||||
commandManager.register("abc", jest.fn());
|
||||
commandManager.register("def", jest.fn());
|
||||
expect(dispose1).not.toHaveBeenCalled();
|
||||
expect(dispose2).not.toHaveBeenCalled();
|
||||
commandManager.dispose();
|
||||
@@ -76,7 +76,7 @@ describe("CommandManager", () => {
|
||||
jest.fn(),
|
||||
commandExecute,
|
||||
);
|
||||
const result = await commandManager.executeCommand("abc", "hello", true);
|
||||
const result = await commandManager.execute("abc", "hello", true);
|
||||
expect(result).toEqual(7);
|
||||
expect(commandExecute).toHaveBeenCalledTimes(1);
|
||||
expect(commandExecute).toHaveBeenCalledWith("abc", "hello", true);
|
||||
@@ -94,18 +94,18 @@ describe("CommandManager", () => {
|
||||
);
|
||||
|
||||
// @ts-expect-error wrong command name should give a type error
|
||||
await commandManager.executeCommand("abc", 4);
|
||||
await commandManager.execute("abc", 4);
|
||||
|
||||
await commandManager.executeCommand(
|
||||
await commandManager.execute(
|
||||
"codeQL.openVariantAnalysisLogs",
|
||||
// @ts-expect-error wrong argument type should give a type error
|
||||
"xyz",
|
||||
);
|
||||
|
||||
// @ts-expect-error wrong number of arguments should give a type error
|
||||
await commandManager.executeCommand("codeQL.openVariantAnalysisLogs", 2, 3);
|
||||
await commandManager.execute("codeQL.openVariantAnalysisLogs", 2, 3);
|
||||
|
||||
// Working types
|
||||
await commandManager.executeCommand("codeQL.openVariantAnalysisLogs", 7);
|
||||
await commandManager.execute("codeQL.openVariantAnalysisLogs", 7);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- [ "@example/read-write-user-data", "Member[readUserData].ReturnValue.Awaited", "remote" ]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- [ "@example/read-write-user-data", "Member[writeUserData].Argument[0]", "command-line-injection" ]
|
||||
@@ -0,0 +1,8 @@
|
||||
name: github/extension-pack-for-testing
|
||||
version: 0.0.0
|
||||
library: true
|
||||
extensionTargets:
|
||||
github/remote-query-pack: "*"
|
||||
|
||||
dataExtensions:
|
||||
- extension-file.yml
|
||||
@@ -12,7 +12,7 @@ import * as ghApiClient from "../../../../src/variant-analysis/gh-api/gh-api-cli
|
||||
import { join } from "path";
|
||||
|
||||
import { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
|
||||
import { CodeQLCliServer } from "../../../../src/cli";
|
||||
import { CliVersionConstraint, CodeQLCliServer } from "../../../../src/cli";
|
||||
import {
|
||||
fixWorkspaceReferences,
|
||||
restoreWorkspaceReferences,
|
||||
@@ -255,6 +255,30 @@ describe("Variant Analysis Manager", () => {
|
||||
qlxFilesThatExist: ["subfolder/in-pack.qlx"],
|
||||
});
|
||||
});
|
||||
|
||||
it("should run a remote query with extension packs inside a qlpack", async () => {
|
||||
if (!(await cli.cliConstraints.supportsQlpacksKind())) {
|
||||
console.log(
|
||||
`Skipping test because qlpacks kind is only suppported in CLI version ${CliVersionConstraint.CLI_VERSION_WITH_QLPACKS_KIND} or later.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await cli.setUseExtensionPacks(true);
|
||||
await doVariantAnalysisTest({
|
||||
queryPath: "data-remote-qlpack-nested/subfolder/in-pack.ql",
|
||||
filesThatExist: [
|
||||
"subfolder/in-pack.ql",
|
||||
"otherfolder/lib.qll",
|
||||
".codeql/libraries/semmle/targets-extension/0.0.0/ext/extension.yml",
|
||||
],
|
||||
filesThatDoNotExist: ["subfolder/not-in-pack.ql"],
|
||||
qlxFilesThatExist: ["subfolder/in-pack.qlx"],
|
||||
dependenciesToCheck: [
|
||||
"codeql/javascript-all",
|
||||
"semmle/targets-extension",
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function doVariantAnalysisTest({
|
||||
@@ -262,11 +286,13 @@ describe("Variant Analysis Manager", () => {
|
||||
filesThatExist,
|
||||
qlxFilesThatExist,
|
||||
filesThatDoNotExist,
|
||||
dependenciesToCheck = ["codeql/javascript-all"],
|
||||
}: {
|
||||
queryPath: string;
|
||||
filesThatExist: string[];
|
||||
qlxFilesThatExist: string[];
|
||||
filesThatDoNotExist: string[];
|
||||
dependenciesToCheck?: string[];
|
||||
}) {
|
||||
const fileUri = getFile(queryPath);
|
||||
await variantAnalysisManager.runVariantAnalysis(
|
||||
@@ -328,8 +354,10 @@ describe("Variant Analysis Manager", () => {
|
||||
|
||||
const actualLockKeys = Object.keys(qlpackLockContents.dependencies);
|
||||
|
||||
// The lock file should contain at least codeql/javascript-all.
|
||||
expect(actualLockKeys).toContain("codeql/javascript-all");
|
||||
// The lock file should contain at least the specified dependencies.
|
||||
dependenciesToCheck.forEach((dep) =>
|
||||
expect(actualLockKeys).toContain(dep),
|
||||
);
|
||||
}
|
||||
|
||||
function getFile(file: string): Uri {
|
||||
|
||||
Reference in New Issue
Block a user