Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4adf60a978 | ||
|
|
69775414f0 | ||
|
|
325f5d6667 | ||
|
|
e968c226ba | ||
|
|
44429c2513 | ||
|
|
96881f2bf2 | ||
|
|
06f4e33719 | ||
|
|
570e410e06 | ||
|
|
1d5b4f3fbb | ||
|
|
0e5b460e67 | ||
|
|
a39f4582e3 | ||
|
|
18f12e53c9 | ||
|
|
ac8dd9189e | ||
|
|
7d564b50e7 | ||
|
|
013bf65c0a | ||
|
|
bd6862efda | ||
|
|
64f0115070 | ||
|
|
596049a8cb | ||
|
|
58a5a43609 | ||
|
|
67ad9cb6ce | ||
|
|
29c36584f8 | ||
|
|
e59145372e | ||
|
|
ad17a4f828 | ||
|
|
f17319a5cf | ||
|
|
0d52be1dbf | ||
|
|
13a61094c1 | ||
|
|
230cdd024c | ||
|
|
29a47305a9 | ||
|
|
26ec371730 | ||
|
|
58de4864c0 | ||
|
|
17ff592a60 | ||
|
|
1c267e4c28 | ||
|
|
2f67ccea56 | ||
|
|
b3fdb3a126 | ||
|
|
07ba537590 | ||
|
|
8abde36dcb | ||
|
|
27d6a749f8 | ||
|
|
e7af631737 | ||
|
|
cdd889452a | ||
|
|
4abaa22f62 | ||
|
|
df322ce17d | ||
|
|
0df508d1c6 | ||
|
|
2e7586c39f | ||
|
|
f854ab50d5 | ||
|
|
3cc4005500 | ||
|
|
0d4a95d08e | ||
|
|
d67cf4b9f7 | ||
|
|
c37bd9969c | ||
|
|
1dac41d8ea | ||
|
|
48a03bd383 | ||
|
|
b6897e4faf | ||
|
|
d20a1eb61b | ||
|
|
8ef3ae0ff5 | ||
|
|
c7a048b0dc | ||
|
|
9026076635 | ||
|
|
135dc7084a | ||
|
|
8c784d8391 | ||
|
|
fed30b6fed | ||
|
|
232d4c3f41 | ||
|
|
acb6b8b490 | ||
|
|
3f4ed48787 | ||
|
|
e1d9aa813d | ||
|
|
8a3a6d7e7e | ||
|
|
bf9bfb13b7 |
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@@ -17,6 +17,9 @@ updates:
|
||||
octokit:
|
||||
patterns:
|
||||
- "@octokit/*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
storybook:
|
||||
patterns:
|
||||
- "@storybook/*"
|
||||
@@ -34,3 +37,10 @@ updates:
|
||||
day: "thursday" # Thursday is arbitrary
|
||||
labels:
|
||||
- "Update dependencies"
|
||||
- package-ecosystem: docker
|
||||
directory: "extensions/ql-vscode/test/e2e"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "thursday" # Thursday is arbitrary
|
||||
labels:
|
||||
- "Update dependencies"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# CodeQL for Visual Studio Code: Changelog
|
||||
|
||||
## 1.13.0 - 1 May 2024
|
||||
|
||||
- Add Ruby support to the CodeQL Model Editor. [#3584](https://github.com/github/vscode-codeql/pull/3584)
|
||||
- Remove support for CodeQL CLI versions older than 2.14.6. [#3562](https://github.com/github/vscode-codeql/pull/3562)
|
||||
|
||||
## 1.12.5 - 9 April 2024
|
||||
|
||||
- Add new supported source and sink kinds in the CodeQL Model Editor [#3511](https://github.com/github/vscode-codeql/pull/3511)
|
||||
@@ -492,7 +497,7 @@ No user facing changes.
|
||||
- Allow setting `codeQL.runningQueries.numberOfThreads` and `codeQL.runningTests.numberOfThreads` to 0, (which is interpreted as 'use one thread per core on the machine').
|
||||
- Clear the problems view of all CodeQL query results when a database is removed.
|
||||
- Add a `View DIL` command on query history items. This opens a text editor containing the Datalog Intermediary Language representation of the compiled query.
|
||||
- Remove feature flag for the AST Viewer. For more information on how to use the AST Viewer, [see the documentation](https://help.semmle.com/codeql/codeql-for-vscode/procedures/exploring-the-structure-of-your-source-code.html).
|
||||
- Remove feature flag for the AST Viewer. For more information on how to use the AST Viewer, [see the documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/exploring-the-structure-of-your-source-code/).
|
||||
- The `codeQL.runningTests.numberOfThreads` setting is now used correctly when running tests.
|
||||
- Alter structure of the _Test Explorer_ tree. It now follows the structure of the filesystem instead of the qlpacks.
|
||||
- Ensure output of CodeQL test runs includes compilation error messages and test failure messages.
|
||||
|
||||
2718
extensions/ql-vscode/package-lock.json
generated
2718
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"description": "CodeQL for Visual Studio Code",
|
||||
"author": "GitHub",
|
||||
"private": true,
|
||||
"version": "1.12.5",
|
||||
"version": "1.13.0",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -470,6 +470,11 @@
|
||||
"type": "string",
|
||||
"default": ".github/codeql/extensions/${name}-${language}",
|
||||
"markdownDescription": "Location for newly created CodeQL model packs. The location can be either absolute or relative. If relative, it is relative to the workspace root.\n\nThe following variables are supported:\n* **${database}** - the name of the database\n* **${language}** - the name of the language\n* **${name}** - the name of the GitHub repository, or the name of the database if the database was not downloaded from GitHub\n* **${owner}** - the owner of the GitHub repository, or an empty string if the database was not downloaded from GitHub"
|
||||
},
|
||||
"codeQL.model.packName": {
|
||||
"type": "string",
|
||||
"default": "${owner}/${name}-${language}",
|
||||
"markdownDescription": "Name of newly created CodeQL model packs. If the result is not a valid pack name, it will be converted to a valid pack name.\n\nThe following variables are supported:\n* **${database}** - the name of the database\n* **${language}** - the name of the language\n* **${name}** - the name of the GitHub repository, or the name of the database if the database was not downloaded from GitHub\n* **${owner}** - the owner of the GitHub repository, or an empty string if the database was not downloaded from GitHub"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1963,8 +1968,9 @@
|
||||
"prepare": "cd ../.. && husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.10",
|
||||
"@floating-ui/react": "^0.26.12",
|
||||
"@octokit/plugin-retry": "^6.0.1",
|
||||
"@octokit/plugin-throttling": "^8.0.0",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@vscode/codicons": "^0.0.35",
|
||||
"@vscode/debugadapter": "^1.59.0",
|
||||
@@ -1977,8 +1983,8 @@
|
||||
"d3-graphviz": "^5.0.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"msw": "^2.0.13",
|
||||
"nanoid": "^5.0.6",
|
||||
"msw": "^2.2.13",
|
||||
"nanoid": "^5.0.7",
|
||||
"node-fetch": "^2.6.7",
|
||||
"p-queue": "^8.0.1",
|
||||
"react": "^18.2.0",
|
||||
@@ -1998,31 +2004,30 @@
|
||||
"zip-a-folder": "^3.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.0",
|
||||
"@babel/core": "^7.24.4",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.18.6",
|
||||
"@babel/preset-env": "^7.24.0",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-env": "^7.24.4",
|
||||
"@babel/preset-react": "^7.24.1",
|
||||
"@babel/preset-typescript": "^7.21.4",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@github/markdownlint-github": "^0.6.2",
|
||||
"@octokit/plugin-throttling": "^8.0.0",
|
||||
"@playwright/test": "^1.40.1",
|
||||
"@storybook/addon-a11y": "^8.0.5",
|
||||
"@storybook/addon-actions": "^8.0.5",
|
||||
"@storybook/addon-essentials": "^8.0.5",
|
||||
"@storybook/addon-interactions": "^8.0.5",
|
||||
"@storybook/addon-links": "^8.0.5",
|
||||
"@storybook/addon-a11y": "^8.0.9",
|
||||
"@storybook/addon-actions": "^8.0.9",
|
||||
"@storybook/addon-essentials": "^8.0.9",
|
||||
"@storybook/addon-interactions": "^8.0.9",
|
||||
"@storybook/addon-links": "^8.0.9",
|
||||
"@storybook/blocks": "^8.0.2",
|
||||
"@storybook/components": "^8.0.2",
|
||||
"@storybook/csf": "^0.1.3",
|
||||
"@storybook/csf": "^0.1.6",
|
||||
"@storybook/icons": "^1.2.9",
|
||||
"@storybook/manager-api": "^8.0.5",
|
||||
"@storybook/react": "^8.0.5",
|
||||
"@storybook/react-vite": "^8.0.5",
|
||||
"@storybook/theming": "^8.0.5",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@storybook/manager-api": "^8.0.9",
|
||||
"@storybook/react": "^8.0.9",
|
||||
"@storybook/react-vite": "^8.0.9",
|
||||
"@storybook/theming": "^8.0.9",
|
||||
"@testing-library/dom": "^10.1.0",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^14.2.2",
|
||||
"@testing-library/react": "^15.0.5",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/child-process-promise": "^2.2.1",
|
||||
"@types/d3": "^7.4.0",
|
||||
@@ -2052,7 +2057,7 @@
|
||||
"@vscode/test-electron": "^2.3.9",
|
||||
"@vscode/vsce": "^2.24.0",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"applicationinsights": "^2.9.4",
|
||||
"applicationinsights": "^2.9.5",
|
||||
"cosmiconfig": "^9.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"del": "^6.0.0",
|
||||
@@ -2065,7 +2070,7 @@
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jest-dom": "^5.2.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.31.8",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"glob": "^10.0.0",
|
||||
@@ -2073,21 +2078,21 @@
|
||||
"gulp-esbuild": "^0.12.0",
|
||||
"gulp-replace": "^1.1.3",
|
||||
"gulp-typescript": "^5.0.1",
|
||||
"husky": "^8.0.0",
|
||||
"husky": "^9.0.11",
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
"jest-runner-vscode": "^3.0.1",
|
||||
"lint-staged": "^15.2.2",
|
||||
"markdownlint-cli2": "^0.12.1",
|
||||
"markdownlint-cli2": "^0.13.0",
|
||||
"markdownlint-cli2-formatter-pretty": "^0.0.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"patch-package": "^8.0.0",
|
||||
"prettier": "^3.2.5",
|
||||
"storybook": "^8.0.5",
|
||||
"storybook": "^8.0.9",
|
||||
"tar-stream": "^3.1.7",
|
||||
"through2": "^4.0.2",
|
||||
"ts-jest": "^29.0.1",
|
||||
"ts-json-schema-generator": "^1.1.2",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-json-schema-generator": "^2.0.1",
|
||||
"ts-node": "^10.7.0",
|
||||
"ts-unused-exports": "^10.0.0",
|
||||
"typescript": "^5.0.2",
|
||||
|
||||
@@ -1912,22 +1912,7 @@ function shouldDebugCliServer() {
|
||||
export class CliVersionConstraint {
|
||||
// The oldest version of the CLI that we support. This is used to determine
|
||||
// whether to show a warning about the CLI being too old on startup.
|
||||
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.13.5");
|
||||
|
||||
/**
|
||||
* CLI version where the `generate extensible-predicate-metadata`
|
||||
* command was implemented.
|
||||
*/
|
||||
public static CLI_VERSION_WITH_EXTENSIBLE_PREDICATE_METADATA = new SemVer(
|
||||
"2.14.3",
|
||||
);
|
||||
|
||||
/**
|
||||
* CLI version where the langauge server supports visisbility change notifications.
|
||||
*/
|
||||
public static CLI_VERSION_WITH_VISIBILITY_NOTIFICATIONS = new SemVer(
|
||||
"2.14.0",
|
||||
);
|
||||
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.14.6");
|
||||
|
||||
/**
|
||||
* CLI version where the query server supports the `evaluation/trimCache` method
|
||||
@@ -1952,18 +1937,6 @@ export class CliVersionConstraint {
|
||||
return (await this.cli.getVersion()).compare(v) >= 0;
|
||||
}
|
||||
|
||||
async supportsVisibilityNotifications() {
|
||||
return this.isVersionAtLeast(
|
||||
CliVersionConstraint.CLI_VERSION_WITH_VISIBILITY_NOTIFICATIONS,
|
||||
);
|
||||
}
|
||||
|
||||
async supportsGenerateExtensiblePredicateMetadata() {
|
||||
return this.isVersionAtLeast(
|
||||
CliVersionConstraint.CLI_VERSION_WITH_EXTENSIBLE_PREDICATE_METADATA,
|
||||
);
|
||||
}
|
||||
|
||||
async preservesExtensiblePredicatesInMrvaPack() {
|
||||
// Negated, because we _stopped_ preserving these in 2.16.1.
|
||||
return !(await this.isVersionAtLeast(
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
ConfigurationScope,
|
||||
Event,
|
||||
} from "vscode";
|
||||
import { ConfigurationTarget, EventEmitter, workspace } from "vscode";
|
||||
import { ConfigurationTarget, EventEmitter, workspace, Uri } from "vscode";
|
||||
import type { DistributionManager } from "./codeql-cli/distribution";
|
||||
import { extLogger } from "./common/logging/vscode";
|
||||
import { ONE_DAY_IN_MS } from "./common/time";
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
SortKey,
|
||||
} from "./variant-analysis/shared/variant-analysis-filter-sort";
|
||||
import { substituteConfigVariables } from "./common/config-template";
|
||||
import { getErrorMessage } from "./common/helpers-pure";
|
||||
|
||||
export const ALL_SETTINGS: Setting[] = [];
|
||||
|
||||
@@ -69,6 +70,52 @@ export const VSCODE_SAVE_BEFORE_START_SETTING = new Setting(
|
||||
VSCODE_DEBUG_SETTING,
|
||||
);
|
||||
|
||||
const VSCODE_GITHUB_ENTERPRISE_SETTING = new Setting(
|
||||
"github-enterprise",
|
||||
undefined,
|
||||
);
|
||||
export const VSCODE_GITHUB_ENTERPRISE_URI_SETTING = new Setting(
|
||||
"uri",
|
||||
VSCODE_GITHUB_ENTERPRISE_SETTING,
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the value of the `github-enterprise.uri` setting, parsed as a URI.
|
||||
* If the value is not set or cannot be parsed, return `undefined`.
|
||||
*/
|
||||
export function getEnterpriseUri(): Uri | undefined {
|
||||
const config = VSCODE_GITHUB_ENTERPRISE_URI_SETTING.getValue<string>();
|
||||
if (config) {
|
||||
try {
|
||||
let uri = Uri.parse(config, true);
|
||||
if (uri.scheme === "http") {
|
||||
uri = uri.with({ scheme: "https" });
|
||||
}
|
||||
return uri;
|
||||
} catch (e) {
|
||||
void extLogger.log(
|
||||
`Failed to parse the GitHub Enterprise URI: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the GitHub Enterprise URI set?
|
||||
*/
|
||||
export function hasEnterpriseUri(): boolean {
|
||||
return getEnterpriseUri() !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the GitHub Enterprise URI set to something that looks like GHEC-DR?
|
||||
*/
|
||||
export function hasGhecDrUri(): boolean {
|
||||
const uri = getEnterpriseUri();
|
||||
return uri !== undefined && uri.authority.toLowerCase().endsWith(".ghe.com");
|
||||
}
|
||||
|
||||
const ROOT_SETTING = new Setting("codeQL");
|
||||
|
||||
// Telemetry configuration
|
||||
@@ -522,6 +569,7 @@ export async function setRemoteControllerRepo(repo: string | undefined) {
|
||||
|
||||
export interface VariantAnalysisConfig {
|
||||
controllerRepo: string | undefined;
|
||||
showSystemDefinedRepositoryLists: boolean;
|
||||
onDidChangeConfiguration?: Event<void>;
|
||||
}
|
||||
|
||||
@@ -531,7 +579,7 @@ export class VariantAnalysisConfigListener
|
||||
{
|
||||
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
|
||||
this.handleDidChangeConfigurationForRelevantSettings(
|
||||
[VARIANT_ANALYSIS_SETTING],
|
||||
[VARIANT_ANALYSIS_SETTING, VSCODE_GITHUB_ENTERPRISE_URI_SETTING],
|
||||
e,
|
||||
);
|
||||
}
|
||||
@@ -539,6 +587,10 @@ export class VariantAnalysisConfigListener
|
||||
public get controllerRepo(): string | undefined {
|
||||
return getRemoteControllerRepo();
|
||||
}
|
||||
|
||||
public get showSystemDefinedRepositoryLists(): boolean {
|
||||
return !hasEnterpriseUri();
|
||||
}
|
||||
}
|
||||
|
||||
const VARIANT_ANALYSIS_FILTER_RESULTS = new Setting(
|
||||
@@ -576,6 +628,11 @@ export function getVariantAnalysisDefaultResultsSort(): SortKey {
|
||||
*/
|
||||
const ACTION_BRANCH = new Setting("actionBranch", VARIANT_ANALYSIS_SETTING);
|
||||
|
||||
export const VARIANT_ANALYSIS_ENABLE_GHEC_DR = new Setting(
|
||||
"enableGhecDr",
|
||||
VARIANT_ANALYSIS_SETTING,
|
||||
);
|
||||
|
||||
export function getActionBranch(): string {
|
||||
return ACTION_BRANCH.getValue<string>() || "main";
|
||||
}
|
||||
@@ -725,7 +782,6 @@ export async function setAutogenerateQlPacks(choice: AutogenerateQLPacks) {
|
||||
const MODEL_SETTING = new Setting("model", ROOT_SETTING);
|
||||
const FLOW_GENERATION = new Setting("flowGeneration", MODEL_SETTING);
|
||||
const LLM_GENERATION = new Setting("llmGeneration", MODEL_SETTING);
|
||||
const SHOW_TYPE_MODELS = new Setting("showTypeModels", MODEL_SETTING);
|
||||
const LLM_GENERATION_BATCH_SIZE = new Setting(
|
||||
"llmGenerationBatchSize",
|
||||
MODEL_SETTING,
|
||||
@@ -736,11 +792,8 @@ const LLM_GENERATION_DEV_ENDPOINT = new Setting(
|
||||
);
|
||||
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
||||
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
|
||||
const MODEL_PACK_NAME = new Setting("packName", MODEL_SETTING);
|
||||
const ENABLE_PYTHON = new Setting("enablePython", MODEL_SETTING);
|
||||
const ENABLE_ACCESS_PATH_SUGGESTIONS = new Setting(
|
||||
"enableAccessPathSuggestions",
|
||||
MODEL_SETTING,
|
||||
);
|
||||
|
||||
export type ModelConfigPackVariables = {
|
||||
database: string;
|
||||
@@ -752,18 +805,20 @@ export type ModelConfigPackVariables = {
|
||||
export interface ModelConfig {
|
||||
flowGeneration: boolean;
|
||||
llmGeneration: boolean;
|
||||
showTypeModels: boolean;
|
||||
getPackLocation(
|
||||
languageId: string,
|
||||
variables: ModelConfigPackVariables,
|
||||
): string;
|
||||
getPackName(languageId: string, variables: ModelConfigPackVariables): string;
|
||||
enablePython: boolean;
|
||||
enableAccessPathSuggestions: boolean;
|
||||
}
|
||||
|
||||
export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
|
||||
this.handleDidChangeConfigurationForRelevantSettings([MODEL_SETTING], e);
|
||||
this.handleDidChangeConfigurationForRelevantSettings(
|
||||
[MODEL_SETTING, VSCODE_GITHUB_ENTERPRISE_URI_SETTING],
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
public get flowGeneration(): boolean {
|
||||
@@ -771,11 +826,7 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||
}
|
||||
|
||||
public get llmGeneration(): boolean {
|
||||
return !!LLM_GENERATION.getValue<boolean>();
|
||||
}
|
||||
|
||||
public get showTypeModels(): boolean {
|
||||
return !!SHOW_TYPE_MODELS.getValue<boolean>();
|
||||
return !!LLM_GENERATION.getValue<boolean>() && !hasEnterpriseUri();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -810,12 +861,20 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||
);
|
||||
}
|
||||
|
||||
public get enablePython(): boolean {
|
||||
return !!ENABLE_PYTHON.getValue<boolean>();
|
||||
public getPackName(
|
||||
languageId: string,
|
||||
variables: ModelConfigPackVariables,
|
||||
): string {
|
||||
return substituteConfigVariables(
|
||||
MODEL_PACK_NAME.getValue<string>({
|
||||
languageId,
|
||||
}),
|
||||
variables,
|
||||
);
|
||||
}
|
||||
|
||||
public get enableAccessPathSuggestions(): boolean {
|
||||
return !!ENABLE_ACCESS_PATH_SUGGESTIONS.getValue<boolean>();
|
||||
public get enablePython(): boolean {
|
||||
return !!ENABLE_PYTHON.getValue<boolean>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from "./db-item-selection";
|
||||
import { createRemoteTree } from "./db-tree-creator";
|
||||
import type { DbConfigValidationError } from "./db-validation-errors";
|
||||
import type { VariantAnalysisConfig } from "../config";
|
||||
|
||||
export class DbManager extends DisposableObject {
|
||||
public readonly onDbItemsChanged: AppEvent<void>;
|
||||
@@ -25,6 +26,7 @@ export class DbManager extends DisposableObject {
|
||||
constructor(
|
||||
private readonly app: App,
|
||||
private readonly dbConfigStore: DbConfigStore,
|
||||
private readonly variantAnalysisConfigListener: VariantAnalysisConfig,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -36,6 +38,10 @@ export class DbManager extends DisposableObject {
|
||||
this.dbConfigStore.onDidChangeConfig(() => {
|
||||
this.onDbItemsChangesEventEmitter.fire();
|
||||
});
|
||||
|
||||
this.variantAnalysisConfigListener.onDidChangeConfiguration?.(() => {
|
||||
this.onDbItemsChangesEventEmitter.fire();
|
||||
});
|
||||
}
|
||||
|
||||
public getSelectedDbItem(): DbItem | undefined {
|
||||
@@ -56,7 +62,11 @@ export class DbManager extends DisposableObject {
|
||||
|
||||
const expandedItems = this.getExpandedItems();
|
||||
|
||||
const remoteTree = createRemoteTree(configResult.value, expandedItems);
|
||||
const remoteTree = createRemoteTree(
|
||||
configResult.value,
|
||||
this.variantAnalysisConfigListener,
|
||||
expandedItems,
|
||||
);
|
||||
return ValueResult.ok(remoteTree.children);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { DbManager } from "./db-manager";
|
||||
import { DbPanel } from "./ui/db-panel";
|
||||
import { DbSelectionDecorationProvider } from "./ui/db-selection-decoration-provider";
|
||||
import type { DatabasePanelCommands } from "../common/commands";
|
||||
import { VariantAnalysisConfigListener } from "../config";
|
||||
|
||||
export class DbModule extends DisposableObject {
|
||||
public readonly dbManager: DbManager;
|
||||
@@ -17,7 +18,13 @@ export class DbModule extends DisposableObject {
|
||||
super();
|
||||
|
||||
this.dbConfigStore = new DbConfigStore(app);
|
||||
this.dbManager = this.push(new DbManager(app, this.dbConfigStore));
|
||||
this.dbManager = this.push(
|
||||
new DbManager(
|
||||
app,
|
||||
this.dbConfigStore,
|
||||
new VariantAnalysisConfigListener(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public static async initialize(app: App): Promise<DbModule> {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { VariantAnalysisConfig } from "../config";
|
||||
import type { DbConfig, RemoteRepositoryList } from "./config/db-config";
|
||||
import { SelectedDbItemKind } from "./config/db-config";
|
||||
import type {
|
||||
@@ -13,13 +14,17 @@ import { ExpandedDbItemKind } from "./db-item-expansion";
|
||||
|
||||
export function createRemoteTree(
|
||||
dbConfig: DbConfig,
|
||||
variantAnalysisConfig: VariantAnalysisConfig,
|
||||
expandedItems: ExpandedDbItem[],
|
||||
): RootRemoteDbItem {
|
||||
const systemDefinedLists = [
|
||||
createSystemDefinedList(10, dbConfig),
|
||||
createSystemDefinedList(100, dbConfig),
|
||||
createSystemDefinedList(1000, dbConfig),
|
||||
];
|
||||
const systemDefinedLists =
|
||||
variantAnalysisConfig.showSystemDefinedRepositoryLists
|
||||
? [
|
||||
createSystemDefinedList(10, dbConfig),
|
||||
createSystemDefinedList(100, dbConfig),
|
||||
createSystemDefinedList(1000, dbConfig),
|
||||
]
|
||||
: [];
|
||||
|
||||
const userDefinedRepoLists =
|
||||
dbConfig.databases.variantAnalysis.repositoryLists.map((r) =>
|
||||
|
||||
@@ -1078,14 +1078,12 @@ async function activateWithInstalledDistribution(
|
||||
});
|
||||
|
||||
// Handle visibility changes in the CodeQL language client.
|
||||
if (await cliServer.cliConstraints.supportsVisibilityNotifications()) {
|
||||
Window.onDidChangeVisibleTextEditors((editors) => {
|
||||
languageClient.notifyVisibilityChange(editors);
|
||||
});
|
||||
// Send an inital notification to the language server
|
||||
// to set the initial state of the visible editors.
|
||||
languageClient.notifyVisibilityChange(Window.visibleTextEditors);
|
||||
}
|
||||
Window.onDidChangeVisibleTextEditors((editors) => {
|
||||
languageClient.notifyVisibilityChange(editors);
|
||||
});
|
||||
// Send an inital notification to the language server
|
||||
// to set the initial state of the visible editors.
|
||||
languageClient.notifyVisibilityChange(Window.visibleTextEditors);
|
||||
|
||||
// Jump-to-definition and find-references
|
||||
void extLogger.log("Registering jump-to-definition handlers.");
|
||||
|
||||
@@ -15,11 +15,19 @@ export function formatPackName(packName: ExtensionPackName): string {
|
||||
return `${packName.scope}/${packName.name}`;
|
||||
}
|
||||
|
||||
export function autoNameExtensionPack(
|
||||
name: string,
|
||||
language: string,
|
||||
): ExtensionPackName | undefined {
|
||||
let packName = `${name}-${language}`;
|
||||
export function sanitizePackName(userPackName: string): ExtensionPackName {
|
||||
let packName = userPackName;
|
||||
|
||||
packName = packName.trim();
|
||||
|
||||
while (packName.startsWith("/")) {
|
||||
packName = packName.slice(1);
|
||||
}
|
||||
|
||||
while (packName.endsWith("/")) {
|
||||
packName = packName.slice(0, -1);
|
||||
}
|
||||
|
||||
if (!packName.includes("/")) {
|
||||
packName = `pack/${packName}`;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ import type { NotificationLogger } from "../common/logging";
|
||||
import { showAndLogErrorMessage } from "../common/logging";
|
||||
import type { ModelConfig, ModelConfigPackVariables } from "../config";
|
||||
import type { ExtensionPackName } from "./extension-pack-name";
|
||||
import { autoNameExtensionPack, formatPackName } from "./extension-pack-name";
|
||||
import {
|
||||
validatePackName,
|
||||
sanitizePackName,
|
||||
formatPackName,
|
||||
} from "./extension-pack-name";
|
||||
import {
|
||||
ensurePackLocationIsInWorkspaceFolder,
|
||||
packLocationToAbsolute,
|
||||
@@ -80,15 +84,20 @@ export async function pickExtensionPack(
|
||||
|
||||
await ensurePackLocationIsInWorkspaceFolder(packPath, modelConfig, logger);
|
||||
|
||||
// Generate the name of the extension pack
|
||||
const packName = autoNameExtensionPack(
|
||||
databaseItem.name,
|
||||
const userPackName = modelConfig.getPackName(
|
||||
databaseItem.language,
|
||||
getModelConfigPackVariables(databaseItem),
|
||||
);
|
||||
if (!packName) {
|
||||
|
||||
// Generate the name of the extension pack
|
||||
const packName = sanitizePackName(userPackName);
|
||||
|
||||
// Validate that the name isn't too long etc.
|
||||
const packNameError = validatePackName(formatPackName(packName));
|
||||
if (packNameError) {
|
||||
void showAndLogErrorMessage(
|
||||
logger,
|
||||
`Could not automatically name extension pack for database ${databaseItem.name}`,
|
||||
`Invalid model pack name '${formatPackName(packName)}' for database ${databaseItem.name}: ${packNameError}`,
|
||||
);
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -20,7 +20,7 @@ import type { AccessPathSuggestionRow } from "../suggestions";
|
||||
// This is a subset of the model config that doesn't import the vscode module.
|
||||
// It only includes settings that are actually used.
|
||||
export type ModelConfig = {
|
||||
showTypeModels: boolean;
|
||||
flowGeneration: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -32,12 +32,12 @@ export type ModelConfig = {
|
||||
*/
|
||||
export function createModelConfig(modelConfig: ModelConfig): ModelConfig {
|
||||
return {
|
||||
showTypeModels: modelConfig.showTypeModels,
|
||||
flowGeneration: modelConfig.flowGeneration,
|
||||
};
|
||||
}
|
||||
|
||||
export const defaultModelConfig: ModelConfig = {
|
||||
showTypeModels: false,
|
||||
flowGeneration: false,
|
||||
};
|
||||
|
||||
type GenerateMethodDefinition<T> = (method: T) => DataTuple[];
|
||||
@@ -105,15 +105,34 @@ type ParseResultsToYaml = (
|
||||
logger: BaseLogger,
|
||||
) => ModelExtension[];
|
||||
|
||||
export enum AutoModelGenerationType {
|
||||
/**
|
||||
* Auto model generation is disabled and will not be run.
|
||||
*/
|
||||
Disabled = "disabled",
|
||||
/**
|
||||
* The models are generated to a separate file (suffixed with .model.generated.yml).
|
||||
*/
|
||||
SeparateFile = "separateFile",
|
||||
/**
|
||||
* The models are added as a model in the model editor, but are not automatically saved.
|
||||
* The user can view them and choose to save them.
|
||||
*/
|
||||
Models = "models",
|
||||
}
|
||||
|
||||
type ModelsAsDataLanguageAutoModelGeneration = {
|
||||
queryConstraints: (mode: Mode) => QueryConstraints;
|
||||
filterQueries?: (queryPath: string) => boolean;
|
||||
/**
|
||||
* This function is only used when type is `separateFile`.
|
||||
*/
|
||||
parseResultsToYaml: ParseResultsToYaml;
|
||||
/**
|
||||
* By default, auto model generation is enabled for all modes. This function can be used to
|
||||
* override that behavior.
|
||||
* This function is only used when type is `models`.
|
||||
*/
|
||||
enabled?: (context: GenerationContext) => boolean;
|
||||
parseResults: ParseGenerationResults;
|
||||
type: (context: GenerationContext) => AutoModelGenerationType;
|
||||
};
|
||||
|
||||
type ModelsAsDataLanguageAccessPathSuggestions = {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import type { BaseLogger } from "../../../common/logging";
|
||||
import type { DecodedBqrs } from "../../../common/bqrs-cli-types";
|
||||
import type {
|
||||
GenerationContext,
|
||||
ModelsAsDataLanguage,
|
||||
} from "../models-as-data";
|
||||
import type { ModelsAsDataLanguage } from "../models-as-data";
|
||||
import type { ModeledMethod } from "../../modeled-method";
|
||||
import type { DataTuple } from "../../model-extension-file";
|
||||
|
||||
@@ -12,21 +9,10 @@ export function parseGenerateModelResults(
|
||||
bqrs: DecodedBqrs,
|
||||
modelsAsDataLanguage: ModelsAsDataLanguage,
|
||||
logger: BaseLogger,
|
||||
{ config }: GenerationContext,
|
||||
): ModeledMethod[] {
|
||||
const modeledMethods: ModeledMethod[] = [];
|
||||
|
||||
for (const resultSetName in bqrs) {
|
||||
if (
|
||||
resultSetName ===
|
||||
modelsAsDataLanguage.predicates.type?.extensiblePredicate &&
|
||||
!config.showTypeModels
|
||||
) {
|
||||
// Don't load generated type results when type models are hidden. These are already
|
||||
// automatically generated on start-up.
|
||||
continue;
|
||||
}
|
||||
|
||||
const definition = Object.values(modelsAsDataLanguage.predicates).find(
|
||||
(definition) => definition.extensiblePredicate === resultSetName,
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ModelsAsDataLanguage } from "../models-as-data";
|
||||
import { AutoModelGenerationType } from "../models-as-data";
|
||||
import { sharedExtensiblePredicates, sharedKinds } from "../shared";
|
||||
import { Mode } from "../../shared/mode";
|
||||
import { parseGenerateModelResults } from "./generate";
|
||||
@@ -169,7 +170,6 @@ export const ruby: ModelsAsDataLanguage = {
|
||||
methodParameters: "",
|
||||
};
|
||||
},
|
||||
isHidden: ({ config }) => !config.showTypeModels,
|
||||
},
|
||||
},
|
||||
modelGeneration: {
|
||||
@@ -209,9 +209,32 @@ export const ruby: ModelsAsDataLanguage = {
|
||||
},
|
||||
];
|
||||
},
|
||||
parseResults: (queryPath, bqrs, modelsAsDataLanguage, logger) => {
|
||||
// Only parse type models when automatically generating models
|
||||
const typePredicate = modelsAsDataLanguage.predicates.type;
|
||||
if (!typePredicate) {
|
||||
throw new Error("Type predicate not found");
|
||||
}
|
||||
|
||||
const typeTuples = bqrs[typePredicate.extensiblePredicate];
|
||||
if (!typeTuples) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return parseGenerateModelResults(
|
||||
queryPath,
|
||||
{
|
||||
[typePredicate.extensiblePredicate]: typeTuples,
|
||||
},
|
||||
modelsAsDataLanguage,
|
||||
logger,
|
||||
);
|
||||
},
|
||||
// Only enabled for framework mode when type models are hidden
|
||||
enabled: ({ mode, config }) =>
|
||||
mode === Mode.Framework && !config.showTypeModels,
|
||||
type: ({ mode }) =>
|
||||
mode === Mode.Framework
|
||||
? AutoModelGenerationType.Models
|
||||
: AutoModelGenerationType.Disabled,
|
||||
},
|
||||
accessPathSuggestions: {
|
||||
queryConstraints: (mode) => ({
|
||||
|
||||
@@ -54,7 +54,11 @@ import { telemetryListener } from "../common/vscode/telemetry";
|
||||
import type { ModelingStore } from "./modeling-store";
|
||||
import type { ModelingEvents } from "./modeling-events";
|
||||
import type { ModelsAsDataLanguage } from "./languages";
|
||||
import { createModelConfig, getModelsAsDataLanguage } from "./languages";
|
||||
import {
|
||||
AutoModelGenerationType,
|
||||
createModelConfig,
|
||||
getModelsAsDataLanguage,
|
||||
} from "./languages";
|
||||
import { runGenerateQueries } from "./generate";
|
||||
import { ResponseError } from "vscode-jsonrpc";
|
||||
import { LSPErrorCodes } from "vscode-languageclient";
|
||||
@@ -274,7 +278,6 @@ export class ModelEditorView extends AbstractWebview<
|
||||
modeledMethods,
|
||||
mode,
|
||||
this.cliServer,
|
||||
this.modelConfig,
|
||||
this.app.logger,
|
||||
);
|
||||
|
||||
@@ -348,17 +351,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
withProgress((progress) => this.loadMethods(progress), {
|
||||
cancellable: false,
|
||||
}),
|
||||
// Only load access path suggestions if the feature is enabled
|
||||
this.modelConfig.enableAccessPathSuggestions
|
||||
? withProgress(
|
||||
(progress) => this.loadAccessPathSuggestions(progress),
|
||||
{
|
||||
cancellable: false,
|
||||
location: ProgressLocation.Window,
|
||||
title: "Loading access path suggestions",
|
||||
},
|
||||
)
|
||||
: undefined,
|
||||
this.loadAccessPathSuggestions(),
|
||||
]);
|
||||
void telemetryListener?.sendUIInteraction("model-editor-switch-modes");
|
||||
|
||||
@@ -416,14 +409,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
await this.generateModeledMethodsOnStartup();
|
||||
}),
|
||||
this.loadExistingModeledMethods(),
|
||||
// Only load access path suggestions if the feature is enabled
|
||||
this.modelConfig.enableAccessPathSuggestions
|
||||
? withProgress((progress) => this.loadAccessPathSuggestions(progress), {
|
||||
cancellable: false,
|
||||
location: ProgressLocation.Window,
|
||||
title: "Loading access path suggestions",
|
||||
})
|
||||
: undefined,
|
||||
this.loadAccessPathSuggestions(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -500,7 +486,6 @@ export class ModelEditorView extends AbstractWebview<
|
||||
this.extensionPack,
|
||||
this.language,
|
||||
this.cliServer,
|
||||
this.modelConfig,
|
||||
this.app.logger,
|
||||
);
|
||||
this.modelingStore.setModeledMethods(this.databaseItem, modeledMethods);
|
||||
@@ -568,9 +553,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadAccessPathSuggestions(
|
||||
progress: ProgressCallback,
|
||||
): Promise<void> {
|
||||
protected async loadAccessPathSuggestions(): Promise<void> {
|
||||
const mode = this.modelingStore.getMode(this.databaseItem);
|
||||
|
||||
const modelsAsDataLanguage = getModelsAsDataLanguage(this.language);
|
||||
@@ -579,46 +562,55 @@ export class ModelEditorView extends AbstractWebview<
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const suggestions = await runSuggestionsQuery(mode, {
|
||||
parseResults: (results) =>
|
||||
accessPathSuggestions.parseResults(
|
||||
results,
|
||||
modelsAsDataLanguage,
|
||||
await withProgress(
|
||||
async (progress) => {
|
||||
try {
|
||||
const suggestions = await runSuggestionsQuery(mode, {
|
||||
parseResults: (results) =>
|
||||
accessPathSuggestions.parseResults(
|
||||
results,
|
||||
modelsAsDataLanguage,
|
||||
this.app.logger,
|
||||
),
|
||||
queryConstraints: accessPathSuggestions.queryConstraints(mode),
|
||||
cliServer: this.cliServer,
|
||||
queryRunner: this.queryRunner,
|
||||
queryStorageDir: this.queryStorageDir,
|
||||
databaseItem: this.databaseItem,
|
||||
progress,
|
||||
token: this.cancellationTokenSource.token,
|
||||
logger: this.app.logger,
|
||||
});
|
||||
|
||||
if (!suggestions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options: AccessPathSuggestionOptions = {
|
||||
input: parseAccessPathSuggestionRowsToOptions(suggestions.input),
|
||||
output: parseAccessPathSuggestionRowsToOptions(suggestions.output),
|
||||
};
|
||||
|
||||
await this.postMessage({
|
||||
t: "setAccessPathSuggestions",
|
||||
accessPathSuggestions: options,
|
||||
});
|
||||
} catch (e: unknown) {
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
this.app.logger,
|
||||
),
|
||||
queryConstraints: accessPathSuggestions.queryConstraints(mode),
|
||||
cliServer: this.cliServer,
|
||||
queryRunner: this.queryRunner,
|
||||
queryStorageDir: this.queryStorageDir,
|
||||
databaseItem: this.databaseItem,
|
||||
progress,
|
||||
token: this.cancellationTokenSource.token,
|
||||
logger: this.app.logger,
|
||||
});
|
||||
|
||||
if (!suggestions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options: AccessPathSuggestionOptions = {
|
||||
input: parseAccessPathSuggestionRowsToOptions(suggestions.input),
|
||||
output: parseAccessPathSuggestionRowsToOptions(suggestions.output),
|
||||
};
|
||||
|
||||
await this.postMessage({
|
||||
t: "setAccessPathSuggestions",
|
||||
accessPathSuggestions: options,
|
||||
});
|
||||
} catch (e: unknown) {
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
this.app.logger,
|
||||
this.app.telemetry,
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Failed to fetch access path suggestions: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
this.app.telemetry,
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Failed to fetch access path suggestions: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
cancellable: false,
|
||||
location: ProgressLocation.Window,
|
||||
title: "Loading access path suggestions",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
protected async generateModeledMethods(): Promise<void> {
|
||||
@@ -710,10 +702,12 @@ export class ModelEditorView extends AbstractWebview<
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
autoModelGeneration.enabled &&
|
||||
!autoModelGeneration.enabled({ mode, config: this.modelConfig })
|
||||
) {
|
||||
const autoModelType = autoModelGeneration.type({
|
||||
mode,
|
||||
config: this.modelConfig,
|
||||
});
|
||||
|
||||
if (autoModelType === AutoModelGenerationType.Disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -734,14 +728,37 @@ export class ModelEditorView extends AbstractWebview<
|
||||
queryConstraints: autoModelGeneration.queryConstraints(mode),
|
||||
filterQueries: autoModelGeneration.filterQueries,
|
||||
onResults: (queryPath, results) => {
|
||||
const extensions = autoModelGeneration.parseResultsToYaml(
|
||||
queryPath,
|
||||
results,
|
||||
modelsAsDataLanguage,
|
||||
this.app.logger,
|
||||
);
|
||||
switch (autoModelType) {
|
||||
case AutoModelGenerationType.SeparateFile: {
|
||||
const extensions = autoModelGeneration.parseResultsToYaml(
|
||||
queryPath,
|
||||
results,
|
||||
modelsAsDataLanguage,
|
||||
this.app.logger,
|
||||
);
|
||||
|
||||
extensionFile.extensions.push(...extensions);
|
||||
extensionFile.extensions.push(...extensions);
|
||||
break;
|
||||
}
|
||||
case AutoModelGenerationType.Models: {
|
||||
const modeledMethods = autoModelGeneration.parseResults(
|
||||
queryPath,
|
||||
results,
|
||||
modelsAsDataLanguage,
|
||||
this.app.logger,
|
||||
{
|
||||
mode,
|
||||
config: this.modelConfig,
|
||||
},
|
||||
);
|
||||
|
||||
this.addModeledMethodsFromArray(modeledMethods);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertNever(autoModelType);
|
||||
}
|
||||
}
|
||||
},
|
||||
cliServer: this.cliServer,
|
||||
queryRunner: this.queryRunner,
|
||||
@@ -761,22 +778,24 @@ export class ModelEditorView extends AbstractWebview<
|
||||
return;
|
||||
}
|
||||
|
||||
progress({
|
||||
step: 4000,
|
||||
maxStep: 4000,
|
||||
message: "Saving generated models",
|
||||
});
|
||||
if (autoModelType === AutoModelGenerationType.SeparateFile) {
|
||||
progress({
|
||||
step: 4000,
|
||||
maxStep: 4000,
|
||||
message: "Saving generated models",
|
||||
});
|
||||
|
||||
const fileContents = `# This file was automatically generated from ${this.databaseItem.name}. Manual changes will not persist.\n\n${modelExtensionFileToYaml(extensionFile)}`;
|
||||
const filePath = join(
|
||||
this.extensionPack.path,
|
||||
"models",
|
||||
`${this.language}${GENERATED_MODELS_SUFFIX}`,
|
||||
);
|
||||
const fileContents = `# This file was automatically generated from ${this.databaseItem.name}. Manual changes will not persist.\n\n${modelExtensionFileToYaml(extensionFile)}`;
|
||||
const filePath = join(
|
||||
this.extensionPack.path,
|
||||
"models",
|
||||
`${this.language}${GENERATED_MODELS_SUFFIX}`,
|
||||
);
|
||||
|
||||
await outputFile(filePath, fileContents);
|
||||
await outputFile(filePath, fileContents);
|
||||
|
||||
void this.app.logger.log(`Saved generated model file to ${filePath}`);
|
||||
void this.app.logger.log(`Saved generated model file to ${filePath}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
cancellable: false,
|
||||
|
||||
@@ -12,7 +12,6 @@ import { load as loadYaml } from "js-yaml";
|
||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { pathsEqual } from "../common/files";
|
||||
import type { QueryLanguage } from "../common/query-language";
|
||||
import type { ModelConfig } from "./languages";
|
||||
|
||||
export const GENERATED_MODELS_SUFFIX = ".model.generated.yml";
|
||||
|
||||
@@ -23,14 +22,12 @@ export async function saveModeledMethods(
|
||||
modeledMethods: Readonly<Record<string, readonly ModeledMethod[]>>,
|
||||
mode: Mode,
|
||||
cliServer: CodeQLCliServer,
|
||||
modelConfig: ModelConfig,
|
||||
logger: NotificationLogger,
|
||||
): Promise<void> {
|
||||
const existingModeledMethods = await loadModeledMethodFiles(
|
||||
extensionPack,
|
||||
language,
|
||||
cliServer,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
|
||||
@@ -53,14 +50,9 @@ async function loadModeledMethodFiles(
|
||||
extensionPack: ExtensionPack,
|
||||
language: QueryLanguage,
|
||||
cliServer: CodeQLCliServer,
|
||||
modelConfig: ModelConfig,
|
||||
logger: NotificationLogger,
|
||||
): Promise<Record<string, Record<string, ModeledMethod[]>>> {
|
||||
const modelFiles = await listModelFiles(
|
||||
extensionPack.path,
|
||||
cliServer,
|
||||
modelConfig,
|
||||
);
|
||||
const modelFiles = await listModelFiles(extensionPack.path, cliServer);
|
||||
|
||||
const modeledMethodsByFile: Record<
|
||||
string,
|
||||
@@ -92,7 +84,6 @@ export async function loadModeledMethods(
|
||||
extensionPack: ExtensionPack,
|
||||
language: QueryLanguage,
|
||||
cliServer: CodeQLCliServer,
|
||||
modelConfig: ModelConfig,
|
||||
logger: NotificationLogger,
|
||||
): Promise<Record<string, ModeledMethod[]>> {
|
||||
const existingModeledMethods: Record<string, ModeledMethod[]> = {};
|
||||
@@ -101,7 +92,6 @@ export async function loadModeledMethods(
|
||||
extensionPack,
|
||||
language,
|
||||
cliServer,
|
||||
modelConfig,
|
||||
logger,
|
||||
);
|
||||
for (const modeledMethods of Object.values(modeledMethodsByFile)) {
|
||||
@@ -120,7 +110,6 @@ export async function loadModeledMethods(
|
||||
export async function listModelFiles(
|
||||
extensionPackPath: string,
|
||||
cliServer: CodeQLCliServer,
|
||||
modelConfig: ModelConfig,
|
||||
): Promise<Set<string>> {
|
||||
const result = await cliServer.resolveExtensions(
|
||||
extensionPackPath,
|
||||
@@ -131,11 +120,8 @@ export async function listModelFiles(
|
||||
for (const [path, extensions] of Object.entries(result.data)) {
|
||||
if (pathsEqual(path, extensionPackPath)) {
|
||||
for (const extension of extensions) {
|
||||
// We only load generated models when type models are shown
|
||||
if (
|
||||
!modelConfig.showTypeModels &&
|
||||
extension.file.endsWith(GENERATED_MODELS_SUFFIX)
|
||||
) {
|
||||
// We never load generated models
|
||||
if (extension.file.endsWith(GENERATED_MODELS_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { QueryLanguage } from "../common/query-language";
|
||||
import type { ModelConfig } from "../config";
|
||||
import { isCanary } from "../config";
|
||||
|
||||
/**
|
||||
* Languages that are always supported by the model editor. These languages
|
||||
@@ -9,6 +8,7 @@ import { isCanary } from "../config";
|
||||
export const SUPPORTED_LANGUAGES: QueryLanguage[] = [
|
||||
QueryLanguage.Java,
|
||||
QueryLanguage.CSharp,
|
||||
QueryLanguage.Ruby,
|
||||
];
|
||||
|
||||
export function isSupportedLanguage(
|
||||
@@ -19,11 +19,6 @@ export function isSupportedLanguage(
|
||||
return true;
|
||||
}
|
||||
|
||||
if (language === QueryLanguage.Ruby) {
|
||||
// Ruby is only enabled when in canary mode
|
||||
return isCanary();
|
||||
}
|
||||
|
||||
if (language === QueryLanguage.Python) {
|
||||
// Python is only enabled when the config setting is set
|
||||
return modelConfig.enablePython;
|
||||
|
||||
18
extensions/ql-vscode/src/variant-analysis/ghec-dr.ts
Normal file
18
extensions/ql-vscode/src/variant-analysis/ghec-dr.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
VARIANT_ANALYSIS_ENABLE_GHEC_DR,
|
||||
hasEnterpriseUri,
|
||||
hasGhecDrUri,
|
||||
} from "../config";
|
||||
|
||||
/**
|
||||
* Determines whether MRVA should be enabled or not for the current GitHub host.
|
||||
* This is based on the `github-enterprise.uri` setting.
|
||||
*/
|
||||
export function isVariantAnalysisEnabledForGitHubHost(): boolean {
|
||||
return (
|
||||
// MRVA is always enabled on github.com
|
||||
!hasEnterpriseUri() ||
|
||||
// MRVA can be enabled on GHEC-DR using a feature flag
|
||||
(hasGhecDrUri() && !!VARIANT_ANALYSIS_ENABLE_GHEC_DR.getValue<boolean>())
|
||||
);
|
||||
}
|
||||
@@ -204,18 +204,14 @@ async function copyExistingQueryPack(
|
||||
// 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
|
||||
// of data extensions.
|
||||
if (
|
||||
await cliServer.cliConstraints.supportsGenerateExtensiblePredicateMetadata()
|
||||
) {
|
||||
const metadata = await cliServer.generateExtensiblePredicateMetadata(
|
||||
qlPackDetails.qlPackRootPath,
|
||||
);
|
||||
metadata.extensible_predicates.forEach((predicate) => {
|
||||
if (predicate.path.endsWith(".ql")) {
|
||||
toCopy.push(join(qlPackDetails.qlPackRootPath, predicate.path));
|
||||
}
|
||||
});
|
||||
}
|
||||
const metadata = await cliServer.generateExtensiblePredicateMetadata(
|
||||
qlPackDetails.qlPackRootPath,
|
||||
);
|
||||
metadata.extensible_predicates.forEach((predicate) => {
|
||||
if (predicate.path.endsWith(".ql")) {
|
||||
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.
|
||||
|
||||
@@ -97,6 +97,8 @@ import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
||||
import { findVariantAnalysisQlPackRoot } from "./ql";
|
||||
import { resolveCodeScanningQueryPack } from "./code-scanning-pack";
|
||||
import { isSarifResultsQueryKind } from "../common/query-metadata";
|
||||
import { isVariantAnalysisEnabledForGitHubHost } from "./ghec-dr";
|
||||
import { getEnterpriseUri } from "../config";
|
||||
|
||||
const maxRetryCount = 3;
|
||||
|
||||
@@ -327,6 +329,12 @@ export class VariantAnalysisManager
|
||||
token: CancellationToken,
|
||||
openViewAfterSubmission = true,
|
||||
): Promise<number | undefined> {
|
||||
if (!isVariantAnalysisEnabledForGitHubHost()) {
|
||||
throw new Error(
|
||||
`Multi-repository variant analysis is not enabled for ${getEnterpriseUri()}`,
|
||||
);
|
||||
}
|
||||
|
||||
await saveBeforeStart();
|
||||
|
||||
progress({
|
||||
|
||||
@@ -36,33 +36,7 @@ describe(ModelTypeDropdown.name, () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("allows changing the type to 'Type' for Ruby when type models are shown", async () => {
|
||||
const method = createMethod();
|
||||
const modeledMethod = createNoneModeledMethod();
|
||||
|
||||
render(
|
||||
<ModelTypeDropdown
|
||||
language={QueryLanguage.Ruby}
|
||||
modeledMethod={modeledMethod}
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
method={method}
|
||||
modelConfig={{
|
||||
...defaultModelConfig,
|
||||
showTypeModels: true,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await userEvent.selectOptions(screen.getByRole("combobox"), "type");
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: "type",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not allow changing the type to 'Type' for Ruby when type models are not shown", async () => {
|
||||
it("allows changing the type to 'Type' for Ruby", async () => {
|
||||
const method = createMethod();
|
||||
const modeledMethod = createNoneModeledMethod();
|
||||
|
||||
@@ -77,9 +51,12 @@ describe(ModelTypeDropdown.name, () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByRole("option", { name: "Type" }),
|
||||
).not.toBeInTheDocument();
|
||||
await userEvent.selectOptions(screen.getByRole("combobox"), "type");
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: "type",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not allow changing the type to 'Type' for Java", async () => {
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
"v2.16.6",
|
||||
"v2.15.5",
|
||||
"v2.14.6",
|
||||
"v2.13.5",
|
||||
"nightly"
|
||||
]
|
||||
|
||||
@@ -38,7 +38,7 @@ services:
|
||||
depends_on:
|
||||
- files-init
|
||||
files-init:
|
||||
image: alpine:3.19.0
|
||||
image: alpine:3.19.1
|
||||
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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM codercom/code-server:4.20.0
|
||||
FROM codercom/code-server:4.23.1
|
||||
|
||||
USER root
|
||||
|
||||
@@ -7,7 +7,7 @@ RUN apt-get update \
|
||||
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 \
|
||||
RUN wget -q -O /tmp/codeql.zip https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql-linux64.zip \
|
||||
&& unzip -q /tmp/codeql.zip -d /opt \
|
||||
&& rm -rf /tmp/codeql.zip
|
||||
|
||||
|
||||
9
extensions/ql-vscode/test/factories/config.ts
Normal file
9
extensions/ql-vscode/test/factories/config.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { VariantAnalysisConfig } from "../../src/config";
|
||||
|
||||
export function createMockVariantAnalysisConfig(): VariantAnalysisConfig {
|
||||
return {
|
||||
controllerRepo: "foo/bar",
|
||||
showSystemDefinedRepositoryLists: true,
|
||||
onDidChangeConfiguration: jest.fn(),
|
||||
};
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import { DbManager } from "../../../src/databases/db-manager";
|
||||
import { createDbConfig } from "../../factories/db-config-factories";
|
||||
import { createRemoteUserDefinedListDbItem } from "../../factories/db-item-factories";
|
||||
import { createMockApp } from "../../__mocks__/appMock";
|
||||
import { createMockVariantAnalysisConfig } from "../../factories/config";
|
||||
|
||||
// Note: Although these are "unit tests" (i.e. not integrating with VS Code), they do
|
||||
// test the interaction/"integration" between the DbManager and the DbConfigStore.
|
||||
@@ -46,7 +47,11 @@ describe("db manager", () => {
|
||||
// We don't need to watch changes to the config file in these tests, so we
|
||||
// pass `false` to the dbConfigStore constructor.
|
||||
dbConfigStore = new DbConfigStore(app, false);
|
||||
dbManager = new DbManager(app, dbConfigStore);
|
||||
dbManager = new DbManager(
|
||||
app,
|
||||
dbConfigStore,
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
await ensureDir(tempWorkspaceStoragePath);
|
||||
|
||||
dbConfigFilePath = join(
|
||||
|
||||
@@ -10,13 +10,20 @@ import type { ExpandedDbItem } from "../../../src/databases/db-item-expansion";
|
||||
import { ExpandedDbItemKind } from "../../../src/databases/db-item-expansion";
|
||||
import { createRemoteTree } from "../../../src/databases/db-tree-creator";
|
||||
import { createDbConfig } from "../../factories/db-config-factories";
|
||||
import { createMockVariantAnalysisConfig } from "../../factories/config";
|
||||
|
||||
describe("db tree creator", () => {
|
||||
const defaultVariantAnalysisConfig = createMockVariantAnalysisConfig();
|
||||
|
||||
describe("createRemoteTree", () => {
|
||||
it("should build root node and system defined lists", () => {
|
||||
const dbConfig = createDbConfig();
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
@@ -45,6 +52,24 @@ describe("db tree creator", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("displays empty list when no remote user defined list nodes and system defined lists are disabled", () => {
|
||||
const dbConfig = createDbConfig();
|
||||
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
{
|
||||
...defaultVariantAnalysisConfig,
|
||||
showSystemDefinedRepositoryLists: false,
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
expect(dbTreeRoot.expanded).toBe(false);
|
||||
expect(dbTreeRoot.children.length).toBe(0);
|
||||
});
|
||||
|
||||
it("should create remote user defined list nodes", () => {
|
||||
const dbConfig = createDbConfig({
|
||||
remoteLists: [
|
||||
@@ -59,10 +84,15 @@ describe("db tree creator", () => {
|
||||
],
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
expect(dbTreeRoot.children.length).toBe(5);
|
||||
const repositoryListNodes = dbTreeRoot.children.filter(
|
||||
isRemoteUserDefinedListDbItem,
|
||||
);
|
||||
@@ -102,12 +132,76 @@ describe("db tree creator", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("shows only user defined list nodes when system defined lists are disabled", () => {
|
||||
const dbConfig = createDbConfig({
|
||||
remoteLists: [
|
||||
{
|
||||
name: "my-list-1",
|
||||
repositories: ["owner1/repo1", "owner1/repo2", "owner2/repo1"],
|
||||
},
|
||||
{
|
||||
name: "my-list-2",
|
||||
repositories: ["owner3/repo1", "owner3/repo2", "owner4/repo1"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
{
|
||||
...defaultVariantAnalysisConfig,
|
||||
showSystemDefinedRepositoryLists: false,
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
expect(dbTreeRoot.children.length).toBe(2);
|
||||
expect(dbTreeRoot.children[0]).toEqual({
|
||||
kind: DbItemKind.RemoteUserDefinedList,
|
||||
selected: false,
|
||||
expanded: false,
|
||||
listName: dbConfig.databases.variantAnalysis.repositoryLists[0].name,
|
||||
repos:
|
||||
dbConfig.databases.variantAnalysis.repositoryLists[0].repositories.map(
|
||||
(repo) => ({
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
selected: false,
|
||||
repoFullName: repo,
|
||||
parentListName:
|
||||
dbConfig.databases.variantAnalysis.repositoryLists[0].name,
|
||||
}),
|
||||
),
|
||||
});
|
||||
expect(dbTreeRoot.children[1]).toEqual({
|
||||
kind: DbItemKind.RemoteUserDefinedList,
|
||||
selected: false,
|
||||
expanded: false,
|
||||
listName: dbConfig.databases.variantAnalysis.repositoryLists[1].name,
|
||||
repos:
|
||||
dbConfig.databases.variantAnalysis.repositoryLists[1].repositories.map(
|
||||
(repo) => ({
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
selected: false,
|
||||
repoFullName: repo,
|
||||
parentListName:
|
||||
dbConfig.databases.variantAnalysis.repositoryLists[1].name,
|
||||
}),
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
it("should create remote owner nodes", () => {
|
||||
const dbConfig: DbConfig = createDbConfig({
|
||||
remoteOwners: ["owner1", "owner2"],
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
@@ -131,7 +225,11 @@ describe("db tree creator", () => {
|
||||
remoteRepos: ["owner1/repo1", "owner1/repo2", "owner2/repo1"],
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
@@ -170,7 +268,11 @@ describe("db tree creator", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
@@ -191,7 +293,11 @@ describe("db tree creator", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
@@ -213,7 +319,11 @@ describe("db tree creator", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
@@ -240,7 +350,11 @@ describe("db tree creator", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, []);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
[],
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
|
||||
@@ -265,7 +379,11 @@ describe("db tree creator", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, expanded);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
expanded,
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
@@ -291,7 +409,11 @@ describe("db tree creator", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const dbTreeRoot = createRemoteTree(dbConfig, expanded);
|
||||
const dbTreeRoot = createRemoteTree(
|
||||
dbConfig,
|
||||
defaultVariantAnalysisConfig,
|
||||
expanded,
|
||||
);
|
||||
|
||||
expect(dbTreeRoot).toBeTruthy();
|
||||
expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote);
|
||||
|
||||
@@ -1,82 +1,81 @@
|
||||
import {
|
||||
autoNameExtensionPack,
|
||||
sanitizePackName,
|
||||
formatPackName,
|
||||
parsePackName,
|
||||
validatePackName,
|
||||
} from "../../../src/model-editor/extension-pack-name";
|
||||
|
||||
describe("autoNameExtensionPack", () => {
|
||||
describe("sanitizePackName", () => {
|
||||
const testCases: Array<{
|
||||
name: string;
|
||||
language: string;
|
||||
expected: string;
|
||||
}> = [
|
||||
{
|
||||
name: "github/vscode-codeql",
|
||||
language: "javascript",
|
||||
name: "github/vscode-codeql-javascript",
|
||||
expected: "github/vscode-codeql-javascript",
|
||||
},
|
||||
{
|
||||
name: "vscode-codeql",
|
||||
language: "a",
|
||||
name: "vscode-codeql-a",
|
||||
expected: "pack/vscode-codeql-a",
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
language: "java",
|
||||
name: "b-java",
|
||||
expected: "pack/b-java",
|
||||
},
|
||||
{
|
||||
name: "a/b",
|
||||
language: "csharp",
|
||||
name: "a/b-csharp",
|
||||
expected: "a/b-csharp",
|
||||
},
|
||||
{
|
||||
name: "-/b",
|
||||
language: "csharp",
|
||||
name: "-/b-csharp",
|
||||
expected: "pack/b-csharp",
|
||||
},
|
||||
{
|
||||
name: "a/b/c/d",
|
||||
language: "csharp",
|
||||
name: "a/b/c/d-csharp",
|
||||
expected: "a/b-c-d-csharp",
|
||||
},
|
||||
{
|
||||
name: "JAVA/CodeQL",
|
||||
language: "csharp",
|
||||
name: "JAVA/CodeQL-csharp",
|
||||
expected: "java/codeql-csharp",
|
||||
},
|
||||
{
|
||||
name: "my new pack",
|
||||
language: "swift",
|
||||
name: "my new pack-swift",
|
||||
expected: "pack/my-new-pack-swift",
|
||||
},
|
||||
{
|
||||
name: "gïthub/vscode-codeql",
|
||||
language: "javascript",
|
||||
name: "gïthub/vscode-codeql-javascript",
|
||||
expected: "gthub/vscode-codeql-javascript",
|
||||
},
|
||||
{
|
||||
name: "a/b-",
|
||||
language: "csharp",
|
||||
name: "a/b-csharp",
|
||||
expected: "a/b-csharp",
|
||||
},
|
||||
{
|
||||
name: "-a-/b",
|
||||
language: "ruby",
|
||||
name: "-a-/b-ruby",
|
||||
expected: "a/b-ruby",
|
||||
},
|
||||
{
|
||||
name: "a/b--d--e-d-",
|
||||
language: "csharp",
|
||||
name: "a/b--d--e-d-csharp",
|
||||
expected: "a/b-d-e-d-csharp",
|
||||
},
|
||||
{
|
||||
name: "/github/vscode-codeql",
|
||||
expected: "github/vscode-codeql",
|
||||
},
|
||||
{
|
||||
name: "github/vscode-codeql/",
|
||||
expected: "github/vscode-codeql",
|
||||
},
|
||||
{
|
||||
name: "///github/vscode-codeql///",
|
||||
expected: "github/vscode-codeql",
|
||||
},
|
||||
];
|
||||
|
||||
test.each(testCases)(
|
||||
"$name with $language = $expected",
|
||||
({ name, language, expected }) => {
|
||||
const result = autoNameExtensionPack(name, language);
|
||||
({ name, expected }) => {
|
||||
const result = sanitizePackName(name);
|
||||
expect(result).not.toBeUndefined();
|
||||
if (!result) {
|
||||
return;
|
||||
|
||||
@@ -4,8 +4,6 @@ import { ruby } from "../../../../../src/model-editor/languages/ruby";
|
||||
import { createMockLogger } from "../../../../__mocks__/loggerMock";
|
||||
import type { ModeledMethod } from "../../../../../src/model-editor/modeled-method";
|
||||
import { EndpointType } from "../../../../../src/model-editor/method";
|
||||
import { Mode } from "../../../../../src/model-editor/shared/mode";
|
||||
import { defaultModelConfig } from "../../../../../src/model-editor/languages";
|
||||
|
||||
describe("parseGenerateModelResults", () => {
|
||||
it("should return the results", async () => {
|
||||
@@ -78,13 +76,6 @@ describe("parseGenerateModelResults", () => {
|
||||
bqrs,
|
||||
ruby,
|
||||
createMockLogger(),
|
||||
{
|
||||
mode: Mode.Framework,
|
||||
config: {
|
||||
...defaultModelConfig,
|
||||
showTypeModels: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
writeRepoStates,
|
||||
} from "../../../../src/variant-analysis/repo-states-store";
|
||||
import { permissiveFilterSortState } from "../../../unit-tests/variant-analysis-filter-sort.test";
|
||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||
|
||||
// up to 3 minutes per test
|
||||
jest.setTimeout(3 * 60 * 1000);
|
||||
@@ -72,7 +73,11 @@ describe("Variant Analysis Manager", () => {
|
||||
const extension = await getActivatedExtension();
|
||||
const cli = mockedObject<CodeQLCliServer>({});
|
||||
app = new ExtensionApp(extension.ctx);
|
||||
const dbManager = new DbManager(app, new DbConfigStore(app));
|
||||
const dbManager = new DbManager(
|
||||
app,
|
||||
new DbConfigStore(app),
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cli,
|
||||
extLogger,
|
||||
|
||||
@@ -13,7 +13,6 @@ import { homedir, tmpdir } from "os";
|
||||
import { mkdir, rm } from "fs-extra";
|
||||
import { nanoid } from "nanoid";
|
||||
import { QueryLanguage } from "../../../../src/common/query-language";
|
||||
import { defaultModelConfig } from "../../../../src/model-editor/languages";
|
||||
|
||||
const dummyExtensionPackContents = `
|
||||
name: dummy/pack
|
||||
@@ -56,6 +55,12 @@ describe("modeled-method-fs", () => {
|
||||
let cli: CodeQLCliServer;
|
||||
|
||||
beforeEach(async () => {
|
||||
if (!process.env.TEST_CODEQL_PATH) {
|
||||
fail(
|
||||
"TEST_CODEQL_PATH environment variable not set. It should point to the absolute path to a checkout of the codeql repository.",
|
||||
);
|
||||
}
|
||||
|
||||
// On windows, make sure to use a temp directory that isn't an alias and therefore won't be canonicalised by CodeQL.
|
||||
// The tmp package doesn't support this, so we have to do it manually.
|
||||
// See https://github.com/github/vscode-codeql/pull/2605 for more context.
|
||||
@@ -74,11 +79,16 @@ describe("modeled-method-fs", () => {
|
||||
name: "workspace",
|
||||
index: 0,
|
||||
};
|
||||
const codeqlWorkspaceFolder = {
|
||||
uri: Uri.file(process.env.TEST_CODEQL_PATH),
|
||||
name: "ql",
|
||||
index: 1,
|
||||
};
|
||||
workspacePath = workspaceFolder.uri.fsPath;
|
||||
mkdirSync(workspacePath);
|
||||
jest
|
||||
.spyOn(workspace, "workspaceFolders", "get")
|
||||
.mockReturnValue([workspaceFolder]);
|
||||
.mockReturnValue([workspaceFolder, codeqlWorkspaceFolder]);
|
||||
|
||||
const extension = await getActivatedExtension();
|
||||
cli = extension.cliServer;
|
||||
@@ -136,11 +146,7 @@ describe("modeled-method-fs", () => {
|
||||
it("should return the empty set when the extension pack is empty", async () => {
|
||||
const extensionPackPath = writeExtensionPackFiles("extension-pack", []);
|
||||
|
||||
const modelFiles = await listModelFiles(
|
||||
extensionPackPath,
|
||||
cli,
|
||||
defaultModelConfig,
|
||||
);
|
||||
const modelFiles = await listModelFiles(extensionPackPath, cli);
|
||||
expect(modelFiles).toEqual(new Set());
|
||||
});
|
||||
|
||||
@@ -150,11 +156,7 @@ describe("modeled-method-fs", () => {
|
||||
"library2.model.yml",
|
||||
]);
|
||||
|
||||
const modelFiles = await listModelFiles(
|
||||
extensionPackPath,
|
||||
cli,
|
||||
defaultModelConfig,
|
||||
);
|
||||
const modelFiles = await listModelFiles(extensionPackPath, cli);
|
||||
expect(modelFiles).toEqual(
|
||||
new Set([
|
||||
join("models", "library1.model.yml"),
|
||||
@@ -163,18 +165,14 @@ describe("modeled-method-fs", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should ignore generated type models when type models are hidden", async () => {
|
||||
it("should ignore generated models", async () => {
|
||||
const extensionPackPath = writeExtensionPackFiles("extension-pack", [
|
||||
"library1.model.yml",
|
||||
"library2.model.yml",
|
||||
"library.model.generated.yml",
|
||||
]);
|
||||
|
||||
const modelFiles = await listModelFiles(
|
||||
extensionPackPath,
|
||||
cli,
|
||||
defaultModelConfig,
|
||||
);
|
||||
const modelFiles = await listModelFiles(extensionPackPath, cli);
|
||||
expect(modelFiles).toEqual(
|
||||
new Set([
|
||||
join("models", "library1.model.yml"),
|
||||
@@ -183,37 +181,13 @@ describe("modeled-method-fs", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should include generated type models when type models are shown", async () => {
|
||||
const extensionPackPath = writeExtensionPackFiles("extension-pack", [
|
||||
"library1.model.yml",
|
||||
"library2.model.yml",
|
||||
"library.model.generated.yml",
|
||||
]);
|
||||
|
||||
const modelFiles = await listModelFiles(extensionPackPath, cli, {
|
||||
...defaultModelConfig,
|
||||
showTypeModels: true,
|
||||
});
|
||||
expect(modelFiles).toEqual(
|
||||
new Set([
|
||||
join("models", "library.model.generated.yml"),
|
||||
join("models", "library1.model.yml"),
|
||||
join("models", "library2.model.yml"),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("should ignore model files from other extension packs", async () => {
|
||||
const extensionPackPath = writeExtensionPackFiles("extension-pack", [
|
||||
"library1.model.yml",
|
||||
]);
|
||||
writeExtensionPackFiles("another-extension-pack", ["library2.model.yml"]);
|
||||
|
||||
const modelFiles = await listModelFiles(
|
||||
extensionPackPath,
|
||||
cli,
|
||||
defaultModelConfig,
|
||||
);
|
||||
const modelFiles = await listModelFiles(extensionPackPath, cli);
|
||||
expect(modelFiles).toEqual(
|
||||
new Set([join("models", "library1.model.yml")]),
|
||||
);
|
||||
@@ -230,7 +204,6 @@ describe("modeled-method-fs", () => {
|
||||
makeExtensionPack(extensionPackPath),
|
||||
QueryLanguage.Java,
|
||||
cli,
|
||||
defaultModelConfig,
|
||||
extLogger,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { CancellationTokenSource, commands, window, Uri } from "vscode";
|
||||
import {
|
||||
CancellationTokenSource,
|
||||
commands,
|
||||
window,
|
||||
Uri,
|
||||
ConfigurationTarget,
|
||||
} from "vscode";
|
||||
import { extLogger } from "../../../../src/common/logging/vscode";
|
||||
import { setRemoteControllerRepo } from "../../../../src/config";
|
||||
import {
|
||||
VSCODE_GITHUB_ENTERPRISE_URI_SETTING,
|
||||
setRemoteControllerRepo,
|
||||
} from "../../../../src/config";
|
||||
import * as ghApiClient from "../../../../src/variant-analysis/gh-api/gh-api-client";
|
||||
import { isAbsolute, join } from "path";
|
||||
|
||||
@@ -27,6 +36,7 @@ import type { QlPackLockFile } from "../../../../src/packaging/qlpack-lock-file"
|
||||
//import { expect } from "@jest/globals";
|
||||
import "../../../matchers/toExistInCodeQLPack";
|
||||
import type { QlPackDetails } from "../../../../src/variant-analysis/ql-pack-details";
|
||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||
|
||||
describe("Variant Analysis Manager", () => {
|
||||
let cli: CodeQLCliServer;
|
||||
@@ -41,7 +51,11 @@ describe("Variant Analysis Manager", () => {
|
||||
const extension = await getActivatedExtension();
|
||||
cli = extension.cliServer;
|
||||
const app = new ExtensionApp(extension.ctx);
|
||||
const dbManager = new DbManager(app, new DbConfigStore(app));
|
||||
const dbManager = new DbManager(
|
||||
app,
|
||||
new DbConfigStore(app),
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cli,
|
||||
extLogger,
|
||||
@@ -99,6 +113,32 @@ describe("Variant Analysis Manager", () => {
|
||||
await setRemoteControllerRepo("github/vscode-codeql");
|
||||
});
|
||||
|
||||
it("fails if MRVA is not supported for this GHE URI", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://github.example.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
|
||||
const qlPackDetails: QlPackDetails = {
|
||||
queryFiles: [getFileOrDir("data-remote-qlpack/in-pack.ql")],
|
||||
qlPackRootPath: getFileOrDir("data-remote-qlpack"),
|
||||
qlPackFilePath: getFileOrDir("data-remote-qlpack/qlpack.yml"),
|
||||
language: QueryLanguage.Javascript,
|
||||
};
|
||||
|
||||
await expect(
|
||||
variantAnalysisManager.runVariantAnalysis(
|
||||
qlPackDetails,
|
||||
progress,
|
||||
cancellationTokenSource.token,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
new Error(
|
||||
"Multi-repository variant analysis is not enabled for https://github.example.com/",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("should run a variant analysis that is part of a qlpack", async () => {
|
||||
const filePath = getFileOrDir("data-remote-qlpack/in-pack.ql");
|
||||
const qlPackRootPath = getFileOrDir("data-remote-qlpack");
|
||||
@@ -295,15 +335,6 @@ describe("Variant Analysis Manager", () => {
|
||||
// Test running core java queries to ensure that we can compile queries in packs
|
||||
// that contain queries with extensible predicates
|
||||
it("should run a remote query that is part of the java pack", async () => {
|
||||
if (
|
||||
!(await cli.cliConstraints.supportsGenerateExtensiblePredicateMetadata())
|
||||
) {
|
||||
console.log(
|
||||
`Skipping test because generating extensible predicate metadata was only introduced in CLI version ${CliVersionConstraint.CLI_VERSION_WITH_EXTENSIBLE_PREDICATE_METADATA}.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.TEST_CODEQL_PATH) {
|
||||
fail(
|
||||
"TEST_CODEQL_PATH environment variable not set. It should point to the absolute path to a checkout of the codeql repository.",
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { workspace } from "vscode";
|
||||
import { ConfigurationTarget, workspace } from "vscode";
|
||||
|
||||
import type { ConfigListener } from "../../../src/config";
|
||||
import {
|
||||
CliConfigListener,
|
||||
QueryHistoryConfigListener,
|
||||
QueryServerConfigListener,
|
||||
VSCODE_GITHUB_ENTERPRISE_URI_SETTING,
|
||||
getEnterpriseUri,
|
||||
hasEnterpriseUri,
|
||||
hasGhecDrUri,
|
||||
} from "../../../src/config";
|
||||
import { vscodeGetConfigurationMock } from "../test-config";
|
||||
|
||||
@@ -126,3 +130,49 @@ describe("config listeners", () => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
});
|
||||
|
||||
describe("enterprise URI", () => {
|
||||
it("detects no enterprise URI when config value is not set", async () => {
|
||||
expect(getEnterpriseUri()).toBeUndefined();
|
||||
expect(hasEnterpriseUri()).toBe(false);
|
||||
expect(hasGhecDrUri()).toBe(false);
|
||||
});
|
||||
|
||||
it("detects no enterprise URI when config value is set to an invalid value", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"invalid-uri",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(getEnterpriseUri()).toBeUndefined();
|
||||
expect(hasEnterpriseUri()).toBe(false);
|
||||
expect(hasGhecDrUri()).toBe(false);
|
||||
});
|
||||
|
||||
it("detects an enterprise URI when config value is set to a GHES URI", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://github.example.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(getEnterpriseUri()?.toString()).toBe("https://github.example.com/");
|
||||
expect(hasEnterpriseUri()).toBe(true);
|
||||
expect(hasGhecDrUri()).toBe(false);
|
||||
});
|
||||
|
||||
it("detects a GHEC-DR URI when config value is set to a GHEC-DR URI", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://example.ghe.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(getEnterpriseUri()?.toString()).toBe("https://example.ghe.com/");
|
||||
expect(hasEnterpriseUri()).toBe(true);
|
||||
expect(hasGhecDrUri()).toBe(true);
|
||||
});
|
||||
|
||||
it("Upgrades HTTP URIs to HTTPS", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"http://example.ghe.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(getEnterpriseUri()?.toString()).toBe("https://example.ghe.com/");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ExtensionApp } from "../../../../src/common/vscode/extension-app";
|
||||
import { createMockExtensionContext } from "../../../factories/extension-context";
|
||||
import { createDbConfig } from "../../../factories/db-config-factories";
|
||||
import { setRemoteControllerRepo } from "../../../../src/config";
|
||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||
|
||||
describe("db panel rendering nodes", () => {
|
||||
const workspaceStoragePath = join(__dirname, "test-workspace-storage");
|
||||
@@ -35,7 +36,11 @@ describe("db panel rendering nodes", () => {
|
||||
const app = new ExtensionApp(extensionContext);
|
||||
|
||||
dbConfigStore = new DbConfigStore(app, false);
|
||||
dbManager = new DbManager(app, dbConfigStore);
|
||||
dbManager = new DbManager(
|
||||
app,
|
||||
dbConfigStore,
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ExtensionApp } from "../../../../src/common/vscode/extension-app";
|
||||
import { createMockExtensionContext } from "../../../factories/extension-context";
|
||||
import { createDbConfig } from "../../../factories/db-config-factories";
|
||||
import { setRemoteControllerRepo } from "../../../../src/config";
|
||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||
|
||||
describe("db panel", () => {
|
||||
const workspaceStoragePath = join(__dirname, "test-workspace-storage");
|
||||
@@ -34,7 +35,11 @@ describe("db panel", () => {
|
||||
const app = new ExtensionApp(extensionContext);
|
||||
|
||||
dbConfigStore = new DbConfigStore(app, false);
|
||||
dbManager = new DbManager(app, dbConfigStore);
|
||||
dbManager = new DbManager(
|
||||
app,
|
||||
dbConfigStore,
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { ConfigurationTarget } from "vscode";
|
||||
import {
|
||||
VARIANT_ANALYSIS_ENABLE_GHEC_DR,
|
||||
VSCODE_GITHUB_ENTERPRISE_URI_SETTING,
|
||||
} from "../../../../src/config";
|
||||
import { isVariantAnalysisEnabledForGitHubHost } from "../../../../src/variant-analysis/ghec-dr";
|
||||
|
||||
describe("checkVariantAnalysisEnabled", () => {
|
||||
it("returns cleanly when no enterprise URI is set", async () => {
|
||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false when GHES enterprise URI is set and variant analysis feature flag is not set", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://github.example.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when GHES enterprise URI is set and variant analysis feature flag is set", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://github.example.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
await VARIANT_ANALYSIS_ENABLE_GHEC_DR.updateValue(
|
||||
"true",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when GHEC-DR URI is set and variant analysis feature flag is not set", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://example.ghe.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true when GHEC-DR URI is set and variant analysis feature flag is set", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://example.ghe.com",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
await VARIANT_ANALYSIS_ENABLE_GHEC_DR.updateValue(
|
||||
"true",
|
||||
ConfigurationTarget.Global,
|
||||
);
|
||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,7 @@ import { ExtensionApp } from "../../../../src/common/vscode/extension-app";
|
||||
import { createMockExtensionContext } from "../../../factories/extension-context";
|
||||
import { createDbConfig } from "../../../factories/db-config-factories";
|
||||
import { setRemoteControllerRepo } from "../../../../src/config";
|
||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||
|
||||
describe("db panel selection", () => {
|
||||
const workspaceStoragePath = join(__dirname, "test-workspace-storage");
|
||||
@@ -36,7 +37,11 @@ describe("db panel selection", () => {
|
||||
const app = new ExtensionApp(extensionContext);
|
||||
|
||||
dbConfigStore = new DbConfigStore(app, false);
|
||||
dbManager = new DbManager(app, dbConfigStore);
|
||||
dbManager = new DbManager(
|
||||
app,
|
||||
dbConfigStore,
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
@@ -37,6 +37,9 @@ describe("pickExtensionPack", () => {
|
||||
let workspaceFoldersSpy: jest.SpyInstance;
|
||||
let additionalPacks: string[];
|
||||
let workspaceFolder: WorkspaceFolder;
|
||||
|
||||
let getPackLocation: jest.MockedFunction<ModelConfig["getPackLocation"]>;
|
||||
let getPackName: jest.MockedFunction<ModelConfig["getPackName"]>;
|
||||
let modelConfig: ModelConfig;
|
||||
|
||||
const logger = createMockLogger();
|
||||
@@ -74,13 +77,20 @@ describe("pickExtensionPack", () => {
|
||||
.spyOn(workspace, "workspaceFolders", "get")
|
||||
.mockReturnValue([workspaceFolder]);
|
||||
|
||||
getPackLocation = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name }) => `.github/codeql/extensions/${name}-${language}`,
|
||||
);
|
||||
getPackName = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name, owner }) => `${owner}/${name}-${language}`,
|
||||
);
|
||||
|
||||
modelConfig = mockedObject<ModelConfig>({
|
||||
getPackLocation: jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(language, { name }) =>
|
||||
`.github/codeql/extensions/${name}-${language}`,
|
||||
),
|
||||
getPackLocation,
|
||||
getPackName,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -178,6 +188,12 @@ describe("pickExtensionPack", () => {
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
expect(modelConfig.getPackName).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(newPackDir, "codeql-pack.yml"), "utf8")),
|
||||
@@ -195,9 +211,7 @@ describe("pickExtensionPack", () => {
|
||||
it("creates a new extension pack when absolute custom pack location is set in config", async () => {
|
||||
const packLocation = join(Uri.file(tmpDir).fsPath, "java/ql/lib");
|
||||
|
||||
const modelConfig = mockedObject<ModelConfig>({
|
||||
getPackLocation: jest.fn().mockReturnValue(packLocation),
|
||||
});
|
||||
getPackLocation.mockReturnValue(packLocation);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
@@ -246,11 +260,7 @@ describe("pickExtensionPack", () => {
|
||||
it("creates a new extension pack when relative custom pack location is set in config", async () => {
|
||||
const packLocation = join(Uri.file(tmpDir).fsPath, "java/ql/lib");
|
||||
|
||||
const modelConfig = mockedObject<ModelConfig>({
|
||||
getPackLocation: jest
|
||||
.fn()
|
||||
.mockImplementation((language) => `${language}/ql/lib`),
|
||||
});
|
||||
getPackLocation.mockImplementation((language) => `${language}/ql/lib`);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
@@ -296,6 +306,120 @@ describe("pickExtensionPack", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack when valid custom pack name is set in config", async () => {
|
||||
const packName = "codeql/java-extensions";
|
||||
const packLocation = join(
|
||||
Uri.file(tmpDir).fsPath,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
"vscode-codeql-java",
|
||||
);
|
||||
|
||||
getPackName.mockImplementation(
|
||||
(language) => `codeql/${language}-extensions`,
|
||||
);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
expect(
|
||||
await pickExtensionPack(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
path: packLocation,
|
||||
yamlPath: join(packLocation, "codeql-pack.yml"),
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
language: "java",
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(packLocation, "codeql-pack.yml"), "utf8")),
|
||||
).toEqual({
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack when invalid custom pack name is set in config", async () => {
|
||||
const packName = "pack/java-extensions";
|
||||
const packLocation = join(
|
||||
Uri.file(tmpDir).fsPath,
|
||||
".github",
|
||||
"codeql",
|
||||
"extensions",
|
||||
"vscode-codeql-java",
|
||||
);
|
||||
|
||||
getPackName.mockImplementation((language) => `${language} Extensions`);
|
||||
|
||||
const cliServer = mockCliServer({});
|
||||
|
||||
expect(
|
||||
await pickExtensionPack(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
path: packLocation,
|
||||
yamlPath: join(packLocation, "codeql-pack.yml"),
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
language: "java",
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(modelConfig.getPackLocation).toHaveBeenCalledWith("java", {
|
||||
database: "github/vscode-codeql",
|
||||
language: "java",
|
||||
name: "vscode-codeql",
|
||||
owner: "github",
|
||||
});
|
||||
|
||||
expect(
|
||||
loadYaml(await readFile(join(packLocation, "codeql-pack.yml"), "utf8")),
|
||||
).toEqual({
|
||||
name: packName,
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new extension pack with non-github origin database", async () => {
|
||||
const databaseItem: Pick<DatabaseItem, "name" | "language" | "origin"> = {
|
||||
name: "vscode-codeql",
|
||||
|
||||
@@ -138,10 +138,7 @@ describe("runGenerateQueries", () => {
|
||||
createMockLogger(),
|
||||
{
|
||||
mode: Mode.Framework,
|
||||
config: {
|
||||
...defaultModelConfig,
|
||||
showTypeModels: true,
|
||||
},
|
||||
config: defaultModelConfig,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -4,7 +4,10 @@ import { MethodModelingViewProvider } from "../../../../../src/model-editor/meth
|
||||
import { createMockApp } from "../../../../__mocks__/appMock";
|
||||
import { createMockModelingStore } from "../../../../__mocks__/model-editor/modelingStoreMock";
|
||||
import { mockedObject } from "../../../../mocked-object";
|
||||
import type { FromMethodModelingMessage } from "../../../../../src/common/interface-types";
|
||||
import type {
|
||||
FromMethodModelingMessage,
|
||||
ToMethodModelingMessage,
|
||||
} from "../../../../../src/common/interface-types";
|
||||
import { DisposableObject } from "../../../../../src/common/disposable-object";
|
||||
import { ModelingEvents } from "../../../../../src/model-editor/modeling-events";
|
||||
import type {
|
||||
@@ -17,6 +20,7 @@ import {
|
||||
createMethod,
|
||||
createUsage,
|
||||
} from "../../../../factories/model-editor/method-factories";
|
||||
import { QueryLanguage } from "../../../../../src/common/query-language";
|
||||
|
||||
describe("method modeling view provider", () => {
|
||||
// Modeling store
|
||||
@@ -45,7 +49,7 @@ describe("method modeling view provider", () => {
|
||||
const modelingEvents = new ModelingEvents(app);
|
||||
|
||||
const modelConfigListener = mockedObject<ModelConfigListener>({
|
||||
showTypeModels: true,
|
||||
flowGeneration: true,
|
||||
onDidChangeConfiguration: jest.fn(),
|
||||
});
|
||||
|
||||
@@ -91,10 +95,10 @@ describe("method modeling view provider", () => {
|
||||
viewState: {
|
||||
language: undefined,
|
||||
modelConfig: {
|
||||
showTypeModels: true,
|
||||
flowGeneration: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
} satisfies ToMethodModelingMessage);
|
||||
});
|
||||
|
||||
it("should load webview when active DB but no selected method", async () => {
|
||||
@@ -116,10 +120,10 @@ describe("method modeling view provider", () => {
|
||||
viewState: {
|
||||
language: undefined,
|
||||
modelConfig: {
|
||||
showTypeModels: true,
|
||||
flowGeneration: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
} satisfies ToMethodModelingMessage);
|
||||
expect(postMessage).toHaveBeenNthCalledWith(2, {
|
||||
t: "setInModelingMode",
|
||||
inModelingMode: true,
|
||||
@@ -127,12 +131,12 @@ describe("method modeling view provider", () => {
|
||||
expect(postMessage).toHaveBeenNthCalledWith(3, {
|
||||
t: "setMethodModelingPanelViewState",
|
||||
viewState: {
|
||||
language: "java",
|
||||
language: QueryLanguage.Java,
|
||||
modelConfig: {
|
||||
showTypeModels: true,
|
||||
flowGeneration: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
} satisfies ToMethodModelingMessage);
|
||||
});
|
||||
|
||||
it("should load webview when active DB and a selected method", async () => {
|
||||
@@ -165,10 +169,10 @@ describe("method modeling view provider", () => {
|
||||
viewState: {
|
||||
language: undefined,
|
||||
modelConfig: {
|
||||
showTypeModels: true,
|
||||
flowGeneration: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
} satisfies ToMethodModelingMessage);
|
||||
expect(postMessage).toHaveBeenNthCalledWith(2, {
|
||||
t: "setInModelingMode",
|
||||
inModelingMode: true,
|
||||
@@ -176,12 +180,12 @@ describe("method modeling view provider", () => {
|
||||
expect(postMessage).toHaveBeenNthCalledWith(3, {
|
||||
t: "setMethodModelingPanelViewState",
|
||||
viewState: {
|
||||
language: "java",
|
||||
language: QueryLanguage.Java,
|
||||
modelConfig: {
|
||||
showTypeModels: true,
|
||||
flowGeneration: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
} satisfies ToMethodModelingMessage);
|
||||
expect(postMessage).toHaveBeenNthCalledWith(4, {
|
||||
t: "setSelectedMethod",
|
||||
method: selectedMethodDetails.method,
|
||||
|
||||
Reference in New Issue
Block a user