Compare commits

...

43 Commits

Author SHA1 Message Date
Andrew Eisenberg
b2422216b5 Update changelog for v2.5.4 release
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
2021-09-02 11:51:35 -07:00
Andrew Eisenberg
71f374d797 Fix unit test and add new test
Test that old CLIs properly ignore the library packs.
2021-09-02 11:14:32 -07:00
Dave Bartolomeo
7e78a6bc5c Fix PR feedback 2021-09-02 11:14:32 -07:00
Dave Bartolomeo
a4532fdc61 Add changenote for AST viewer failure fix 2021-09-02 11:14:32 -07:00
Dave Bartolomeo
7c5135d7d0 Fix AST viewer for refactored language packs
Most of the languages have recently been refactored into separate library and query packs, with the contextual queries defined in the query pack. In the near future, these contextual queries will move to the library pack.

Current CLI releases throw an error in `codeql resolve queries` when the extension tries to search the library pack for contextual queries. This change makes two related fixes:

1. If the queries are not found in the library pack, it then scans the corresponding standard query pack as a fallback.
2. It detects the problematic combination of CLI and packs, and avoids scanning the library pack at all in those cases. If no queries are found in the problematic scenario, the error message instructs the user to upgrade to the latest CLI version, instead of claiming that the language simply doesn't support the contextual queries yet.

This change depends on CLI 2.6.1, which is being released soon, adding the `--allow-library-packs` option to `codeql resolve queries`. That PR is already open against the CLI.
2021-09-02 11:14:32 -07:00
shati-patel
cdd6738748 Try using a separate test query 2021-09-02 16:44:22 +01:00
shati-patel
6f16192865 Add test for resolveQueryByLanguage 2021-09-02 16:44:22 +01:00
shati-patel
8151739f87 Update syntax 2021-09-01 11:02:21 +01:00
shati-patel
72fc53ba9c Add "resolveLanguages" test 2021-09-01 11:02:21 +01:00
shati-patel
3e6ee01c4e Move findLanguage function into helpers.ts 2021-09-01 11:02:21 +01:00
Benjamin Muskalla
f6485dac95 Add changelog for sticky pagination controls 2021-08-26 08:14:25 -07:00
Benjamin Muskalla
48f15b5fc7 Stick result pagination to top 2021-08-26 08:14:25 -07:00
shati-patel
f856e3ac2c Address review comments 2021-08-25 09:27:37 +01:00
shati-patel
38a64017f2 New setting to specify number of paths per alert 2021-08-25 09:27:37 +01:00
Andrew Eisenberg
20b15b6e1d Add v2.6.0 to list of versions we use for integration testing 2021-08-24 21:03:27 -07:00
Edoardo Pirovano
e119218828 Update extensions/ql-vscode/CHANGELOG.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2021-08-23 21:30:30 +01:00
Edoardo Pirovano
f494988ba6 Address PR comments from @aeisenberg and @shati-patel 2021-08-23 21:30:30 +01:00
Edoardo Pirovano
2561db1721 Allow exporting of results for non-alert queries 2021-08-23 21:30:30 +01:00
Benjamin Muskalla
089b23f0aa Remove old changelog entry 2021-08-19 13:04:16 +01:00
Benjamin Muskalla
fbed7dd1ca Mention filename pattern in changelog 2021-08-19 13:04:16 +01:00
Benjamin Muskalla
06ef67f22d Add support for filename pattern in history view 2021-08-19 13:04:16 +01:00
shati-patel
3d647f68e1 Bump version to v1.5.4 2021-08-18 16:43:37 +01:00
shati-patel
6a36dc34cc v1.5.3
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
2021-08-18 16:29:41 +01:00
Shati Patel
b48aaeac7b Fix type for "remote repo list" setting 2021-08-18 15:33:26 +01:00
Edoardo Pirovano
2da1065027 PR Checks: Use version of codeql repo suitable for CLI version 2021-08-18 15:25:21 +01:00
shati-patel
3536124fbc Tweak the regex 2021-08-17 16:54:48 +01:00
shati-patel
10b4e08bf8 Validate user input for "owner/repo" 2021-08-17 16:54:48 +01:00
shati-patel
b1f426672c Add option to enter a single repo 2021-08-17 16:54:48 +01:00
shati-patel
087cae287f Add a new "remote repository lists" setting 2021-08-17 16:54:48 +01:00
Eric Kim
3d8032c9b7 Update Changelog 2021-08-17 08:28:25 -07:00
Eric Kim
6470238311 Adjust test-adapter to display diffs only for failing tests 2021-08-17 08:28:25 -07:00
Andrew Eisenberg
0093af8994 Update the CLI versions to run integration tests against 2021-08-09 15:00:01 -07:00
shati-patel
2bfcd119db Don't show empty list of DBs 2021-08-04 20:00:49 +01:00
shati-patel
5932bdba96 Address review comments
- Tweak return types + logging
- Update changelog
2021-08-04 20:00:49 +01:00
shati-patel
1afe6b56fa Autodetect language using "resolve queries"
Also use autodection in relevant places
- When running on multiple databases
- When running a remote query
2021-08-04 20:00:49 +01:00
Andrew Eisenberg
72776e8254 Update the CLI versions to run integration tests against 2021-07-26 19:24:10 +01:00
shati-patel
d2d1a09723 Update changelog 2021-07-16 09:34:45 +01:00
shati-patel
793b82333f Rename variable and tweak error display 2021-07-16 09:34:45 +01:00
shati-patel
b3abff3e88 Add some error handling 2021-07-16 09:34:45 +01:00
shati-patel
890549f9e7 Fix database selection 2021-07-16 09:34:45 +01:00
shati-patel
66825d6a37 Add command for running queries on multiple databases 2021-07-16 09:34:45 +01:00
Andrew Eisenberg
d42982ee4c Fix dependabot errors
Updates the package dependencies to avoid dependabot errors.

I updated the `@types/gulp` to avoid some typings errors that were
introduced by incompatible versions of `@types/undertaker`.

Also, I forced resolution on `"glob-parent": "~6.0.0"` that avoids
a vulnerability on earlier versions.

I did a smoke test of features that use glob, as well as running a few
queries. All looks good.
2021-07-15 20:03:48 -07:00
shati-patel
7df634f050 Bump version to v1.5.3 2021-07-13 18:50:52 +01:00
25 changed files with 1272 additions and 1096 deletions

View File

@@ -66,7 +66,7 @@ jobs:
run: |
LATEST=`gh api repos/dsp-testing/codeql-cli-nightlies/releases --jq '.[].tag_name' --method GET --raw-field 'per_page=1'`
echo "::set-output name=nightly-url::https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/$LATEST"
test:
name: Test
runs-on: ${{ matrix.os }}
@@ -151,7 +151,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
version: ['v2.2.6', 'v2.3.3', 'v2.4.6', 'v2.5.7', 'nightly']
version: ['v2.2.6', 'v2.3.3', 'v2.4.6', 'v2.5.9', 'v2.6.0', 'nightly']
env:
CLI_VERSION: ${{ matrix.version }}
NIGHTLY_URL: ${{ needs.find-nightly.outputs.url }}
@@ -177,10 +177,26 @@ jobs:
npm run build
shell: bash
- name: Decide on ref of CodeQL repo
id: choose-ref
shell: bash
run: |
if [[ "${{ matrix.version }}" == "nightly" ]]
then
REF="codeql-cli/latest"
elif [[ "${{ matrix.version }}" == "v2.2.6" || "${{ matrix.version }}" == "v2.3.3" ]]
then
REF="codeql-cli/v2.4.5"
else
REF="codeql-cli/${{ matrix.version }}"
fi
echo "::set-output name=ref::$REF"
- name: Checkout QL
uses: actions/checkout@v2
with:
repository: github/codeql
ref: ${{ steps.choose-ref.outputs.ref }}
path: codeql
- name: Run CLI tests (Linux)

