Merge remote-tracking branch 'origin/main' into koesie10/improve-error-message
This commit is contained in:
1
.github/workflows/cli-test.yml
vendored
1
.github/workflows/cli-test.yml
vendored
@@ -11,6 +11,7 @@ on:
|
|||||||
- extensions/ql-vscode/src/language-support/**
|
- extensions/ql-vscode/src/language-support/**
|
||||||
- extensions/ql-vscode/src/query-server/**
|
- extensions/ql-vscode/src/query-server/**
|
||||||
- extensions/ql-vscode/supported_cli_versions.json
|
- extensions/ql-vscode/supported_cli_versions.json
|
||||||
|
- extensions/ql-vscode/src/variant-analysis/run-remote-query.ts
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
find-nightly:
|
find-nightly:
|
||||||
|
|||||||
46
.github/workflows/e2e-tests.yml
vendored
Normal file
46
.github/workflows/e2e-tests.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: Run E2E Playwright tests
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
e2e-test:
|
||||||
|
name: E2E Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: extensions/ql-vscode/.nvmrc
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: extensions/ql-vscode
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Start containers
|
||||||
|
working-directory: extensions/ql-vscode/test/e2e
|
||||||
|
run: docker-compose -f "docker-compose.yml" up -d --build
|
||||||
|
|
||||||
|
- name: Install Playwright Browsers
|
||||||
|
working-directory: extensions/ql-vscode
|
||||||
|
run: npx playwright install --with-deps
|
||||||
|
- name: Run Playwright tests
|
||||||
|
working-directory: extensions/ql-vscode/test/e2e
|
||||||
|
run: npx playwright test
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: extensions/ql-vscode/playwright-report/
|
||||||
|
retention-days: 30
|
||||||
|
- name: Stop containers
|
||||||
|
working-directory: extensions/ql-vscode/test/e2e
|
||||||
|
if: always()
|
||||||
|
run: docker-compose -f "docker-compose.yml" down -v
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -19,3 +19,7 @@ artifacts/
|
|||||||
# CodeQL metadata
|
# CodeQL metadata
|
||||||
.cache/
|
.cache/
|
||||||
.codeql/
|
.codeql/
|
||||||
|
|
||||||
|
# E2E Reports
|
||||||
|
**/playwright-report/**
|
||||||
|
**/test-results/**
|
||||||
@@ -28,6 +28,7 @@ const baseConfig = {
|
|||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
"plugin:import/recommended",
|
"plugin:import/recommended",
|
||||||
"plugin:import/typescript",
|
"plugin:import/typescript",
|
||||||
|
"plugin:deprecation/recommended",
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/await-thenable": "error",
|
"@typescript-eslint/await-thenable": "error",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { addons, types } from "@storybook/manager-api";
|
import { addons } from "@storybook/manager-api";
|
||||||
|
import { Addon_TypesEnum } from "@storybook/types";
|
||||||
import { ThemeSelector } from "./ThemeSelector";
|
import { ThemeSelector } from "./ThemeSelector";
|
||||||
|
|
||||||
const ADDON_ID = "vscode-theme-addon";
|
const ADDON_ID = "vscode-theme-addon";
|
||||||
@@ -7,7 +8,7 @@ const ADDON_ID = "vscode-theme-addon";
|
|||||||
addons.register(ADDON_ID, () => {
|
addons.register(ADDON_ID, () => {
|
||||||
addons.add(ADDON_ID, {
|
addons.add(ADDON_ID, {
|
||||||
title: "VSCode Themes",
|
title: "VSCode Themes",
|
||||||
type: types.TOOL,
|
type: Addon_TypesEnum.TOOL,
|
||||||
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
|
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
|
||||||
render: () => <ThemeSelector />,
|
render: () => <ThemeSelector />,
|
||||||
});
|
});
|
||||||
|
|||||||
717
extensions/ql-vscode/package-lock.json
generated
717
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1952,7 +1952,7 @@
|
|||||||
"source-map": "^0.7.4",
|
"source-map": "^0.7.4",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"stream-json": "^1.7.3",
|
"stream-json": "^1.7.3",
|
||||||
"styled-components": "^6.0.2",
|
"styled-components": "^6.1.8",
|
||||||
"tmp": "^0.2.1",
|
"tmp": "^0.2.1",
|
||||||
"tmp-promise": "^3.0.2",
|
"tmp-promise": "^3.0.2",
|
||||||
"tree-kill": "^1.2.2",
|
"tree-kill": "^1.2.2",
|
||||||
@@ -1960,7 +1960,7 @@
|
|||||||
"vscode-jsonrpc": "^8.0.2",
|
"vscode-jsonrpc": "^8.0.2",
|
||||||
"vscode-languageclient": "^8.0.2",
|
"vscode-languageclient": "^8.0.2",
|
||||||
"yauzl": "^2.10.0",
|
"yauzl": "^2.10.0",
|
||||||
"zip-a-folder": "^3.1.3"
|
"zip-a-folder": "^3.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.18.13",
|
"@babel/core": "^7.18.13",
|
||||||
@@ -1971,6 +1971,7 @@
|
|||||||
"@faker-js/faker": "^8.0.2",
|
"@faker-js/faker": "^8.0.2",
|
||||||
"@github/markdownlint-github": "^0.6.0",
|
"@github/markdownlint-github": "^0.6.0",
|
||||||
"@octokit/plugin-throttling": "^8.0.0",
|
"@octokit/plugin-throttling": "^8.0.0",
|
||||||
|
"@playwright/test": "^1.40.1",
|
||||||
"@storybook/addon-a11y": "^7.6.9",
|
"@storybook/addon-a11y": "^7.6.9",
|
||||||
"@storybook/addon-actions": "^7.1.0",
|
"@storybook/addon-actions": "^7.1.0",
|
||||||
"@storybook/addon-essentials": "^7.1.0",
|
"@storybook/addon-essentials": "^7.1.0",
|
||||||
@@ -1999,7 +2000,7 @@
|
|||||||
"@types/node": "18.15.*",
|
"@types/node": "18.15.*",
|
||||||
"@types/node-fetch": "^2.5.2",
|
"@types/node-fetch": "^2.5.2",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.2.18",
|
||||||
"@types/sarif": "^2.1.2",
|
"@types/sarif": "^2.1.2",
|
||||||
"@types/semver": "^7.2.0",
|
"@types/semver": "^7.2.0",
|
||||||
"@types/stream-json": "^1.7.1",
|
"@types/stream-json": "^1.7.1",
|
||||||
@@ -2023,6 +2024,7 @@
|
|||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
|
"eslint-plugin-deprecation": "^2.0.0",
|
||||||
"eslint-plugin-etc": "^2.0.2",
|
"eslint-plugin-etc": "^2.0.2",
|
||||||
"eslint-plugin-github": "^4.4.1",
|
"eslint-plugin-github": "^4.4.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
@@ -2042,13 +2044,13 @@
|
|||||||
"jest-environment-jsdom": "^29.0.3",
|
"jest-environment-jsdom": "^29.0.3",
|
||||||
"jest-runner-vscode": "^3.0.1",
|
"jest-runner-vscode": "^3.0.1",
|
||||||
"lint-staged": "^15.0.2",
|
"lint-staged": "^15.0.2",
|
||||||
"markdownlint-cli2": "^0.11.0",
|
"markdownlint-cli2": "^0.12.1",
|
||||||
"markdownlint-cli2-formatter-pretty": "^0.0.5",
|
"markdownlint-cli2-formatter-pretty": "^0.0.5",
|
||||||
"mini-css-extract-plugin": "^2.6.1",
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.2.4",
|
||||||
"storybook": "^7.6.7",
|
"storybook": "^7.6.10",
|
||||||
"tar-stream": "^3.0.0",
|
"tar-stream": "^3.0.0",
|
||||||
"through2": "^4.0.2",
|
"through2": "^4.0.2",
|
||||||
"ts-jest": "^29.0.1",
|
"ts-jest": "^29.0.1",
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ function ignoreFile(file: string): boolean {
|
|||||||
) ||
|
) ||
|
||||||
basename(file) === "jest.config.ts" ||
|
basename(file) === "jest.config.ts" ||
|
||||||
basename(file) === "index.tsx" ||
|
basename(file) === "index.tsx" ||
|
||||||
basename(file) === "index.ts"
|
basename(file) === "index.ts" ||
|
||||||
|
basename(file) === "playwright.config.ts"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ export async function getVersionInformation(
|
|||||||
vscodeVersion: string,
|
vscodeVersion: string,
|
||||||
): Promise<VersionInformation> {
|
): Promise<VersionInformation> {
|
||||||
const vsCodePackageJson = await getVsCodePackageJson(vscodeVersion);
|
const vsCodePackageJson = await getVsCodePackageJson(vscodeVersion);
|
||||||
const electronVersion = minVersion(vsCodePackageJson.devDependencies.electron)
|
const electronVersion = minVersion(
|
||||||
?.version;
|
vsCodePackageJson.devDependencies.electron,
|
||||||
|
)?.version;
|
||||||
if (!electronVersion) {
|
if (!electronVersion) {
|
||||||
throw new Error("Could not find Electron version");
|
throw new Error("Could not find Electron version");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1739,6 +1739,15 @@ export class CliVersionConstraint {
|
|||||||
*/
|
*/
|
||||||
public static CLI_VERSION_WITH_TRIM_CACHE = new SemVer("2.15.1");
|
public static CLI_VERSION_WITH_TRIM_CACHE = new SemVer("2.15.1");
|
||||||
|
|
||||||
|
public static CLI_VERSION_WITHOUT_MRVA_EXTENSIBLE_PREDICATE_HACK = new SemVer(
|
||||||
|
"2.16.1",
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI version where there is support for multiple queries on the pack create command.
|
||||||
|
*/
|
||||||
|
public static CLI_VERSION_WITH_MULTI_QUERY_PACK_CREATE = new SemVer("2.16.1");
|
||||||
|
|
||||||
constructor(private readonly cli: CodeQLCliServer) {
|
constructor(private readonly cli: CodeQLCliServer) {
|
||||||
/**/
|
/**/
|
||||||
}
|
}
|
||||||
@@ -1781,6 +1790,19 @@ export class CliVersionConstraint {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async preservesExtensiblePredicatesInMrvaPack() {
|
||||||
|
// Negated, because we _stopped_ preserving these in 2.16.1.
|
||||||
|
return !(await this.isVersionAtLeast(
|
||||||
|
CliVersionConstraint.CLI_VERSION_WITHOUT_MRVA_EXTENSIBLE_PREDICATE_HACK,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
async supportsPackCreateWithMultipleQueries() {
|
||||||
|
return this.isVersionAtLeast(
|
||||||
|
CliVersionConstraint.CLI_VERSION_WITH_MULTI_QUERY_PACK_CREATE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async supportsMrvaPackCreate(): Promise<boolean> {
|
async supportsMrvaPackCreate(): Promise<boolean> {
|
||||||
return (await this.cli.getFeatures()).mrvaPackCreate === true;
|
return (await this.cli.getFeatures()).mrvaPackCreate === true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { join } from "path";
|
import { dirname, join, parse } from "path";
|
||||||
import { pathExists } from "fs-extra";
|
import { pathExists } from "fs-extra";
|
||||||
|
|
||||||
export const QLPACK_FILENAMES = ["qlpack.yml", "codeql-pack.yml"];
|
export const QLPACK_FILENAMES = ["qlpack.yml", "codeql-pack.yml"];
|
||||||
@@ -8,7 +8,13 @@ export const QLPACK_LOCK_FILENAMES = [
|
|||||||
];
|
];
|
||||||
export const FALLBACK_QLPACK_FILENAME = QLPACK_FILENAMES[0];
|
export const FALLBACK_QLPACK_FILENAME = QLPACK_FILENAMES[0];
|
||||||
|
|
||||||
export async function getQlPackPath(
|
/**
|
||||||
|
* Gets the path to the QL pack file (a qlpack.yml or
|
||||||
|
* codeql-pack.yml).
|
||||||
|
* @param packRoot The root of the pack.
|
||||||
|
* @returns The path to the qlpack file, or undefined if it doesn't exist.
|
||||||
|
*/
|
||||||
|
export async function getQlPackFilePath(
|
||||||
packRoot: string,
|
packRoot: string,
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
for (const filename of QLPACK_FILENAMES) {
|
for (const filename of QLPACK_FILENAMES) {
|
||||||
@@ -21,3 +27,28 @@ export async function getQlPackPath(
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively find the directory containing qlpack.yml or codeql-pack.yml. If
|
||||||
|
* no such directory is found, the directory containing the query file is returned.
|
||||||
|
* @param queryFile The query file to start from.
|
||||||
|
* @returns The path to the pack root.
|
||||||
|
*/
|
||||||
|
export async function findPackRoot(queryFile: string): Promise<string> {
|
||||||
|
let dir = dirname(queryFile);
|
||||||
|
while (!(await getQlPackFilePath(dir))) {
|
||||||
|
dir = dirname(dir);
|
||||||
|
if (isFileSystemRoot(dir)) {
|
||||||
|
// there is no qlpack.yml or codeql-pack.yml in this directory or any parent directory.
|
||||||
|
// just use the query file's directory as the pack root.
|
||||||
|
return dirname(queryFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFileSystemRoot(dir: string): boolean {
|
||||||
|
const pathObj = parse(dir);
|
||||||
|
return pathObj.root === dir && pathObj.base === "";
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
export type DeepReadonly<T> = T extends Array<infer R>
|
export type DeepReadonly<T> =
|
||||||
? DeepReadonlyArray<R>
|
T extends Array<infer R>
|
||||||
: // eslint-disable-next-line @typescript-eslint/ban-types
|
? DeepReadonlyArray<R>
|
||||||
T extends Function
|
: // eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
? T
|
T extends Function
|
||||||
: T extends object
|
? T
|
||||||
? DeepReadonlyObject<T>
|
: T extends object
|
||||||
: T;
|
? DeepReadonlyObject<T>
|
||||||
|
: T;
|
||||||
|
|
||||||
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
|
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
|
||||||
|
|
||||||
|
|||||||
@@ -716,12 +716,17 @@ const LLM_GENERATION_DEV_ENDPOINT = new Setting(
|
|||||||
);
|
);
|
||||||
const EXTENSIONS_DIRECTORY = new Setting("extensionsDirectory", MODEL_SETTING);
|
const EXTENSIONS_DIRECTORY = new Setting("extensionsDirectory", MODEL_SETTING);
|
||||||
const ENABLE_RUBY = new Setting("enableRuby", MODEL_SETTING);
|
const ENABLE_RUBY = new Setting("enableRuby", MODEL_SETTING);
|
||||||
|
const ENABLE_ACCESS_PATH_SUGGESTIONS = new Setting(
|
||||||
|
"enableAccessPathSuggestions",
|
||||||
|
MODEL_SETTING,
|
||||||
|
);
|
||||||
|
|
||||||
export interface ModelConfig {
|
export interface ModelConfig {
|
||||||
flowGeneration: boolean;
|
flowGeneration: boolean;
|
||||||
llmGeneration: boolean;
|
llmGeneration: boolean;
|
||||||
getExtensionsDirectory(languageId: string): string | undefined;
|
getExtensionsDirectory(languageId: string): string | undefined;
|
||||||
enableRuby: boolean;
|
enableRuby: boolean;
|
||||||
|
enableAccessPathSuggestions: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||||
@@ -762,6 +767,10 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
|||||||
public get enableRuby(): boolean {
|
public get enableRuby(): boolean {
|
||||||
return !!ENABLE_RUBY.getValue<boolean>();
|
return !!ENABLE_RUBY.getValue<boolean>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get enableAccessPathSuggestions(): boolean {
|
||||||
|
return !!ENABLE_ACCESS_PATH_SUGGESTIONS.getValue<boolean>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const GITHUB_DATABASE_SETTING = new Setting("githubDatabase", ROOT_SETTING);
|
const GITHUB_DATABASE_SETTING = new Setting("githubDatabase", ROOT_SETTING);
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ async function chooseDatabaseDir(byFolder: boolean): Promise<Uri | undefined> {
|
|||||||
return getFirst(chosen);
|
return getFirst(chosen);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DatabaseSelectionQuickPickItem extends QuickPickItem {
|
export interface DatabaseSelectionQuickPickItem extends QuickPickItem {
|
||||||
databaseKind: "new" | "existing";
|
databaseKind: "new" | "existing";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ export interface DatabaseQuickPickItem extends QuickPickItem {
|
|||||||
databaseItem: DatabaseItem;
|
databaseItem: DatabaseItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DatabaseImportQuickPickItems extends QuickPickItem {
|
export interface DatabaseImportQuickPickItems extends QuickPickItem {
|
||||||
importType: "URL" | "github" | "archive" | "folder";
|
importType: "URL" | "github" | "archive" | "folder";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { glob } from "glob";
|
|||||||
import { basename } from "path";
|
import { basename } from "path";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { readFile } from "fs-extra";
|
import { readFile } from "fs-extra";
|
||||||
import { getQlPackPath } from "../common/ql";
|
import { getQlPackFilePath } from "../common/ql";
|
||||||
import type { CodeQLCliServer, QlpacksInfo } from "../codeql-cli/cli";
|
import type { CodeQLCliServer, QlpacksInfo } from "../codeql-cli/cli";
|
||||||
import { extLogger } from "../common/logging/vscode";
|
import { extLogger } from "../common/logging/vscode";
|
||||||
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
||||||
@@ -31,7 +31,7 @@ async function findDbschemePack(
|
|||||||
): Promise<{ name: string; isLibraryPack: boolean }> {
|
): Promise<{ name: string; isLibraryPack: boolean }> {
|
||||||
for (const { packDir, packName } of packs) {
|
for (const { packDir, packName } of packs) {
|
||||||
if (packDir !== undefined) {
|
if (packDir !== undefined) {
|
||||||
const qlpackPath = await getQlPackPath(packDir);
|
const qlpackPath = await getQlPackFilePath(packDir);
|
||||||
|
|
||||||
if (qlpackPath !== undefined) {
|
if (qlpackPath !== undefined) {
|
||||||
const qlpack = load(await readFile(qlpackPath, "utf8")) as {
|
const qlpack = load(await readFile(qlpackPath, "utf8")) as {
|
||||||
|
|||||||
@@ -215,8 +215,9 @@ function getCommands(
|
|||||||
"codeQL.restartLegacyQueryServerOnConfigChange": restartQueryServer,
|
"codeQL.restartLegacyQueryServerOnConfigChange": restartQueryServer,
|
||||||
"codeQL.restartQueryServerOnExternalConfigChange": restartQueryServer,
|
"codeQL.restartQueryServerOnExternalConfigChange": restartQueryServer,
|
||||||
"codeQL.copyVersion": async () => {
|
"codeQL.copyVersion": async () => {
|
||||||
const text = `CodeQL extension version: ${extension?.packageJSON
|
const text = `CodeQL extension version: ${
|
||||||
.version} \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${platform()} ${arch()}`;
|
extension?.packageJSON.version
|
||||||
|
} \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${platform()} ${arch()}`;
|
||||||
await env.clipboard.writeText(text);
|
await env.clipboard.writeText(text);
|
||||||
void showAndLogInformationMessage(extLogger, text);
|
void showAndLogInformationMessage(extLogger, text);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { getPrimaryDbscheme, getQlPackForDbscheme } from "../databases/qlpack";
|
|||||||
import type { ProgressCallback } from "../common/vscode/progress";
|
import type { ProgressCallback } from "../common/vscode/progress";
|
||||||
import { UserCancellationException } from "../common/vscode/progress";
|
import { UserCancellationException } from "../common/vscode/progress";
|
||||||
import { getErrorMessage } from "../common/helpers-pure";
|
import { getErrorMessage } from "../common/helpers-pure";
|
||||||
import { FALLBACK_QLPACK_FILENAME, getQlPackPath } from "../common/ql";
|
import { FALLBACK_QLPACK_FILENAME, getQlPackFilePath } from "../common/ql";
|
||||||
import type { App } from "../common/app";
|
import type { App } from "../common/app";
|
||||||
import type { ExtensionApp } from "../common/vscode/vscode-app";
|
import type { ExtensionApp } from "../common/vscode/vscode-app";
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ export async function displayQuickQuery(
|
|||||||
const dbscheme = await getPrimaryDbscheme(datasetFolder);
|
const dbscheme = await getPrimaryDbscheme(datasetFolder);
|
||||||
const qlpack = (await getQlPackForDbscheme(cliServer, dbscheme))
|
const qlpack = (await getQlPackForDbscheme(cliServer, dbscheme))
|
||||||
.dbschemePack;
|
.dbschemePack;
|
||||||
const qlPackFile = await getQlPackPath(queriesDir);
|
const qlPackFile = await getQlPackFilePath(queriesDir);
|
||||||
const qlFile = join(queriesDir, QUICK_QUERY_QUERY_NAME);
|
const qlFile = join(queriesDir, QUICK_QUERY_QUERY_NAME);
|
||||||
const shouldRewrite = await checkShouldRewrite(qlPackFile, qlpack);
|
const shouldRewrite = await checkShouldRewrite(qlPackFile, qlpack);
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import { redactableError } from "../common/errors";
|
|||||||
import type { App } from "../common/app";
|
import type { App } from "../common/app";
|
||||||
import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
|
import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
|
||||||
import { containsPath, pathsEqual } from "../common/files";
|
import { containsPath, pathsEqual } from "../common/files";
|
||||||
import { getQlPackPath } from "../common/ql";
|
import { getQlPackFilePath } from "../common/ql";
|
||||||
import { getQlPackLanguage } from "../common/qlpack-language";
|
import { getQlPackLanguage } from "../common/qlpack-language";
|
||||||
|
|
||||||
type QueryLanguagesToDatabaseMap = Record<string, string>;
|
type QueryLanguagesToDatabaseMap = Record<string, string>;
|
||||||
@@ -111,7 +111,7 @@ export class SkeletonQueryWizard {
|
|||||||
|
|
||||||
// Try to detect if there is already a qlpack in this location. We will assume that
|
// Try to detect if there is already a qlpack in this location. We will assume that
|
||||||
// the user hasn't changed the language of the qlpack.
|
// the user hasn't changed the language of the qlpack.
|
||||||
const qlPackPath = await getQlPackPath(this.qlPackStoragePath);
|
const qlPackPath = await getQlPackFilePath(this.qlPackStoragePath);
|
||||||
|
|
||||||
// If we are creating or using a qlpack in the user's selected folder, we will also
|
// If we are creating or using a qlpack in the user's selected folder, we will also
|
||||||
// create the query in that folder
|
// create the query in that folder
|
||||||
@@ -248,7 +248,7 @@ export class SkeletonQueryWizard {
|
|||||||
|
|
||||||
const matchingQueryPackPath = matchingQueryPacks[0];
|
const matchingQueryPackPath = matchingQueryPacks[0];
|
||||||
|
|
||||||
const qlPackPath = await getQlPackPath(matchingQueryPackPath);
|
const qlPackPath = await getQlPackFilePath(matchingQueryPackPath);
|
||||||
if (!qlPackPath) {
|
if (!qlPackPath) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
|||||||
import type { ProgressCallback } from "../common/vscode/progress";
|
import type { ProgressCallback } from "../common/vscode/progress";
|
||||||
import { UserCancellationException } from "../common/vscode/progress";
|
import { UserCancellationException } from "../common/vscode/progress";
|
||||||
import type { DatabaseItem } from "../databases/local-databases";
|
import type { DatabaseItem } from "../databases/local-databases";
|
||||||
import { getQlPackPath, QLPACK_FILENAMES } from "../common/ql";
|
import { getQlPackFilePath, QLPACK_FILENAMES } from "../common/ql";
|
||||||
import { getErrorMessage } from "../common/helpers-pure";
|
import { getErrorMessage } from "../common/helpers-pure";
|
||||||
import type { ExtensionPack } from "./shared/extension-pack";
|
import type { ExtensionPack } from "./shared/extension-pack";
|
||||||
import type { NotificationLogger } from "../common/logging";
|
import type { NotificationLogger } from "../common/logging";
|
||||||
@@ -208,7 +208,7 @@ async function readExtensionPack(
|
|||||||
path: string,
|
path: string,
|
||||||
language: string,
|
language: string,
|
||||||
): Promise<ExtensionPack> {
|
): Promise<ExtensionPack> {
|
||||||
const qlpackPath = await getQlPackPath(path);
|
const qlpackPath = await getQlPackFilePath(path);
|
||||||
if (!qlpackPath) {
|
if (!qlpackPath) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not find any of ${QLPACK_FILENAMES.join(", ")} in ${path}`,
|
`Could not find any of ${QLPACK_FILENAMES.join(", ")} in ${path}`,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Meta, StoryFn } from "@storybook/react";
|
import type { Meta, StoryFn } from "@storybook/react";
|
||||||
|
|
||||||
import { faker } from "@faker-js/faker";
|
import { faker } from "@faker-js/faker";
|
||||||
|
import { customAlphabet } from "nanoid";
|
||||||
|
|
||||||
import { VariantAnalysisContainer } from "../../view/variant-analysis/VariantAnalysisContainer";
|
import { VariantAnalysisContainer } from "../../view/variant-analysis/VariantAnalysisContainer";
|
||||||
import { VariantAnalysisAnalyzedRepos } from "../../view/variant-analysis/VariantAnalysisAnalyzedRepos";
|
import { VariantAnalysisAnalyzedRepos } from "../../view/variant-analysis/VariantAnalysisAnalyzedRepos";
|
||||||
@@ -125,11 +126,10 @@ Example.args = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
faker.seed(42);
|
faker.seed(42);
|
||||||
const uniqueStore = {};
|
|
||||||
|
|
||||||
const manyScannedRepos = Array.from({ length: 1000 }, (_, i) => {
|
const manyScannedRepos = Array.from({ length: 1000 }, (_, i) => {
|
||||||
const mockedScannedRepo = createMockScannedRepo();
|
const mockedScannedRepo = createMockScannedRepo();
|
||||||
|
const nanoid = customAlphabet("123456789");
|
||||||
return {
|
return {
|
||||||
...mockedScannedRepo,
|
...mockedScannedRepo,
|
||||||
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
|
||||||
@@ -137,12 +137,8 @@ const manyScannedRepos = Array.from({ length: 1000 }, (_, i) => {
|
|||||||
repository: {
|
repository: {
|
||||||
...mockedScannedRepo.repository,
|
...mockedScannedRepo.repository,
|
||||||
// We need to ensure the ID is unique for React keys
|
// We need to ensure the ID is unique for React keys
|
||||||
id: faker.helpers.unique(faker.number.int, [], {
|
id: parseInt(nanoid()),
|
||||||
store: uniqueStore,
|
fullName: `octodemo/${nanoid()}`,
|
||||||
}),
|
|
||||||
fullName: `octodemo/${faker.helpers.unique(faker.word.sample, [], {
|
|
||||||
store: uniqueStore,
|
|
||||||
})}`,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
20
extensions/ql-vscode/src/variant-analysis/ql-pack-details.ts
Normal file
20
extensions/ql-vscode/src/variant-analysis/ql-pack-details.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { QueryLanguage } from "../common/query-language";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details about the original QL pack that is used for triggering
|
||||||
|
* a variant analysis.
|
||||||
|
*/
|
||||||
|
export interface QlPackDetails {
|
||||||
|
// The absolute paths of the query files.
|
||||||
|
queryFiles: string[];
|
||||||
|
|
||||||
|
// The absolute path to the QL pack that is used for triggering a variant analysis.
|
||||||
|
// If there is no query pack, this is the same as the directory of the query files.
|
||||||
|
qlPackRootPath: string;
|
||||||
|
|
||||||
|
// The absolute path to the QL pack file (a qlpack.yml or codeql-pack.yml) or undefined if
|
||||||
|
// it doesn't exist.
|
||||||
|
qlPackFilePath: string | undefined;
|
||||||
|
|
||||||
|
language: QueryLanguage;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CancellationToken } from "vscode";
|
import type { CancellationToken } from "vscode";
|
||||||
import { Uri, window } from "vscode";
|
import { Uri, window } from "vscode";
|
||||||
import { relative, join, sep, dirname, parse, basename } from "path";
|
import { join, sep, basename, relative } from "path";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { copy, writeFile, readFile, mkdirp } from "fs-extra";
|
import { copy, writeFile, readFile, mkdirp } from "fs-extra";
|
||||||
import type { DirectoryResult } from "tmp-promise";
|
import type { DirectoryResult } from "tmp-promise";
|
||||||
@@ -29,27 +29,20 @@ import {
|
|||||||
import type { Repository } from "./shared/repository";
|
import type { Repository } from "./shared/repository";
|
||||||
import type { DbManager } from "../databases/db-manager";
|
import type { DbManager } from "../databases/db-manager";
|
||||||
import {
|
import {
|
||||||
getQlPackPath,
|
getQlPackFilePath,
|
||||||
FALLBACK_QLPACK_FILENAME,
|
FALLBACK_QLPACK_FILENAME,
|
||||||
QLPACK_FILENAMES,
|
QLPACK_FILENAMES,
|
||||||
QLPACK_LOCK_FILENAMES,
|
QLPACK_LOCK_FILENAMES,
|
||||||
} from "../common/ql";
|
} from "../common/ql";
|
||||||
import type { QueryLanguage } from "../common/query-language";
|
|
||||||
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
|
||||||
import { askForLanguage, findLanguage } from "../codeql-cli/query-language";
|
|
||||||
import type { QlPackFile } from "../packaging/qlpack-file";
|
import type { QlPackFile } from "../packaging/qlpack-file";
|
||||||
import { expandShortPaths } from "../common/short-paths";
|
import { expandShortPaths } from "../common/short-paths";
|
||||||
|
import type { QlPackDetails } from "./ql-pack-details";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Well-known names for the query pack used by the server.
|
* Well-known names for the query pack used by the server.
|
||||||
*/
|
*/
|
||||||
const QUERY_PACK_NAME = "codeql-remote/query";
|
const QUERY_PACK_NAME = "codeql-remote/query";
|
||||||
|
|
||||||
interface GeneratedQueryPack {
|
|
||||||
base64Pack: string;
|
|
||||||
language: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Two possibilities:
|
* Two possibilities:
|
||||||
* 1. There is no qlpack.yml (or codeql-pack.yml) in this directory. Assume this is a lone query and generate a synthetic qlpack for it.
|
* 1. There is no qlpack.yml (or codeql-pack.yml) in this directory. Assume this is a lone query and generate a synthetic qlpack for it.
|
||||||
@@ -59,45 +52,30 @@ interface GeneratedQueryPack {
|
|||||||
*/
|
*/
|
||||||
async function generateQueryPack(
|
async function generateQueryPack(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
queryFile: string,
|
qlPackDetails: QlPackDetails,
|
||||||
tmpDir: RemoteQueryTempDir,
|
tmpDir: RemoteQueryTempDir,
|
||||||
): Promise<GeneratedQueryPack> {
|
): Promise<string> {
|
||||||
const originalPackRoot = await findPackRoot(queryFile);
|
|
||||||
const packRelativePath = relative(originalPackRoot, queryFile);
|
|
||||||
const workspaceFolders = getOnDiskWorkspaceFolders();
|
const workspaceFolders = getOnDiskWorkspaceFolders();
|
||||||
const extensionPacks = await getExtensionPacksToInject(
|
const extensionPacks = await getExtensionPacksToInject(
|
||||||
cliServer,
|
cliServer,
|
||||||
workspaceFolders,
|
workspaceFolders,
|
||||||
);
|
);
|
||||||
|
|
||||||
const mustSynthesizePack =
|
const mustSynthesizePack = qlPackDetails.qlPackFilePath === undefined;
|
||||||
(await getQlPackPath(originalPackRoot)) === undefined;
|
|
||||||
const cliSupportsMrvaPackCreate =
|
const cliSupportsMrvaPackCreate =
|
||||||
await cliServer.cliConstraints.supportsMrvaPackCreate();
|
await cliServer.cliConstraints.supportsMrvaPackCreate();
|
||||||
|
|
||||||
const language: QueryLanguage | undefined = mustSynthesizePack
|
let targetPackPath: string;
|
||||||
? await askForLanguage(cliServer) // open popup to ask for language if not already hardcoded
|
|
||||||
: await findLanguage(cliServer, Uri.file(queryFile));
|
|
||||||
if (!language) {
|
|
||||||
throw new UserCancellationException("Could not determine language");
|
|
||||||
}
|
|
||||||
|
|
||||||
let queryPackDir: string;
|
|
||||||
let needsInstall: boolean;
|
let needsInstall: boolean;
|
||||||
if (mustSynthesizePack) {
|
if (mustSynthesizePack) {
|
||||||
// This section applies whether or not the CLI supports MRVA pack creation directly.
|
// This section applies whether or not the CLI supports MRVA pack creation directly.
|
||||||
|
|
||||||
queryPackDir = tmpDir.queryPackDir;
|
targetPackPath = tmpDir.queryPackDir;
|
||||||
|
|
||||||
// Synthesize a query pack for the query.
|
// Synthesize a query pack for the query.
|
||||||
// copy only the query file to the query pack directory
|
// copy only the query file to the query pack directory
|
||||||
// and generate a synthetic query pack
|
// and generate a synthetic query pack
|
||||||
await createNewQueryPack(
|
await createNewQueryPack(qlPackDetails, targetPackPath);
|
||||||
queryFile,
|
|
||||||
queryPackDir,
|
|
||||||
language,
|
|
||||||
packRelativePath,
|
|
||||||
);
|
|
||||||
// Clear the cliServer cache so that the previous qlpack text is purged from the CLI.
|
// Clear the cliServer cache so that the previous qlpack text is purged from the CLI.
|
||||||
await cliServer.clearCache();
|
await cliServer.clearCache();
|
||||||
|
|
||||||
@@ -105,14 +83,8 @@ async function generateQueryPack(
|
|||||||
needsInstall = true;
|
needsInstall = true;
|
||||||
} else if (!cliSupportsMrvaPackCreate) {
|
} else if (!cliSupportsMrvaPackCreate) {
|
||||||
// We need to copy the query pack to a temporary directory and then fix it up to work with MRVA.
|
// We need to copy the query pack to a temporary directory and then fix it up to work with MRVA.
|
||||||
queryPackDir = tmpDir.queryPackDir;
|
targetPackPath = tmpDir.queryPackDir;
|
||||||
await copyExistingQueryPack(
|
await copyExistingQueryPack(cliServer, qlPackDetails, targetPackPath);
|
||||||
cliServer,
|
|
||||||
originalPackRoot,
|
|
||||||
queryFile,
|
|
||||||
queryPackDir,
|
|
||||||
packRelativePath,
|
|
||||||
);
|
|
||||||
|
|
||||||
// We should already have all the dependencies available, but these older versions of the CLI
|
// We should already have all the dependencies available, but these older versions of the CLI
|
||||||
// have a bug where they will not search `--additional-packs` during validation in `codeql pack bundle`.
|
// have a bug where they will not search `--additional-packs` during validation in `codeql pack bundle`.
|
||||||
@@ -120,14 +92,14 @@ async function generateQueryPack(
|
|||||||
needsInstall = true;
|
needsInstall = true;
|
||||||
} else {
|
} else {
|
||||||
// The CLI supports creating a MRVA query pack directly from the source pack.
|
// The CLI supports creating a MRVA query pack directly from the source pack.
|
||||||
queryPackDir = originalPackRoot;
|
targetPackPath = qlPackDetails.qlPackRootPath;
|
||||||
// We expect any dependencies to be available already.
|
// We expect any dependencies to be available already.
|
||||||
needsInstall = false;
|
needsInstall = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsInstall) {
|
if (needsInstall) {
|
||||||
// Install the dependencies of the synthesized query pack.
|
// Install the dependencies of the synthesized query pack.
|
||||||
await cliServer.packInstall(queryPackDir, {
|
await cliServer.packInstall(targetPackPath, {
|
||||||
workspaceFolders,
|
workspaceFolders,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -137,10 +109,23 @@ async function generateQueryPack(
|
|||||||
|
|
||||||
let precompilationOpts: string[];
|
let precompilationOpts: string[];
|
||||||
if (cliSupportsMrvaPackCreate) {
|
if (cliSupportsMrvaPackCreate) {
|
||||||
|
if (
|
||||||
|
qlPackDetails.queryFiles.length > 1 &&
|
||||||
|
!(await cliServer.cliConstraints.supportsPackCreateWithMultipleQueries())
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Installed CLI version does not allow creating a MRVA pack with multiple queries`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryOpts = qlPackDetails.queryFiles.flatMap((q) => [
|
||||||
|
"--query",
|
||||||
|
join(targetPackPath, relative(qlPackDetails.qlPackRootPath, q)),
|
||||||
|
]);
|
||||||
|
|
||||||
precompilationOpts = [
|
precompilationOpts = [
|
||||||
"--mrva",
|
"--mrva",
|
||||||
"--query",
|
...queryOpts,
|
||||||
join(queryPackDir, packRelativePath),
|
|
||||||
// We need to specify the extension packs as dependencies so that they are included in the MRVA pack.
|
// We need to specify the extension packs as dependencies so that they are included in the MRVA pack.
|
||||||
// The version range doesn't matter, since they'll always be found by source lookup.
|
// The version range doesn't matter, since they'll always be found by source lookup.
|
||||||
...extensionPacks.map((p) => `--extension-pack=${p}@*`),
|
...extensionPacks.map((p) => `--extension-pack=${p}@*`),
|
||||||
@@ -149,7 +134,7 @@ async function generateQueryPack(
|
|||||||
if (await cliServer.cliConstraints.usesGlobalCompilationCache()) {
|
if (await cliServer.cliConstraints.usesGlobalCompilationCache()) {
|
||||||
precompilationOpts = ["--qlx"];
|
precompilationOpts = ["--qlx"];
|
||||||
} else {
|
} else {
|
||||||
const cache = join(originalPackRoot, ".cache");
|
const cache = join(qlPackDetails.qlPackRootPath, ".cache");
|
||||||
precompilationOpts = [
|
precompilationOpts = [
|
||||||
"--qlx",
|
"--qlx",
|
||||||
"--no-default-compilation-cache",
|
"--no-default-compilation-cache",
|
||||||
@@ -158,60 +143,61 @@ async function generateQueryPack(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (extensionPacks.length > 0) {
|
if (extensionPacks.length > 0) {
|
||||||
await addExtensionPacksAsDependencies(queryPackDir, extensionPacks);
|
await addExtensionPacksAsDependencies(targetPackPath, extensionPacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bundlePath = tmpDir.bundleFile;
|
const bundlePath = tmpDir.bundleFile;
|
||||||
void extLogger.log(
|
void extLogger.log(
|
||||||
`Compiling and bundling query pack from ${queryPackDir} to ${bundlePath}. (This may take a while.)`,
|
`Compiling and bundling query pack from ${targetPackPath} to ${bundlePath}. (This may take a while.)`,
|
||||||
);
|
);
|
||||||
await cliServer.packBundle(
|
await cliServer.packBundle(
|
||||||
queryPackDir,
|
targetPackPath,
|
||||||
workspaceFolders,
|
workspaceFolders,
|
||||||
bundlePath,
|
bundlePath,
|
||||||
tmpDir.compiledPackDir,
|
tmpDir.compiledPackDir,
|
||||||
precompilationOpts,
|
precompilationOpts,
|
||||||
);
|
);
|
||||||
const base64Pack = (await readFile(bundlePath)).toString("base64");
|
const base64Pack = (await readFile(bundlePath)).toString("base64");
|
||||||
return {
|
return base64Pack;
|
||||||
base64Pack,
|
|
||||||
language,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createNewQueryPack(
|
async function createNewQueryPack(
|
||||||
queryFile: string,
|
qlPackDetails: QlPackDetails,
|
||||||
queryPackDir: string,
|
targetPackPath: string,
|
||||||
language: string | undefined,
|
|
||||||
packRelativePath: string,
|
|
||||||
) {
|
) {
|
||||||
void extLogger.log(`Copying ${queryFile} to ${queryPackDir}`);
|
for (const queryFile of qlPackDetails.queryFiles) {
|
||||||
const targetQueryFileName = join(queryPackDir, packRelativePath);
|
void extLogger.log(`Copying ${queryFile} to ${targetPackPath}`);
|
||||||
await copy(queryFile, targetQueryFileName);
|
const relativeQueryPath = relative(qlPackDetails.qlPackRootPath, queryFile);
|
||||||
|
const targetQueryFileName = join(targetPackPath, relativeQueryPath);
|
||||||
|
await copy(queryFile, targetQueryFileName);
|
||||||
|
}
|
||||||
|
|
||||||
void extLogger.log("Generating synthetic query pack");
|
void extLogger.log("Generating synthetic query pack");
|
||||||
const syntheticQueryPack = {
|
const syntheticQueryPack = {
|
||||||
name: QUERY_PACK_NAME,
|
name: QUERY_PACK_NAME,
|
||||||
version: "0.0.0",
|
version: "0.0.0",
|
||||||
dependencies: {
|
dependencies: {
|
||||||
[`codeql/${language}-all`]: "*",
|
[`codeql/${qlPackDetails.language}-all`]: "*",
|
||||||
},
|
},
|
||||||
defaultSuite: generateDefaultSuite(packRelativePath),
|
defaultSuite: generateDefaultSuite(qlPackDetails),
|
||||||
};
|
};
|
||||||
|
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(queryPackDir, FALLBACK_QLPACK_FILENAME),
|
join(targetPackPath, FALLBACK_QLPACK_FILENAME),
|
||||||
dump(syntheticQueryPack),
|
dump(syntheticQueryPack),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyExistingQueryPack(
|
async function copyExistingQueryPack(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
originalPackRoot: string,
|
qlPackDetails: QlPackDetails,
|
||||||
queryFile: string,
|
targetPackPath: string,
|
||||||
queryPackDir: string,
|
|
||||||
packRelativePath: string,
|
|
||||||
) {
|
) {
|
||||||
const toCopy = await cliServer.packPacklist(originalPackRoot, false);
|
const toCopy = await cliServer.packPacklist(
|
||||||
|
qlPackDetails.qlPackRootPath,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
// Also include query files that contain extensible predicates. These query files are not
|
// Also include query files that contain extensible predicates. These query files are not
|
||||||
// needed for the query to run, but they are needed for the query pack to pass deep validation
|
// needed for the query to run, but they are needed for the query pack to pass deep validation
|
||||||
@@ -219,19 +205,20 @@ async function copyExistingQueryPack(
|
|||||||
if (
|
if (
|
||||||
await cliServer.cliConstraints.supportsGenerateExtensiblePredicateMetadata()
|
await cliServer.cliConstraints.supportsGenerateExtensiblePredicateMetadata()
|
||||||
) {
|
) {
|
||||||
const metadata =
|
const metadata = await cliServer.generateExtensiblePredicateMetadata(
|
||||||
await cliServer.generateExtensiblePredicateMetadata(originalPackRoot);
|
qlPackDetails.qlPackRootPath,
|
||||||
|
);
|
||||||
metadata.extensible_predicates.forEach((predicate) => {
|
metadata.extensible_predicates.forEach((predicate) => {
|
||||||
if (predicate.path.endsWith(".ql")) {
|
if (predicate.path.endsWith(".ql")) {
|
||||||
toCopy.push(join(originalPackRoot, predicate.path));
|
toCopy.push(join(qlPackDetails.qlPackRootPath, predicate.path));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[
|
[
|
||||||
// also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist.
|
// 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)),
|
...QLPACK_LOCK_FILENAMES.map((f) => join(qlPackDetails.qlPackRootPath, f)),
|
||||||
queryFile,
|
...qlPackDetails.queryFiles,
|
||||||
].forEach((absolutePath) => {
|
].forEach((absolutePath) => {
|
||||||
if (absolutePath) {
|
if (absolutePath) {
|
||||||
toCopy.push(absolutePath);
|
toCopy.push(absolutePath);
|
||||||
@@ -239,7 +226,7 @@ async function copyExistingQueryPack(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let copiedCount = 0;
|
let copiedCount = 0;
|
||||||
await copy(originalPackRoot, queryPackDir, {
|
await copy(qlPackDetails.qlPackRootPath, targetPackPath, {
|
||||||
filter: (file: string) =>
|
filter: (file: string) =>
|
||||||
// copy file if it is in the packlist, or it is a parent directory of a file in the packlist
|
// copy file if it is in the packlist, or it is a parent directory of a file in the packlist
|
||||||
!!toCopy.find((f) => {
|
!!toCopy.find((f) => {
|
||||||
@@ -254,29 +241,9 @@ async function copyExistingQueryPack(
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
void extLogger.log(`Copied ${copiedCount} files to ${queryPackDir}`);
|
void extLogger.log(`Copied ${copiedCount} files to ${targetPackPath}`);
|
||||||
|
|
||||||
await fixPackFile(queryPackDir, packRelativePath);
|
await fixPackFile(targetPackPath, qlPackDetails);
|
||||||
}
|
|
||||||
|
|
||||||
async function findPackRoot(queryFile: string): Promise<string> {
|
|
||||||
// recursively find the directory containing qlpack.yml or codeql-pack.yml
|
|
||||||
let dir = dirname(queryFile);
|
|
||||||
while (!(await getQlPackPath(dir))) {
|
|
||||||
dir = dirname(dir);
|
|
||||||
if (isFileSystemRoot(dir)) {
|
|
||||||
// there is no qlpack.yml or codeql-pack.yml in this directory or any parent directory.
|
|
||||||
// just use the query file's directory as the pack root.
|
|
||||||
return dirname(queryFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFileSystemRoot(dir: string): boolean {
|
|
||||||
const pathObj = parse(dir);
|
|
||||||
return pathObj.root === dir && pathObj.base === "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RemoteQueryTempDir {
|
interface RemoteQueryTempDir {
|
||||||
@@ -319,35 +286,26 @@ interface PreparedRemoteQuery {
|
|||||||
actionBranch: string;
|
actionBranch: string;
|
||||||
base64Pack: string;
|
base64Pack: string;
|
||||||
repoSelection: RepositorySelection;
|
repoSelection: RepositorySelection;
|
||||||
queryFile: string;
|
|
||||||
queryMetadata: QueryMetadata | undefined;
|
|
||||||
controllerRepo: Repository;
|
controllerRepo: Repository;
|
||||||
queryStartTime: number;
|
queryStartTime: number;
|
||||||
language: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function prepareRemoteQueryRun(
|
export async function prepareRemoteQueryRun(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
uris: Uri[],
|
qlPackDetails: QlPackDetails,
|
||||||
progress: ProgressCallback,
|
progress: ProgressCallback,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
dbManager: DbManager,
|
dbManager: DbManager,
|
||||||
): Promise<PreparedRemoteQuery> {
|
): Promise<PreparedRemoteQuery> {
|
||||||
if (uris.length !== 1) {
|
for (const queryFile of qlPackDetails.queryFiles) {
|
||||||
// For now we only support a single file, but we're aiming
|
if (!queryFile.endsWith(".ql")) {
|
||||||
// to support multiple files in the near future.
|
throw new UserCancellationException(
|
||||||
throw Error("Exactly one query file must be selected.");
|
`Not a CodeQL query file: ${queryFile}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uri = uris[0];
|
|
||||||
|
|
||||||
if (!uri.fsPath.endsWith(".ql")) {
|
|
||||||
throw new UserCancellationException("Not a CodeQL query file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryFile = uri.fsPath;
|
|
||||||
|
|
||||||
progress({
|
progress({
|
||||||
maxStep: 4,
|
maxStep: 4,
|
||||||
step: 1,
|
step: 1,
|
||||||
@@ -379,16 +337,14 @@ export async function prepareRemoteQueryRun(
|
|||||||
|
|
||||||
const tempDir = await createRemoteQueriesTempDirectory();
|
const tempDir = await createRemoteQueriesTempDirectory();
|
||||||
|
|
||||||
let pack: GeneratedQueryPack;
|
let base64Pack: string;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pack = await generateQueryPack(cliServer, queryFile, tempDir);
|
base64Pack = await generateQueryPack(cliServer, qlPackDetails, tempDir);
|
||||||
} finally {
|
} finally {
|
||||||
await tempDir.remoteQueryDir.cleanup();
|
await tempDir.remoteQueryDir.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { base64Pack, language } = pack;
|
|
||||||
|
|
||||||
if (token.isCancellationRequested) {
|
if (token.isCancellationRequested) {
|
||||||
throw new UserCancellationException("Cancelled");
|
throw new UserCancellationException("Cancelled");
|
||||||
}
|
}
|
||||||
@@ -401,17 +357,13 @@ export async function prepareRemoteQueryRun(
|
|||||||
|
|
||||||
const actionBranch = getActionBranch();
|
const actionBranch = getActionBranch();
|
||||||
const queryStartTime = Date.now();
|
const queryStartTime = Date.now();
|
||||||
const queryMetadata = await tryGetQueryMetadata(cliServer, queryFile);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
actionBranch,
|
actionBranch,
|
||||||
base64Pack,
|
base64Pack,
|
||||||
repoSelection,
|
repoSelection,
|
||||||
queryFile,
|
|
||||||
queryMetadata,
|
|
||||||
controllerRepo,
|
controllerRepo,
|
||||||
queryStartTime,
|
queryStartTime,
|
||||||
language,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,26 +378,26 @@ export async function prepareRemoteQueryRun(
|
|||||||
* - Removes any `${workspace}` version references from the qlpack.yml or codeql-pack.yml file. Converts them
|
* - Removes any `${workspace}` version references from the qlpack.yml or codeql-pack.yml file. Converts them
|
||||||
* to `*` versions.
|
* to `*` versions.
|
||||||
*
|
*
|
||||||
* @param queryPackDir The directory containing the query pack
|
* @param targetPackPath The path to the directory containing the target pack
|
||||||
* @param packRelativePath The relative path to the query pack from the root of the query pack
|
* @param qlPackDetails The details of the original QL pack
|
||||||
*/
|
*/
|
||||||
async function fixPackFile(
|
async function fixPackFile(
|
||||||
queryPackDir: string,
|
targetPackPath: string,
|
||||||
packRelativePath: string,
|
qlPackDetails: QlPackDetails,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const packPath = await getQlPackPath(queryPackDir);
|
const packPath = await getQlPackFilePath(targetPackPath);
|
||||||
|
|
||||||
// This should not happen since we create the pack ourselves.
|
// This should not happen since we create the pack ourselves.
|
||||||
if (!packPath) {
|
if (!packPath) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not find ${QLPACK_FILENAMES.join(
|
`Could not find ${QLPACK_FILENAMES.join(
|
||||||
" or ",
|
" or ",
|
||||||
)} file in '${queryPackDir}'`,
|
)} file in '${targetPackPath}'`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const qlpack = load(await readFile(packPath, "utf8")) as QlPackFile;
|
const qlpack = load(await readFile(packPath, "utf8")) as QlPackFile;
|
||||||
|
|
||||||
updateDefaultSuite(qlpack, packRelativePath);
|
updateDefaultSuite(qlpack, qlPackDetails);
|
||||||
removeWorkspaceRefs(qlpack);
|
removeWorkspaceRefs(qlpack);
|
||||||
|
|
||||||
await writeFile(packPath, dump(qlpack));
|
await writeFile(packPath, dump(qlpack));
|
||||||
@@ -483,7 +435,7 @@ async function addExtensionPacksAsDependencies(
|
|||||||
queryPackDir: string,
|
queryPackDir: string,
|
||||||
extensionPacks: string[],
|
extensionPacks: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const qlpackFile = await getQlPackPath(queryPackDir);
|
const qlpackFile = await getQlPackFilePath(queryPackDir);
|
||||||
if (!qlpackFile) {
|
if (!qlpackFile) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not find ${QLPACK_FILENAMES.join(
|
`Could not find ${QLPACK_FILENAMES.join(
|
||||||
@@ -509,19 +461,23 @@ async function addExtensionPacksAsDependencies(
|
|||||||
await writeFile(qlpackFile, dump(syntheticQueryPack));
|
await writeFile(qlpackFile, dump(syntheticQueryPack));
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDefaultSuite(qlpack: QlPackFile, packRelativePath: string) {
|
function updateDefaultSuite(qlpack: QlPackFile, qlPackDetails: QlPackDetails) {
|
||||||
delete qlpack.defaultSuiteFile;
|
delete qlpack.defaultSuiteFile;
|
||||||
qlpack.defaultSuite = generateDefaultSuite(packRelativePath);
|
qlpack.defaultSuite = generateDefaultSuite(qlPackDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDefaultSuite(packRelativePath: string) {
|
function generateDefaultSuite(qlPackDetails: QlPackDetails) {
|
||||||
|
const queries = qlPackDetails.queryFiles.map((query) => {
|
||||||
|
const relativePath = relative(qlPackDetails.qlPackRootPath, query);
|
||||||
|
return {
|
||||||
|
query: relativePath.replace(/\\/g, "/"),
|
||||||
|
};
|
||||||
|
});
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
description: "Query suite for variant analysis",
|
description: "Query suite for variant analysis",
|
||||||
},
|
},
|
||||||
{
|
...queries,
|
||||||
query: packRelativePath.replace(/\\/g, "/"),
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ export function filterAndSortRepositoriesWithResults<
|
|||||||
filterSortState.repositoryIds.length > 0
|
filterSortState.repositoryIds.length > 0
|
||||||
) {
|
) {
|
||||||
return repositories
|
return repositories
|
||||||
.filter(
|
.filter((repo) =>
|
||||||
(repo) => filterSortState.repositoryIds?.includes(repo.repository.id),
|
filterSortState.repositoryIds?.includes(repo.repository.id),
|
||||||
)
|
)
|
||||||
.sort(compareWithResults(filterSortState));
|
.sort(compareWithResults(filterSortState));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ export interface VariantAnalysisSubmission {
|
|||||||
// unclear what it will look like in the future.
|
// unclear what it will look like in the future.
|
||||||
export interface VariantAnalysisQueries {
|
export interface VariantAnalysisQueries {
|
||||||
language: QueryLanguage;
|
language: QueryLanguage;
|
||||||
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isVariantAnalysisComplete(
|
export async function isVariantAnalysisComplete(
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { DisposableObject } from "../common/disposable-object";
|
|||||||
import { VariantAnalysisMonitor } from "./variant-analysis-monitor";
|
import { VariantAnalysisMonitor } from "./variant-analysis-monitor";
|
||||||
import type {
|
import type {
|
||||||
VariantAnalysis,
|
VariantAnalysis,
|
||||||
|
VariantAnalysisQueries,
|
||||||
VariantAnalysisRepositoryTask,
|
VariantAnalysisRepositoryTask,
|
||||||
VariantAnalysisScannedRepository,
|
VariantAnalysisScannedRepository,
|
||||||
VariantAnalysisScannedRepositoryResult,
|
VariantAnalysisScannedRepositoryResult,
|
||||||
@@ -87,7 +88,10 @@ import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
|
|||||||
import { RequestError } from "@octokit/request-error";
|
import { RequestError } from "@octokit/request-error";
|
||||||
import { handleRequestError } from "./custom-errors";
|
import { handleRequestError } from "./custom-errors";
|
||||||
import { createMultiSelectionCommand } from "../common/vscode/selection-commands";
|
import { createMultiSelectionCommand } from "../common/vscode/selection-commands";
|
||||||
import { askForLanguage } from "../codeql-cli/query-language";
|
import { askForLanguage, findLanguage } from "../codeql-cli/query-language";
|
||||||
|
import type { QlPackDetails } from "./ql-pack-details";
|
||||||
|
import { findPackRoot, getQlPackFilePath } from "../common/ql";
|
||||||
|
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
||||||
|
|
||||||
const maxRetryCount = 3;
|
const maxRetryCount = 3;
|
||||||
|
|
||||||
@@ -191,26 +195,22 @@ export class VariantAnalysisManager
|
|||||||
throw new Error("Please select a .ql file to run as a variant analysis");
|
throw new Error("Please select a .ql file to run as a variant analysis");
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.runVariantAnalysisCommand(fileUri);
|
await this.runVariantAnalysisCommand([fileUri]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runVariantAnalysisFromContextEditor(uri: Uri) {
|
private async runVariantAnalysisFromContextEditor(uri: Uri) {
|
||||||
await this.runVariantAnalysisCommand(uri);
|
await this.runVariantAnalysisCommand([uri]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runVariantAnalysisFromExplorer(fileURIs: Uri[]): Promise<void> {
|
private async runVariantAnalysisFromExplorer(fileURIs: Uri[]): Promise<void> {
|
||||||
if (fileURIs.length !== 1) {
|
return this.runVariantAnalysisCommand(fileURIs);
|
||||||
throw new Error("Can only run a single query at a time");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.runVariantAnalysisCommand(fileURIs[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runVariantAnalysisFromQueriesPanel(
|
private async runVariantAnalysisFromQueriesPanel(
|
||||||
queryTreeViewItem: QueryTreeViewItem,
|
queryTreeViewItem: QueryTreeViewItem,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (queryTreeViewItem.path !== undefined) {
|
if (queryTreeViewItem.path !== undefined) {
|
||||||
await this.runVariantAnalysisCommand(Uri.file(queryTreeViewItem.path));
|
await this.runVariantAnalysisCommand([Uri.file(queryTreeViewItem.path)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +223,9 @@ export class VariantAnalysisManager
|
|||||||
});
|
});
|
||||||
|
|
||||||
const language = await askForLanguage(this.cliServer);
|
const language = await askForLanguage(this.cliServer);
|
||||||
|
if (!language) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
progress({
|
progress({
|
||||||
maxStep: 8,
|
maxStep: 8,
|
||||||
@@ -263,8 +266,18 @@ export class VariantAnalysisManager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const qlPackFilePath = await getQlPackFilePath(packDir);
|
||||||
|
|
||||||
|
// Build up details to pass to the functions that run the variant analysis.
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: problemQueries,
|
||||||
|
qlPackRootPath: packDir,
|
||||||
|
qlPackFilePath,
|
||||||
|
language,
|
||||||
|
};
|
||||||
|
|
||||||
await this.runVariantAnalysis(
|
await this.runVariantAnalysis(
|
||||||
problemQueries.map((q) => Uri.file(q)),
|
qlPackDetails,
|
||||||
(p) =>
|
(p) =>
|
||||||
progress({
|
progress({
|
||||||
...p,
|
...p,
|
||||||
@@ -294,10 +307,43 @@ export class VariantAnalysisManager
|
|||||||
return problemQueries;
|
return problemQueries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runVariantAnalysisCommand(uri: Uri): Promise<void> {
|
private async runVariantAnalysisCommand(queryFiles: Uri[]): Promise<void> {
|
||||||
|
if (queryFiles.length === 0) {
|
||||||
|
throw new Error("Please select a .ql file to run as a variant analysis");
|
||||||
|
}
|
||||||
|
|
||||||
|
const qlPackRootPath = await findPackRoot(queryFiles[0].fsPath);
|
||||||
|
const qlPackFilePath = await getQlPackFilePath(qlPackRootPath);
|
||||||
|
|
||||||
|
// Make sure that all remaining queries have the same pack root
|
||||||
|
for (let i = 1; i < queryFiles.length; i++) {
|
||||||
|
const packRoot = await findPackRoot(queryFiles[i].fsPath);
|
||||||
|
if (packRoot !== qlPackRootPath) {
|
||||||
|
throw new Error(
|
||||||
|
"Please select queries that all belong to the same query pack",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open popup to ask for language if not already hardcoded
|
||||||
|
const language = qlPackFilePath
|
||||||
|
? await findLanguage(this.cliServer, queryFiles[0])
|
||||||
|
: await askForLanguage(this.cliServer);
|
||||||
|
|
||||||
|
if (!language) {
|
||||||
|
throw new UserCancellationException("Could not determine query language");
|
||||||
|
}
|
||||||
|
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: queryFiles.map((uri) => uri.fsPath),
|
||||||
|
qlPackRootPath,
|
||||||
|
qlPackFilePath,
|
||||||
|
language,
|
||||||
|
};
|
||||||
|
|
||||||
return withProgress(
|
return withProgress(
|
||||||
async (progress, token) => {
|
async (progress, token) => {
|
||||||
await this.runVariantAnalysis([uri], progress, token);
|
await this.runVariantAnalysis(qlPackDetails, progress, token);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Run Variant Analysis",
|
title: "Run Variant Analysis",
|
||||||
@@ -307,7 +353,7 @@ export class VariantAnalysisManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async runVariantAnalysis(
|
public async runVariantAnalysis(
|
||||||
uris: Uri[],
|
qlPackDetails: QlPackDetails,
|
||||||
progress: ProgressCallback,
|
progress: ProgressCallback,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -323,35 +369,43 @@ export class VariantAnalysisManager
|
|||||||
actionBranch,
|
actionBranch,
|
||||||
base64Pack,
|
base64Pack,
|
||||||
repoSelection,
|
repoSelection,
|
||||||
queryFile,
|
|
||||||
queryMetadata,
|
|
||||||
controllerRepo,
|
controllerRepo,
|
||||||
queryStartTime,
|
queryStartTime,
|
||||||
language,
|
|
||||||
} = await prepareRemoteQueryRun(
|
} = await prepareRemoteQueryRun(
|
||||||
this.cliServer,
|
this.cliServer,
|
||||||
this.app.credentials,
|
this.app.credentials,
|
||||||
uris,
|
qlPackDetails,
|
||||||
progress,
|
progress,
|
||||||
token,
|
token,
|
||||||
this.dbManager,
|
this.dbManager,
|
||||||
);
|
);
|
||||||
|
|
||||||
const queryName = getQueryName(queryMetadata, queryFile);
|
// For now we get the metadata for the first query in the pack.
|
||||||
const variantAnalysisLanguage = parseVariantAnalysisQueryLanguage(language);
|
// and use that in the submission and query history. In the future
|
||||||
|
// we'll need to consider how to handle having multiple queries.
|
||||||
|
const firstQueryFile = qlPackDetails.queryFiles[0];
|
||||||
|
const queryMetadata = await tryGetQueryMetadata(
|
||||||
|
this.cliServer,
|
||||||
|
firstQueryFile,
|
||||||
|
);
|
||||||
|
const queryName = getQueryName(queryMetadata, firstQueryFile);
|
||||||
|
const variantAnalysisLanguage = parseVariantAnalysisQueryLanguage(
|
||||||
|
qlPackDetails.language,
|
||||||
|
);
|
||||||
if (variantAnalysisLanguage === undefined) {
|
if (variantAnalysisLanguage === undefined) {
|
||||||
throw new UserCancellationException(
|
throw new UserCancellationException(
|
||||||
`Found unsupported language: ${language}`,
|
`Found unsupported language: ${qlPackDetails.language}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryText = await readFile(queryFile, "utf8");
|
const queryText = await readFile(firstQueryFile, "utf8");
|
||||||
|
|
||||||
const queries =
|
const queries: VariantAnalysisQueries | undefined =
|
||||||
uris.length === 1
|
qlPackDetails.queryFiles.length === 1
|
||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
language: variantAnalysisLanguage,
|
language: qlPackDetails.language,
|
||||||
|
count: qlPackDetails.queryFiles.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
const variantAnalysisSubmission: VariantAnalysisSubmission = {
|
const variantAnalysisSubmission: VariantAnalysisSubmission = {
|
||||||
@@ -360,7 +414,7 @@ export class VariantAnalysisManager
|
|||||||
controllerRepoId: controllerRepo.id,
|
controllerRepoId: controllerRepo.id,
|
||||||
query: {
|
query: {
|
||||||
name: queryName,
|
name: queryName,
|
||||||
filePath: queryFile,
|
filePath: firstQueryFile,
|
||||||
pack: base64Pack,
|
pack: base64Pack,
|
||||||
language: variantAnalysisLanguage,
|
language: variantAnalysisLanguage,
|
||||||
text: queryText,
|
text: queryText,
|
||||||
|
|||||||
@@ -218,9 +218,15 @@ export class VariantAnalysisView
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getTitle(variantAnalysis: VariantAnalysis | undefined): string {
|
private getTitle(variantAnalysis: VariantAnalysis | undefined): string {
|
||||||
return variantAnalysis
|
if (!variantAnalysis) {
|
||||||
? `${variantAnalysis.query.name} - Variant Analysis Results`
|
return `Variant Analysis ${this.variantAnalysisId} - Results`;
|
||||||
: `Variant Analysis ${this.variantAnalysisId} - Results`;
|
}
|
||||||
|
|
||||||
|
if (variantAnalysis.queries) {
|
||||||
|
return `Variant Analysis using multiple queries - Results`;
|
||||||
|
} else {
|
||||||
|
return `${variantAnalysis.query.name} - Variant Analysis Results`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async showDataFlows(dataFlows: DataFlowPaths): Promise<void> {
|
private async showDataFlows(dataFlows: DataFlowPaths): Promise<void> {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const Message = styled.div`
|
|||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function Compare(_: Record<string, never>): JSX.Element {
|
export function Compare(_: Record<string, never>): React.JSX.Element {
|
||||||
const [queryInfo, setQueryInfo] =
|
const [queryInfo, setQueryInfo] =
|
||||||
useState<SetComparisonQueryInfoMessage | null>(null);
|
useState<SetComparisonQueryInfoMessage | null>(null);
|
||||||
const [comparison, setComparison] = useState<SetComparisonsMessage | null>(
|
const [comparison, setComparison] = useState<SetComparisonsMessage | null>(
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const DataFlowPaths = ({
|
|||||||
dataFlowPaths,
|
dataFlowPaths,
|
||||||
}: {
|
}: {
|
||||||
dataFlowPaths: DataFlowPathsDomainModel;
|
dataFlowPaths: DataFlowPathsDomainModel;
|
||||||
}): JSX.Element => {
|
}): React.JSX.Element => {
|
||||||
const [selectedCodeFlow, setSelectedCodeFlow] = useState(
|
const [selectedCodeFlow, setSelectedCodeFlow] = useState(
|
||||||
dataFlowPaths.codeFlows[0],
|
dataFlowPaths.codeFlows[0],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export type DataFlowPathsViewProps = {
|
|||||||
|
|
||||||
export function DataFlowPathsView({
|
export function DataFlowPathsView({
|
||||||
dataFlowPaths: initialDataFlowPaths,
|
dataFlowPaths: initialDataFlowPaths,
|
||||||
}: DataFlowPathsViewProps): JSX.Element {
|
}: DataFlowPathsViewProps): React.JSX.Element {
|
||||||
const [dataFlowPaths, setDataFlowPaths] = useState<
|
const [dataFlowPaths, setDataFlowPaths] = useState<
|
||||||
DataFlowPathsDomainModel | undefined
|
DataFlowPathsDomainModel | undefined
|
||||||
>(initialDataFlowPaths);
|
>(initialDataFlowPaths);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const MethodModeling = ({
|
|||||||
method,
|
method,
|
||||||
isModelingInProgress,
|
isModelingInProgress,
|
||||||
onChange,
|
onChange,
|
||||||
}: MethodModelingProps): JSX.Element => {
|
}: MethodModelingProps): React.JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Title>
|
<Title>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const MethodModelingInputs = ({
|
|||||||
modelingStatus,
|
modelingStatus,
|
||||||
isModelingInProgress,
|
isModelingInProgress,
|
||||||
onChange,
|
onChange,
|
||||||
}: MethodModelingInputsProps): JSX.Element => {
|
}: MethodModelingInputsProps): React.JSX.Element => {
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ type Props = {
|
|||||||
initialViewState?: MethodModelingPanelViewState;
|
initialViewState?: MethodModelingPanelViewState;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function MethodModelingView({ initialViewState }: Props): JSX.Element {
|
export function MethodModelingView({
|
||||||
|
initialViewState,
|
||||||
|
}: Props): React.JSX.Element {
|
||||||
const [viewState, setViewState] = useState<
|
const [viewState, setViewState] = useState<
|
||||||
MethodModelingPanelViewState | undefined
|
MethodModelingPanelViewState | undefined
|
||||||
>(initialViewState);
|
>(initialViewState);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const TypeMethodName = (method: Method) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MethodName = (method: Method): JSX.Element => {
|
export const MethodName = (method: Method): React.JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Name>
|
<Name>
|
||||||
{method.packageName && <>{method.packageName}.</>}
|
{method.packageName && <>{method.packageName}.</>}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function ModelEditor({
|
|||||||
initialMethods = [],
|
initialMethods = [],
|
||||||
initialModeledMethods = {},
|
initialModeledMethods = {},
|
||||||
initialHideModeledMethods = INITIAL_HIDE_MODELED_METHODS_VALUE,
|
initialHideModeledMethods = INITIAL_HIDE_MODELED_METHODS_VALUE,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.JSX.Element {
|
||||||
const [viewState, setViewState] = useState<ModelEditorViewState | undefined>(
|
const [viewState, setViewState] = useState<ModelEditorViewState | undefined>(
|
||||||
initialViewState,
|
initialViewState,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const ModelInputDropdown = ({
|
|||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelingStatus,
|
modelingStatus,
|
||||||
onChange,
|
onChange,
|
||||||
}: Props): JSX.Element => {
|
}: Props): React.JSX.Element => {
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
|
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const ModelOutputDropdown = ({
|
|||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelingStatus,
|
modelingStatus,
|
||||||
onChange,
|
onChange,
|
||||||
}: Props): JSX.Element => {
|
}: Props): React.JSX.Element => {
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
|
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const ModelTypeDropdown = ({
|
|||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelingStatus,
|
modelingStatus,
|
||||||
onChange,
|
onChange,
|
||||||
}: Props): JSX.Element => {
|
}: Props): React.JSX.Element => {
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
const baseOptions: Array<{ value: ModeledMethodType; label: string }> = [
|
const baseOptions: Array<{ value: ModeledMethodType; label: string }> = [
|
||||||
{ value: "none", label: "Unmodeled" },
|
{ value: "none", label: "Unmodeled" },
|
||||||
|
|||||||
@@ -15,12 +15,16 @@ type Props = {
|
|||||||
"aria-label"?: string;
|
"aria-label"?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stopClickPropagation = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
export const ModelTypeTextbox = ({
|
export const ModelTypeTextbox = ({
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
typeInfo,
|
typeInfo,
|
||||||
onChange,
|
onChange,
|
||||||
...props
|
...props
|
||||||
}: Props): JSX.Element => {
|
}: Props): React.JSX.Element => {
|
||||||
const [value, setValue] = useState<string | undefined>(
|
const [value, setValue] = useState<string | undefined>(
|
||||||
modeledMethod[typeInfo],
|
modeledMethod[typeInfo],
|
||||||
);
|
);
|
||||||
@@ -48,5 +52,12 @@ export const ModelTypeTextbox = ({
|
|||||||
500,
|
500,
|
||||||
);
|
);
|
||||||
|
|
||||||
return <VSCodeTextField value={value} onInput={handleChange} {...props} />;
|
return (
|
||||||
|
<VSCodeTextField
|
||||||
|
value={value}
|
||||||
|
onInput={handleChange}
|
||||||
|
onClick={stopClickPropagation}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ interface Props {
|
|||||||
showRawResults: () => void;
|
showRawResults: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AlertTableNoResults(props: Props): JSX.Element {
|
export function AlertTableNoResults(props: Props): React.JSX.Element {
|
||||||
if (props.nonemptyRawResults) {
|
if (props.nonemptyRawResults) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ interface Props {
|
|||||||
numTruncatedResults: number;
|
numTruncatedResults: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AlertTableTruncatedMessage(props: Props): JSX.Element | null {
|
export function AlertTableTruncatedMessage(
|
||||||
|
props: Props,
|
||||||
|
): React.JSX.Element | null {
|
||||||
if (props.numTruncatedResults === 0) {
|
if (props.numTruncatedResults === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const Container = styled.span`
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function EmptyQueryResultsMessage(): JSX.Element {
|
export function EmptyQueryResultsMessage(): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Root>
|
<Root>
|
||||||
<Container>
|
<Container>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface Props {
|
|||||||
handleCheckboxChanged: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
handleCheckboxChanged: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProblemsViewCheckbox(props: Props): JSX.Element | null {
|
export function ProblemsViewCheckbox(props: Props): React.JSX.Element | null {
|
||||||
const { selectedTable, problemsViewSelected, handleCheckboxChanged } = props;
|
const { selectedTable, problemsViewSelected, handleCheckboxChanged } = props;
|
||||||
|
|
||||||
if (selectedTable !== ALERTS_TABLE_NAME) {
|
if (selectedTable !== ALERTS_TABLE_NAME) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default function RawTableValue({
|
|||||||
value,
|
value,
|
||||||
databaseUri,
|
databaseUri,
|
||||||
onSelected,
|
onSelected,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.JSX.Element {
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return <span>{value.value.toString()}</span>;
|
return <span>{value.value.toString()}</span>;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function getResultCount(resultSet: ResultSet): number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ResultCount(props: Props): JSX.Element | null {
|
export function ResultCount(props: Props): React.JSX.Element | null {
|
||||||
if (!props.resultSet) {
|
if (!props.resultSet) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export function ClickableLocation({
|
|||||||
label,
|
label,
|
||||||
databaseUri,
|
databaseUri,
|
||||||
onClick: onClick,
|
onClick: onClick,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.JSX.Element {
|
||||||
const handleClick = useCallback(
|
const handleClick = useCallback(
|
||||||
(e: React.MouseEvent) => {
|
(e: React.MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function Location({
|
|||||||
databaseUri,
|
databaseUri,
|
||||||
title,
|
title,
|
||||||
onClick,
|
onClick,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.JSX.Element {
|
||||||
const displayLabel = useMemo(() => convertNonPrintableChars(label), [label]);
|
const displayLabel = useMemo(() => convertNonPrintableChars(label), [label]);
|
||||||
|
|
||||||
if (loc === undefined) {
|
if (loc === undefined) {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function VariantAnalysis({
|
|||||||
variantAnalysis: initialVariantAnalysis,
|
variantAnalysis: initialVariantAnalysis,
|
||||||
repoStates: initialRepoStates = [],
|
repoStates: initialRepoStates = [],
|
||||||
repoResults: initialRepoResults = [],
|
repoResults: initialRepoResults = [],
|
||||||
}: VariantAnalysisProps): JSX.Element {
|
}: VariantAnalysisProps): React.JSX.Element {
|
||||||
const [variantAnalysis, setVariantAnalysis] = useState<
|
const [variantAnalysis, setVariantAnalysis] = useState<
|
||||||
VariantAnalysisDomainModel | undefined
|
VariantAnalysisDomainModel | undefined
|
||||||
>(initialVariantAnalysis);
|
>(initialVariantAnalysis);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
defaultFilterSortState,
|
defaultFilterSortState,
|
||||||
filterAndSortRepositoriesWithResults,
|
filterAndSortRepositoriesWithResults,
|
||||||
} from "../../variant-analysis/shared/variant-analysis-filter-sort";
|
} from "../../variant-analysis/shared/variant-analysis-filter-sort";
|
||||||
|
import { ViewTitle } from "../common";
|
||||||
|
|
||||||
type VariantAnalysisHeaderProps = {
|
type VariantAnalysisHeaderProps = {
|
||||||
variantAnalysis: VariantAnalysis;
|
variantAnalysis: VariantAnalysis;
|
||||||
@@ -50,6 +51,29 @@ const Row = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const QueryInfo = ({
|
||||||
|
variantAnalysis,
|
||||||
|
onOpenQueryFileClick,
|
||||||
|
onViewQueryTextClick,
|
||||||
|
}: {
|
||||||
|
variantAnalysis: VariantAnalysis;
|
||||||
|
onOpenQueryFileClick: () => void;
|
||||||
|
onViewQueryTextClick: () => void;
|
||||||
|
}) => {
|
||||||
|
if (variantAnalysis.queries) {
|
||||||
|
return <ViewTitle>{variantAnalysis.queries?.count} queries</ViewTitle>;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<QueryDetails
|
||||||
|
queryName={variantAnalysis.query.name}
|
||||||
|
queryFileName={basename(variantAnalysis.query.filePath)}
|
||||||
|
onOpenQueryFileClick={onOpenQueryFileClick}
|
||||||
|
onViewQueryTextClick={onViewQueryTextClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const VariantAnalysisHeader = ({
|
export const VariantAnalysisHeader = ({
|
||||||
variantAnalysis,
|
variantAnalysis,
|
||||||
repositoryStates,
|
repositoryStates,
|
||||||
@@ -117,9 +141,8 @@ export const VariantAnalysisHeader = ({
|
|||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Row>
|
<Row>
|
||||||
<QueryDetails
|
<QueryInfo
|
||||||
queryName={variantAnalysis.query.name}
|
variantAnalysis={variantAnalysis}
|
||||||
queryFileName={basename(variantAnalysis.query.filePath)}
|
|
||||||
onOpenQueryFileClick={onOpenQueryFileClick}
|
onOpenQueryFileClick={onOpenQueryFileClick}
|
||||||
onViewQueryTextClick={onViewQueryTextClick}
|
onViewQueryTextClick={onViewQueryTextClick}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export type WebviewDefinition = {
|
export type WebviewDefinition = {
|
||||||
component: JSX.Element;
|
component: React.JSX.Element;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[
|
[
|
||||||
"v2.16.0",
|
"v2.16.1",
|
||||||
"v2.15.5",
|
"v2.15.5",
|
||||||
"v2.14.6",
|
"v2.14.6",
|
||||||
"v2.13.5",
|
"v2.13.5",
|
||||||
|
|||||||
20
extensions/ql-vscode/test/e2e/README.md
Normal file
20
extensions/ql-vscode/test/e2e/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
## VS Code CodeQL E2E Tests
|
||||||
|
|
||||||
|
When running the tests locally on a mac a different processor has to be emulated, which makes everythign VERY slow. Therefore we need to add higher timeouts in the test, so that they pass locally.
|
||||||
|
|
||||||
|
### How to use locally
|
||||||
|
|
||||||
|
Setup
|
||||||
|
|
||||||
|
- install playwright if you haven't yet (`npx playwright install`)
|
||||||
|
- go to the e2e test folder on your terminal
|
||||||
|
- make sure docker is running
|
||||||
|
- run `docker-compose build`
|
||||||
|
- run `docker-compose up`
|
||||||
|
|
||||||
|
Run tests
|
||||||
|
|
||||||
|
- run `npx playwright test --ui` from the e2e test folder to follow the test while it's running. This UI has a 'locator' tool with which elements on the test screen can be found
|
||||||
|
- use `npx playwright test --debug` to follow the test in real time and interact with the interface, e.g. press enter or input into fields, stop and start
|
||||||
|
|
||||||
|
During the test elements are created in the docker volume, e.g. the downloaded database or query data. This might interfer with other tests or when running a test twice. If that happens restart your docker volume by using `docker-compose down -v` and `docker-compose up`. Sometimes already existing queries from former runs change the input the extension needs.
|
||||||
55
extensions/ql-vscode/test/e2e/docker-compose.yml
Normal file
55
extensions/ql-vscode/test/e2e/docker-compose.yml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
code-server:
|
||||||
|
build:
|
||||||
|
context: docker
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
platform: linux/amd64
|
||||||
|
container_name: code-server
|
||||||
|
user: "1000"
|
||||||
|
volumes:
|
||||||
|
- local-data:/home/coder/.local/share/code-server
|
||||||
|
- local-user-data:/home/coder/.local/share/code-server/User
|
||||||
|
- ./docker/config/config.yaml:/home/coder/.config/code-server/config.yaml
|
||||||
|
- ./docker/User/settings.json:/home/coder/.local/share/code-server/User/settings.json
|
||||||
|
- project-data:/home/coder/project
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
code-server-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
code-server-init:
|
||||||
|
build:
|
||||||
|
context: docker
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
platform: linux/amd64
|
||||||
|
user: "1000"
|
||||||
|
volumes:
|
||||||
|
- local-data:/home/coder/.local/share/code-server
|
||||||
|
- local-user-data:/home/coder/.local/share/code-server/User
|
||||||
|
- ./docker/config/config.yaml:/home/coder/.config/code-server/config.yaml
|
||||||
|
- ./docker/User/settings.json:/home/coder/.local/share/code-server/User/settings.json
|
||||||
|
- project-data:/home/coder/project
|
||||||
|
entrypoint: |
|
||||||
|
/usr/bin/entrypoint.sh --install-extension GitHub.vscode-codeql
|
||||||
|
restart: "no"
|
||||||
|
depends_on:
|
||||||
|
- files-init
|
||||||
|
files-init:
|
||||||
|
image: alpine:3.19.0
|
||||||
|
restart: "no"
|
||||||
|
# Since we're not running the code-server container using the same user as our host user,
|
||||||
|
# we need to set the permissions on the mounted volumes to match the user inside the container.
|
||||||
|
entrypoint: |
|
||||||
|
/bin/sh -c "chown 1000:1000 /home/coder/.local/share/code-server /home/coder/.local/share/code-server/User /home/coder/project"
|
||||||
|
volumes:
|
||||||
|
- local-data:/home/coder/.local/share/code-server
|
||||||
|
- local-user-data:/home/coder/.local/share/code-server/User
|
||||||
|
- project-data:/home/coder/project
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
local-data:
|
||||||
|
local-user-data:
|
||||||
|
project-data:
|
||||||
16
extensions/ql-vscode/test/e2e/docker/Dockerfile
Normal file
16
extensions/ql-vscode/test/e2e/docker/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM codercom/code-server:4.20.0
|
||||||
|
|
||||||
|
USER root
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
unzip \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN wget -q -O /tmp/codeql.zip https://github.com/github/codeql-cli-binaries/releases/download/v2.15.5/codeql-linux64.zip \
|
||||||
|
&& unzip -q /tmp/codeql.zip -d /opt \
|
||||||
|
&& rm -rf /tmp/codeql.zip
|
||||||
|
|
||||||
|
ENV PATH="/opt/codeql:${PATH}"
|
||||||
|
|
||||||
|
USER 1000
|
||||||
6
extensions/ql-vscode/test/e2e/docker/User/settings.json
Normal file
6
extensions/ql-vscode/test/e2e/docker/User/settings.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"workbench.startupEditor": "none",
|
||||||
|
"security.workspace.trust.enabled": false,
|
||||||
|
"codeQL.cli.executablePath": "/opt/codeql/codeql",
|
||||||
|
"codeQL.telemetry.enableTelemetry": false
|
||||||
|
}
|
||||||
6
extensions/ql-vscode/test/e2e/docker/config/config.yaml
Normal file
6
extensions/ql-vscode/test/e2e/docker/config/config.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
bind-addr: 127.0.0.1:8080
|
||||||
|
auth: none
|
||||||
|
cert: false
|
||||||
|
disable-workspace-trust: true
|
||||||
|
disable-telemetry: true
|
||||||
|
disable-update-check: true
|
||||||
36
extensions/ql-vscode/test/e2e/playwright.config.ts
Normal file
36
extensions/ql-vscode/test/e2e/playwright.config.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: ".",
|
||||||
|
|
||||||
|
timeout: 5 * 60 * 1000,
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: "html",
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
|
baseURL: "http://localhost:8080",
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: "on-first-retry",
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: { ...devices["Desktop Chrome"] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
79
extensions/ql-vscode/test/e2e/run-query.spec.ts
Normal file
79
extensions/ql-vscode/test/e2e/run-query.spec.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
test("run query and open it from history", async ({ page }) => {
|
||||||
|
await page.goto("/?folder=/home/coder/project");
|
||||||
|
|
||||||
|
await page.getByRole("tab", { name: "CodeQL" }).locator("a").click();
|
||||||
|
|
||||||
|
// decline extension telemetry
|
||||||
|
await page.getByRole("button", { name: "No", exact: true }).click({
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.keyboard.press("Control+Shift+P");
|
||||||
|
await page.keyboard.type("Create Query");
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
await page.getByLabel("JavaScript, javascript").locator("a").click({
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// select folder for first query
|
||||||
|
await page
|
||||||
|
.getByText(
|
||||||
|
"Results0 SelectedPress 'Enter' to confirm your input or 'Escape' to cancelOK",
|
||||||
|
)
|
||||||
|
.press("Enter");
|
||||||
|
|
||||||
|
// download database
|
||||||
|
await page
|
||||||
|
.getByRole("button", { name: "Download database" })
|
||||||
|
.click({ timeout: 60000 });
|
||||||
|
await page.getByPlaceholder("https://github.com/<owner>/<").press("Enter");
|
||||||
|
await page
|
||||||
|
.locator("#list_id_3_0")
|
||||||
|
.getByText("javascript")
|
||||||
|
.click({ timeout: 60000 });
|
||||||
|
|
||||||
|
await page.keyboard.press("Control+Shift+P");
|
||||||
|
await page.keyboard.type("Run Query on selected");
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
// check if results page is visible
|
||||||
|
await expect(page.getByText("CodeQL Query Results")).toBeVisible({
|
||||||
|
timeout: 600000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait for query history item to be finished
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.locator("#list_id_6_0")
|
||||||
|
.getByLabel("Hello world on d3/d3 -")
|
||||||
|
.locator("div")
|
||||||
|
.first(),
|
||||||
|
).toBeVisible({ timeout: 60000 });
|
||||||
|
|
||||||
|
// close results page and open query from history
|
||||||
|
await page
|
||||||
|
.getByLabel("CodeQL Query Results, Editor Group")
|
||||||
|
.getByLabel("Close (Ctrl+F4)")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.frameLocator(".webview")
|
||||||
|
.frameLocator('iframe[title="CodeQL Query Results"]')
|
||||||
|
.getByText("#selectalerts32 resultsShow"),
|
||||||
|
).not.toBeVisible();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.locator("#list_id_6_0")
|
||||||
|
.getByLabel("Hello world on d3/d3 -")
|
||||||
|
.locator("div")
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByLabel("CodeQL Query Results", { exact: true }).locator("div"),
|
||||||
|
).toBeVisible({ timeout: 60000 });
|
||||||
|
});
|
||||||
@@ -115,7 +115,7 @@ describe("Releases API consumer", () => {
|
|||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
consumer.getLatestRelease(new Range("5.*.*")),
|
consumer.getLatestRelease(new Range("5.*.*")),
|
||||||
).rejects.toThrowError();
|
).rejects.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("picked release passes additional compatibility test if an additional compatibility test is specified", async () => {
|
it("picked release passes additional compatibility test if an additional compatibility test is specified", async () => {
|
||||||
@@ -140,7 +140,7 @@ describe("Releases API consumer", () => {
|
|||||||
(asset) => asset.name === "otherExampleAsset.txt",
|
(asset) => asset.name === "otherExampleAsset.txt",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).rejects.toThrowError();
|
).rejects.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("picked release is the most recent prerelease when includePrereleases is set", async () => {
|
it("picked release is the most recent prerelease when includePrereleases is set", async () => {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { join } from "path";
|
|||||||
import { dirSync } from "tmp-promise";
|
import { dirSync } from "tmp-promise";
|
||||||
import type { DirResult } from "tmp";
|
import type { DirResult } from "tmp";
|
||||||
import { writeFile } from "fs-extra";
|
import { writeFile } from "fs-extra";
|
||||||
import { getQlPackPath } from "../../../src/common/ql";
|
import { getQlPackFilePath } from "../../../src/common/ql";
|
||||||
|
|
||||||
describe("getQlPackPath", () => {
|
describe("getQlPackFilePath", () => {
|
||||||
let tmpDir: DirResult;
|
let tmpDir: DirResult;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -22,14 +22,14 @@ describe("getQlPackPath", () => {
|
|||||||
it("should find a qlpack.yml when it exists", async () => {
|
it("should find a qlpack.yml when it exists", async () => {
|
||||||
await writeFile(join(tmpDir.name, "qlpack.yml"), "name: test");
|
await writeFile(join(tmpDir.name, "qlpack.yml"), "name: test");
|
||||||
|
|
||||||
const result = await getQlPackPath(tmpDir.name);
|
const result = await getQlPackFilePath(tmpDir.name);
|
||||||
expect(result).toEqual(join(tmpDir.name, "qlpack.yml"));
|
expect(result).toEqual(join(tmpDir.name, "qlpack.yml"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should find a codeql-pack.yml when it exists", async () => {
|
it("should find a codeql-pack.yml when it exists", async () => {
|
||||||
await writeFile(join(tmpDir.name, "codeql-pack.yml"), "name: test");
|
await writeFile(join(tmpDir.name, "codeql-pack.yml"), "name: test");
|
||||||
|
|
||||||
const result = await getQlPackPath(tmpDir.name);
|
const result = await getQlPackFilePath(tmpDir.name);
|
||||||
expect(result).toEqual(join(tmpDir.name, "codeql-pack.yml"));
|
expect(result).toEqual(join(tmpDir.name, "codeql-pack.yml"));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,12 +37,12 @@ describe("getQlPackPath", () => {
|
|||||||
await writeFile(join(tmpDir.name, "qlpack.yml"), "name: test");
|
await writeFile(join(tmpDir.name, "qlpack.yml"), "name: test");
|
||||||
await writeFile(join(tmpDir.name, "codeql-pack.yml"), "name: test");
|
await writeFile(join(tmpDir.name, "codeql-pack.yml"), "name: test");
|
||||||
|
|
||||||
const result = await getQlPackPath(tmpDir.name);
|
const result = await getQlPackFilePath(tmpDir.name);
|
||||||
expect(result).toEqual(join(tmpDir.name, "qlpack.yml"));
|
expect(result).toEqual(join(tmpDir.name, "qlpack.yml"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should find nothing when it doesn't exist", async () => {
|
it("should find nothing when it doesn't exist", async () => {
|
||||||
const result = await getQlPackPath(tmpDir.name);
|
const result = await getQlPackFilePath(tmpDir.name);
|
||||||
expect(result).toEqual(undefined);
|
expect(result).toEqual(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
async function* generateInParts() {
|
async function* generateInParts() {
|
||||||
const partLength = fileContents.length / 5;
|
const partLength = fileContents.length / 5;
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
yield fileContents.slice(i * partLength, (i + 1) * partLength);
|
yield fileContents.subarray(i * partLength, (i + 1) * partLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CancellationTokenSource, commands, Uri, window } from "vscode";
|
import { CancellationTokenSource, commands, window, Uri } from "vscode";
|
||||||
import { extLogger } from "../../../../src/common/logging/vscode";
|
import { extLogger } from "../../../../src/common/logging/vscode";
|
||||||
import { setRemoteControllerRepo } from "../../../../src/config";
|
import { setRemoteControllerRepo } from "../../../../src/config";
|
||||||
import * as ghApiClient from "../../../../src/variant-analysis/gh-api/gh-api-client";
|
import * as ghApiClient from "../../../../src/variant-analysis/gh-api/gh-api-client";
|
||||||
@@ -26,6 +26,7 @@ import type { ExtensionPackMetadata } from "../../../../src/model-editor/extensi
|
|||||||
import type { QlPackLockFile } from "../../../../src/packaging/qlpack-lock-file";
|
import type { QlPackLockFile } from "../../../../src/packaging/qlpack-lock-file";
|
||||||
//import { expect } from "@jest/globals";
|
//import { expect } from "@jest/globals";
|
||||||
import "../../../matchers/toExistInCodeQLPack";
|
import "../../../matchers/toExistInCodeQLPack";
|
||||||
|
import type { QlPackDetails } from "../../../../src/variant-analysis/ql-pack-details";
|
||||||
|
|
||||||
describe("Variant Analysis Manager", () => {
|
describe("Variant Analysis Manager", () => {
|
||||||
let cli: CodeQLCliServer;
|
let cli: CodeQLCliServer;
|
||||||
@@ -99,10 +100,18 @@ describe("Variant Analysis Manager", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should run a variant analysis that is part of a qlpack", async () => {
|
it("should run a variant analysis that is part of a qlpack", async () => {
|
||||||
const fileUri = getFile("data-remote-qlpack/in-pack.ql");
|
const filePath = getFileOrDir("data-remote-qlpack/in-pack.ql");
|
||||||
|
const qlPackRootPath = getFileOrDir("data-remote-qlpack");
|
||||||
|
const qlPackFilePath = getFileOrDir("data-remote-qlpack/qlpack.yml");
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: [filePath],
|
||||||
|
qlPackRootPath,
|
||||||
|
qlPackFilePath,
|
||||||
|
language: QueryLanguage.Javascript,
|
||||||
|
};
|
||||||
|
|
||||||
await variantAnalysisManager.runVariantAnalysis(
|
await variantAnalysisManager.runVariantAnalysis(
|
||||||
[fileUri],
|
qlPackDetails,
|
||||||
progress,
|
progress,
|
||||||
cancellationTokenSource.token,
|
cancellationTokenSource.token,
|
||||||
);
|
);
|
||||||
@@ -120,10 +129,17 @@ describe("Variant Analysis Manager", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should run a remote query that is not part of a qlpack", async () => {
|
it("should run a remote query that is not part of a qlpack", async () => {
|
||||||
const fileUri = getFile("data-remote-no-qlpack/in-pack.ql");
|
const filePath = getFileOrDir("data-remote-no-qlpack/in-pack.ql");
|
||||||
|
const qlPackRootPath = getFileOrDir("data-remote-no-qlpack");
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: [filePath],
|
||||||
|
qlPackRootPath,
|
||||||
|
qlPackFilePath: undefined,
|
||||||
|
language: QueryLanguage.Javascript,
|
||||||
|
};
|
||||||
|
|
||||||
await variantAnalysisManager.runVariantAnalysis(
|
await variantAnalysisManager.runVariantAnalysis(
|
||||||
[fileUri],
|
qlPackDetails,
|
||||||
progress,
|
progress,
|
||||||
cancellationTokenSource.token,
|
cancellationTokenSource.token,
|
||||||
);
|
);
|
||||||
@@ -141,10 +157,22 @@ describe("Variant Analysis Manager", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should run a remote query that is nested inside a qlpack", async () => {
|
it("should run a remote query that is nested inside a qlpack", async () => {
|
||||||
const fileUri = getFile("data-remote-qlpack-nested/subfolder/in-pack.ql");
|
const filePath = getFileOrDir(
|
||||||
|
"data-remote-qlpack-nested/subfolder/in-pack.ql",
|
||||||
|
);
|
||||||
|
const qlPackRootPath = getFileOrDir("data-remote-qlpack-nested");
|
||||||
|
const qlPackFilePath = getFileOrDir(
|
||||||
|
"data-remote-qlpack-nested/codeql-pack.yml",
|
||||||
|
);
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: [filePath],
|
||||||
|
qlPackRootPath,
|
||||||
|
qlPackFilePath,
|
||||||
|
language: QueryLanguage.Javascript,
|
||||||
|
};
|
||||||
|
|
||||||
await variantAnalysisManager.runVariantAnalysis(
|
await variantAnalysisManager.runVariantAnalysis(
|
||||||
[fileUri],
|
qlPackDetails,
|
||||||
progress,
|
progress,
|
||||||
cancellationTokenSource.token,
|
cancellationTokenSource.token,
|
||||||
);
|
);
|
||||||
@@ -162,10 +190,17 @@ describe("Variant Analysis Manager", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should cancel a run before uploading", async () => {
|
it("should cancel a run before uploading", async () => {
|
||||||
const fileUri = getFile("data-remote-no-qlpack/in-pack.ql");
|
const filePath = getFileOrDir("data-remote-no-qlpack/in-pack.ql");
|
||||||
|
const qlPackRootPath = getFileOrDir("data-remote-no-qlpack");
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: [filePath],
|
||||||
|
qlPackRootPath,
|
||||||
|
qlPackFilePath: undefined,
|
||||||
|
language: QueryLanguage.Javascript,
|
||||||
|
};
|
||||||
|
|
||||||
const promise = variantAnalysisManager.runVariantAnalysis(
|
const promise = variantAnalysisManager.runVariantAnalysis(
|
||||||
[fileUri],
|
qlPackDetails,
|
||||||
progress,
|
progress,
|
||||||
cancellationTokenSource.token,
|
cancellationTokenSource.token,
|
||||||
);
|
);
|
||||||
@@ -202,6 +237,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
it("should run a remote query that is part of a qlpack", async () => {
|
it("should run a remote query that is part of a qlpack", async () => {
|
||||||
await doVariantAnalysisTest({
|
await doVariantAnalysisTest({
|
||||||
queryPath: "data-remote-qlpack/in-pack.ql",
|
queryPath: "data-remote-qlpack/in-pack.ql",
|
||||||
|
qlPackRootPath: "data-remote-qlpack",
|
||||||
|
qlPackFilePath: "data-remote-qlpack/qlpack.yml",
|
||||||
expectedPackName: "github/remote-query-pack",
|
expectedPackName: "github/remote-query-pack",
|
||||||
filesThatExist: ["in-pack.ql", "lib.qll"],
|
filesThatExist: ["in-pack.ql", "lib.qll"],
|
||||||
filesThatDoNotExist: [],
|
filesThatDoNotExist: [],
|
||||||
@@ -212,6 +249,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
it("should run a remote query that is not part of a qlpack", async () => {
|
it("should run a remote query that is not part of a qlpack", async () => {
|
||||||
await doVariantAnalysisTest({
|
await doVariantAnalysisTest({
|
||||||
queryPath: "data-remote-no-qlpack/in-pack.ql",
|
queryPath: "data-remote-no-qlpack/in-pack.ql",
|
||||||
|
qlPackRootPath: "data-remote-no-qlpack",
|
||||||
|
qlPackFilePath: undefined,
|
||||||
expectedPackName: "codeql-remote/query",
|
expectedPackName: "codeql-remote/query",
|
||||||
filesThatExist: ["in-pack.ql"],
|
filesThatExist: ["in-pack.ql"],
|
||||||
filesThatDoNotExist: ["lib.qll", "not-in-pack.ql"],
|
filesThatDoNotExist: ["lib.qll", "not-in-pack.ql"],
|
||||||
@@ -222,6 +261,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
it("should run a remote query that is nested inside a qlpack", async () => {
|
it("should run a remote query that is nested inside a qlpack", async () => {
|
||||||
await doVariantAnalysisTest({
|
await doVariantAnalysisTest({
|
||||||
queryPath: "data-remote-qlpack-nested/subfolder/in-pack.ql",
|
queryPath: "data-remote-qlpack-nested/subfolder/in-pack.ql",
|
||||||
|
qlPackRootPath: "data-remote-qlpack-nested",
|
||||||
|
qlPackFilePath: "data-remote-qlpack-nested/codeql-pack.yml",
|
||||||
expectedPackName: "github/remote-query-pack",
|
expectedPackName: "github/remote-query-pack",
|
||||||
filesThatExist: ["subfolder/in-pack.ql", "otherfolder/lib.qll"],
|
filesThatExist: ["subfolder/in-pack.ql", "otherfolder/lib.qll"],
|
||||||
filesThatDoNotExist: ["subfolder/not-in-pack.ql"],
|
filesThatDoNotExist: ["subfolder/not-in-pack.ql"],
|
||||||
@@ -239,6 +280,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
await cli.setUseExtensionPacks(true);
|
await cli.setUseExtensionPacks(true);
|
||||||
await doVariantAnalysisTest({
|
await doVariantAnalysisTest({
|
||||||
queryPath: "data-remote-qlpack-nested/subfolder/in-pack.ql",
|
queryPath: "data-remote-qlpack-nested/subfolder/in-pack.ql",
|
||||||
|
qlPackRootPath: "data-remote-qlpack-nested",
|
||||||
|
qlPackFilePath: "data-remote-qlpack-nested/codeql-pack.yml",
|
||||||
expectedPackName: "github/remote-query-pack",
|
expectedPackName: "github/remote-query-pack",
|
||||||
filesThatExist: [
|
filesThatExist: [
|
||||||
"subfolder/in-pack.ql",
|
"subfolder/in-pack.ql",
|
||||||
@@ -275,16 +318,23 @@ describe("Variant Analysis Manager", () => {
|
|||||||
|
|
||||||
const queryToRun =
|
const queryToRun =
|
||||||
"Security/CWE/CWE-020/ExternalAPIsUsedWithUntrustedData.ql";
|
"Security/CWE/CWE-020/ExternalAPIsUsedWithUntrustedData.ql";
|
||||||
const extraQuery = "Telemetry/ExtractorInformation.ql";
|
|
||||||
|
|
||||||
|
// Recent versions of the CLI don't preserve queries with extensible predicates in MRVA packs,
|
||||||
|
// because all the necessary info is in the `.packinfo` file.
|
||||||
|
const extraQueries =
|
||||||
|
(await cli.cliConstraints.preservesExtensiblePredicatesInMrvaPack())
|
||||||
|
? ["Telemetry/ExtractorInformation.ql"]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const qlPackRootPath = join(process.env.TEST_CODEQL_PATH, "java/ql/src");
|
||||||
|
const queryPath = join(qlPackRootPath, queryToRun);
|
||||||
|
const qlPackFilePath = join(qlPackRootPath, "qlpack.yml");
|
||||||
await doVariantAnalysisTest({
|
await doVariantAnalysisTest({
|
||||||
queryPath: join(
|
queryPath,
|
||||||
process.env.TEST_CODEQL_PATH,
|
qlPackRootPath,
|
||||||
"java/ql/src",
|
qlPackFilePath,
|
||||||
queryToRun,
|
|
||||||
),
|
|
||||||
expectedPackName: "codeql/java-queries",
|
expectedPackName: "codeql/java-queries",
|
||||||
filesThatExist: [queryToRun, extraQuery],
|
filesThatExist: [queryToRun, ...extraQueries],
|
||||||
filesThatDoNotExist: [],
|
filesThatDoNotExist: [],
|
||||||
qlxFilesThatExist: [],
|
qlxFilesThatExist: [],
|
||||||
dependenciesToCheck: ["codeql/java-all"],
|
dependenciesToCheck: ["codeql/java-all"],
|
||||||
@@ -295,6 +345,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
|
|
||||||
async function doVariantAnalysisTest({
|
async function doVariantAnalysisTest({
|
||||||
queryPath,
|
queryPath,
|
||||||
|
qlPackRootPath,
|
||||||
|
qlPackFilePath,
|
||||||
expectedPackName,
|
expectedPackName,
|
||||||
filesThatExist,
|
filesThatExist,
|
||||||
qlxFilesThatExist,
|
qlxFilesThatExist,
|
||||||
@@ -306,6 +358,8 @@ describe("Variant Analysis Manager", () => {
|
|||||||
checkVersion = true,
|
checkVersion = true,
|
||||||
}: {
|
}: {
|
||||||
queryPath: string;
|
queryPath: string;
|
||||||
|
qlPackRootPath: string;
|
||||||
|
qlPackFilePath: string | undefined;
|
||||||
expectedPackName: string;
|
expectedPackName: string;
|
||||||
filesThatExist: string[];
|
filesThatExist: string[];
|
||||||
qlxFilesThatExist: string[];
|
qlxFilesThatExist: string[];
|
||||||
@@ -313,9 +367,16 @@ describe("Variant Analysis Manager", () => {
|
|||||||
dependenciesToCheck?: string[];
|
dependenciesToCheck?: string[];
|
||||||
checkVersion?: boolean;
|
checkVersion?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const fileUri = getFile(queryPath);
|
const filePath = getFileOrDir(queryPath);
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: [filePath],
|
||||||
|
qlPackRootPath: getFileOrDir(qlPackRootPath),
|
||||||
|
qlPackFilePath: qlPackFilePath && getFileOrDir(qlPackFilePath),
|
||||||
|
language: QueryLanguage.Javascript,
|
||||||
|
};
|
||||||
|
|
||||||
await variantAnalysisManager.runVariantAnalysis(
|
await variantAnalysisManager.runVariantAnalysis(
|
||||||
[fileUri],
|
qlPackDetails,
|
||||||
progress,
|
progress,
|
||||||
cancellationTokenSource.token,
|
cancellationTokenSource.token,
|
||||||
);
|
);
|
||||||
@@ -324,7 +385,7 @@ describe("Variant Analysis Manager", () => {
|
|||||||
expect(executeCommandSpy).toHaveBeenCalledWith(
|
expect(executeCommandSpy).toHaveBeenCalledWith(
|
||||||
"codeQL.monitorNewVariantAnalysis",
|
"codeQL.monitorNewVariantAnalysis",
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
query: expect.objectContaining({ filePath: fileUri.fsPath }),
|
query: expect.objectContaining({ filePath }),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -390,16 +451,19 @@ describe("Variant Analysis Manager", () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFile(file: string): Uri {
|
function getFileOrDir(path: string): string {
|
||||||
if (isAbsolute(file)) {
|
// Use `Uri.file(path).fsPath` to make sure the path is in the correct format for the OS (i.e. forward/backward slashes).
|
||||||
return Uri.file(file);
|
if (isAbsolute(path)) {
|
||||||
|
return Uri.file(path).fsPath;
|
||||||
} else {
|
} else {
|
||||||
return Uri.file(join(baseDir, file));
|
return Uri.file(join(baseDir, path)).fsPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("runVariantAnalysisFromPublishedPack", () => {
|
describe("runVariantAnalysisFromPublishedPack", () => {
|
||||||
|
// Temporarily disabling this until we add a way to receive multiple queries in the
|
||||||
|
// runVariantAnalysis function.
|
||||||
it("should download pack for correct language and identify problem queries", async () => {
|
it("should download pack for correct language and identify problem queries", async () => {
|
||||||
const showQuickPickSpy = jest
|
const showQuickPickSpy = jest
|
||||||
.spyOn(window, "showQuickPick")
|
.spyOn(window, "showQuickPick")
|
||||||
@@ -419,15 +483,17 @@ describe("Variant Analysis Manager", () => {
|
|||||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(runVariantAnalysisMock).toHaveBeenCalledTimes(1);
|
expect(runVariantAnalysisMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const queries: Uri[] = runVariantAnalysisMock.mock.calls[0][0];
|
console.log(runVariantAnalysisMock.mock.calls[0][0]);
|
||||||
|
const queries: string[] =
|
||||||
|
runVariantAnalysisMock.mock.calls[0][0].queryFiles;
|
||||||
// Should include queries. Just check that at least one known query exists.
|
// Should include queries. Just check that at least one known query exists.
|
||||||
// It doesn't particularly matter which query we check for.
|
// It doesn't particularly matter which query we check for.
|
||||||
expect(
|
expect(
|
||||||
queries.find((q) => q.fsPath.includes("PostMessageStar.ql")),
|
queries.find((q) => q.includes("PostMessageStar.ql")),
|
||||||
).toBeDefined();
|
).toBeDefined();
|
||||||
// Should not include non-problem queries.
|
// Should not include non-problem queries.
|
||||||
expect(
|
expect(
|
||||||
queries.find((q) => q.fsPath.includes("LinesOfCode.ql")),
|
queries.find((q) => q.includes("LinesOfCode.ql")),
|
||||||
).not.toBeDefined();
|
).not.toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -86,6 +86,14 @@ describe("Variant Analysis Submission Integration", () => {
|
|||||||
it("shows the error message", async () => {
|
it("shows the error message", async () => {
|
||||||
await showQlDocument("query.ql");
|
await showQlDocument("query.ql");
|
||||||
|
|
||||||
|
// Select target language for your query
|
||||||
|
quickPickSpy.mockResolvedValueOnce(
|
||||||
|
mockedQuickPickItem({
|
||||||
|
label: "JavaScript",
|
||||||
|
language: "javascript",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
await commandManager.execute("codeQL.runVariantAnalysis");
|
await commandManager.execute("codeQL.runVariantAnalysis");
|
||||||
|
|
||||||
expect(showErrorMessageSpy).toHaveBeenCalledWith(
|
expect(showErrorMessageSpy).toHaveBeenCalledWith(
|
||||||
|
|||||||
@@ -296,9 +296,7 @@ describe("local databases", () => {
|
|||||||
Uri.parse("file:/sourceArchive-uri/"),
|
Uri.parse("file:/sourceArchive-uri/"),
|
||||||
);
|
);
|
||||||
(db as any).contents.sourceArchiveUri = undefined;
|
(db as any).contents.sourceArchiveUri = undefined;
|
||||||
expect(() => db.resolveSourceFile("abc")).toThrowError(
|
expect(() => db.resolveSourceFile("abc")).toThrow("Scheme is missing");
|
||||||
"Scheme is missing",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail to resolve when not a file uri", () => {
|
it("should fail to resolve when not a file uri", () => {
|
||||||
@@ -308,7 +306,7 @@ describe("local databases", () => {
|
|||||||
Uri.parse("file:/sourceArchive-uri/"),
|
Uri.parse("file:/sourceArchive-uri/"),
|
||||||
);
|
);
|
||||||
(db as any).contents.sourceArchiveUri = undefined;
|
(db as any).contents.sourceArchiveUri = undefined;
|
||||||
expect(() => db.resolveSourceFile("http://abc")).toThrowError(
|
expect(() => db.resolveSourceFile("http://abc")).toThrow(
|
||||||
"Invalid uri scheme",
|
"Invalid uri scheme",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ describe("listDatabases", () => {
|
|||||||
it("throws an error", async () => {
|
it("throws an error", async () => {
|
||||||
await expect(
|
await expect(
|
||||||
listDatabases(owner, repo, credentials, config),
|
listDatabases(owner, repo, credentials, config),
|
||||||
).rejects.toThrowError("Not found");
|
).rejects.toThrow("Not found");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ describe("listDatabases", () => {
|
|||||||
it("throws an error", async () => {
|
it("throws an error", async () => {
|
||||||
await expect(
|
await expect(
|
||||||
listDatabases(owner, repo, credentials, config),
|
listDatabases(owner, repo, credentials, config),
|
||||||
).rejects.toThrowError("Internal server error");
|
).rejects.toThrow("Internal server error");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -199,7 +199,7 @@ describe("listDatabases", () => {
|
|||||||
it("throws an error", async () => {
|
it("throws an error", async () => {
|
||||||
await expect(
|
await expect(
|
||||||
listDatabases(owner, repo, credentials, config),
|
listDatabases(owner, repo, credentials, config),
|
||||||
).rejects.toThrowError("Internal server error");
|
).rejects.toThrow("Internal server error");
|
||||||
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
|
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -270,7 +270,7 @@ describe("listDatabases", () => {
|
|||||||
it("throws an error", async () => {
|
it("throws an error", async () => {
|
||||||
await expect(
|
await expect(
|
||||||
listDatabases(owner, repo, credentials, config),
|
listDatabases(owner, repo, credentials, config),
|
||||||
).rejects.toThrowError("Not found");
|
).rejects.toThrow("Not found");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ describe("listDatabases", () => {
|
|||||||
it("throws an error", async () => {
|
it("throws an error", async () => {
|
||||||
await expect(
|
await expect(
|
||||||
listDatabases(owner, repo, credentials, config),
|
listDatabases(owner, repo, credentials, config),
|
||||||
).rejects.toThrowError("Internal server error");
|
).rejects.toThrow("Internal server error");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,14 +7,42 @@ import {
|
|||||||
createFileSync,
|
createFileSync,
|
||||||
pathExistsSync,
|
pathExistsSync,
|
||||||
} from "fs-extra";
|
} from "fs-extra";
|
||||||
import { Uri } from "vscode";
|
import { CancellationTokenSource, Uri, window } from "vscode";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
DatabaseImportQuickPickItems,
|
||||||
|
DatabaseQuickPickItem,
|
||||||
|
DatabaseSelectionQuickPickItem,
|
||||||
|
} from "../../../../src/databases/local-databases-ui";
|
||||||
|
|
||||||
import { DatabaseUI } from "../../../../src/databases/local-databases-ui";
|
import { DatabaseUI } from "../../../../src/databases/local-databases-ui";
|
||||||
import { testDisposeHandler } from "../../test-dispose-handler";
|
import { testDisposeHandler } from "../../test-dispose-handler";
|
||||||
import { createMockApp } from "../../../__mocks__/appMock";
|
import { createMockApp } from "../../../__mocks__/appMock";
|
||||||
import { QueryLanguage } from "../../../../src/common/query-language";
|
import { QueryLanguage } from "../../../../src/common/query-language";
|
||||||
|
import { mockedQuickPickItem, mockedObject } from "../../utils/mocking.helpers";
|
||||||
|
|
||||||
describe("local-databases-ui", () => {
|
describe("local-databases-ui", () => {
|
||||||
|
const storageDir = dirSync({ unsafeCleanup: true }).name;
|
||||||
|
const db1 = createDatabase(storageDir, "db1-imported", QueryLanguage.Cpp);
|
||||||
|
const db2 = createDatabase(storageDir, "db2-notimported", QueryLanguage.Cpp);
|
||||||
|
const db3 = createDatabase(storageDir, "db3-invalidlanguage", "hucairz");
|
||||||
|
|
||||||
|
// these two should be deleted
|
||||||
|
const db4 = createDatabase(
|
||||||
|
storageDir,
|
||||||
|
"db2-notimported-with-db-info",
|
||||||
|
QueryLanguage.Cpp,
|
||||||
|
".dbinfo",
|
||||||
|
);
|
||||||
|
const db5 = createDatabase(
|
||||||
|
storageDir,
|
||||||
|
"db2-notimported-with-codeql-database.yml",
|
||||||
|
QueryLanguage.Cpp,
|
||||||
|
"codeql-database.yml",
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = createMockApp({});
|
||||||
|
|
||||||
describe("fixDbUri", () => {
|
describe("fixDbUri", () => {
|
||||||
const fixDbUri = (DatabaseUI.prototype as any).fixDbUri;
|
const fixDbUri = (DatabaseUI.prototype as any).fixDbUri;
|
||||||
it("should choose current directory normally", async () => {
|
it("should choose current directory normally", async () => {
|
||||||
@@ -64,30 +92,6 @@ describe("local-databases-ui", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should delete orphaned databases", async () => {
|
it("should delete orphaned databases", async () => {
|
||||||
const storageDir = dirSync({ unsafeCleanup: true }).name;
|
|
||||||
const db1 = createDatabase(storageDir, "db1-imported", QueryLanguage.Cpp);
|
|
||||||
const db2 = createDatabase(
|
|
||||||
storageDir,
|
|
||||||
"db2-notimported",
|
|
||||||
QueryLanguage.Cpp,
|
|
||||||
);
|
|
||||||
const db3 = createDatabase(storageDir, "db3-invalidlanguage", "hucairz");
|
|
||||||
|
|
||||||
// these two should be deleted
|
|
||||||
const db4 = createDatabase(
|
|
||||||
storageDir,
|
|
||||||
"db2-notimported-with-db-info",
|
|
||||||
QueryLanguage.Cpp,
|
|
||||||
".dbinfo",
|
|
||||||
);
|
|
||||||
const db5 = createDatabase(
|
|
||||||
storageDir,
|
|
||||||
"db2-notimported-with-codeql-database.yml",
|
|
||||||
QueryLanguage.Cpp,
|
|
||||||
"codeql-database.yml",
|
|
||||||
);
|
|
||||||
|
|
||||||
const app = createMockApp({});
|
|
||||||
const databaseUI = new DatabaseUI(
|
const databaseUI = new DatabaseUI(
|
||||||
app,
|
app,
|
||||||
{
|
{
|
||||||
@@ -98,6 +102,7 @@ describe("local-databases-ui", () => {
|
|||||||
onDidChangeCurrentDatabaseItem: () => {
|
onDidChangeCurrentDatabaseItem: () => {
|
||||||
/**/
|
/**/
|
||||||
},
|
},
|
||||||
|
setCurrentDatabaseItem: () => {},
|
||||||
} as any,
|
} as any,
|
||||||
{
|
{
|
||||||
onLanguageContextChanged: () => {
|
onLanguageContextChanged: () => {
|
||||||
@@ -108,7 +113,6 @@ describe("local-databases-ui", () => {
|
|||||||
storageDir,
|
storageDir,
|
||||||
storageDir,
|
storageDir,
|
||||||
);
|
);
|
||||||
|
|
||||||
await databaseUI.handleRemoveOrphanedDatabases();
|
await databaseUI.handleRemoveOrphanedDatabases();
|
||||||
|
|
||||||
expect(pathExistsSync(db1)).toBe(true);
|
expect(pathExistsSync(db1)).toBe(true);
|
||||||
@@ -121,6 +125,130 @@ describe("local-databases-ui", () => {
|
|||||||
databaseUI.dispose(testDisposeHandler);
|
databaseUI.dispose(testDisposeHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getDatabaseItem", () => {
|
||||||
|
const progress = jest.fn();
|
||||||
|
const token = new CancellationTokenSource().token;
|
||||||
|
describe("when there is a current database", () => {
|
||||||
|
const databaseUI = new DatabaseUI(
|
||||||
|
app,
|
||||||
|
{
|
||||||
|
databaseItems: [{ databaseUri: Uri.file(db1) }],
|
||||||
|
onDidChangeDatabaseItem: () => {
|
||||||
|
/**/
|
||||||
|
},
|
||||||
|
onDidChangeCurrentDatabaseItem: () => {
|
||||||
|
/**/
|
||||||
|
},
|
||||||
|
setCurrentDatabaseItem: () => {},
|
||||||
|
currentDatabaseItem: { databaseUri: Uri.file(db1) },
|
||||||
|
} as any,
|
||||||
|
{
|
||||||
|
onLanguageContextChanged: () => {
|
||||||
|
/**/
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
{} as any,
|
||||||
|
storageDir,
|
||||||
|
storageDir,
|
||||||
|
);
|
||||||
|
|
||||||
|
it("should return current database", async () => {
|
||||||
|
const databaseItem = await databaseUI.getDatabaseItem(progress, token);
|
||||||
|
|
||||||
|
expect(databaseItem).toEqual({ databaseUri: Uri.file(db1) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when there is no current database", () => {
|
||||||
|
const databaseManager = {
|
||||||
|
databaseItems: [
|
||||||
|
{ databaseUri: Uri.file(db1) },
|
||||||
|
{ databaseUri: Uri.file(db2) },
|
||||||
|
],
|
||||||
|
onDidChangeDatabaseItem: () => {
|
||||||
|
/**/
|
||||||
|
},
|
||||||
|
onDidChangeCurrentDatabaseItem: () => {
|
||||||
|
/**/
|
||||||
|
},
|
||||||
|
setCurrentDatabaseItem: () => {},
|
||||||
|
currentDatabaseItem: undefined,
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const databaseUI = new DatabaseUI(
|
||||||
|
app,
|
||||||
|
databaseManager,
|
||||||
|
{
|
||||||
|
onLanguageContextChanged: () => {
|
||||||
|
/**/
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
{} as any,
|
||||||
|
storageDir,
|
||||||
|
storageDir,
|
||||||
|
);
|
||||||
|
|
||||||
|
it("should prompt for a database and select existing one", async () => {
|
||||||
|
const showQuickPickSpy = jest
|
||||||
|
.spyOn(window, "showQuickPick")
|
||||||
|
.mockResolvedValueOnce(
|
||||||
|
mockedQuickPickItem(
|
||||||
|
mockedObject<DatabaseSelectionQuickPickItem>({
|
||||||
|
databaseKind: "existing",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.mockResolvedValueOnce(
|
||||||
|
mockedQuickPickItem(
|
||||||
|
mockedObject<DatabaseQuickPickItem>({
|
||||||
|
databaseItem: { databaseUri: Uri.file(db2) },
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCurrentDatabaseItemSpy = jest.spyOn(
|
||||||
|
databaseManager,
|
||||||
|
"setCurrentDatabaseItem",
|
||||||
|
);
|
||||||
|
|
||||||
|
await databaseUI.getDatabaseItem(progress, token);
|
||||||
|
|
||||||
|
expect(showQuickPickSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(setCurrentDatabaseItemSpy).toHaveBeenCalledWith({
|
||||||
|
databaseUri: Uri.file(db2),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prompt for a database and import a new one", async () => {
|
||||||
|
const showQuickPickSpy = jest
|
||||||
|
.spyOn(window, "showQuickPick")
|
||||||
|
.mockResolvedValueOnce(
|
||||||
|
mockedQuickPickItem(
|
||||||
|
mockedObject<DatabaseSelectionQuickPickItem>({
|
||||||
|
databaseKind: "new",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.mockResolvedValueOnce(
|
||||||
|
mockedQuickPickItem(
|
||||||
|
mockedObject<DatabaseImportQuickPickItems>({
|
||||||
|
importType: "github",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChooseDatabaseGithubSpy = jest
|
||||||
|
.spyOn(databaseUI as any, "handleChooseDatabaseGithub")
|
||||||
|
.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
await databaseUI.getDatabaseItem(progress, token);
|
||||||
|
|
||||||
|
expect(showQuickPickSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(handleChooseDatabaseGithubSpy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function createDatabase(
|
function createDatabase(
|
||||||
storageDir: string,
|
storageDir: string,
|
||||||
dbName: string,
|
dbName: string,
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ describe("resolveQueries", () => {
|
|||||||
"tags contain": ["ide-contextual-queries/print-ast"],
|
"tags contain": ["ide-contextual-queries/print-ast"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).rejects.toThrowError(
|
).rejects.toThrow(
|
||||||
'No my query queries (kind "graph", tagged "ide-contextual-queries/print-ast") could be found in the current library path (tried searching the following packs: my-qlpack). Try upgrading the CodeQL libraries. If that doesn\'t work, then my query queries are not yet available for this language.',
|
'No my query queries (kind "graph", tagged "ide-contextual-queries/print-ast") could be found in the current library path (tried searching the following packs: my-qlpack). Try upgrading the CodeQL libraries. If that doesn\'t work, then my query queries are not yet available for this language.',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,5 +24,5 @@
|
|||||||
"noEmit": true
|
"noEmit": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
"include": ["src/**/*.ts"],
|
||||||
"exclude": ["node_modules", "test", "**/view"]
|
"exclude": ["node_modules", "*.config.ts", "test", "**/view"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user