General refactoring and adding comments
There is no new behaviour added in this commit. Just some cleanup: - Move some shared constants to the `helpers` module - Add comments to some of the query related modules - Some general formatting and tidying
This commit is contained in:
Родитель
95e818898e
Коммит
b53657344c
|
@ -35,7 +35,7 @@
|
|||
- Fix a bug with importing large databases. Databases over 4GB can now be imported directly from LGTM or from a zip file. This functionality is only available when using CodeQL CLI version 2.6.0 or later. [#971](https://github.com/github/vscode-codeql/pull/971)
|
||||
- Replace certain control codes (`U+0000` - `U+001F`) with their corresponding control labels (`U+2400` - `U+241F`) in the results view. [#963](https://github.com/github/vscode-codeql/pull/963)
|
||||
- Allow case-insensitive project slugs for GitHub repositories when adding a CodeQL database from LGTM. [#978](https://github.com/github/vscode-codeql/pull/961)
|
||||
- Add a _CodeQL: Preview Query Help_ command to generate Markdown previews of `.qhelp` query help files. This command should only be run in trusted workspaces. See https://codeql.github.com/docs/codeql-cli/testing-query-help-files for more information about query help. [#988](https://github.com/github/vscode-codeql/pull/988)
|
||||
- Add a _CodeQL: Preview Query Help_ command to generate Markdown previews of `.qhelp` query help files. This command should only be run in trusted workspaces. See [the CodeQL CLI docs](https://codeql.github.com/docs/codeql-cli/testing-query-help-files) for more information about query help. [#988](https://github.com/github/vscode-codeql/pull/988)
|
||||
- Make "Open Referenced File" command accessible from the active editor menu. [#989](https://github.com/github/vscode-codeql/pull/989)
|
||||
- Fix a bug where result set names in the result set drop-down were disappearing when viewing a sorted table. [#1007](https://github.com/github/vscode-codeql/pull/1007)
|
||||
- Allow query result locations with 0 as the end column value. These are treated as the first column in the line. [#1002](https://github.com/github/vscode-codeql/pull/1002)
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
import { tmpDir } from '../run-queries';
|
||||
import { tmpDir } from '../helpers';
|
||||
import {
|
||||
FromCompareViewMessage,
|
||||
ToCompareViewMessage,
|
||||
|
|
|
@ -2,8 +2,7 @@ import { DisposableObject } from './pure/disposable-object';
|
|||
import { workspace, Event, EventEmitter, ConfigurationChangeEvent, ConfigurationTarget } from 'vscode';
|
||||
import { DistributionManager } from './distribution';
|
||||
import { logger } from './logging';
|
||||
|
||||
const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||
import { ONE_DAY_IN_MS } from './pure/helpers-pure';
|
||||
|
||||
/** Helper class to look up a labelled (and possibly nested) setting. */
|
||||
export class Setting {
|
||||
|
@ -258,7 +257,7 @@ export class QueryHistoryConfigListener extends ConfigListener implements QueryH
|
|||
}
|
||||
|
||||
/**
|
||||
* The configuration value is in days, but return the value in milliseconds.
|
||||
* The configuration value is in days, but return the value in milliseconds to make it easier to use.
|
||||
*/
|
||||
public get ttlInMillis(): number {
|
||||
return (QUERY_HISTORY_TTL.getValue<number>() || 30) * ONE_DAY_IN_MS;
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
ProgressCallback,
|
||||
} from './commandRunner';
|
||||
import { logger } from './logging';
|
||||
import { tmpDir } from './run-queries';
|
||||
import { tmpDir } from './helpers';
|
||||
|
||||
/**
|
||||
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
|
||||
|
@ -434,7 +434,7 @@ function convertRawLgtmSlug(maybeSlug: string): string | undefined {
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
function extractProjectSlug(lgtmUrl: string): string | undefined {
|
||||
// Only matches the '/g/' provider (github)
|
||||
const re = new RegExp('https://lgtm.com/projects/g/(.*[^/])');
|
||||
|
|
|
@ -54,7 +54,15 @@ import {
|
|||
GithubApiError,
|
||||
GithubRateLimitedError
|
||||
} from './distribution';
|
||||
import * as helpers from './helpers';
|
||||
import {
|
||||
findLanguage,
|
||||
tmpDirDisposal,
|
||||
showBinaryChoiceDialog,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogWarningMessage,
|
||||
showAndLogInformationMessage,
|
||||
showInformationMessageWithAction
|
||||
} from './helpers';
|
||||
import { assertNever } from './pure/helpers-pure';
|
||||
import { spawnIdeServer } from './ide-server';
|
||||
import { InterfaceManager } from './interface';
|
||||
|
@ -64,7 +72,7 @@ import { QueryHistoryManager } from './query-history';
|
|||
import { FullCompletedQueryInfo, FullQueryInfo } from './query-results';
|
||||
import * as qsClient from './queryserver-client';
|
||||
import { displayQuickQuery } from './quick-query';
|
||||
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo, tmpDirDisposal } from './run-queries';
|
||||
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo } from './run-queries';
|
||||
import { QLTestAdapterFactory } from './test-adapter';
|
||||
import { TestUIService } from './test-ui';
|
||||
import { CompareInterfaceManager } from './compare/compare-interface';
|
||||
|
@ -189,7 +197,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
|||
const shouldUpdateOnNextActivationKey = 'shouldUpdateOnNextActivation';
|
||||
|
||||
registerErrorStubs([checkForUpdatesCommand], command => (async () => {
|
||||
void helpers.showAndLogErrorMessage(`Can't execute ${command}: waiting to finish loading CodeQL CLI.`);
|
||||
void showAndLogErrorMessage(`Can't execute ${command}: waiting to finish loading CodeQL CLI.`);
|
||||
}));
|
||||
|
||||
interface DistributionUpdateConfig {
|
||||
|
@ -201,7 +209,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
|||
async function installOrUpdateDistributionWithProgressTitle(progressTitle: string, config: DistributionUpdateConfig): Promise<void> {
|
||||
const minSecondsSinceLastUpdateCheck = config.isUserInitiated ? 0 : 86400;
|
||||
const noUpdatesLoggingFunc = config.shouldDisplayMessageWhenNoUpdates ?
|
||||
helpers.showAndLogInformationMessage : async (message: string) => void logger.log(message);
|
||||
showAndLogInformationMessage : async (message: string) => void logger.log(message);
|
||||
const result = await distributionManager.checkForUpdatesToExtensionManagedDistribution(minSecondsSinceLastUpdateCheck);
|
||||
|
||||
// We do want to auto update if there is no distribution at all
|
||||
|
@ -223,7 +231,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
|||
const updateAvailableMessage = `Version "${result.updatedRelease.name}" of the CodeQL CLI is now available. ` +
|
||||
'Do you wish to upgrade?';
|
||||
await ctx.globalState.update(shouldUpdateOnNextActivationKey, true);
|
||||
if (await helpers.showInformationMessageWithAction(updateAvailableMessage, 'Restart and Upgrade')) {
|
||||
if (await showInformationMessageWithAction(updateAvailableMessage, 'Restart and Upgrade')) {
|
||||
await commands.executeCommand('workbench.action.reloadWindow');
|
||||
}
|
||||
} else {
|
||||
|
@ -236,7 +244,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
|||
distributionManager.installExtensionManagedDistributionRelease(result.updatedRelease, progress));
|
||||
|
||||
await ctx.globalState.update(shouldUpdateOnNextActivationKey, false);
|
||||
void helpers.showAndLogInformationMessage(`CodeQL CLI updated to version "${result.updatedRelease.name}".`);
|
||||
void showAndLogInformationMessage(`CodeQL CLI updated to version "${result.updatedRelease.name}".`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -263,7 +271,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
|||
// Don't rethrow the exception, because if the config is changed, we want to be able to retry installing
|
||||
// or updating the distribution.
|
||||
const alertFunction = (codeQlInstalled && !config.isUserInitiated) ?
|
||||
helpers.showAndLogWarningMessage : helpers.showAndLogErrorMessage;
|
||||
showAndLogWarningMessage : showAndLogErrorMessage;
|
||||
const taskDescription = (willUpdateCodeQl ? 'update' :
|
||||
codeQlInstalled ? 'check for updates to' : 'install') + ' CodeQL CLI';
|
||||
|
||||
|
@ -298,20 +306,20 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
|||
}
|
||||
})();
|
||||
|
||||
void helpers.showAndLogWarningMessage(
|
||||
void showAndLogWarningMessage(
|
||||
`The current version of the CodeQL CLI (${result.version.raw}) ` +
|
||||
`is incompatible with this extension. ${fixGuidanceMessage}`
|
||||
);
|
||||
break;
|
||||
}
|
||||
case FindDistributionResultKind.UnknownCompatibilityDistribution:
|
||||
void helpers.showAndLogWarningMessage(
|
||||
void showAndLogWarningMessage(
|
||||
'Compatibility with the configured CodeQL CLI could not be determined. ' +
|
||||
'You may experience problems using the extension.'
|
||||
);
|
||||
break;
|
||||
case FindDistributionResultKind.NoDistribution:
|
||||
void helpers.showAndLogErrorMessage('The CodeQL CLI could not be found.');
|
||||
void showAndLogErrorMessage('The CodeQL CLI could not be found.');
|
||||
break;
|
||||
default:
|
||||
assertNever(result);
|
||||
|
@ -338,7 +346,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
|||
} else if (distributionResult.kind === FindDistributionResultKind.NoDistribution) {
|
||||
registerErrorStubs([checkForUpdatesCommand], command => async () => {
|
||||
const installActionName = 'Install CodeQL CLI';
|
||||
const chosenAction = await void helpers.showAndLogErrorMessage(`Can't execute ${command}: missing CodeQL CLI.`, {
|
||||
const chosenAction = await void showAndLogErrorMessage(`Can't execute ${command}: missing CodeQL CLI.`, {
|
||||
items: [installActionName]
|
||||
});
|
||||
if (chosenAction === installActionName) {
|
||||
|
@ -477,7 +485,7 @@ async function activateWithInstalledDistribution(
|
|||
try {
|
||||
await cmpm.showResults(from, to);
|
||||
} catch (e) {
|
||||
void helpers.showAndLogErrorMessage(e.message);
|
||||
void showAndLogErrorMessage(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -559,7 +567,7 @@ async function activateWithInstalledDistribution(
|
|||
const errorMessage = err.message.includes('Generating qhelp in markdown') ? (
|
||||
`Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
|
||||
) : `Could not open a preview of the generated file (${absolutePathToMd}).`;
|
||||
void helpers.showAndLogErrorMessage(errorMessage, { fullMessage: `${errorMessage}\n${err}` });
|
||||
void showAndLogErrorMessage(errorMessage, { fullMessage: `${errorMessage}\n${err}` });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,7 +584,7 @@ async function activateWithInstalledDistribution(
|
|||
const uri = Uri.file(resolved.resolvedPath);
|
||||
await window.showTextDocument(uri, { preview: false });
|
||||
} else {
|
||||
void helpers.showAndLogErrorMessage(
|
||||
void showAndLogErrorMessage(
|
||||
'Jumping from a .qlref file to the .ql file it references is not '
|
||||
+ 'supported with the CLI version you are running.\n'
|
||||
+ `Please upgrade your CLI to version ${CliVersionConstraint.CLI_VERSION_WITH_RESOLVE_QLREF
|
||||
|
@ -646,15 +654,15 @@ async function activateWithInstalledDistribution(
|
|||
) => {
|
||||
let filteredDBs = dbm.databaseItems;
|
||||
if (filteredDBs.length === 0) {
|
||||
void helpers.showAndLogErrorMessage('No databases found. Please add a suitable database to your workspace.');
|
||||
void 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);
|
||||
const queryLanguage = await 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.`);
|
||||
void showAndLogErrorMessage(`No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -686,12 +694,12 @@ async function activateWithInstalledDistribution(
|
|||
}
|
||||
if (skippedDatabases.length > 0) {
|
||||
void logger.log(`Errors:\n${errors.join('\n')}`);
|
||||
void helpers.showAndLogWarningMessage(
|
||||
void 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.');
|
||||
void showAndLogErrorMessage('No databases selected.');
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -718,7 +726,7 @@ async function activateWithInstalledDistribution(
|
|||
// files may be hidden from the user.
|
||||
if (dirFound) {
|
||||
const fileString = files.map(file => path.basename(file)).join(', ');
|
||||
const res = await helpers.showBinaryChoiceDialog(
|
||||
const res = await showBinaryChoiceDialog(
|
||||
`You are about to run ${files.length} queries: ${fileString} Do you want to continue?`
|
||||
);
|
||||
if (!res) {
|
||||
|
@ -882,7 +890,7 @@ async function activateWithInstalledDistribution(
|
|||
token: CancellationToken
|
||||
) => {
|
||||
await qs.restartQueryServer(progress, token);
|
||||
void helpers.showAndLogInformationMessage('CodeQL Query Server restarted.', {
|
||||
void showAndLogInformationMessage('CodeQL Query Server restarted.', {
|
||||
outputLogger: queryServerLogger,
|
||||
});
|
||||
}, {
|
||||
|
@ -938,7 +946,7 @@ async function activateWithInstalledDistribution(
|
|||
commandRunner('codeQL.copyVersion', async () => {
|
||||
const text = `CodeQL extension version: ${extension?.packageJSON.version} \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${os.platform()} ${os.arch()}`;
|
||||
await env.clipboard.writeText(text);
|
||||
void helpers.showAndLogInformationMessage(text);
|
||||
void showAndLogInformationMessage(text);
|
||||
}));
|
||||
|
||||
const getCliVersion = async () => {
|
||||
|
@ -960,7 +968,7 @@ async function activateWithInstalledDistribution(
|
|||
const credentials = await Credentials.initialize(ctx);
|
||||
const octokit = await credentials.getOctokit();
|
||||
const userInfo = await octokit.users.getAuthenticated();
|
||||
void helpers.showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`);
|
||||
void showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`);
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as fs from 'fs-extra';
|
|||
import * as glob from 'glob-promise';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as path from 'path';
|
||||
import * as tmp from 'tmp-promise';
|
||||
import {
|
||||
ExtensionContext,
|
||||
Uri,
|
||||
|
@ -14,6 +15,17 @@ import { UserCancellationException } from './commandRunner';
|
|||
import { logger } from './logging';
|
||||
import { QueryMetadata } from './pure/interface-types';
|
||||
|
||||
// Shared temporary folder for the extension.
|
||||
export const tmpDir = tmp.dirSync({ prefix: 'queries_', keep: false, unsafeCleanup: true });
|
||||
export const upgradesTmpDir = path.join(tmpDir.name, 'upgrades');
|
||||
fs.ensureDirSync(upgradesTmpDir);
|
||||
|
||||
export const tmpDirDisposal = {
|
||||
dispose: () => {
|
||||
tmpDir.removeCallback();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show an error message and log it to the console
|
||||
*
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
import * as cli from './cli';
|
||||
import { CodeQLCliServer } from './cli';
|
||||
import { DatabaseEventKind, DatabaseItem, DatabaseManager } from './databases';
|
||||
import { showAndLogErrorMessage } from './helpers';
|
||||
import { showAndLogErrorMessage, tmpDir } from './helpers';
|
||||
import { assertNever } from './pure/helpers-pure';
|
||||
import {
|
||||
FromResultsViewMsg,
|
||||
|
@ -33,7 +33,7 @@ import { Logger } from './logging';
|
|||
import * as messages from './pure/messages';
|
||||
import { commandRunner } from './commandRunner';
|
||||
import { CompletedQueryInfo, interpretResults } from './query-results';
|
||||
import { QueryEvaluationInfo, tmpDir } from './run-queries';
|
||||
import { QueryEvaluationInfo } from './run-queries';
|
||||
import { parseSarifLocation, parseSarifPlainTextMessage } from './pure/sarif-utils';
|
||||
import {
|
||||
WebviewReveal,
|
||||
|
|
|
@ -29,3 +29,8 @@ export const asyncFilter = async function <T>(arr: T[], predicate: (arg0: T) =>
|
|||
const results = await Promise.all(arr.map(predicate));
|
||||
return arr.filter((_, index) => results[index]);
|
||||
};
|
||||
|
||||
export const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||
export const ONE_HOUR_IN_MS = 1000 * 60 * 60;
|
||||
export const TWO_HOURS_IN_MS = 1000 * 60 * 60 * 2;
|
||||
export const THREE_HOURS_IN_MS = 1000 * 60 * 60 * 3;
|
||||
|
|
|
@ -28,7 +28,7 @@ import { URLSearchParams } from 'url';
|
|||
import { QueryServerClient } from './queryserver-client';
|
||||
import { DisposableObject } from './pure/disposable-object';
|
||||
import { commandRunner } from './commandRunner';
|
||||
import { assertNever } from './pure/helpers-pure';
|
||||
import { assertNever, ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from './pure/helpers-pure';
|
||||
import { FullCompletedQueryInfo, FullQueryInfo, QueryStatus } from './query-results';
|
||||
import { DatabaseManager } from './databases';
|
||||
import { registerQueryHistoryScubber } from './query-history-scrubber';
|
||||
|
@ -83,8 +83,15 @@ export enum SortOrder {
|
|||
CountDesc = 'CountDesc',
|
||||
}
|
||||
|
||||
const ONE_HOUR_IN_MS = 1000 * 60 * 60;
|
||||
const TWO_HOURS_IN_MS = 1000 * 60 * 60 * 2;
|
||||
/**
|
||||
* Number of milliseconds two clicks have to arrive apart to be
|
||||
* considered a double-click.
|
||||
*/
|
||||
const DOUBLE_CLICK_TIME = 500;
|
||||
|
||||
const NO_QUERY_SELECTED = 'No query selected. Select a query history item you have already run and try again.';
|
||||
|
||||
const WORKSPACE_QUERY_HISTORY_FILE = 'workspace-query-history.json';
|
||||
|
||||
/**
|
||||
* Tree data provider for the query history view.
|
||||
|
@ -244,15 +251,6 @@ export class HistoryTreeDataProvider extends DisposableObject {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of milliseconds two clicks have to arrive apart to be
|
||||
* considered a double-click.
|
||||
*/
|
||||
const DOUBLE_CLICK_TIME = 500;
|
||||
|
||||
const NO_QUERY_SELECTED = 'No query selected. Select a query history item you have already run and try again.';
|
||||
|
||||
const WORKSPACE_QUERY_HISTORY_FILE = 'workspace-query-history.json';
|
||||
export class QueryHistoryManager extends DisposableObject {
|
||||
|
||||
treeDataProvider: HistoryTreeDataProvider;
|
||||
|
@ -742,7 +740,7 @@ export class QueryHistoryManager extends DisposableObject {
|
|||
}
|
||||
|
||||
await this.tryOpenExternalFile(
|
||||
await finalSingleItem.completedQuery.query.ensureCsvProduced(this.qs, this.dbm)
|
||||
await finalSingleItem.completedQuery.query.ensureCsvAlerts(this.qs, this.dbm)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,14 @@ import { DatabaseInfo } from './pure/interface-types';
|
|||
import { showAndLogErrorMessage } from './helpers';
|
||||
import { asyncFilter } from './pure/helpers-pure';
|
||||
|
||||
/**
|
||||
* query-results.ts
|
||||
* ----------------
|
||||
*
|
||||
* A collection of classes and functions that collectively
|
||||
* manage query results.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A description of the information about a query
|
||||
* that is available before results are populated.
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import * as unzipper from 'unzipper';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs-extra';
|
||||
import { showAndLogWarningMessage } from '../helpers';
|
||||
import { showAndLogWarningMessage, tmpDir } from '../helpers';
|
||||
import { Credentials } from '../authentication';
|
||||
import { logger } from '../logging';
|
||||
import { tmpDir } from '../run-queries';
|
||||
import { RemoteQueryWorkflowResult } from './remote-query-workflow-result';
|
||||
import { DownloadLink } from './download-link';
|
||||
import { RemoteQuery } from './remote-query';
|
||||
|
@ -106,8 +105,8 @@ async function getResultIndexItems(
|
|||
/**
|
||||
* Gets the status of a workflow run.
|
||||
* @param credentials Credentials for authenticating to the GitHub API.
|
||||
* @param owner
|
||||
* @param repo
|
||||
* @param owner
|
||||
* @param repo
|
||||
* @param workflowRunId The ID of the workflow run to get the result index for.
|
||||
* @returns The workflow run status.
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
} from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
import { tmpDir } from '../run-queries';
|
||||
import {
|
||||
ToRemoteQueriesMessage,
|
||||
FromRemoteQueriesMessage,
|
||||
|
@ -25,7 +24,7 @@ import { AnalysisSummary, RemoteQueryResult } from './remote-query-result';
|
|||
import { RemoteQuery } from './remote-query';
|
||||
import { RemoteQueryResult as RemoteQueryResultViewModel } from './shared/remote-query-result';
|
||||
import { AnalysisSummary as AnalysisResultViewModel } from './shared/remote-query-result';
|
||||
import { showAndLogWarningMessage } from '../helpers';
|
||||
import { showAndLogWarningMessage, tmpDir } from '../helpers';
|
||||
import { URLSearchParams } from 'url';
|
||||
import { SHOW_QUERY_TEXT_MSG } from '../query-history';
|
||||
import { AnalysesResultsManager } from './analyses-results-manager';
|
||||
|
|
|
@ -10,13 +10,13 @@ import {
|
|||
showAndLogErrorMessage,
|
||||
showAndLogInformationMessage,
|
||||
showInformationMessageWithAction,
|
||||
tryGetQueryMetadata
|
||||
tryGetQueryMetadata,
|
||||
tmpDir
|
||||
} from '../helpers';
|
||||
import { Credentials } from '../authentication';
|
||||
import * as cli from '../cli';
|
||||
import { logger } from '../logging';
|
||||
import { getRemoteControllerRepo, getRemoteRepositoryLists, setRemoteControllerRepo } from '../config';
|
||||
import { tmpDir } from '../run-queries';
|
||||
import { ProgressCallback, UserCancellationException } from '../commandRunner';
|
||||
import { OctokitResponse } from '@octokit/types/dist-types';
|
||||
import { RemoteQuery } from './remote-query';
|
||||
|
@ -439,7 +439,7 @@ async function buildRemoteQueryEntity(
|
|||
queryStartTime: Date,
|
||||
workflowRunId: number
|
||||
): Promise<RemoteQuery> {
|
||||
// The query name is either the name as specified in the query metadata, or the file name.
|
||||
// The query name is either the name as specified in the query metadata, or the file name.
|
||||
const queryName = queryMetadata?.name ?? path.basename(queryFilePath);
|
||||
|
||||
const queryRepos = repositories.map(r => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as tmp from 'tmp-promise';
|
||||
import * as path from 'path';
|
||||
import { nanoid } from 'nanoid';
|
||||
import {
|
||||
CancellationToken,
|
||||
|
@ -17,7 +17,7 @@ import { ErrorCodes, ResponseError } from 'vscode-languageclient';
|
|||
import * as cli from './cli';
|
||||
import * as config from './config';
|
||||
import { DatabaseItem, DatabaseManager } from './databases';
|
||||
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, tryGetQueryMetadata } from './helpers';
|
||||
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, tryGetQueryMetadata, upgradesTmpDir } from './helpers';
|
||||
import { ProgressCallback, UserCancellationException } from './commandRunner';
|
||||
import { DatabaseInfo, QueryMetadata } from './pure/interface-types';
|
||||
import { logger } from './logging';
|
||||
|
@ -32,19 +32,20 @@ import { DecodedBqrsChunk } from './pure/bqrs-cli-types';
|
|||
|
||||
/**
|
||||
* run-queries.ts
|
||||
* -------------
|
||||
* --------------
|
||||
*
|
||||
* Compiling and running QL queries.
|
||||
*/
|
||||
|
||||
export const tmpDir = tmp.dirSync({ prefix: 'queries_', keep: false, unsafeCleanup: true });
|
||||
export const upgradesTmpDir = tmp.dirSync({ dir: tmpDir.name, prefix: 'upgrades_', keep: false, unsafeCleanup: true });
|
||||
export const tmpDirDisposal = {
|
||||
dispose: () => {
|
||||
upgradesTmpDir.removeCallback();
|
||||
tmpDir.removeCallback();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Information about which query will be to be run. `quickEvalPosition` and `quickEvalText`
|
||||
* is only filled in if the query is a quick query.
|
||||
*/
|
||||
interface SelectedQuery {
|
||||
queryPath: string;
|
||||
quickEvalPosition?: messages.Position;
|
||||
quickEvalText?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of evaluation-time information about a query,
|
||||
|
@ -131,6 +132,7 @@ export class QueryEvaluationInfo {
|
|||
id: callbackId,
|
||||
timeoutSecs: qs.config.timeoutSecs,
|
||||
};
|
||||
|
||||
const dataset: messages.Dataset = {
|
||||
dbDir: dbItem.contents.datasetUri.fsPath,
|
||||
workingSet: 'default'
|
||||
|
@ -235,6 +237,10 @@ export class QueryEvaluationInfo {
|
|||
return fs.pathExists(this.csvPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the DIL file produced by this query. If the query has not yet produced DIL,
|
||||
* this will return first create the DIL file and then return the path to the DIL file.
|
||||
*/
|
||||
async ensureDilPath(qs: qsClient.QueryServerClient): Promise<string> {
|
||||
if (await this.hasDil()) {
|
||||
return this.dilPath;
|
||||
|
@ -250,6 +256,10 @@ export class QueryEvaluationInfo {
|
|||
return this.dilPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the CSV file containing the results of this query. This will only be called if the query
|
||||
* does not have interpreted results and the CSV file does not already exist.
|
||||
*/
|
||||
async exportCsvResults(qs: qsClient.QueryServerClient, csvPath: string, onFinish: () => void): Promise<void> {
|
||||
let stopDecoding = false;
|
||||
const out = fs.createWriteStream(csvPath);
|
||||
|
@ -266,14 +276,21 @@ export class QueryEvaluationInfo {
|
|||
pageSize: 100,
|
||||
offset: nextOffset,
|
||||
});
|
||||
for (const tuple of chunk.tuples)
|
||||
for (const tuple of chunk.tuples) {
|
||||
out.write(tuple.join(',') + '\n');
|
||||
}
|
||||
nextOffset = chunk.next;
|
||||
}
|
||||
out.end();
|
||||
}
|
||||
|
||||
async ensureCsvProduced(qs: qsClient.QueryServerClient, dbm: DatabaseManager): Promise<string> {
|
||||
/**
|
||||
* Returns the path to the CSV alerts interpretation of this query results. If CSV results have
|
||||
* not yet been produced, this will return first create the CSV results and then return the path.
|
||||
*
|
||||
* This method only works for queries with interpreted results.
|
||||
*/
|
||||
async ensureCsvAlerts(qs: qsClient.QueryServerClient, dbm: DatabaseManager): Promise<string> {
|
||||
if (await this.hasCsv()) {
|
||||
return this.csvPath;
|
||||
}
|
||||
|
@ -297,6 +314,9 @@ export class QueryEvaluationInfo {
|
|||
return this.csvPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans this query's results directory.
|
||||
*/
|
||||
async deleteQuery(): Promise<void> {
|
||||
await fs.remove(this.querySaveDir);
|
||||
}
|
||||
|
@ -333,9 +353,7 @@ export async function clearCacheInDatabase(
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filePath This needs to be equivalent to java Path.toRealPath(NO_FOLLOW_LINKS)
|
||||
*
|
||||
* @param filePath This needs to be equivalent to Java's `Path.toRealPath(NO_FOLLOW_LINKS)`
|
||||
*/
|
||||
async function convertToQlPath(filePath: string): Promise<string> {
|
||||
if (process.platform === 'win32') {
|
||||
|
@ -381,9 +399,9 @@ async function getSelectedPosition(editor: TextEditor, range?: Range): Promise<m
|
|||
|
||||
/**
|
||||
* Compare the dbscheme implied by the query `query` and that of the current database.
|
||||
* If they are compatible, do nothing.
|
||||
* If they are incompatible but the database can be upgraded, suggest that upgrade.
|
||||
* If they are incompatible and the database cannot be upgraded, throw an error.
|
||||
* - If they are compatible, do nothing.
|
||||
* - If they are incompatible but the database can be upgraded, suggest that upgrade.
|
||||
* - If they are incompatible and the database cannot be upgraded, throw an error.
|
||||
*/
|
||||
async function checkDbschemeCompatibility(
|
||||
cliServer: cli.CodeQLCliServer,
|
||||
|
@ -431,7 +449,9 @@ async function checkDbschemeCompatibility(
|
|||
}
|
||||
|
||||
function reportNoUpgradePath(qlProgram: messages.QlProgram, query: QueryEvaluationInfo): void {
|
||||
throw new Error(`Query ${qlProgram.queryPath} expects database scheme ${query.queryDbscheme}, but the current database has a different scheme, and no database upgrades are available. The current database scheme may be newer than the CodeQL query libraries in your workspace.\n\nPlease try using a newer version of the query libraries.`);
|
||||
throw new Error(
|
||||
`Query ${qlProgram.queryPath} expects database scheme ${query.queryDbscheme}, but the current database has a different scheme, and no database upgrades are available. The current database scheme may be newer than the CodeQL query libraries in your workspace.\n\nPlease try using a newer version of the query libraries.`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -476,7 +496,6 @@ async function compileNonDestructiveUpgrade(
|
|||
qlProgram.dbschemePath = query.queryDbscheme;
|
||||
// We are new enough that we will always support single file upgrades.
|
||||
return result.compiledUpgrade;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -521,12 +540,6 @@ async function promptUserToSaveChanges(document: TextDocument): Promise<boolean>
|
|||
return false;
|
||||
}
|
||||
|
||||
type SelectedQuery = {
|
||||
queryPath: string;
|
||||
quickEvalPosition?: messages.Position;
|
||||
quickEvalText?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines which QL file to run during an invocation of `Run Query` or `Quick Evaluation`, as follows:
|
||||
* - If the command was called by clicking on a file, then use that file.
|
||||
|
@ -537,12 +550,19 @@ type SelectedQuery = {
|
|||
* @param selectedResourceUri The selected resource when the command was run.
|
||||
* @param quickEval Whether the command being run is `Quick Evaluation`.
|
||||
*/
|
||||
export async function determineSelectedQuery(selectedResourceUri: Uri | undefined, quickEval: boolean, range?: Range): Promise<SelectedQuery> {
|
||||
export async function determineSelectedQuery(
|
||||
selectedResourceUri: Uri | undefined,
|
||||
quickEval: boolean,
|
||||
range?: Range
|
||||
): Promise<SelectedQuery> {
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
// Choose which QL file to use.
|
||||
let queryUri: Uri;
|
||||
if (selectedResourceUri === undefined) {
|
||||
if (selectedResourceUri) {
|
||||
// A resource was passed to the command handler, so use it.
|
||||
queryUri = selectedResourceUri;
|
||||
} else {
|
||||
// No resource was passed to the command handler, so obtain it from the active editor.
|
||||
// This usually happens when the command is called from the Command Palette.
|
||||
if (editor === undefined) {
|
||||
|
@ -550,9 +570,6 @@ export async function determineSelectedQuery(selectedResourceUri: Uri | undefine
|
|||
} else {
|
||||
queryUri = editor.document.uri;
|
||||
}
|
||||
} else {
|
||||
// A resource was passed to the command handler, so use it.
|
||||
queryUri = selectedResourceUri;
|
||||
}
|
||||
|
||||
if (queryUri.scheme !== 'file') {
|
||||
|
@ -680,7 +697,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
|||
try {
|
||||
let upgradeQlo;
|
||||
if (await hasNondestructiveUpgradeCapabilities(qs)) {
|
||||
upgradeDir = await tmp.dir({ dir: upgradesTmpDir.name, unsafeCleanup: true });
|
||||
upgradeDir = await tmp.dir({ dir: upgradesTmpDir, unsafeCleanup: true });
|
||||
upgradeQlo = await compileNonDestructiveUpgrade(qs, upgradeDir, query, qlProgram, dbItem, progress, token);
|
||||
} else {
|
||||
await checkDbschemeCompatibility(cliServer, qs, query, qlProgram, dbItem, progress, token);
|
||||
|
@ -746,6 +763,16 @@ export async function compileAndRunQueryAgainstDatabase(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the initial information for a query. This is everything of interest
|
||||
* we know about this query that is available before it is run.
|
||||
*
|
||||
* @param selectedQueryUri The Uri of the document containing the query to be run.
|
||||
* @param databaseInfo The database to run the query against.
|
||||
* @param isQuickEval true if this is a quick evaluation.
|
||||
* @param range the selection range of the query to be run. Only used if isQuickEval is true.
|
||||
* @returns The initial information for the query to be run.
|
||||
*/
|
||||
export async function createInitialQueryInfo(
|
||||
selectedQueryUri: Uri | undefined,
|
||||
databaseInfo: DatabaseInfo,
|
||||
|
@ -776,12 +803,14 @@ const compilationFailedErrorTail = ' compilation failed. Please make sure there
|
|||
' and the query and database use the same target language. For more details on the error, go to View > Output,' +
|
||||
' and choose CodeQL Query Server from the dropdown.';
|
||||
|
||||
/**
|
||||
* Create a synthetic result for a query that failed to compile.
|
||||
*/
|
||||
function createSyntheticResult(
|
||||
query: QueryEvaluationInfo,
|
||||
message: string,
|
||||
resultType: number
|
||||
): QueryWithResults {
|
||||
|
||||
return {
|
||||
query,
|
||||
result: {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage } from './helpers';
|
||||
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, tmpDir } from './helpers';
|
||||
import { ProgressCallback, UserCancellationException } from './commandRunner';
|
||||
import { logger } from './logging';
|
||||
import * as messages from './pure/messages';
|
||||
import * as qsClient from './queryserver-client';
|
||||
import { upgradesTmpDir } from './run-queries';
|
||||
import * as tmp from 'tmp-promise';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
|
@ -180,7 +179,7 @@ export async function upgradeDatabaseExplicit(
|
|||
if (finalDbscheme === undefined) {
|
||||
throw new Error('Could not determine target dbscheme to upgrade to.');
|
||||
}
|
||||
const currentUpgradeTmp = await tmp.dir({ dir: upgradesTmpDir.name, prefix: 'upgrade_', keep: false, unsafeCleanup: true });
|
||||
const currentUpgradeTmp = await tmp.dir({ dir: tmpDir.name, prefix: 'upgrade_', keep: false, unsafeCleanup: true });
|
||||
try {
|
||||
let compileUpgradeResult: messages.CompileUpgradeResult;
|
||||
try {
|
||||
|
|
|
@ -11,11 +11,12 @@ import { DatabaseItem, DatabaseManager } from '../../databases';
|
|||
import { CodeQLExtensionInterface } from '../../extension';
|
||||
import { dbLoc, storagePath } from './global.helper';
|
||||
import { importArchiveDatabase } from '../../databaseFetcher';
|
||||
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo, tmpDir } from '../../run-queries';
|
||||
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo } from '../../run-queries';
|
||||
import { CodeQLCliServer } from '../../cli';
|
||||
import { QueryServerClient } from '../../queryserver-client';
|
||||
import { skipIfNoCodeQL } from '../ensureCli';
|
||||
import { QueryResultType } from '../../pure/messages';
|
||||
import { tmpDir } from '../../helpers';
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,21 +8,22 @@ import * as sinon from 'sinon';
|
|||
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import { logger } from '../../logging';
|
||||
import { QueryHistoryManager, HistoryTreeDataProvider, SortOrder } from '../../query-history';
|
||||
import { registerQueryHistoryScubber } from '../../query-history-scrubber';
|
||||
import { QueryEvaluationInfo, QueryWithResults, tmpDir } from '../../run-queries';
|
||||
import { QueryHistoryManager, HistoryTreeDataProvider, SortOrder } from '../../query-history';
|
||||
import { QueryEvaluationInfo, QueryWithResults } from '../../run-queries';
|
||||
import { QueryHistoryConfigListener } from '../../config';
|
||||
import * as messages from '../../pure/messages';
|
||||
import { QueryServerClient } from '../../queryserver-client';
|
||||
import { FullQueryInfo, InitialQueryInfo } from '../../query-results';
|
||||
import { DatabaseManager } from '../../databases';
|
||||
import * as tmp from 'tmp-promise';
|
||||
import { ONE_DAY_IN_MS, ONE_HOUR_IN_MS, TWO_HOURS_IN_MS, THREE_HOURS_IN_MS } from '../../pure/helpers-pure';
|
||||
import { tmpDir } from '../../helpers';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
const expect = chai.expect;
|
||||
const assert = chai.assert;
|
||||
|
||||
|
||||
describe('query-history', () => {
|
||||
const mockExtensionLocation = path.join(tmpDir.name, 'mock-extension-location');
|
||||
let configListener: QueryHistoryConfigListener;
|
||||
|
@ -552,10 +553,6 @@ describe('query-history', () => {
|
|||
let mockCtx: vscode.ExtensionContext;
|
||||
let runCount = 0;
|
||||
|
||||
const ONE_HOUR_IN_MS = 60 * 60 * 1000;
|
||||
const TWO_HOURS_IN_MS = 2 * ONE_HOUR_IN_MS;
|
||||
const THREE_HOURS_IN_MS = 3 * ONE_HOUR_IN_MS;
|
||||
const ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS;
|
||||
// We don't want our times to align exactly with the hour,
|
||||
// so we can better mimic real life
|
||||
const LESS_THAN_ONE_DAY = ONE_DAY_IN_MS - 1000;
|
||||
|
|
|
@ -6,12 +6,13 @@ import 'sinon-chai';
|
|||
import * as sinon from 'sinon';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import { FullQueryInfo, InitialQueryInfo, interpretResults } from '../../query-results';
|
||||
import { QueryEvaluationInfo, QueryWithResults, tmpDir } from '../../run-queries';
|
||||
import { QueryEvaluationInfo, QueryWithResults } from '../../run-queries';
|
||||
import { QueryHistoryConfig } from '../../config';
|
||||
import { EvaluationResult, QueryResultType } from '../../pure/messages';
|
||||
import { DatabaseInfo, SortDirection, SortedResultSetInfo } from '../../pure/interface-types';
|
||||
import { CodeQLCliServer, SourceInfo } from '../../cli';
|
||||
import { CancellationTokenSource, Uri, env } from 'vscode';
|
||||
import { tmpDir } from '../../helpers';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
const expect = chai.expect;
|
||||
|
|
Загрузка…
Ссылка в новой задаче