View File

@@ -1,5 +1,19 @@
# CodeQL for Visual Studio Code: Changelog
## 1.5.4 - 02 September 2021
- Add support for filename pattern in history view. [#930](https://github.com/github/vscode-codeql/pull/930)
- Add an option _View Results (CSV)_ to view the results of a non-alert query. The existing options for alert queries have been renamed to _View Alerts_ to avoid confusion. [#929](https://github.com/github/vscode-codeql/pull/929)
- Allow users to specify the number of paths to display for each alert. [#931](https://github.com/github/vscode-codeql/pull/931)
- Adjust pagination controls in _CodeQL Query Results_ to always be visible [#936](https://github.com/github/vscode-codeql/pull/936)
- Fix bug where _View AST_ fails due to recent refactoring in the standard library and query packs. [#939](https://github.com/github/vscode-codeql/pull/939)
## 1.5.3 - 18 August 2021
- Add a command _CodeQL: Run Query on Multiple Databases_, which lets users select multiple databases to run a query on. [#898](https://github.com/github/vscode-codeql/pull/898)
- Autodetect what language a query targets. This refines the _CodeQL: Run Query on Multiple Databases_ command to only show relevant databases. [#915](https://github.com/github/vscode-codeql/pull/915)
- Adjust test log output to display diffs only when comparing failed test results with expected test results. [#920](https://github.com/github/vscode-codeql/pull/920)
## 1.5.2 - 13 July 2021
- Add the _Add Database Source to Workspace_ command to the right-click context menu in the databases view. This lets users re-add a database's source folder to the workspace and browse the source code. [#891](https://github.com/github/vscode-codeql/pull/891)

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code",
"author": "GitHub",
"private": true,
"version": "1.5.2",
"version": "1.5.4",
"publisher": "GitHub",
"license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -180,6 +180,13 @@
"default": false,
"description": "Enable debug logging and tuple counting when running CodeQL queries. This information is useful for debugging query performance."
},
"codeQL.runningQueries.maxPaths": {
"type": "integer",
"default": 4,
"minimum": 1,
"maximum": 256,
"markdownDescription": "Max number of paths to display for each alert found by a path query (`@kind path-problem`)."
},
"codeQL.runningQueries.autoSave": {
"type": "boolean",
"default": false,
@@ -206,7 +213,7 @@
"codeQL.queryHistory.format": {
"type": "string",
"default": "%q on %d - %s, %r result count [%t]",
"description": "Default string for how to label query history items. %t is the time of the query, %q is the query name, %d is the database name, %r is the number of results, and %s is a status string."
"markdownDescription": "Default string for how to label query history items.\n* %t is the time of the query\n* %q is the human-readable query name\n*%f is the query file name\n* %d is the database name\n* %r is the number of results\n* %s is a status string"
},
"codeQL.runningTests.additionalTestArguments": {
"scope": "window",
@@ -233,6 +240,22 @@
"default": false,
"scope": "application",
"description": "Specifies whether or not to write telemetry events to the extension log."
},
"codeQL.remoteRepositoryLists": {
"type": [
"object",
null
],
"patternProperties": {
".*": {
"type": "array",
"items": {
"type": "string"
}
}
},
"default": null,
"markdownDescription": "[For internal use only] Lists of GitHub repositories that you want to query remotely. This should be a JSON object where each key is a user-specified name for this repository list, and the value is an array of GitHub repositories (of the form `<owner>/<repo>`)."
}
}
},
@@ -245,6 +268,10 @@
"command": "codeQL.runQuery",
"title": "CodeQL: Run Query"
},
{
"command": "codeQL.runQueryOnMultipleDatabases",
"title": "CodeQL: Run Query on Multiple Databases"
},
{
"command": "codeQL.runRemoteQuery",
"title": "CodeQL: Run Remote Query"
@@ -446,8 +473,12 @@
"title": "View Results (CSV)"
},
{
"command": "codeQLQueryHistory.viewSarifResults",
"title": "View Results (SARIF)"
"command": "codeQLQueryHistory.viewCsvAlerts",
"title": "View Alerts (CSV)"
},
{
"command": "codeQLQueryHistory.viewSarifAlerts",
"title": "View Alerts (SARIF)"
},
{
"command": "codeQLQueryHistory.viewDil",
@@ -626,10 +657,15 @@
{
"command": "codeQLQueryHistory.viewCsvResults",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem != interpretedResultsItem"
},
{
"command": "codeQLQueryHistory.viewCsvAlerts",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem == interpretedResultsItem"
},
{
"command": "codeQLQueryHistory.viewSarifResults",
"command": "codeQLQueryHistory.viewSarifAlerts",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem == interpretedResultsItem"
},
@@ -680,6 +716,10 @@
"command": "codeQL.runQuery",
"when": "resourceLangId == ql && resourceExtname == .ql"
},
{
"command": "codeQL.runQueryOnMultipleDatabases",
"when": "resourceLangId == ql && resourceExtname == .ql"
},
{
"command": "codeQL.runRemoteQuery",
"when": "config.codeQL.canary && editorLangId == ql && resourceExtname == .ql"
@@ -781,7 +821,11 @@
"when": "false"
},
{
"command": "codeQLQueryHistory.viewSarifResults",
"command": "codeQLQueryHistory.viewCsvAlerts",
"when": "false"
},
{
"command": "codeQLQueryHistory.viewSarifAlerts",
"when": "false"
},
{
@@ -830,6 +874,10 @@
"command": "codeQL.runQuery",
"when": "editorLangId == ql && resourceExtname == .ql"
},
{
"command": "codeQL.runQueryOnMultipleDatabases",
"when": "editorLangId == ql && resourceExtname == .ql"
},
{
"command": "codeQL.runRemoteQuery",
"when": "config.codeQL.canary && editorLangId == ql && resourceExtname == .ql"
@@ -933,7 +981,7 @@
"@types/fs-extra": "^9.0.6",
"@types/glob": "^7.1.1",
"@types/google-protobuf": "^3.2.7",
"@types/gulp": "^4.0.6",
"@types/gulp": "^4.0.9",
"@types/gulp-replace": "0.0.31",
"@types/gulp-sourcemaps": "0.0.32",
"@types/js-yaml": "^3.12.5",
@@ -1004,5 +1052,8 @@
"tsfmt -r",
"eslint --fix"
]
},
"resolutions": {
"glob-parent": "~6.0.0"
}
}

View File

@@ -8,7 +8,7 @@ import { Readable } from 'stream';
import { StringDecoder } from 'string_decoder';
import * as tk from 'tree-kill';
import { promisify } from 'util';
import { CancellationToken, Disposable } from 'vscode';
import { CancellationToken, Disposable, Uri } from 'vscode';
import { BQRSInfo, DecodedBqrsChunk } from './pure/bqrs-cli-types';
import { CliConfig } from './config';
@@ -43,6 +43,16 @@ export interface QuerySetup {
compilationCache?: string;
}
/**
* The expected output of `codeql resolve queries --format bylanguage`.
*/
export interface QueryInfoByLanguage {
// Using `unknown` as a placeholder. For now, the value is only ever an empty object.
byLanguage: Record<string, Record<string, unknown>>;
noDeclaredLanguage: Record<string, unknown>;
multipleDeclaredLanguages: Record<string, unknown>;
}
/**
* The expected output of `codeql resolve database`.
*/
@@ -71,6 +81,11 @@ export interface UpgradesInfo {
*/
export type QlpacksInfo = { [name: string]: string[] };
/**
* The expected output of `codeql resolve languages`.
*/
export type LanguagesInfo = { [name: string]: string[] };
/**
* The expected output of `codeql resolve qlref`.
*/
@@ -108,6 +123,7 @@ export interface TestCompleted {
expected: string;
diff: string[] | undefined;
failureDescription?: string;
failureStage?: string;
}
/**
@@ -482,6 +498,20 @@ export class CodeQLCliServer implements Disposable {
return await this.runJsonCodeQlCliCommand<QuerySetup>(['resolve', 'library-path'], subcommandArgs, 'Resolving library paths');
}
/**
* Resolves the language for a query.
* @param queryUri The URI of the query
*/
async resolveQueryByLanguage(workspaces: string[], queryUri: Uri): Promise<QueryInfoByLanguage> {
const subcommandArgs = [
'--format', 'bylanguage',
queryUri.fsPath,
'--additional-packs',
workspaces.join(path.delimiter)
];
return JSON.parse(await this.runCodeQlCliCommand(['resolve', 'queries'], subcommandArgs, 'Resolving query by language'));
}
/**
* Finds all available QL tests in a given directory.
* @param testPath Root of directory tree to search for tests.
@@ -619,6 +649,11 @@ export class CodeQLCliServer implements Disposable {
this.cliConfig.numberThreads.toString(),
);
args.push(
'--max-paths',
this.cliConfig.maxPaths.toString(),
);
args.push(resultsPath);
await this.runCodeQlCliCommand(['bqrs', 'interpret'], args, 'Interpreting query results');
}
@@ -724,6 +759,14 @@ export class CodeQLCliServer implements Disposable {
);
}
/**
* Gets information about the available languages.
* @returns A dictionary mapping language name to the directory it comes from
*/
async resolveLanguages(): Promise<LanguagesInfo> {
return await this.runJsonCodeQlCliCommand<LanguagesInfo>(['resolve', 'languages'], [], 'Resolving languages');
}
/**
* Gets information about queries in a query suite.
* @param suite The suite to resolve.
@@ -732,11 +775,15 @@ export class CodeQLCliServer implements Disposable {
* the default CLI search path is used.
* @returns A list of query files found.
*/
resolveQueriesInSuite(suite: string, additionalPacks: string[], searchPath?: string[]): Promise<string[]> {
async resolveQueriesInSuite(suite: string, additionalPacks: string[], searchPath?: string[]): Promise<string[]> {
const args = ['--additional-packs', additionalPacks.join(path.delimiter)];
if (searchPath !== undefined) {
args.push('--search-path', path.join(...searchPath));
}
if (await this.cliConstraints.supportsAllowLibraryPacksInResolveQueries()) {
// All of our usage of `codeql resolve queries` needs to handle library packs.
args.push('--allow-library-packs');
}
args.push(suite);
return this.runJsonCodeQlCliCommand<string[]>(
['resolve', 'queries'],
@@ -1021,6 +1068,12 @@ export class CliVersionConstraint {
*/
public static CLI_VERSION_WITH_DB_REGISTRATION = new SemVer('2.4.1');
/**
* CLI version where the `--allow-library-packs` option to `codeql resolve queries` was
* introduced.
*/
public static CLI_VERSION_WITH_ALLOW_LIBRARY_PACKS_IN_RESOLVE_QUERIES = new SemVer('2.6.1');
constructor(private readonly cli: CodeQLCliServer) {
/**/
}
@@ -1045,6 +1098,10 @@ export class CliVersionConstraint {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_RESOLVE_QLREF);
}
public async supportsAllowLibraryPacksInResolveQueries() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_ALLOW_LIBRARY_PACKS_IN_RESOLVE_QUERIES);
}
async supportsDatabaseRegistration() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_DB_REGISTRATION);
}

View File

@@ -79,6 +79,7 @@ const CACHE_SIZE_SETTING = new Setting('cacheSize', RUNNING_QUERIES_SETTING);
const TIMEOUT_SETTING = new Setting('timeout', RUNNING_QUERIES_SETTING);
const MEMORY_SETTING = new Setting('memory', RUNNING_QUERIES_SETTING);
const DEBUG_SETTING = new Setting('debug', RUNNING_QUERIES_SETTING);
const MAX_PATHS = new Setting('maxPaths', RUNNING_QUERIES_SETTING);
const RUNNING_TESTS_SETTING = new Setting('runningTests', ROOT_SETTING);
const RESULTS_DISPLAY_SETTING = new Setting('resultsDisplay', ROOT_SETTING);
@@ -112,12 +113,13 @@ export interface QueryHistoryConfig {
onDidChangeConfiguration: Event<void>;
}
const CLI_SETTINGS = [ADDITIONAL_TEST_ARGUMENTS_SETTING, NUMBER_OF_TEST_THREADS_SETTING, NUMBER_OF_THREADS_SETTING];
const CLI_SETTINGS = [ADDITIONAL_TEST_ARGUMENTS_SETTING, NUMBER_OF_TEST_THREADS_SETTING, NUMBER_OF_THREADS_SETTING, MAX_PATHS];
export interface CliConfig {
additionalTestArguments: string[];
numberTestThreads: number;
numberThreads: number;
maxPaths: number;
onDidChangeConfiguration?: Event<void>;
}
@@ -264,6 +266,10 @@ export class CliConfigListener extends ConfigListener implements CliConfig {
return NUMBER_OF_THREADS_SETTING.getValue<number>();
}
public get maxPaths(): number {
return MAX_PATHS.getValue<number>();
}
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
this.handleDidChangeConfigurationForRelevantSettings(CLI_SETTINGS, e);
}
@@ -291,3 +297,16 @@ export function isCanary() {
* Avoids caching in the AST viewer if the user is also a canary user.
*/
export const NO_CACHE_AST_VIEWER = new Setting('disableCache', AST_VIEWER_SETTING);
/**
* Lists of GitHub repositories that you want to query remotely via the "Run Remote query" command.
* Note: This command is only available for internal users.
*
* This setting should be a JSON object where each key is a user-specified name (string),
* and the value is an array of GitHub repositories (of the form `<owner>/<repo>`).
*/
const REMOTE_REPO_LISTS = new Setting('remoteRepositoryLists', ROOT_SETTING);
export function getRemoteRepositoryLists(): Record<string, string[]> | undefined {
return REMOTE_REPO_LISTS.getValue<Record<string, string[]>>() || undefined;
}

View File

@@ -12,7 +12,7 @@ import { ProgressCallback } from '../commandRunner';
import { KeyType } from './keyType';
import { qlpackOfDatabase, resolveQueries } from './queryResolver';
const SELECT_QUERY_NAME = '#select';
export const SELECT_QUERY_NAME = '#select';
export const TEMPLATE_NAME = 'selectedSourceFile';
export interface FullLocationLink extends vscode.LocationLink {

View File

@@ -11,8 +11,9 @@ import {
} from './keyType';
import { CodeQLCliServer } from '../cli';
import { DatabaseItem } from '../databases';
import { QlPacksForLanguage } from '../helpers';
export async function qlpackOfDatabase(cli: CodeQLCliServer, db: DatabaseItem): Promise<string> {
export async function qlpackOfDatabase(cli: CodeQLCliServer, db: DatabaseItem): Promise<QlPacksForLanguage> {
if (db.contents === undefined) {
throw new Error('Database is invalid and cannot infer QLPack.');
}
@@ -21,28 +22,87 @@ export async function qlpackOfDatabase(cli: CodeQLCliServer, db: DatabaseItem):
return await helpers.getQlPackForDbscheme(cli, dbscheme);
}
/**
* Finds the contextual queries with the specified key in a list of CodeQL packs.
*
* @param cli The CLI instance to use.
* @param qlpacks The list of packs to search.
* @param keyType The contextual query key of the query to search for.
* @returns The found queries from the first pack in which any matching queries were found.
*/
async function resolveQueriesFromPacks(cli: CodeQLCliServer, qlpacks: string[], keyType: KeyType): Promise<string[]> {
for (const qlpack of qlpacks) {
const suiteFile = (await tmp.file({
postfix: '.qls'
})).path;
const suiteYaml = {
qlpack,
include: {
kind: kindOfKeyType(keyType),
'tags contain': tagOfKeyType(keyType)
}
};
await fs.writeFile(suiteFile, yaml.safeDump(suiteYaml), 'utf8');
export async function resolveQueries(cli: CodeQLCliServer, qlpack: string, keyType: KeyType): Promise<string[]> {
const suiteFile = (await tmp.file({
postfix: '.qls'
})).path;
const suiteYaml = {
qlpack,
include: {
kind: kindOfKeyType(keyType),
'tags contain': tagOfKeyType(keyType)
const queries = await cli.resolveQueriesInSuite(suiteFile, helpers.getOnDiskWorkspaceFolders());
if (queries.length > 0) {
return queries;
}
};
await fs.writeFile(suiteFile, yaml.safeDump(suiteYaml), 'utf8');
const queries = await cli.resolveQueriesInSuite(suiteFile, helpers.getOnDiskWorkspaceFolders());
if (queries.length === 0) {
void helpers.showAndLogErrorMessage(
`No ${nameOfKeyType(keyType)} queries (tagged "${tagOfKeyType(keyType)}") could be found in the current library path. \
Try upgrading the CodeQL libraries. If that doesn't work, then ${nameOfKeyType(keyType)} queries are not yet available \
for this language.`
);
throw new Error(`Couldn't find any queries tagged ${tagOfKeyType(keyType)} for qlpack ${qlpack}`);
}
return queries;
return [];
}
export async function resolveQueries(cli: CodeQLCliServer, qlpacks: QlPacksForLanguage, keyType: KeyType): Promise<string[]> {
const cliCanHandleLibraryPack = await cli.cliConstraints.supportsAllowLibraryPacksInResolveQueries();
const packsToSearch: string[] = [];
let blameCli: boolean;
if (cliCanHandleLibraryPack) {
// The CLI can handle both library packs and query packs, so search both packs in order.
packsToSearch.push(qlpacks.dbschemePack);
if (qlpacks.queryPack !== undefined) {
packsToSearch.push(qlpacks.queryPack);
}
// If we don't find the query, it's because it's not there, not because the CLI was unable to
// search the pack.
blameCli = false;
} else {
// Older CLIs can't handle `codeql resolve queries` with a suite that references a library pack.
if (qlpacks.dbschemePackIsLibraryPack) {
if (qlpacks.queryPack !== undefined) {
// Just search the query pack, because some older library/query releases still had the
// contextual queries in the query pack.
packsToSearch.push(qlpacks.queryPack);
}
// If we don't find it, it's because the CLI was unable to search the library pack that
// actually contains the query. Blame any failure on the CLI, not the packs.
blameCli = true;
} else {
// We have an old CLI, but the dbscheme pack is old enough that it's still a unified pack with
// both libraries and queries. Just search that pack.
packsToSearch.push(qlpacks.dbschemePack);
// Any CLI should be able to search the single query pack, so if we don't find it, it's
// because the language doesn't support it.
blameCli = false;
}
}
const queries = await resolveQueriesFromPacks(cli, packsToSearch, keyType);
if (queries.length > 0) {
return queries;
}
// No queries found. Determine the correct error message for the various scenarios.
const errorMessage = blameCli ?
`Your current version of the CodeQL CLI, '${(await cli.getVersion()).version}', \
is unable to use contextual queries from recent versions of the standard CodeQL libraries. \
Please upgrade to the latest version of the CodeQL CLI.`
:
`No ${nameOfKeyType(keyType)} queries (tagged "${tagOfKeyType(keyType)}") could be found in the current library path. \
Try upgrading the CodeQL libraries. If that doesn't work, then ${nameOfKeyType(keyType)} queries are not yet available \
for this language.`;
void helpers.showAndLogErrorMessage(errorMessage);
throw new Error(`Couldn't find any queries tagged ${tagOfKeyType(keyType)} in any of the following packs: ${packsToSearch.join(', ')}.`);
}

View File

@@ -175,8 +175,8 @@ export class TemplatePrintAstProvider {
throw new Error('Can\'t infer database from the provided source.');
}
const qlpack = await qlpackOfDatabase(this.cli, db);
const queries = await resolveQueries(this.cli, qlpack, KeyType.PrintAstQuery);
const qlpacks = await qlpackOfDatabase(this.cli, db);
const queries = await resolveQueries(this.cli, qlpacks, KeyType.PrintAstQuery);
if (queries.length > 1) {
throw new Error('Found multiple Print AST queries. Can\'t continue');
}

View File

@@ -10,7 +10,8 @@ import {
Uri,
window as Window,
env,
window
window,
QuickPickItem
} from 'vscode';
import { LanguageClient } from 'vscode-languageclient';
import * as os from 'os';
@@ -29,7 +30,7 @@ import {
QueryServerConfigListener
} from './config';
import * as languageSupport from './languageSupport';
import { DatabaseManager } from './databases';
import { DatabaseItem, DatabaseManager } from './databases';
import { DatabaseUI } from './databases-ui';
import {
TemplateQueryDefinitionProvider,
@@ -72,7 +73,7 @@ import {
import { CodeQlStatusBarHandler } from './status-bar';
import { Credentials } from './authentication';
import runRemoteQuery from './run-remote-query';
import { runRemoteQuery } from './run-remote-query';
/**
* extension.ts
@@ -467,16 +468,18 @@ async function activateWithInstalledDistribution(
selectedQuery: Uri | undefined,
progress: ProgressCallback,
token: CancellationToken,
databaseItem: DatabaseItem | undefined,
): Promise<void> {
if (qs !== undefined) {
const dbItem = await databaseUI.getDatabaseItem(progress, token);
if (dbItem === undefined) {
// If no databaseItem is specified, use the database currently selected in the Databases UI
databaseItem = databaseItem || await databaseUI.getDatabaseItem(progress, token);
if (databaseItem === undefined) {
throw new Error('Can\'t run query without a selected database');
}
const info = await compileAndRunQueryAgainstDatabase(
cliServer,
qs,
dbItem,
databaseItem,
quickEval,
selectedQuery,
progress,
@@ -549,13 +552,80 @@ async function activateWithInstalledDistribution(
progress: ProgressCallback,
token: CancellationToken,
uri: Uri | undefined
) => await compileAndRunQuery(false, uri, progress, token),
) => await compileAndRunQuery(false, uri, progress, token, undefined),
{
title: 'Running query',
cancellable: true
}
)
);
interface DatabaseQuickPickItem extends QuickPickItem {
databaseItem: DatabaseItem;
}
ctx.subscriptions.push(
commandRunnerWithProgress(
'codeQL.runQueryOnMultipleDatabases',
async (
progress: ProgressCallback,
token: CancellationToken,
uri: Uri | undefined
) => {
let filteredDBs = dbm.databaseItems;
if (filteredDBs.length === 0) {
void helpers.showAndLogErrorMessage('No databases found. Please add a suitable database to your workspace.');
return;
}
// If possible, only show databases with the right language (otherwise show all databases).
const queryLanguage = await helpers.findLanguage(cliServer, uri);
if (queryLanguage) {
filteredDBs = dbm.databaseItems.filter(db => db.language === queryLanguage);
if (filteredDBs.length === 0) {
void helpers.showAndLogErrorMessage(`No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`);
return;
}
}
const quickPickItems = filteredDBs.map<DatabaseQuickPickItem>(dbItem => (
{
databaseItem: dbItem,
label: dbItem.name,
description: dbItem.language,
}
));
/**
* Databases that were selected in the quick pick menu.
*/
const quickpick = await window.showQuickPick<DatabaseQuickPickItem>(
quickPickItems,
{ canPickMany: true, ignoreFocusOut: true }
);
if (quickpick !== undefined) {
// Collect all skipped databases and display them at the end (instead of popping up individual errors)
const skippedDatabases = [];
const errors = [];
for (const item of quickpick) {
try {
await compileAndRunQuery(false, uri, progress, token, item.databaseItem);
} catch (error) {
skippedDatabases.push(item.label);
errors.push(error.message);
}
}
if (skippedDatabases.length > 0) {
void logger.log(`Errors:\n${errors.join('\n')}`);
void helpers.showAndLogWarningMessage(
`The following databases were skipped:\n${skippedDatabases.join('\n')}.\nFor details about the errors, see the logs.`
);
}
} else {
void helpers.showAndLogErrorMessage('No databases selected.');
}
},
{
title: 'Running query on selected databases',
cancellable: true
}
)
);
ctx.subscriptions.push(
commandRunnerWithProgress(
'codeQL.runQueries',
@@ -611,7 +681,7 @@ async function activateWithInstalledDistribution(
});
await Promise.all(queryUris.map(async uri =>
compileAndRunQuery(false, uri, wrappedProgress, token)
compileAndRunQuery(false, uri, wrappedProgress, token, undefined)
.then(() => queriesRemaining--)
));
},
@@ -627,7 +697,7 @@ async function activateWithInstalledDistribution(
progress: ProgressCallback,
token: CancellationToken,
uri: Uri | undefined
) => await compileAndRunQuery(true, uri, progress, token),
) => await compileAndRunQuery(true, uri, progress, token, undefined),
{
title: 'Running query',
cancellable: true
@@ -651,7 +721,7 @@ async function activateWithInstalledDistribution(
) => {
if (isCanary()) {
const credentials = await Credentials.initialize(ctx);
await runRemoteQuery(credentials, uri || window.activeTextEditor?.document.uri);
await runRemoteQuery(cliServer, credentials, uri || window.activeTextEditor?.document.uri);
}
})
);

View File

@@ -9,7 +9,7 @@ import {
workspace,
env
} from 'vscode';
import { CodeQLCliServer } from './cli';
import { CodeQLCliServer, QlpacksInfo } from './cli';
import { logger } from './logging';
/**
@@ -254,9 +254,55 @@ function createRateLimitedResult(): RateLimitedResult {
};
}
export async function getQlPackForDbscheme(cliServer: CodeQLCliServer, dbschemePath: string): Promise<string> {
export interface QlPacksForLanguage {
/** The name of the pack containing the dbscheme. */
dbschemePack: string;
/** `true` if `dbschemePack` is a library pack. */
dbschemePackIsLibraryPack: boolean;
/**
* The name of the corresponding standard query pack.
* Only defined if `dbschemePack` is a library pack.
*/
queryPack?: string;
}
interface QlPackWithPath {
packName: string;
packDir: string | undefined;
}
async function findDbschemePack(packs: QlPackWithPath[], dbschemePath: string): Promise<{ name: string; isLibraryPack: boolean; }> {
for (const { packDir, packName } of packs) {
if (packDir !== undefined) {
const qlpack = yaml.safeLoad(await fs.readFile(path.join(packDir, 'qlpack.yml'), 'utf8')) as { dbscheme?: string; library?: boolean; };
if (qlpack.dbscheme !== undefined && path.basename(qlpack.dbscheme) === path.basename(dbschemePath)) {
return {
name: packName,
isLibraryPack: qlpack.library === true
};
}
}
}
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
}
function findStandardQueryPack(qlpacks: QlpacksInfo, dbschemePackName: string): string | undefined {
const matches = dbschemePackName.match(/^codeql\/(?<language>[a-z]+)-all$/);
if (matches) {
const queryPackName = `codeql/${matches.groups!.language}-queries`;
if (qlpacks[queryPackName] !== undefined) {
return queryPackName;
}
}
// Either the dbscheme pack didn't look like one where the queries might be in the query pack, or
// no query pack was found in the search path. Either is OK.
return undefined;
}
export async function getQlPackForDbscheme(cliServer: CodeQLCliServer, dbschemePath: string): Promise<QlPacksForLanguage> {
const qlpacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders());
const packs: { packDir: string | undefined; packName: string }[] =
const packs: QlPackWithPath[] =
Object.entries(qlpacks).map(([packName, dirs]) => {
if (dirs.length < 1) {
void logger.log(`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`);
@@ -270,15 +316,13 @@ export async function getQlPackForDbscheme(cliServer: CodeQLCliServer, dbschemeP
packDir: dirs[0]
};
});
for (const { packDir, packName } of packs) {
if (packDir !== undefined) {
const qlpack = yaml.safeLoad(await fs.readFile(path.join(packDir, 'qlpack.yml'), 'utf8')) as { dbscheme: string };
if (qlpack.dbscheme !== undefined && path.basename(qlpack.dbscheme) === path.basename(dbschemePath)) {
return packName;
}
}
}
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
const dbschemePack = await findDbschemePack(packs, dbschemePath);
const queryPack = dbschemePack.isLibraryPack ? findStandardQueryPack(qlpacks, dbschemePack.name) : undefined;
return {
dbschemePack: dbschemePack.name,
dbschemePackIsLibraryPack: dbschemePack.isLibraryPack,
queryPack
};
}
export async function getPrimaryDbscheme(datasetFolder: string): Promise<string> {
@@ -424,3 +468,34 @@ export async function isLikelyDatabaseRoot(maybeRoot: string) {
export function isLikelyDbLanguageFolder(dbPath: string) {
return !!path.basename(dbPath).startsWith('db-');
}
/**
* Finds the language that a query targets.
* If it can't be autodetected, prompt the user to specify the language manually.
*/
export async function findLanguage(
cliServer: CodeQLCliServer,
queryUri: Uri | undefined
): Promise<string | undefined> {
const uri = queryUri || Window.activeTextEditor?.document.uri;
if (uri !== undefined) {
try {
const queryInfo = await cliServer.resolveQueryByLanguage(getOnDiskWorkspaceFolders(), uri);
const language = (Object.keys(queryInfo.byLanguage))[0];
void logger.log(`Detected query language: ${language}`);
return language;
} catch (e) {
void logger.log('Could not autodetect query language. Select language manually.');
}
}
const availableLanguages = Object.keys(await cliServer.resolveLanguages());
const language = await Window.showQuickPick(
availableLanguages,
{ placeHolder: 'Select target language for your query', ignoreFocusOut: true }
);
if (!language) {
// This only happens if the user cancels the quick pick.
void showAndLogErrorMessage('Language not found. Language must be specified manually.');
}
return language;
}

View File

@@ -312,8 +312,14 @@ export class QueryHistoryManager extends DisposableObject {
);
this.push(
commandRunner(
'codeQLQueryHistory.viewSarifResults',
this.handleViewSarifResults.bind(this)
'codeQLQueryHistory.viewCsvAlerts',
this.handleViewCsvAlerts.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.viewSarifAlerts',
this.handleViewSarifAlerts.bind(this)
)
);
this.push(
@@ -550,7 +556,7 @@ export class QueryHistoryManager extends DisposableObject {
await vscode.window.showTextDocument(doc, { preview: false });
}
async handleViewSarifResults(
async handleViewSarifAlerts(
singleItem: CompletedQuery,
multiSelect: CompletedQuery[]
) {
@@ -578,6 +584,24 @@ export class QueryHistoryManager extends DisposableObject {
if (!this.assertSingleQuery(multiSelect)) {
return;
}
if (await singleItem.query.hasCsv()) {
void this.tryOpenExternalFile(singleItem.query.csvPath);
return;
}
await singleItem.query.exportCsvResults(this.qs, singleItem.query.csvPath, () => {
void this.tryOpenExternalFile(
singleItem.query.csvPath
);
});
}
async handleViewCsvAlerts(
singleItem: CompletedQuery,
multiSelect: CompletedQuery[]
) {
if (!this.assertSingleQuery(multiSelect)) {
return;
}
await this.tryOpenExternalFile(
await singleItem.query.ensureCsvProduced(this.qs)

View File

@@ -62,6 +62,9 @@ export class CompletedQuery implements QueryWithResults {
get queryName(): string {
return getQueryName(this.query);
}
get queryFileName(): string {
return getQueryFileName(this.query);
}
get statusString(): string {
switch (this.result.resultType) {
@@ -88,13 +91,14 @@ export class CompletedQuery implements QueryWithResults {
}
interpolate(template: string): string {
const { databaseName, queryName, time, resultCount, statusString } = this;
const { databaseName, queryName, time, resultCount, statusString, queryFileName } = this;
const replacements: { [k: string]: string } = {
t: time,
q: queryName,
d: databaseName,
r: resultCount.toString(),
s: statusString,
f: queryFileName,
'%': '%',
};
return template.replace(/%(.)/g, (match, key) => {
@@ -152,17 +156,28 @@ export class CompletedQuery implements QueryWithResults {
* Uses metadata if it exists, and defaults to the query file name.
*/
export function getQueryName(query: QueryInfo) {
if (query.quickEvalPosition !== undefined) {
return 'Quick evaluation of ' + getQueryFileName(query);
} else if (query.metadata?.name) {
return query.metadata.name;
} else {
return getQueryFileName(query);
}
}
/**
* Gets the file name for an evaluated query.
* Defaults to the query file name and may contain position information for quick eval queries.
*/
export function getQueryFileName(query: QueryInfo) {
// Queries run through quick evaluation are not usually the entire query file.
// Label them differently and include the line numbers.
if (query.quickEvalPosition !== undefined) {
const { line, endLine, fileName } = query.quickEvalPosition;
const lineInfo = line === endLine ? `${line}` : `${line}-${endLine}`;
return `Quick evaluation of ${path.basename(fileName)}:${lineInfo}`;
} else if (query.metadata?.name) {
return query.metadata.name;
} else {
return path.basename(query.program.queryPath);
return `${path.basename(fileName)}:${lineInfo}`;
}
return path.basename(query.program.queryPath);
}

View File

@@ -110,7 +110,7 @@ export async function displayQuickQuery(
const datasetFolder = await dbItem.getDatasetFolder(cliServer);
const dbscheme = await getPrimaryDbscheme(datasetFolder);
const qlpack = await getQlPackForDbscheme(cliServer, dbscheme);
const qlpack = (await getQlPackForDbscheme(cliServer, dbscheme)).dbschemePack;
const qlPackFile = path.join(queriesDir, 'qlpack.yml');
const qlFile = path.join(queriesDir, QUICK_QUERY_QUERY_NAME);
const shouldRewrite = await checkShouldRewrite(qlPackFile, qlpack);

View File

@@ -25,6 +25,8 @@ import * as qsClient from './queryserver-client';
import { isQuickQueryPath } from './quick-query';
import { compileDatabaseUpgradeSequence, hasNondestructiveUpgradeCapabilities, upgradeDatabaseExplicit } from './upgrades';
import { ensureMetadataIsComplete } from './query-results';
import { SELECT_QUERY_NAME } from './contextual/locationFinder';
import { DecodedBqrsChunk } from './pure/bqrs-cli-types';
/**
* run-queries.ts
@@ -216,6 +218,29 @@ export class QueryInfo {
return this.dilPath;
}
async exportCsvResults(qs: qsClient.QueryServerClient, csvPath: string, onFinish: () => void): Promise<void> {
let stopDecoding = false;
const out = fs.createWriteStream(csvPath);
out.on('finish', onFinish);
out.on('error', () => {
if (!stopDecoding) {
stopDecoding = true;
void showAndLogErrorMessage(`Failed to write CSV results to ${csvPath}`);
}
});
let nextOffset: number | undefined = 0;
while (nextOffset !== undefined && !stopDecoding) {
const chunk: DecodedBqrsChunk = await qs.cliServer.bqrsDecode(this.resultsPaths.resultsPath, SELECT_QUERY_NAME, {
pageSize: 100,
offset: nextOffset,
});
for (const tuple of chunk.tuples)
out.write(tuple.join(',') + '\n');
nextOffset = chunk.next;
}
out.end();
}
async ensureCsvProduced(qs: qsClient.QueryServerClient): Promise<string> {
if (await this.hasCsv()) {
return this.csvPath;
@@ -563,7 +588,7 @@ export async function compileAndRunQueryAgainstDatabase(
const dbSchemaName = path.basename(db.contents.dbSchemeUri.fsPath);
if (querySchemaName != dbSchemaName) {
void logger.log(`Query schema was ${querySchemaName}, but database schema was ${dbSchemaName}.`);
throw new Error(`The query ${path.basename(queryPath)} cannot be run against the selected database: their target languages are different. Please select a different database and try again.`);
throw new Error(`The query ${path.basename(queryPath)} cannot be run against the selected database (${db.name}): their target languages are different. Please select a different database and try again.`);
}
const qlProgram: messages.QlProgram = {

View File

@@ -1,20 +1,77 @@
import { Uri } from 'vscode';
import { QuickPickItem, Uri, window } from 'vscode';
import * as yaml from 'js-yaml';
import * as fs from 'fs-extra';
import { showAndLogErrorMessage, showAndLogInformationMessage } from './helpers';
import { findLanguage, showAndLogErrorMessage, showAndLogInformationMessage } from './helpers';
import { Credentials } from './authentication';
import * as cli from './cli';
import { logger } from './logging';
import { getRemoteRepositoryLists } from './config';
interface Config {
repositories: string[];
ref?: string;
language: string;
language?: string;
}
// Test "controller" repository and workflow.
const OWNER = 'dsp-testing';
const REPO = 'qc-controller';
export default async function runRemoteQuery(credentials: Credentials, uri?: Uri) {
interface RepoListQuickPickItem extends QuickPickItem {
repoList: string[];
}
/**
* Gets the repositories to run the query against.
*/
async function getRepositories(): Promise<string[] | undefined> {
const repoLists = getRemoteRepositoryLists();
if (repoLists && Object.keys(repoLists).length) {
const quickPickItems = Object.entries(repoLists).map<RepoListQuickPickItem>(([key, value]) => (
{
label: key, // the name of the repository list
repoList: value, // the actual array of repositories
}
));
const quickpick = await window.showQuickPick<RepoListQuickPickItem>(
quickPickItems,
{
placeHolder: 'Select a repository list. You can define repository lists in the `codeQL.remoteRepositoryLists` setting.',
ignoreFocusOut: true,
});
if (quickpick?.repoList.length) {
void logger.log(`Selected repositories: ${quickpick.repoList}`);
return quickpick.repoList;
} else {
void showAndLogErrorMessage('No repositories selected.');
return;
}
} else {
void logger.log('No repository lists defined. Displaying text input box.');
/**
* This regex matches strings of the form `owner/repo` where:
* - `owner` is made up of alphanumeric characters or single hyphens, starting and ending in an alphanumeric character
* - `repo` is made up of alphanumeric characters, hyphens, or underscores
*/
const repoRegex = /^(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+\/[a-zA-Z0-9-_]+$/;
const remoteRepo = await window.showInputBox({
title: 'Enter a GitHub repository in the format <owner>/<repo> (e.g. github/codeql)',
placeHolder: '<owner>/<repo>',
prompt: 'Tip: you can save frequently used repositories in the `codeql.remoteRepositoryLists` setting',
ignoreFocusOut: true,
});
if (!remoteRepo) {
void showAndLogErrorMessage('No repositories entered.');
return;
} else if (!repoRegex.test(remoteRepo)) { // Check if user entered invalid input
void showAndLogErrorMessage('Invalid repository format. Must be in the format <owner>/<repo> (e.g. github/codeql)');
return;
}
void logger.log(`Entered repository: ${remoteRepo}`);
return [remoteRepo];
}
}
export async function runRemoteQuery(cliServer: cli.CodeQLCliServer, credentials: Credentials, uri?: Uri) {
if (!uri?.fsPath.endsWith('.ql')) {
return;
}
@@ -26,16 +83,32 @@ export default async function runRemoteQuery(credentials: Credentials, uri?: Uri
const query = await fs.readFile(queryFile, 'utf8');
const repositoriesFile = queryFile.substring(0, queryFile.length - '.ql'.length) + '.repositories';
if (!(await fs.pathExists(repositoriesFile))) {
void showAndLogErrorMessage(`Missing file: '${repositoriesFile}' to specify the repositories to run against. This file must be a sibling of ${queryFile}.`);
return;
let ref: string | undefined;
let language: string | undefined;
let repositories: string[] | undefined;
// If the user has an explicit `.repositories` file, use that.
// Otherwise, prompt user to select repositories from the `codeQL.remoteRepositoryLists` setting.
if (await fs.pathExists(repositoriesFile)) {
void logger.log(`Found '${repositoriesFile}'. Using information from that file to run ${queryFile}.`);
const config = yaml.safeLoad(await fs.readFile(repositoriesFile, 'utf8')) as Config;
ref = config.ref || 'main';
language = config.language || await findLanguage(cliServer, uri);
repositories = config.repositories;
} else {
ref = 'main';
[language, repositories] = await Promise.all([findLanguage(cliServer, uri), getRepositories()]);
}
const config = yaml.safeLoad(await fs.readFile(repositoriesFile, 'utf8')) as Config;
if (!language) {
return; // No error message needed, since `findLanguage` already displays one.
}
const ref = config.ref || 'main';
const language = config.language;
const repositories = config.repositories;
if (!repositories || repositories.length === 0) {
return; // No error message needed, since `getRepositories` already displays one.
}
try {
await octokit.request(

View File

@@ -294,7 +294,9 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
: 'failed';
let message: string | undefined;
if (event.failureDescription || event.diff?.length) {
message = ['', `${state}: ${event.test}`, event.failureDescription || event.diff?.join('\n'), ''].join('\n');
message = event.failureStage === 'RESULT'
? ['', `${state}: ${event.test}`, event.failureDescription || event.diff?.join('\n'), ''].join('\n')
: ['', `${event.failureStage?.toLowerCase()} error: ${event.test}`, event.failureDescription || `${event.messages[0].severity}: ${event.messages[0].message}`, ''].join('\n');
void testLogger.log(message);
}
this._testStates.fire({

View File

@@ -15,6 +15,10 @@
display: flex;
padding: 0.5em 0;
align-items: center;
top: 0;
background-color: var(--vscode-editorGutter-background);
position: sticky;
z-index: 1;
}
.vscode-codeql__table-selection-header-item {

View File

@@ -0,0 +1,3 @@
import javascript
select 1

View File

@@ -1,8 +1,9 @@
import { expect } from 'chai';
import { extensions } from 'vscode';
import { extensions, Uri } from 'vscode';
import * as path from 'path';
import { SemVer } from 'semver';
import { CodeQLCliServer } from '../../cli';
import { CodeQLCliServer, QueryInfoByLanguage } from '../../cli';
import { CodeQLExtensionInterface } from '../../extension';
import { skipIfNoCodeQL } from '../ensureCli';
import { getOnDiskWorkspaceFolders } from '../../helpers';
@@ -52,4 +53,19 @@ describe('Use cli', function() {
expect(qlpacks['codeql-javascript']).not.to.be.undefined;
expect(qlpacks['codeql-python']).not.to.be.undefined;
});
it('should resolve languages', async function() {
skipIfNoCodeQL(this);
const languages = await cli.resolveLanguages();
for (const expectedLanguage of ['cpp', 'csharp', 'go', 'java', 'javascript', 'python']) {
expect(languages).to.have.property(expectedLanguage).that.is.not.undefined;
}
});
it('should resolve query by language', async function() {
skipIfNoCodeQL(this);
const queryPath = path.join(__dirname, 'data', 'simple-javascript-query.ql');
const queryInfo: QueryInfoByLanguage = await cli.resolveQueryByLanguage(getOnDiskWorkspaceFolders(), Uri.file(queryPath));
expect((Object.keys(queryInfo.byLanguage))[0]).to.eql('javascript');
});
});

View File

@@ -12,7 +12,7 @@ import { workspace } from 'vscode';
* version. Note that for now, we must maintain the default version by hand.
* This may be set to `nightly`, in which case the `NIGHTLY_URL` variable must
* also be set.
*
*
* - NIGHTLY_URL: The URL for a nightly release of the CodeQL CLI that will be
* used if `CLI_VERSION` is set to `nightly`.
*
@@ -44,7 +44,7 @@ const _10MB = _1MB * 10;
// CLI version to test. Hard code the latest as default. And be sure
// to update the env if it is not otherwise set.
const CLI_VERSION = process.env.CLI_VERSION || 'v2.5.5';
const CLI_VERSION = process.env.CLI_VERSION || 'v2.5.9';
process.env.CLI_VERSION = CLI_VERSION;
// Base dir where CLIs will be downloaded into

View File

@@ -48,6 +48,10 @@ describe('config listeners', function() {
name: 'codeQL.runningTests.numberOfThreads',
property: 'numberTestThreads',
values: [1, 0]
}, {
name: 'codeQL.runningQueries.maxPaths',
property: 'maxPaths',
values: [0, 1]
}]
},
{

View File

@@ -18,10 +18,13 @@ describe('queryResolver', () => {
let writeFileSpy: sinon.SinonSpy;
let getQlPackForDbschemeSpy: sinon.SinonStub;
let getPrimaryDbschemeSpy: sinon.SinonStub;
let mockCli: Record<string, sinon.SinonStub>;
let mockCli: Record<string, sinon.SinonStub | Record<string, sinon.SinonStub>>;
beforeEach(() => {
mockCli = {
resolveQueriesInSuite: sinon.stub()
resolveQueriesInSuite: sinon.stub(),
cliConstraints: {
supportsAllowLibraryPacksInResolveQueries: sinon.stub().returns(true),
}
};
module = createModule();
});
@@ -30,7 +33,7 @@ describe('queryResolver', () => {
it('should resolve a query', async () => {
mockCli.resolveQueriesInSuite.returns(['a', 'b']);
const result = await module.resolveQueries(mockCli, 'my-qlpack', KeyType.DefinitionQuery);
const result = await module.resolveQueries(mockCli, { dbschemePack: 'my-qlpack' }, KeyType.DefinitionQuery);
expect(result).to.deep.equal(['a', 'b']);
expect(writeFileSpy.getCall(0).args[0]).to.match(/.qls$/);
expect(yaml.safeLoad(writeFileSpy.getCall(0).args[1])).to.deep.equal({
@@ -42,18 +45,34 @@ describe('queryResolver', () => {
});
});
it('should resolve a query from the queries pack if this is an old CLI', async () => {
// pretend this is an older CLI
(mockCli.cliConstraints as any).supportsAllowLibraryPacksInResolveQueries.returns(false);
mockCli.resolveQueriesInSuite.returns(['a', 'b']);
const result = await module.resolveQueries(mockCli, { dbschemePackIsLibraryPack: true, dbschemePack: 'my-qlpack', queryPack: 'my-qlpack2' }, KeyType.DefinitionQuery);
expect(result).to.deep.equal(['a', 'b']);
expect(writeFileSpy.getCall(0).args[0]).to.match(/.qls$/);
expect(yaml.safeLoad(writeFileSpy.getCall(0).args[1])).to.deep.equal({
qlpack: 'my-qlpack2',
include: {
kind: 'definitions',
'tags contain': 'ide-contextual-queries/local-definitions'
}
});
});
it('should throw an error when there are no queries found', async () => {
mockCli.resolveQueriesInSuite.returns([]);
// TODO: Figure out why chai-as-promised isn't failing the test on an
// unhandled rejection.
try {
await module.resolveQueries(mockCli, 'my-qlpack', KeyType.DefinitionQuery);
await module.resolveQueries(mockCli, { dbschemePack: 'my-qlpack' }, KeyType.DefinitionQuery);
// should reject
expect(true).to.be.false;
} catch (e) {
expect(e.message).to.eq(
'Couldn\'t find any queries tagged ide-contextual-queries/local-definitions for qlpack my-qlpack'
'Couldn\'t find any queries tagged ide-contextual-queries/local-definitions in any of the following packs: my-qlpack.'
);
}
});

View File

@@ -54,6 +54,23 @@ describe('CompletedQuery', () => {
expect(completedQuery.queryName).to.eq('Quick evaluation of yz:1');
});
it('should get the query file name', () => {
const completedQuery = mockCompletedQuery();
// from the query path
expect(completedQuery.queryFileName).to.eq('stu');
// from quick eval position
(completedQuery.query as any).quickEvalPosition = {
line: 1,
endLine: 2,
fileName: '/home/users/yz'
};
expect(completedQuery.queryFileName).to.eq('yz:1-2');
(completedQuery.query as any).quickEvalPosition.endLine = 1;
expect(completedQuery.queryFileName).to.eq('yz:1');
});
it('should get the label', () => {
const completedQuery = mockCompletedQuery();
expect(completedQuery.getLabel()).to.eq('ghi');

View File

@@ -96,7 +96,7 @@ describe('test-adapter', () => {
type: 'test',
state: 'errored',
test: gPath,
message: `\nerrored: ${gPath}\npqr\nxyz\n`,
message: `\ncompilation error: ${gPath}\nERROR: abc\n`,
decorations: [
{ line: 1, message: 'abc' }
]
@@ -149,14 +149,16 @@ describe('test-adapter', () => {
pass: false,
diff: ['pqr', 'xyz'],
// a compile error
failureStage: 'COMPILATION',
messages: [
{ position: { line: 1 }, message: 'abc' }
{ position: { line: 1 }, message: 'abc', severity: 'ERROR' }
]
});
yield Promise.resolve({
test: Uri.parse('file:/ab/c/e/f/h.ql').fsPath,
pass: false,
diff: ['jkh', 'tuv'],
failureStage: 'RESULT',
messages: []
});
})()