Split flows for running remote queries
This removes the `runRemoteQuery` method and instead moves all logic specific to remote queries/variant analysis to the remote queries manager and variant analysis manager respectively. This will make it easier to completely remove the remote queries manager in the future.
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
CliConfigListener,
|
||||
DistributionConfigListener,
|
||||
isCanary,
|
||||
isVariantAnalysisLiveResultsEnabled,
|
||||
joinOrderWarningThreshold,
|
||||
MAX_QUERIES,
|
||||
QueryHistoryConfigListener,
|
||||
@@ -489,13 +490,13 @@ async function activateWithInstalledDistribution(
|
||||
const variantAnalysisStorageDir = path.join(ctx.globalStorageUri.fsPath, 'variant-analyses');
|
||||
await fs.ensureDir(variantAnalysisStorageDir);
|
||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(cliServer, logger);
|
||||
const variantAnalysisManager = new VariantAnalysisManager(ctx, variantAnalysisStorageDir, variantAnalysisResultsManager);
|
||||
const variantAnalysisManager = new VariantAnalysisManager(ctx, cliServer, variantAnalysisStorageDir, variantAnalysisResultsManager);
|
||||
ctx.subscriptions.push(variantAnalysisManager);
|
||||
ctx.subscriptions.push(variantAnalysisResultsManager);
|
||||
ctx.subscriptions.push(workspace.registerTextDocumentContentProvider('codeql-variant-analysis', createVariantAnalysisContentProvider(variantAnalysisManager)));
|
||||
|
||||
void logger.log('Initializing remote queries manager.');
|
||||
const rqm = new RemoteQueriesManager(ctx, cliServer, queryStorageDir, logger, variantAnalysisManager);
|
||||
const rqm = new RemoteQueriesManager(ctx, cliServer, queryStorageDir, logger);
|
||||
ctx.subscriptions.push(rqm);
|
||||
|
||||
void logger.log('Initializing query history.');
|
||||
@@ -906,11 +907,20 @@ async function activateWithInstalledDistribution(
|
||||
step: 0,
|
||||
message: 'Getting credentials'
|
||||
});
|
||||
await rqm.runRemoteQuery(
|
||||
uri || window.activeTextEditor?.document.uri,
|
||||
progress,
|
||||
token
|
||||
);
|
||||
|
||||
if (isVariantAnalysisLiveResultsEnabled()) {
|
||||
await variantAnalysisManager.runVariantAnalysis(
|
||||
uri || window.activeTextEditor?.document.uri,
|
||||
progress,
|
||||
token
|
||||
);
|
||||
} else {
|
||||
await rqm.runRemoteQuery(
|
||||
uri || window.activeTextEditor?.document.uri,
|
||||
progress,
|
||||
token
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Variant analysis requires the CodeQL Canary version to run.');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CancellationToken, commands, EventEmitter, ExtensionContext, Uri, env, window } from 'vscode';
|
||||
import { CancellationToken, commands, EventEmitter, ExtensionContext, Uri, env } from 'vscode';
|
||||
import { nanoid } from 'nanoid';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs-extra';
|
||||
@@ -9,7 +9,11 @@ import { CodeQLCliServer } from '../cli';
|
||||
import { ProgressCallback } from '../commandRunner';
|
||||
import { createTimestampFile, showAndLogErrorMessage, showAndLogInformationMessage, showInformationMessageWithAction } from '../helpers';
|
||||
import { Logger } from '../logging';
|
||||
import { runRemoteQuery } from './run-remote-query';
|
||||
import {
|
||||
buildRemoteQueryEntity,
|
||||
prepareRemoteQueryRun,
|
||||
runRemoteQueriesApiRequest,
|
||||
} from './run-remote-query';
|
||||
import { RemoteQueriesView } from './remote-queries-view';
|
||||
import { RemoteQuery } from './remote-query';
|
||||
import { RemoteQueriesMonitor } from './remote-queries-monitor';
|
||||
@@ -22,7 +26,6 @@ import { assertNever } from '../pure/helpers-pure';
|
||||
import { QueryStatus } from '../query-status';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
import { AnalysisResults } from './shared/analysis-result';
|
||||
import { VariantAnalysisManager } from './variant-analysis-manager';
|
||||
|
||||
const autoDownloadMaxSize = 300 * 1024;
|
||||
const autoDownloadMaxCount = 100;
|
||||
@@ -57,7 +60,6 @@ export class RemoteQueriesManager extends DisposableObject {
|
||||
|
||||
private readonly remoteQueriesMonitor: RemoteQueriesMonitor;
|
||||
private readonly analysesResultsManager: AnalysesResultsManager;
|
||||
private readonly variantAnalysisManager: VariantAnalysisManager;
|
||||
private readonly view: RemoteQueriesView;
|
||||
|
||||
constructor(
|
||||
@@ -65,13 +67,11 @@ export class RemoteQueriesManager extends DisposableObject {
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly storagePath: string,
|
||||
logger: Logger,
|
||||
variantAnalysisManager: VariantAnalysisManager,
|
||||
) {
|
||||
super();
|
||||
this.analysesResultsManager = new AnalysesResultsManager(ctx, cliServer, storagePath, logger);
|
||||
this.view = new RemoteQueriesView(ctx, logger, this.analysesResultsManager);
|
||||
this.remoteQueriesMonitor = new RemoteQueriesMonitor(ctx, logger);
|
||||
this.variantAnalysisManager = variantAnalysisManager;
|
||||
|
||||
this.remoteQueryAddedEventEmitter = this.push(new EventEmitter<NewQueryEvent>());
|
||||
this.remoteQueryRemovedEventEmitter = this.push(new EventEmitter<RemovedQueryEvent>());
|
||||
@@ -119,21 +119,47 @@ export class RemoteQueriesManager extends DisposableObject {
|
||||
uri: Uri | undefined,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken
|
||||
): Promise<void> {
|
||||
): Promise<RemoteQuery | undefined> {
|
||||
const credentials = await Credentials.initialize(this.ctx);
|
||||
|
||||
const querySubmission = await runRemoteQuery(this.cliServer, credentials, uri || window.activeTextEditor?.document.uri, progress, token, this.variantAnalysisManager);
|
||||
const {
|
||||
actionBranch,
|
||||
base64Pack,
|
||||
repoSelection,
|
||||
queryFile,
|
||||
queryMetadata,
|
||||
controllerRepo,
|
||||
queryStartTime,
|
||||
language,
|
||||
} = await prepareRemoteQueryRun(this.cliServer, credentials, uri, progress, token);
|
||||
|
||||
if (querySubmission?.query) {
|
||||
const query = querySubmission.query;
|
||||
const queryId = this.createQueryId();
|
||||
const apiResponse = await runRemoteQueriesApiRequest(credentials, actionBranch, language, repoSelection, controllerRepo, base64Pack);
|
||||
|
||||
await this.prepareStorageDirectory(queryId);
|
||||
await this.storeJsonFile(queryId, 'query.json', query);
|
||||
|
||||
this.remoteQueryAddedEventEmitter.fire({ queryId, query });
|
||||
void commands.executeCommand('codeQL.monitorRemoteQuery', queryId, query);
|
||||
if (!apiResponse) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const workflowRunId = apiResponse.workflow_run_id;
|
||||
const repositoryCount = apiResponse.repositories_queried.length;
|
||||
const query = await buildRemoteQueryEntity(
|
||||
queryFile,
|
||||
queryMetadata,
|
||||
controllerRepo,
|
||||
queryStartTime,
|
||||
workflowRunId,
|
||||
language,
|
||||
repositoryCount
|
||||
);
|
||||
|
||||
const queryId = this.createQueryId();
|
||||
|
||||
await this.prepareStorageDirectory(queryId);
|
||||
await this.storeJsonFile(queryId, 'query.json', query);
|
||||
|
||||
this.remoteQueryAddedEventEmitter.fire({ queryId, query });
|
||||
void commands.executeCommand('codeQL.monitorRemoteQuery', queryId, query);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
public async monitorRemoteQuery(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CancellationToken, commands, Uri, window } from 'vscode';
|
||||
import { CancellationToken, Uri, window } from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as fs from 'fs-extra';
|
||||
@@ -16,22 +16,17 @@ import {
|
||||
import { Credentials } from '../authentication';
|
||||
import * as cli from '../cli';
|
||||
import { logger } from '../logging';
|
||||
import { getActionBranch, getRemoteControllerRepo, isVariantAnalysisLiveResultsEnabled, setRemoteControllerRepo } from '../config';
|
||||
import { getActionBranch, getRemoteControllerRepo, setRemoteControllerRepo } from '../config';
|
||||
import { ProgressCallback, UserCancellationException } from '../commandRunner';
|
||||
import { RequestError } from '@octokit/types/dist-types';
|
||||
import { RemoteQuery } from './remote-query';
|
||||
import { RemoteQuerySubmissionResult } from './remote-query-submission-result';
|
||||
import { QueryMetadata } from '../pure/interface-types';
|
||||
import { getErrorMessage, REPO_REGEX } from '../pure/helpers-pure';
|
||||
import { pluralize } from '../pure/word';
|
||||
import * as ghApiClient from './gh-api/gh-api-client';
|
||||
import { RemoteQueriesResponse } from './gh-api/remote-queries';
|
||||
import { getRepositorySelection, isValidSelection, RepositorySelection } from './repository-selection';
|
||||
import { parseVariantAnalysisQueryLanguage, VariantAnalysisSubmission } from './shared/variant-analysis';
|
||||
import { Repository } from './shared/repository';
|
||||
import { processVariantAnalysis } from './variant-analysis-processor';
|
||||
import { VariantAnalysisManager } from './variant-analysis-manager';
|
||||
import { CodeQLCliServer } from '../cli';
|
||||
|
||||
export interface QlPack {
|
||||
name: string;
|
||||
@@ -262,98 +257,7 @@ export async function prepareRemoteQueryRun(
|
||||
};
|
||||
}
|
||||
|
||||
export async function runRemoteQuery(
|
||||
cliServer: CodeQLCliServer,
|
||||
credentials: Credentials,
|
||||
uri: Uri | undefined,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
variantAnalysisManager: VariantAnalysisManager,
|
||||
): Promise<void | RemoteQuerySubmissionResult> {
|
||||
if (!(await cliServer.cliConstraints.supportsRemoteQueries())) {
|
||||
throw new Error(`Variant analysis is not supported by this version of CodeQL. Please upgrade to v${cli.CliVersionConstraint.CLI_VERSION_REMOTE_QUERIES
|
||||
} or later.`);
|
||||
}
|
||||
|
||||
const {
|
||||
actionBranch,
|
||||
base64Pack,
|
||||
repoSelection,
|
||||
queryFile,
|
||||
queryMetadata,
|
||||
controllerRepo,
|
||||
queryStartTime,
|
||||
language,
|
||||
} = await prepareRemoteQueryRun(cliServer, credentials, uri, progress, token);
|
||||
|
||||
if (isVariantAnalysisLiveResultsEnabled()) {
|
||||
const queryName = getQueryName(queryMetadata, queryFile);
|
||||
const variantAnalysisLanguage = parseVariantAnalysisQueryLanguage(language);
|
||||
if (variantAnalysisLanguage === undefined) {
|
||||
throw new UserCancellationException(`Found unsupported language: ${language}`);
|
||||
}
|
||||
|
||||
const queryText = await fs.readFile(queryFile, 'utf8');
|
||||
|
||||
const variantAnalysisSubmission: VariantAnalysisSubmission = {
|
||||
startTime: queryStartTime,
|
||||
actionRepoRef: actionBranch,
|
||||
controllerRepoId: controllerRepo.id,
|
||||
query: {
|
||||
name: queryName,
|
||||
filePath: queryFile,
|
||||
pack: base64Pack,
|
||||
language: variantAnalysisLanguage,
|
||||
text: queryText,
|
||||
},
|
||||
databases: {
|
||||
repositories: repoSelection.repositories,
|
||||
repositoryLists: repoSelection.repositoryLists,
|
||||
repositoryOwners: repoSelection.owners
|
||||
}
|
||||
};
|
||||
|
||||
const variantAnalysisResponse = await ghApiClient.submitVariantAnalysis(
|
||||
credentials,
|
||||
variantAnalysisSubmission
|
||||
);
|
||||
|
||||
const processedVariantAnalysis = processVariantAnalysis(variantAnalysisSubmission, variantAnalysisResponse);
|
||||
|
||||
await variantAnalysisManager.onVariantAnalysisSubmitted(processedVariantAnalysis);
|
||||
|
||||
void logger.log(`Variant analysis:\n${JSON.stringify(processedVariantAnalysis, null, 2)}`);
|
||||
|
||||
void showAndLogInformationMessage(`Variant analysis ${processedVariantAnalysis.query.name} submitted for processing`);
|
||||
|
||||
void commands.executeCommand('codeQL.openVariantAnalysisView', processedVariantAnalysis.id);
|
||||
void commands.executeCommand('codeQL.monitorVariantAnalysis', processedVariantAnalysis);
|
||||
|
||||
return { variantAnalysis: processedVariantAnalysis };
|
||||
} else {
|
||||
const apiResponse = await runRemoteQueriesApiRequest(credentials, actionBranch, language, repoSelection, controllerRepo, base64Pack);
|
||||
|
||||
if (!apiResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workflowRunId = apiResponse.workflow_run_id;
|
||||
const repositoryCount = apiResponse.repositories_queried.length;
|
||||
const remoteQuery = await buildRemoteQueryEntity(
|
||||
queryFile,
|
||||
queryMetadata,
|
||||
controllerRepo,
|
||||
queryStartTime,
|
||||
workflowRunId,
|
||||
language,
|
||||
repositoryCount);
|
||||
|
||||
// don't return the path because it has been deleted
|
||||
return { query: remoteQuery };
|
||||
}
|
||||
}
|
||||
|
||||
async function runRemoteQueriesApiRequest(
|
||||
export async function runRemoteQueriesApiRequest(
|
||||
credentials: Credentials,
|
||||
ref: string,
|
||||
language: string,
|
||||
@@ -455,7 +359,7 @@ async function ensureNameAndSuite(queryPackDir: string, packRelativePath: string
|
||||
await fs.writeFile(packPath, yaml.dump(qlpack));
|
||||
}
|
||||
|
||||
async function buildRemoteQueryEntity(
|
||||
export async function buildRemoteQueryEntity(
|
||||
queryFilePath: string,
|
||||
queryMetadata: QueryMetadata | undefined,
|
||||
controllerRepo: Repository,
|
||||
@@ -483,7 +387,7 @@ async function buildRemoteQueryEntity(
|
||||
};
|
||||
}
|
||||
|
||||
function getQueryName(queryMetadata: QueryMetadata | undefined, queryFilePath: string): string {
|
||||
export function getQueryName(queryMetadata: QueryMetadata | undefined, queryFilePath: string): string {
|
||||
// The query name is either the name as specified in the query metadata, or the file name.
|
||||
return queryMetadata?.name ?? path.basename(queryFilePath);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import * as ghApiClient from './gh-api/gh-api-client';
|
||||
import { CancellationToken, commands, EventEmitter, ExtensionContext, window } from 'vscode';
|
||||
import { CancellationToken, commands, EventEmitter, ExtensionContext, Uri, window } from 'vscode';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
import { Credentials } from '../authentication';
|
||||
import { VariantAnalysisMonitor } from './variant-analysis-monitor';
|
||||
import {
|
||||
isVariantAnalysisComplete,
|
||||
isVariantAnalysisComplete, parseVariantAnalysisQueryLanguage,
|
||||
VariantAnalysis,
|
||||
VariantAnalysisQueryLanguage,
|
||||
VariantAnalysisRepositoryTask,
|
||||
VariantAnalysisScannedRepository,
|
||||
VariantAnalysisScannedRepositoryDownloadStatus,
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
VariantAnalysisScannedRepositoryState
|
||||
VariantAnalysisScannedRepositoryState, VariantAnalysisSubmission
|
||||
} from './shared/variant-analysis';
|
||||
import { getErrorMessage } from '../pure/helpers-pure';
|
||||
import { VariantAnalysisView } from './variant-analysis-view';
|
||||
import { VariantAnalysisViewManager } from './variant-analysis-view-manager';
|
||||
import { VariantAnalysisResultsManager } from './variant-analysis-results-manager';
|
||||
import { getControllerRepo } from './run-remote-query';
|
||||
import { processUpdatedVariantAnalysis, processVariantAnalysisRepositoryTask } from './variant-analysis-processor';
|
||||
import { getControllerRepo, getQueryName, prepareRemoteQueryRun } from './run-remote-query';
|
||||
import {
|
||||
processUpdatedVariantAnalysis,
|
||||
processVariantAnalysis,
|
||||
processVariantAnalysisRepositoryTask
|
||||
} from './variant-analysis-processor';
|
||||
import PQueue from 'p-queue';
|
||||
import { createTimestampFile, showAndLogErrorMessage, showAndLogInformationMessage } from '../helpers';
|
||||
import * as fs from 'fs-extra';
|
||||
import { cancelVariantAnalysis } from './gh-api/gh-actions-api-client';
|
||||
import { ProgressCallback, UserCancellationException } from '../commandRunner';
|
||||
import { CodeQLCliServer } from '../cli';
|
||||
|
||||
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
|
||||
private static readonly REPO_STATES_FILENAME = 'repo_states.json';
|
||||
@@ -47,6 +53,7 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
||||
|
||||
constructor(
|
||||
private readonly ctx: ExtensionContext,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly storagePath: string,
|
||||
private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager
|
||||
) {
|
||||
@@ -58,6 +65,67 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
||||
this.variantAnalysisResultsManager.onResultLoaded(this.onRepoResultLoaded.bind(this));
|
||||
}
|
||||
|
||||
public async runVariantAnalysis(
|
||||
uri: Uri | undefined,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
): Promise<VariantAnalysis> {
|
||||
const credentials = await Credentials.initialize(this.ctx);
|
||||
|
||||
const {
|
||||
actionBranch,
|
||||
base64Pack,
|
||||
repoSelection,
|
||||
queryFile,
|
||||
queryMetadata,
|
||||
controllerRepo,
|
||||
queryStartTime,
|
||||
language,
|
||||
} = await prepareRemoteQueryRun(this.cliServer, credentials, uri, progress, token);
|
||||
|
||||
const queryName = getQueryName(queryMetadata, queryFile);
|
||||
const variantAnalysisLanguage = parseVariantAnalysisQueryLanguage(language);
|
||||
if (variantAnalysisLanguage === undefined) {
|
||||
throw new UserCancellationException(`Found unsupported language: ${language}`);
|
||||
}
|
||||
|
||||
const queryText = await fs.readFile(queryFile, 'utf8');
|
||||
|
||||
const variantAnalysisSubmission: VariantAnalysisSubmission = {
|
||||
startTime: queryStartTime,
|
||||
actionRepoRef: actionBranch,
|
||||
controllerRepoId: controllerRepo.id,
|
||||
query: {
|
||||
name: queryName,
|
||||
filePath: queryFile,
|
||||
pack: base64Pack,
|
||||
language: variantAnalysisLanguage,
|
||||
text: queryText,
|
||||
},
|
||||
databases: {
|
||||
repositories: repoSelection.repositories,
|
||||
repositoryLists: repoSelection.repositoryLists,
|
||||
repositoryOwners: repoSelection.owners
|
||||
}
|
||||
};
|
||||
|
||||
const variantAnalysisResponse = await ghApiClient.submitVariantAnalysis(
|
||||
credentials,
|
||||
variantAnalysisSubmission
|
||||
);
|
||||
|
||||
const processedVariantAnalysis = processVariantAnalysis(variantAnalysisSubmission, variantAnalysisResponse);
|
||||
|
||||
await this.onVariantAnalysisSubmitted(processedVariantAnalysis);
|
||||
|
||||
void showAndLogInformationMessage(`Variant analysis ${processedVariantAnalysis.query.name} submitted for processing`);
|
||||
|
||||
void commands.executeCommand('codeQL.openVariantAnalysisView', processedVariantAnalysis.id);
|
||||
void commands.executeCommand('codeQL.monitorVariantAnalysis', processedVariantAnalysis);
|
||||
|
||||
return processedVariantAnalysis;
|
||||
}
|
||||
|
||||
public async rehydrateVariantAnalysis(variantAnalysis: VariantAnalysis) {
|
||||
if (!(await this.variantAnalysisRecordExists(variantAnalysis.id))) {
|
||||
// In this case, the variant analysis was deleted from disk, most likely because
|
||||
@@ -165,7 +233,7 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
||||
this._onVariantAnalysisStatusUpdated.fire(variantAnalysis);
|
||||
}
|
||||
|
||||
public async onVariantAnalysisSubmitted(variantAnalysis: VariantAnalysis): Promise<void> {
|
||||
private async onVariantAnalysisSubmitted(variantAnalysis: VariantAnalysis): Promise<void> {
|
||||
await this.setVariantAnalysis(variantAnalysis);
|
||||
|
||||
await this.prepareStorageDirectory(variantAnalysis.id);
|
||||
|
||||
@@ -5,27 +5,20 @@ import { CancellationTokenSource, ExtensionContext, extensions, QuickPickItem, U
|
||||
import * as os from 'os';
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
import { QlPack, runRemoteQuery } from '../../../remote-queries/run-remote-query';
|
||||
import { Credentials } from '../../../authentication';
|
||||
import { QlPack } from '../../../remote-queries/run-remote-query';
|
||||
import { CliVersionConstraint, CodeQLCliServer } from '../../../cli';
|
||||
import { CodeQLExtensionInterface } from '../../../extension';
|
||||
import { setRemoteControllerRepo, setRemoteRepositoryLists } from '../../../config';
|
||||
import * as config from '../../../config';
|
||||
import { UserCancellationException } from '../../../commandRunner';
|
||||
import * as ghApiClient from '../../../remote-queries/gh-api/gh-api-client';
|
||||
import { lte } from 'semver';
|
||||
import {
|
||||
VariantAnalysis as VariantAnalysisApiResponse
|
||||
} from '../../../remote-queries/gh-api/variant-analysis';
|
||||
import { Repository } from '../../../remote-queries/gh-api/repository';
|
||||
import { VariantAnalysisStatus } from '../../../remote-queries/shared/variant-analysis';
|
||||
import { createMockApiResponse } from '../../factories/remote-queries/gh-api/variant-analysis-api-response';
|
||||
import { createMockExtensionContext } from '../../no-workspace';
|
||||
import { VariantAnalysisManager } from '../../../remote-queries/variant-analysis-manager';
|
||||
import { OutputChannelLogger } from '../../../logging';
|
||||
import { VariantAnalysisResultsManager } from '../../../remote-queries/variant-analysis-results-manager';
|
||||
import { RemoteQueriesSubmission } from '../../../remote-queries/shared/remote-queries';
|
||||
import { readBundledPack } from '../../utils/bundled-pack-helpers';
|
||||
import { RemoteQueriesManager } from '../../../remote-queries/remote-queries-manager';
|
||||
import { Credentials } from '../../../authentication';
|
||||
|
||||
describe('Remote queries', function() {
|
||||
const baseDir = path.join(__dirname, '../../../../src/vscode-tests/cli-integration');
|
||||
@@ -36,16 +29,13 @@ describe('Remote queries', function() {
|
||||
this.timeout(3 * 60 * 1000);
|
||||
|
||||
let cli: CodeQLCliServer;
|
||||
let credentials: Credentials = {} as unknown as Credentials;
|
||||
let cancellationTokenSource: CancellationTokenSource;
|
||||
let progress: sinon.SinonSpy;
|
||||
let showQuickPickSpy: sinon.SinonStub;
|
||||
let getRepositoryFromNwoStub: sinon.SinonStub;
|
||||
let liveResultsStub: sinon.SinonStub;
|
||||
let ctx: ExtensionContext;
|
||||
let logger: any;
|
||||
let variantAnalysisManager: VariantAnalysisManager;
|
||||
let variantAnalysisResultsManager: VariantAnalysisResultsManager;
|
||||
let remoteQueriesManager: RemoteQueriesManager;
|
||||
|
||||
// use `function` so we have access to `this`
|
||||
beforeEach(async function() {
|
||||
@@ -60,15 +50,13 @@ describe('Remote queries', function() {
|
||||
|
||||
ctx = createMockExtensionContext();
|
||||
logger = new OutputChannelLogger('test-logger');
|
||||
variantAnalysisResultsManager = new VariantAnalysisResultsManager(cli, logger);
|
||||
variantAnalysisManager = new VariantAnalysisManager(ctx, 'fake-storage-dir', variantAnalysisResultsManager);
|
||||
remoteQueriesManager = new RemoteQueriesManager(ctx, cli, 'fake-storage-dir', logger);
|
||||
|
||||
if (!(await cli.cliConstraints.supportsRemoteQueries())) {
|
||||
console.log(`Remote queries are not supported on CodeQL CLI v${CliVersionConstraint.CLI_VERSION_REMOTE_QUERIES
|
||||
}. Skipping this test.`);
|
||||
this.skip();
|
||||
}
|
||||
credentials = {} as unknown as Credentials;
|
||||
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
@@ -90,14 +78,19 @@ describe('Remote queries', function() {
|
||||
await setRemoteControllerRepo('github/vscode-codeql');
|
||||
await setRemoteRepositoryLists({ 'vscode-codeql': ['github/vscode-codeql'] });
|
||||
|
||||
liveResultsStub = sandbox.stub(config, 'isVariantAnalysisLiveResultsEnabled').returns(false);
|
||||
const mockCredentials = {
|
||||
getOctokit: () => Promise.resolve({
|
||||
request: undefined,
|
||||
})
|
||||
} as unknown as Credentials;
|
||||
sandbox.stub(Credentials, 'initialize').resolves(mockCredentials);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('when live results are not enabled', () => {
|
||||
describe('runRemoteQuery', () => {
|
||||
let mockSubmitRemoteQueries: sinon.SinonStub;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -110,7 +103,7 @@ describe('Remote queries', function() {
|
||||
it('should run a remote query that is part of a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-qlpack/in-pack.ql');
|
||||
|
||||
const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
const querySubmissionResult = await remoteQueriesManager.runRemoteQuery(fileUri, progress, cancellationTokenSource.token);
|
||||
expect(querySubmissionResult).to.be.ok;
|
||||
|
||||
expect(mockSubmitRemoteQueries).to.have.been.calledOnce;
|
||||
@@ -155,7 +148,7 @@ describe('Remote queries', function() {
|
||||
it('should run a remote query that is not part of a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-no-qlpack/in-pack.ql');
|
||||
|
||||
const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
const querySubmissionResult = await remoteQueriesManager.runRemoteQuery(fileUri, progress, cancellationTokenSource.token);
|
||||
expect(querySubmissionResult).to.be.ok;
|
||||
|
||||
expect(mockSubmitRemoteQueries).to.have.been.calledOnce;
|
||||
@@ -203,7 +196,7 @@ describe('Remote queries', function() {
|
||||
it('should run a remote query that is nested inside a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-qlpack-nested/subfolder/in-pack.ql');
|
||||
|
||||
const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
const querySubmissionResult = await remoteQueriesManager.runRemoteQuery(fileUri, progress, cancellationTokenSource.token);
|
||||
expect(querySubmissionResult).to.be.ok;
|
||||
|
||||
expect(mockSubmitRemoteQueries).to.have.been.calledOnce;
|
||||
@@ -250,72 +243,7 @@ describe('Remote queries', function() {
|
||||
it('should cancel a run before uploading', async () => {
|
||||
const fileUri = getFile('data-remote-no-qlpack/in-pack.ql');
|
||||
|
||||
const promise = runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
|
||||
cancellationTokenSource.cancel();
|
||||
|
||||
try {
|
||||
await promise;
|
||||
assert.fail('should have thrown');
|
||||
} catch (e) {
|
||||
expect(e).to.be.instanceof(UserCancellationException);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('when live results are enabled', () => {
|
||||
let mockApiResponse: VariantAnalysisApiResponse;
|
||||
let mockSubmitVariantAnalysis: sinon.SinonStub;
|
||||
|
||||
beforeEach(() => {
|
||||
liveResultsStub.returns(true);
|
||||
mockApiResponse = createMockApiResponse('in_progress');
|
||||
mockSubmitVariantAnalysis = sandbox.stub(ghApiClient, 'submitVariantAnalysis').resolves(mockApiResponse);
|
||||
});
|
||||
|
||||
it('should run a variant analysis that is part of a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-qlpack/in-pack.ql');
|
||||
|
||||
const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
expect(querySubmissionResult).to.be.ok;
|
||||
const variantAnalysis = querySubmissionResult!.variantAnalysis!;
|
||||
expect(variantAnalysis.id).to.be.equal(mockApiResponse.id);
|
||||
expect(variantAnalysis.status).to.be.equal(VariantAnalysisStatus.InProgress);
|
||||
|
||||
expect(getRepositoryFromNwoStub).to.have.been.calledOnce;
|
||||
expect(mockSubmitVariantAnalysis).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should run a remote query that is not part of a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-no-qlpack/in-pack.ql');
|
||||
|
||||
const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
expect(querySubmissionResult).to.be.ok;
|
||||
const variantAnalysis = querySubmissionResult!.variantAnalysis!;
|
||||
expect(variantAnalysis.id).to.be.equal(mockApiResponse.id);
|
||||
expect(variantAnalysis.status).to.be.equal(VariantAnalysisStatus.InProgress);
|
||||
|
||||
expect(getRepositoryFromNwoStub).to.have.been.calledOnce;
|
||||
expect(mockSubmitVariantAnalysis).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should run a remote query that is nested inside a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-qlpack-nested/subfolder/in-pack.ql');
|
||||
|
||||
const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
expect(querySubmissionResult).to.be.ok;
|
||||
const variantAnalysis = querySubmissionResult!.variantAnalysis!;
|
||||
expect(variantAnalysis.id).to.be.equal(mockApiResponse.id);
|
||||
expect(variantAnalysis.status).to.be.equal(VariantAnalysisStatus.InProgress);
|
||||
|
||||
expect(getRepositoryFromNwoStub).to.have.been.calledOnce;
|
||||
expect(mockSubmitVariantAnalysis).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should cancel a run before uploading', async () => {
|
||||
const fileUri = getFile('data-remote-no-qlpack/in-pack.ql');
|
||||
|
||||
const promise = runRemoteQuery(cli, credentials, fileUri, progress, cancellationTokenSource.token, variantAnalysisManager);
|
||||
const promise = remoteQueriesManager.runRemoteQuery(fileUri, progress, cancellationTokenSource.token);
|
||||
|
||||
cancellationTokenSource.cancel();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { CancellationTokenSource, commands, extensions } from 'vscode';
|
||||
import { assert, expect } from 'chai';
|
||||
import { CancellationTokenSource, commands, extensions, QuickPickItem, Uri, window } from 'vscode';
|
||||
import { CodeQLExtensionInterface } from '../../../extension';
|
||||
import { logger } from '../../../logging';
|
||||
import * as config from '../../../config';
|
||||
@@ -11,7 +11,7 @@ import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
import { VariantAnalysisManager } from '../../../remote-queries/variant-analysis-manager';
|
||||
import { CodeQLCliServer } from '../../../cli';
|
||||
import { CliVersionConstraint, CodeQLCliServer } from '../../../cli';
|
||||
import { storagePath } from '../global.helper';
|
||||
import { VariantAnalysisResultsManager } from '../../../remote-queries/variant-analysis-results-manager';
|
||||
import { createMockVariantAnalysis } from '../../factories/remote-queries/shared/variant-analysis';
|
||||
@@ -25,13 +25,21 @@ import {
|
||||
} from '../../../remote-queries/shared/variant-analysis';
|
||||
import { createTimestampFile } from '../../../helpers';
|
||||
import { createMockVariantAnalysisRepoTask } from '../../factories/remote-queries/gh-api/variant-analysis-repo-task';
|
||||
import { VariantAnalysisRepoTask } from '../../../remote-queries/gh-api/variant-analysis';
|
||||
import {
|
||||
VariantAnalysis as VariantAnalysisApiResponse,
|
||||
VariantAnalysisRepoTask
|
||||
} from '../../../remote-queries/gh-api/variant-analysis';
|
||||
import { createMockApiResponse } from '../../factories/remote-queries/gh-api/variant-analysis-api-response';
|
||||
import { UserCancellationException } from '../../../commandRunner';
|
||||
import { Repository } from '../../../remote-queries/gh-api/repository';
|
||||
import { setRemoteControllerRepo, setRemoteRepositoryLists } from '../../../config';
|
||||
|
||||
describe('Variant Analysis Manager', async function() {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let pathExistsStub: sinon.SinonStub;
|
||||
let readJsonStub: sinon.SinonStub;
|
||||
let outputJsonStub: sinon.SinonStub;
|
||||
let writeFileStub: sinon.SinonStub;
|
||||
let cli: CodeQLCliServer;
|
||||
let cancellationTokenSource: CancellationTokenSource;
|
||||
let variantAnalysisManager: VariantAnalysisManager;
|
||||
@@ -46,7 +54,7 @@ describe('Variant Analysis Manager', async function() {
|
||||
sandbox.stub(logger, 'log');
|
||||
sandbox.stub(config, 'isVariantAnalysisLiveResultsEnabled').returns(false);
|
||||
sandbox.stub(fs, 'mkdirSync');
|
||||
sandbox.stub(fs, 'writeFile');
|
||||
writeFileStub = sandbox.stub(fs, 'writeFile');
|
||||
pathExistsStub = sandbox.stub(fs, 'pathExists').callThrough();
|
||||
readJsonStub = sandbox.stub(fs, 'readJson').callThrough();
|
||||
outputJsonStub = sandbox.stub(fs, 'outputJson');
|
||||
@@ -63,7 +71,7 @@ describe('Variant Analysis Manager', async function() {
|
||||
const extension = await extensions.getExtension<CodeQLExtensionInterface | Record<string, never>>('GitHub.vscode-codeql')!.activate();
|
||||
cli = extension.cliServer;
|
||||
variantAnalysisResultsManager = new VariantAnalysisResultsManager(cli, logger);
|
||||
variantAnalysisManager = new VariantAnalysisManager(extension.ctx, storagePath, variantAnalysisResultsManager);
|
||||
variantAnalysisManager = new VariantAnalysisManager(extension.ctx, cli, storagePath, variantAnalysisResultsManager);
|
||||
} catch (e) {
|
||||
fail(e as Error);
|
||||
}
|
||||
@@ -73,6 +81,108 @@ describe('Variant Analysis Manager', async function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('runVariantAnalysis', function() {
|
||||
// up to 3 minutes per test
|
||||
this.timeout(3 * 60 * 1000);
|
||||
|
||||
let progress: sinon.SinonSpy;
|
||||
let showQuickPickSpy: sinon.SinonStub;
|
||||
let mockGetRepositoryFromNwo: sinon.SinonStub;
|
||||
let mockSubmitVariantAnalysis: sinon.SinonStub;
|
||||
let mockApiResponse: VariantAnalysisApiResponse;
|
||||
|
||||
const baseDir = path.join(__dirname, '../../../../src/vscode-tests/cli-integration');
|
||||
function getFile(file: string): Uri {
|
||||
return Uri.file(path.join(baseDir, file));
|
||||
}
|
||||
|
||||
beforeEach(async function() {
|
||||
if (!(await cli.cliConstraints.supportsRemoteQueries())) {
|
||||
console.log(`Remote queries are not supported on CodeQL CLI v${CliVersionConstraint.CLI_VERSION_REMOTE_QUERIES
|
||||
}. Skipping this test.`);
|
||||
this.skip();
|
||||
}
|
||||
|
||||
writeFileStub.callThrough();
|
||||
|
||||
progress = sandbox.spy();
|
||||
// Should not have asked for a language
|
||||
showQuickPickSpy = sandbox.stub(window, 'showQuickPick')
|
||||
.onFirstCall().resolves({ repositories: ['github/vscode-codeql'] } as unknown as QuickPickItem)
|
||||
.onSecondCall().resolves('javascript' as unknown as QuickPickItem);
|
||||
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
const dummyRepository: Repository = {
|
||||
id: 123,
|
||||
name: 'vscode-codeql',
|
||||
full_name: 'github/vscode-codeql',
|
||||
private: false,
|
||||
};
|
||||
mockGetRepositoryFromNwo = sandbox.stub(ghApiClient, 'getRepositoryFromNwo').resolves(dummyRepository);
|
||||
|
||||
mockApiResponse = createMockApiResponse('in_progress');
|
||||
mockSubmitVariantAnalysis = sandbox.stub(ghApiClient, 'submitVariantAnalysis').resolves(mockApiResponse);
|
||||
|
||||
// always run in the vscode-codeql repo
|
||||
await setRemoteControllerRepo('github/vscode-codeql');
|
||||
await setRemoteRepositoryLists({ 'vscode-codeql': ['github/vscode-codeql'] });
|
||||
});
|
||||
|
||||
it('should run a variant analysis that is part of a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-qlpack/in-pack.ql');
|
||||
|
||||
const variantAnalysis = await variantAnalysisManager.runVariantAnalysis(fileUri, progress, cancellationTokenSource.token);
|
||||
expect(variantAnalysis).to.be.ok;
|
||||
expect(variantAnalysis.id).to.be.equal(mockApiResponse.id);
|
||||
expect(variantAnalysis.status).to.be.equal(VariantAnalysisStatus.InProgress);
|
||||
|
||||
expect(showQuickPickSpy).to.have.been.calledOnce;
|
||||
|
||||
expect(mockGetRepositoryFromNwo).to.have.been.calledOnce;
|
||||
expect(mockSubmitVariantAnalysis).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should run a remote query that is not part of a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-no-qlpack/in-pack.ql');
|
||||
|
||||
const variantAnalysis = await variantAnalysisManager.runVariantAnalysis(fileUri, progress, cancellationTokenSource.token);
|
||||
expect(variantAnalysis).to.be.ok;
|
||||
expect(variantAnalysis.id).to.be.equal(mockApiResponse.id);
|
||||
expect(variantAnalysis.status).to.be.equal(VariantAnalysisStatus.InProgress);
|
||||
|
||||
expect(mockGetRepositoryFromNwo).to.have.been.calledOnce;
|
||||
expect(mockSubmitVariantAnalysis).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should run a remote query that is nested inside a qlpack', async () => {
|
||||
const fileUri = getFile('data-remote-qlpack-nested/subfolder/in-pack.ql');
|
||||
|
||||
const variantAnalysis = await variantAnalysisManager.runVariantAnalysis(fileUri, progress, cancellationTokenSource.token);
|
||||
expect(variantAnalysis).to.be.ok;
|
||||
expect(variantAnalysis.id).to.be.equal(mockApiResponse.id);
|
||||
expect(variantAnalysis.status).to.be.equal(VariantAnalysisStatus.InProgress);
|
||||
|
||||
expect(mockGetRepositoryFromNwo).to.have.been.calledOnce;
|
||||
expect(mockSubmitVariantAnalysis).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should cancel a run before uploading', async () => {
|
||||
const fileUri = getFile('data-remote-no-qlpack/in-pack.ql');
|
||||
|
||||
const promise = variantAnalysisManager.runVariantAnalysis(fileUri, progress, cancellationTokenSource.token);
|
||||
|
||||
cancellationTokenSource.cancel();
|
||||
|
||||
try {
|
||||
await promise;
|
||||
assert.fail('should have thrown');
|
||||
} catch (e) {
|
||||
expect(e).to.be.instanceof(UserCancellationException);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('rehydrateVariantAnalysis', () => {
|
||||
const variantAnalysis = createMockVariantAnalysis({});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user